Какой хороший, оптимизированный алгоритм на C/C++ для преобразования 24-битного растрового изображения в 16 бит с дизерингом?
Я искал оптимизированный (т. е. быстрый) алгоритм, который преобразует 24-битную растровую карту RGB в 16-битную (RGB565) растровую карту с помощью сглаживания. Я ищу что-то в C/C++, где я могу контролировать то, как применяется дизеринг. GDI+, похоже, предоставляет некоторые методы, но я не могу сказать, дрожат они или нет. И, если они действительно дрожат, какой механизм они используют (Флойд-Штейнберг?)
У кого-нибудь есть хороший пример преобразования глубины цвета растрового изображения с дизеринг?
3 ответов
Как вы упомянули, метод Флойда-Штейнберга популярен, потому что он прост и быстр. Для тонких различий между 24-битным и 16-битным цветом результаты будут почти оптимальными визуально.
Я предложил использовать заказал дизеринг (http://en.wikipedia.org/wiki/Ordered_dithering), поскольку Флойд-Штейнберг нужно больше обработки и вычисление и работает только на неподвижном изображении / не работает хорошо для анимированных или постоянных изменений на дисплее.
Я создал свой собственный оптимизированный упорядоченный сглаживание от 24 / 32bit RGB цвета до 16bit RGB565 цвета, что отдельный tresshold в субпиксель (используется в my аромат проект). Это было намного быстрее, чем Флойд-Штейнберг, потому что нет дорогостоящих вычислений (особенно нет умножает и divs расчеты), и может использоваться на анимации, потому что он использовал фиксированный tresshold.
это качество также намного лучше, чем упорядоченный алгоритм смешивания, определенный на wiki.
вот пример результата смешивания:
и вот источник. Наслаждайтесь!
/* Dither Tresshold for Red Channel */
static const BYTE dither_tresshold_r[64] = {
1, 7, 3, 5, 0, 8, 2, 6,
7, 1, 5, 3, 8, 0, 6, 2,
3, 5, 0, 8, 2, 6, 1, 7,
5, 3, 8, 0, 6, 2, 7, 1,
0, 8, 2, 6, 1, 7, 3, 5,
8, 0, 6, 2, 7, 1, 5, 3,
2, 6, 1, 7, 3, 5, 0, 8,
6, 2, 7, 1, 5, 3, 8, 0
};
/* Dither Tresshold for Green Channel */
static const BYTE dither_tresshold_g[64] = {
1, 3, 2, 2, 3, 1, 2, 2,
2, 2, 0, 4, 2, 2, 4, 0,
3, 1, 2, 2, 1, 3, 2, 2,
2, 2, 4, 0, 2, 2, 0, 4,
1, 3, 2, 2, 3, 1, 2, 2,
2, 2, 0, 4, 2, 2, 4, 0,
3, 1, 2, 2, 1, 3, 2, 2,
2, 2, 4, 0, 2, 2, 0, 4
};
/* Dither Tresshold for Blue Channel */
static const BYTE dither_tresshold_b[64] = {
5, 3, 8, 0, 6, 2, 7, 1,
3, 5, 0, 8, 2, 6, 1, 7,
8, 0, 6, 2, 7, 1, 5, 3,
0, 8, 2, 6, 1, 7, 3, 5,
6, 2, 7, 1, 5, 3, 8, 0,
2, 6, 1, 7, 3, 5, 0, 8,
7, 1, 5, 3, 8, 0, 6, 2,
1, 7, 3, 5, 0, 8, 2, 6
};
/* Get 16bit closest color */
BYTE closest_rb(BYTE c) {
return (c >> 3 << 3); /* red & blue */
}
BYTE closest_g(BYTE c) {
return (c >> 2 << 2); /* green */
}
/* RGB565 */
WORD RGB16BIT(BYTE r, BYTE g, BYTE b) {
return ((WORD)((r>>3)<<11)|((g>>2)<<5)|(b>>3));
}
/* Dithering by individual subpixel */
WORD dither_xy(
int x,
int y,
BYTE r,
BYTE g,
BYTE b
){
/* Get Tresshold Index */
BYTE tresshold_id = ((y & 7) << 3) + (x & 7);
r = closest_rb(
MIN(r + dither_tresshold_r[tresshold_id], 0xff)
);
g = closest_g(
MIN(g + dither_tresshold_g[tresshold_id], 0xff)
);
b = closest_rb(
MIN(b + dither_tresshold_b[tresshold_id], 0xff)
);
return RGB16BIT(r, g, b);
}
/* Dithering Pixel from 32/24bit RGB
*
* GetR, GetG, GetB -> Function to get individual color in pixel
*
*/
WORD dither_color_xy(int x, int y, DWORD col) {
return dither_xy(x, y, GetR(col), GetG(col), GetB(col));
}
/* EXAMPLES */
void ExampleDither1(WORD * dest, DWORD * src, int width, int height){
int x, y;
for (y=0; y<height; y++){
for (x=0; x<width; x++){
int pos = y * width + x;
dest[pos] = dither_color_xy(x,y,src[pos]);
}
}
}
void ExampleDither2(WORD * dest, BYTE * src, int width, int height){
int x, y;
for (y=0; y<height; y++){
for (x=0; x<width; x++){
int pos = y * width + x;
dest[pos] = dither_xy(x,y,src[pos*3],src[pos*3+1],src[pos*3+2]);
}
}
}
другой результат (верхний 24bit-Нижний приказал RGB565-16bit): Просмотр изображения в полном разрешении
Флойда–Стейнберга сглаживание
for each y from top to bottom
for each x from left to right
oldpixel := pixel[x][y]
newpixel := find_closest_palette_color(oldpixel)
pixel[x][y] := newpixel
quant_error := oldpixel - newpixel
pixel[x+1][y] := pixel[x+1][y] + 7/16 * quant_error
pixel[x-1][y+1] := pixel[x-1][y+1] + 3/16 * quant_error
pixel[x][y+1] := pixel[x][y+1] + 5/16 * quant_error
pixel[x+1][y+1] := pixel[x+1][y+1] + 1/16 * quant_error
Я ставлю доллар, что вы можете легко реализовать это!