LINUX.ORG.RU

Помогите с алгоритмом


0

0

Написал программу, фильтр для изображений (получаются рисунки простым карандашом). Алгоритм такой: если каждый пиксель достаточно сильно отличается от следующего (это отличие указывается с помощью value), то ставится серый пиксель, яркость которого определяется разницей цвета и коэффициентом яркости (brightness_quotient). Но некоторые картинки получаются слишком плохо. На самом деле алгоритма три, но они очень похожи, и результаты не всегда заметно отличаются. Хотелось бы у предложения по усовершенствованию алгоритма.

// pencil.c

#include <stdio.h>
#include <getopt.h>
#include <unistd.h>
#include <stdlib.h>
#include "bmp.h"

#define _GNU_SOURCE
#define DEFAULT 0
#define ADDITION 1
#define MULTIPLICATION 2


BMPHEADER bmp;
char *bits;

struct option long_options[] = {
{"algorithm", 1, 0, 0},
{"value", 1, 0, 0},
{"help", 0, 0, 0},
{"version", 0, 0, 0},
{"direction", 1, 0, 0},
{"brightness", 1, 0, 0},
{0, 0, 0, 0}
};

char algorithm=0;
int value=0;
char *ofile;
int direction=2;
float brightness_quotient=1.0;

int myabs(int val)
{
if (val < 0) return -val; else return val;
}

void help(char *argv0)
{
printf("%s options input_file\n", argv0);
printf("Available options:\n");
printf("\t--algorithm,-a [name] - use optional algorithm from following list:\n");
printf("\t\t0 = default\n");
printf("\t\t1 = addition\n");
printf("\t\t2 = multiplication\n");
printf("\t--value,-v [val]\n");
printf("\t--direction,-d [dir] - possible values:\n");
printf("\t\t0 = horizontal\n");
printf("\t\t1 = vertical\n");
printf("\t\t[other value] = horizontal and vertical\n");
printf("\t--brightness,-b - brightness quotient (default value is 1.0)\n");
printf("\t--help,-h - show help\n");
printf("\t--version - show version\n");
printf("\t-o [file]\n");
exit(0);
}

void version()
{
printf("Pencil version 0.1\n");
exit(0);
}

void check_options(int argc, char **argv)
{
int n;
while(1)
{
int c = getopt_long(argc, argv, "a:v:ho:d:b:", long_options, &n);
if (c == -1) break;
switch(c)
{
case 0:
switch(n)
{
case 0:
algorithm = atoi(optarg);
break;
case 1:
value = atoi(optarg);
break;
case 2:
help(argv[0]);
break;
case 3:
version();
break;
case 4:
direction = atoi(optarg);
break;
case 5:
brightness_quotient = atof(optarg);
break;
}
case 'v':
value = atoi(optarg);
break;
case 'h':
help(argv[0]);
break;
case 'o':
ofile = optarg;
break;
case 'a':
algorithm = atoi(optarg);
break;
case 'd':
direction = atoi(optarg);
break;
case 'b':
brightness_quotient = atof(optarg);
break;
}
}
}

int main(int argc, char **argv)
{
check_options(argc, argv);
if (value == 0 || ofile == NULL || optind >= argc)
{
printf("wrong options\n");
exit(1);
}
if (load_bitmap(argv[argc-1], &bmp) == INVALID_BITMAP)
{
printf("INVALID_BITMAP\n");
exit(2);
}
bits = (char *)malloc(bmp.info.biWidth*bmp.info.biHeight*3);
int i, j;
if (direction != 1)
for(i = 0; i < bmp.info.biHeight; i++)
{
for(j = 0; j < bmp.info.biWidth-1; j++)
{
unsigned char rr, gg, bb;
bb = myabs(bmp.bits[(j*bmp.info.biHeight+i)*3] - bmp.bits[(j*bmp.info.biHeight+i+1)*3]);
gg = myabs(bmp.bits[(j*bmp.info.biHeight+i)*3+1] - bmp.bits[(j*bmp.info.biHeight+i+1)*3 + 1]);
rr = myabs(bmp.bits[(j*bmp.info.biHeight+i)*3+2] - bmp.bits[(j*bmp.info.biHeight+i+1)*3 + 2]);
switch(algorithm)
{
case DEFAULT:
if (rr > value || gg > value || bb > value)
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = (int)(brightness_quotient*((float)rr+(float)gg+(float)bb)*9.0);
else
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = 255;
break;
case ADDITION:
if ((rr + gg + bb) > value)
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = (int)(brightness_quotient*((float)rr+(float)gg+(float)bb)*9.0);
else
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = 255;
break;
case MULTIPLICATION:
if (rr*gg*bb > value)
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = (int)(brightness_quotient*((float)rr+(float)gg+(float)bb)*9.0);
else
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = 255;
break;
}
}
}
if (direction != 0)
for(j = 0; j < bmp.info.biWidth; j++)
{
for(i = 0; i < bmp.info.biHeight-1; i++)
{
unsigned char rr, gg, bb;
bb = myabs(bmp.bits[(j*bmp.info.biHeight+i)*3] - bmp.bits[(j*bmp.info.biHeight+i+bmp.info.biWidth)*3]);
gg = myabs(bmp.bits[(j*bmp.info.biHeight+i)*3+1] - bmp.bits[(j*bmp.info.biHeight+i+bmp.info.biWidth)*3 + 1]);
rr = myabs(bmp.bits[(j*bmp.info.biHeight+i)*3+2] - bmp.bits[(j*bmp.info.biHeight+i+bmp.info.biWidth)*3 + 2]);
switch(algorithm)
{
case DEFAULT:
if (rr > value || gg > value || bb > value)
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = (int)(brightness_quotient*((float)rr+(float)gg+(float)bb)*9.0);
break;
case ADDITION:
if ((rr + gg + bb) > value)
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = (int)(brightness_quotient*((float)rr+(float)gg+(float)bb)*9.0);
break;
case MULTIPLICATION:
if (rr*gg*bb > value)
bits[(j*bmp.info.biHeight+i)*3] = bits[(j*bmp.info.biHeight+i)*3+1] = bits[(j*bmp.info.biHeight+i)*3+2] = (int)(brightness_quotient*((float)rr+(float)gg+(float)bb)*9.0);
break;
}
}
}
bmp_writestd(ofile, bits, bmp.info.biWidth, bmp.info.biHeight);
return 0;
}

★★

// bmp.c

#include "bmp.h"

int save_bitmap(char *filename, BMPHEADER hd)
{
FILE *f = fopen(filename, "wb");
if (f == NULL) return BMP_CANTOPEN;
fwrite(&hd.header.bfType, 2, 1, f);
fwrite(&hd.header.bfSize, 4, 1, f);
fwrite(&hd.header.bfReserved1, 2, 1, f);
fwrite(&hd.header.bfReserved2, 2, 1, f);
fwrite(&hd.header.bfOffBits, 4, 1, f);
fwrite(&hd.info.biSize, 4, 1, f);
fwrite(&hd.info.biWidth, 4, 1, f);
fwrite(&hd.info.biHeight, 4, 1, f);
fwrite(&hd.info.biPlanes, 2, 1, f);
fwrite(&hd.info.biBitCount, 2, 1, f);
fwrite(&hd.info.biCompression, 4, 1, f);
fwrite(&hd.info.biSizeImage, 4, 1, f);
fwrite(&hd.info.biXPelsPerMeter, 4, 1, f);
fwrite(&hd.info.biYPelsPerMeter, 4, 1, f);
fwrite(&hd.info.biClrUsed, 4, 1, f);
fwrite(&hd.info.biClrImportant, 4, 1, f);
int i;
for(i = 0; i < hd.info.biWidth*hd.info.biHeight*3; i++) fwrite(&hd.bits[i], 1, 1, f);
fclose(f);
return 0;
}

int bmp_writestd(char *filename, char *bits, int width, int height)
{
BMPHEADER hd;
hd.header.bfType = 'MB';
hd.header.bfSize = width*height*3 + 54;
hd.header.bfReserved1 = hd.header.bfReserved2 = 0;
hd.header.bfOffBits = 54;
hd.info.biSize = 40;
hd.info.biWidth = width;
hd.info.biHeight = height;
hd.info.biPlanes = 1;
hd.info.biBitCount = 24;
hd.info.biCompression = 0;
hd.info.biSizeImage = width*height*3;
hd.info.biXPelsPerMeter = hd.info.biYPelsPerMeter = 2834;
hd.info.biClrUsed = 0;
hd.info.biClrImportant = 0;
hd.bits = bits;
int i = save_bitmap(filename, hd);
return i;
}

int load_bitmap(char *filename, BMPHEADER *hd)
{
FILE *f = fopen(filename, "rb");
if (f == NULL) return BMP_CANTOPEN;
fread(&hd->header.bfType, 2, 1, f);
if (hd->header.bfType != 'MB') return INVALID_BITMAP;
fread(&hd->header.bfSize, 4, 1, f);
fread(&hd->header.bfReserved1, 2, 1, f);
fread(&hd->header.bfReserved2, 2, 1, f);
fread(&hd->header.bfOffBits, 4, 1, f);
fread(&hd->info.biSize, 4, 1, f);
fread(&hd->info.biWidth, 4, 1, f);
fread(&hd->info.biHeight, 4, 1, f);
fread(&hd->info.biPlanes, 2, 1, f);
fread(&hd->info.biBitCount, 2, 1, f);
fread(&hd->info.biCompression, 4, 1, f);
fread(&hd->info.biSizeImage, 4, 1, f);
fread(&hd->info.biXPelsPerMeter, 4, 1, f);
fread(&hd->info.biYPelsPerMeter, 4, 1, f);
fread(&hd->info.biClrUsed, 4, 1, f);
fread(&hd->info.biClrImportant, 4, 1, f);
unsigned char *bits;
bits = malloc(hd->info.biWidth*hd->info.biHeight*3);
int i;
fseek(f, hd->header.bfOffBits, 0);
for(i = 0; i < hd->info.biWidth*hd->info.biHeight*3; i++) fread(&bits[i], 1, 1, f);
hd->bits = bits;
fclose(f);
return 0;
}

drish ★★
() автор топика
Ответ на: комментарий от drish

// bmp.h

#ifndef _BMP_H_

#define _BMP_H_

#include <stdio.h>
#include <stdlib.h>

#define BMP_CANTOPEN 0x111
#define INVALID_BITMAP 0x98

typedef struct
{
unsigned short bfType; /* must always be set to 'BM' (19778) */
unsigned int bfSize; /* the size of the file */
unsigned short bfReserved1; /* always 0 */
unsigned short bfReserved2; /* always 0 */
unsigned int bfOffBits; /* offset to the bitmap (std 1078) */
} BITMAPFILEHEADER;

typedef struct
{
unsigned int biSize; /* size of that (std 40) */
int biWidth; /* width of the image, in pixels */
int biHeight; /* height of the image */
unsigned short biPlanes; /* planes of the target device */
unsigned short biBitCount; /* bits per pixel */
unsigned int biCompression; /* type of compression */
unsigned int biSizeImage; /* the size of image data (if there is no compression, 0) */
int biXPelsPerMeter; /* horizontal pixels per meter */
int biYPelsPerMeter; /* vertical pixels per meter */
unsigned int biClrUsed; /* the number of colors */
unsigned int biClrImportant; /* if 0, all colors are important */
} BITMAPINFOHEADER;

typedef struct
{
unsigned char rgbBlue; /* Blue part of color */
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved; /* always 0 */
} RGBQUAD;

typedef struct
{
BITMAPFILEHEADER header;
BITMAPINFOHEADER info;
unsigned char *bits;
} BMPHEADER;

#endif /* _BMP_H_ */

drish ★★
() автор топика

:-)
Прикольная прога.
Явно конечно не хватает единого направления этому карандашу - слишком как-то разрознено получается.. Думается шире смотреть надо.. Глубже анализ изображения производить..

unDEFER ★★★★★
()
Ответ на: комментарий от drish

Хотите конкретнее? Хорошо, сейчас будет :-)
Вот моё видение на эту проблему:
1) Одним проходом здесь никак не обойтись
2) Без ввода дополнительных, промежуточных массивов данных тоже

Теперь ещё более детализированно:
1) Берём вводим массив с элементами примерно следующего содержания:
направление, и разность цветов и может быть "ширина линии"
2) Первым проходом заполняем этот массив. Здесь сложнее всего будет найти направление - надо анализировать окрестности точки и находить направление по которому проходит "очертание" контура - находить касательную к точке контура.
3) Далее надо уже анализировать полученный массив данных - если для двух рядом стоящих точке получилось примерно одинаковое направление и разность цветов, то надо уж постораться чтобы линия уж явно соединила эти две точки.
4) По полученным данным уже провести линии.

Всё. Более конкретно описывать не буду - это уже реализация :-)
На самом деле сам бы с удовольствием занялся, поизголялся - но.. сессия..

Если что-нибудь у вас получиться то обязательно напишите - меня этот вопрос крайне интересует потому, что самому нужно нечто подобное - выделение однородных областей на изображении. И хотя реализация уже есть - всё же она тоже не совершенна..

unDEFER ★★★★★
()
Ответ на: комментарий от unDEFER

Надо будет попробовать... Только не сейчас, дела. Если что-нибудь получится, напишу. Всё-таки я не понял насчёт первого массива. Если он не битовый, то хотя бы как устроен?

drish ★★
() автор топика
Ответ на: комментарий от unDEFER

Ну, ты бы хоть screenshotы выложил куда-нибудь, что ли ;-)
Невооруженным глазом видно, что алгоритм анизотропен.

И потом, не совсем понятно, что мы хотим?
Превратить цветной рисунок в черно-белый?
Выделить "контур"?

Как говорит Линус "надо карабкаться на плечи гигантов" ..
можно почитать "Foley & Van Dam"

"Мы", вообще, чем по-жизни занимаемся? ...
подобные задачи уже решались (и решились).

Один из примеров, с кем сейчас работаем
http://isdc.unige.ch/index.cgi?Soft+astroroot
и им подобные ...

Mark

++

Завтра посмотрю что там у тебя получается "в упор"

carrot
()
Ответ на: комментарий от carrot

Мы хотим преобразовать картинку в рисунок простым карандашом.

drish ★★
() автор топика
Ответ на: комментарий от drish

> Всё-таки я не понял насчёт первого массива. Если он не битовый, то хотя бы как устроен?
Массив структур или структура массивов (просто нескольких массивов) - как вам больше нравиться, главное для каждой точки записывать нужные параметры:
1) Направление - может быть числом градусов от наперёд заданной оси или значением тангенса угла (y/x) или ещё чем-нибудь - как будет удобней
2) Разность цветов - вы же каким-то образом высчитываете каким цветом точку рисовать - я думаю этот параметр как раз подойдет
3) Ну а ширина линии, ясный пень, - число, но нужен ли этот параметр? Возможно, что нет - там где понадобиться толщина она и так получиться из нескольких рядом идущих линий (если конечно, всё правильно и грамотно сделать)

unDEFER ★★★★★
()
Ответ на: комментарий от unDEFER

> Мы хотим преобразовать картинку в рисунок простым карандашом.

Это просто КВН какой-то .. "физики против лириков" ;-)

1. Ты скажи че надо, лучше математически, на худой конец -
была картинка такая, стала такая.

2. Ладно, забудь про "Foley & Van Dam" (не надо скрипеть мозгами).
Полистай курс молодого бойца для PhotoShopa или Gimpa.
Можно ли сделать то, что ты хочешь с помощью Gimpa?

Mark

carrot
()
Ответ на: комментарий от unDEFER

А смысл указывать направление? Для этого тоже надо границу искать. Есть мысль сделать всё так же, но для нахождения границ использовать целые блоки.

drish ★★
() автор топика
Ответ на: комментарий от drish

> А смысл указывать направление? Для этого тоже надо границу искать.

Чтоб как раз выискивать одну непрерывистую линию - мне кажется так проще - а там смотрите сами..

unDEFER ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.