Размывание изображения: Как рассчитать ошибку квантования и ближайший цвет для реализации в алгоритме Флойда-Стейнбурга? - PullRequest
1 голос
/ 06 октября 2010

Я намерен отображать (4, 8 или 16 бит на канал - без альфа) изображения на 1-битном дисплее во встроенной системе.Изображения хранятся в кортежах RGB.Я намерен использовать Floyd-Steinburg, поскольку он выглядит достаточно хорошо, более чем достаточно быстр и лаконичен в коде.

В отношении статьи WikiPedia у меня есть два вопроса.

Какова будет наилучшая практика для выражения ближайшего цвета? Будет ли работать следующее?(игнорируйте, что я возвращаю структуру в c)

typedef rgb16_tag { unsigned short r, g, b } rgb16;

rgb16 nearest_1bit_colour(rgb16 p) {
    double c; rgb16 r;
    c  = ((double)(p.r + p.g + p.b + 3 * (1 << 15))) / ( 3.0 * (1 << 16));
    if (c>= 1.0) { 
       r.r = r.g = r.b = 1;
    } else {
       r.r = r.g = r.b = 0;
    }
    return r;
 }

и, Выражение ошибки квантования выполняется для каждого канала? то есть имеет ли это смысл?

rgb16 q, new, old, image[X][Y];
int x, y;

... /* (somewhere in the nested loops) */
    old = image[x][y];
    new = nearest_1bit_colour(old);

    /* Repeat the following for each colour channel seperately. */
    q.{r,g,b} = old.{r,g,b} - new.{r,g,b};

    image[x+1][y].{r,g,b}   = image[x+1][y].{r,g,b} + 7/16 *   q.{r,g,b}
    image[x-1][y+1].{r,g,b} = image[x-1][y+1].{r,g,b} + 3/16 * q.{r,g,b}
    image[x][y+1].{r,g,b}   = image[x][y+1].{r,g,b} + 5/16 *   q.{r,g,b}
    image[x+1][y+1].{r,g,b} = image[x+1][y+1].{r,g,b} + 1/16 * q.{r,g,b}

Ответы [ 4 ]

2 голосов
/ 06 октября 2010

Я видел два типичных подхода к измерению разницы между двумя цветами.Наиболее распространенный способ, вероятно, состоит в том, чтобы просто найти евклидово расстояние между ними через цветной куб:

float r = i.r - j.r;
float g = i.g - j.g;
float b = i.b - j.b;
float diff = sqrtf( r * r + g + g + b * b );

Другой способ - просто усреднить абсолютные различия, возможно, взвешивая яркость:

float diff = 0.30f * fabs( i.r - j.r ) +
             0.59f * fabs( i.g - j.g ) +
             0.11f * fabs( i.b - j.b );

Что касается вашего второго вопроса, да.Накапливайте ошибку отдельно в каждом канале.

Редактировать : Сначала неправильно прочитал и пропустил, что это было для двухуровневого дисплея.В этом случае я бы предложил использовать только яркость:

float luminance = 0.30f * p.r + 0.59f * p.g + 0.11f * p.b;
if ( luminance > 0.5f * channelMax ) {
     // white
} else {
     // black
}
1 голос
/ 06 октября 2010

Как чисто целочисленное решение (мой процессор не имеет FPU), я думаю, что это может сработать.

#include <limits.h>
#include <assert.h>

typedef struct rgb16_tag { unsigned short r,g,b; } rgb16;
typedef struct rgb32_tag { unsigned long  r,g,b; } rgb32;

#define LUMINESCENSE_CONSTANT (ULONG_MAX >> (CHAR_BIT * sizeof (unsigned short)))

static const rgb32 luminescence_multiplier = {
    LUMINESCENSE_CONSTANT * 0.30f,
    LUMINESCENSE_CONSTANT * 0.59f,
    LUMINESCENSE_CONSTANT * 0.11f
};

int black_or_white( rgb16 p ) {
    unsigned long luminescence;

    assert((  luminescence_multiplier.r
            + luminescence_multiplier.g
            + luminescence_multiplier.b) < LUMINESCENSE_CONSTANT);

    luminescence =   p.r * luminescence_multiplier.r
                   + p.g * luminescence_multiplier.g
                   + p.b * luminescence_multiplier.b;

    return (luminescence > ULONG_MAX/2);  /* 1 == white; */
}
1 голос
/ 06 октября 2010

Когда вы возвращаете значение rgb16 в nearest_1bit_colour и используете его для сравнения его с другими цветами, и вам нужно использовать белый и черный в качестве возвращаемых цветов, используйте 0 и 0xFFFF вместо 0 и 1 (это черный и очень темно-серый). Кроме того, я думаю, вы должны сравнить c с 0,5 вместо 1,0:

if (c >= 0.5) { 
   r.r = r.g = r.b = 0xFFFF;
} else {
   r.r = r.g = r.b = 0;
}

Кроме того, могут быть подводные камни с (не) подписью:

q.{r,g,b} = old.{r,g,b} - new.{r,g,b};

Это может стать отрицательным, поэтому q должен быть не типа rgb16, который, по-видимому, unsigned short, а типа short.

Конечно, весь код предназначен для 16-разрядных входных данных, для 4- или 8-разрядных входных данных вы должны изменить их (или просто преобразовать 4-разрядные и 8-разрядные данные в 16-разрядные, чтобы вы могли можно использовать тот же код).

0 голосов
/ 06 октября 2010

Есть несколько отличных техник полутонирования ЗДЕСЬ

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...