代码之家  ›  专栏  ›  技术社区  ›  Jamie

图像抖动:在Floyd Steinburg算法中,如何计算量化误差和最接近的颜色?

  •  1
  • Jamie  · 技术社区  · 15 年前

    我打算在嵌入式系统的1位显示器上显示(每个通道4、8或16位-无alpha)图像。图像存储在RGB元组中。我的意图是使用Floyd Steinburg,因为它看起来相当不错,在代码上足够快和简洁。

    参考维基百科 article ,我有两个问题。

    表达最接近的颜色的最佳实践是什么? 下面的方法行吗?(忽略我返回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;
     }
    

    而且, 量化误差的表达式是在每个信道的基础上完成的吗? i、 这有道理吗?

    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 回复  |  直到 15 年前
        1
  •  2
  •   Boojum    15 年前

    我见过两种典型的方法来测量两种颜色之间的差异。最常见的方法可能是通过颜色立方体找到它们之间的欧几里得距离:

    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
    }
    
        2
  •  1
  •   schnaader    15 年前

    当你返回一个 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位数据,以便您可以使用相同的代码)。

        3
  •  1
  •   Jamie    15 年前

    作为一个纯粹的基于整数的解决方案(我的处理器没有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; */
    }
    
        4
  •  0
  •   dawg    15 年前

    有一些很好的半色调技巧 HERE