【问题标题】:Small color defects when aplying mean filter on images在图像上应用均值过滤器时出现小的颜色缺陷
【发布时间】:2016-11-07 09:26:51
【问题描述】:

我一直在尝试做一个对图像应用均值过滤器的程序,我认为我已经接近正确地做到这一点,但图像中仍然存在一些小缺陷。例如:

原创赛车:http://s72.photobucket.com/user/john_smith140/media/gp4_zpstafhejk5.jpg.html?filters[user]=139318132&filters[recent]=1&sort=1&o=2

原始三角形:http://s72.photobucket.com/user/john_smith140/media/input_zpsz2cfhrc7.jpeg.html?filters[user]=139318132&filters[recent]=1&sort=1&o=3

改装赛车:http://s72.photobucket.com/user/john_smith140/media/racing_zpsmzmawjml.jpeg.html?filters[user]=139318132&filters[recent]=1&sort=1&o=0

修改后的三角形:http://s72.photobucket.com/user/john_smith140/media/triangles_zpsaretjfid.jpeg.html?filters[user]=139318132&filters[recent]=1&sort=1&o=1

黑底白点,原文:http://s72.photobucket.com/user/john_smith140/media/black%20background%20white%20dots_zpsuofaagnl.jpg.html?sort=3&o=2

黑底白点,同一个数组:http://s72.photobucket.com/user/john_smith140/media/one%20array_zpswteno2eb.jpg.html?sort=3&o=1

黑底白点,不同数组:http://s72.photobucket.com/user/john_smith140/media/two%20array_zpskbyjg97o.jpg.html?sort=3&o=0

我可以想到造成这些缺陷的两个原因。一个是算法本身,另一个是在将char转换为float然后再次float到char的过程中。

char到float的转换是必要的,因为ifstream的read函数读取char,然后我需要将每个乘以1/9,所以它需要是浮点数。然后转换回 char 以便 write 函数可以将其写回。

关于算法的一些解释。我从第二行的第二个像素开始计算颜色值,然后继续直到倒数第二行的倒数第二个像素。那是因为我使用的是 3x3 的内核,这样我就不会超出图像的限制(以及我存储它的 char 数组的限制)。对于 1024x768 的图像,它将具有 1024x768*3 的大小(3 个颜色分量)。所以它从位置开始:bitsPerPixel * image_width + bitsPerPixel 或 3*1024+ 3 = 4099,即 2° 行的 2° 像素。然后它将计算平均值,直到 2° 最后一行的 2° 最后一个像素,这应该是: imageSize - row_size - bitsPerPixel 或 (1024*768-3) - 1024*3 - 3。 在区间内它会计算char数组中每个位置的值,也就是说一个像素的每个颜色通道的值将由周围像素的颜色通道来计算。 代码如下:

int size2 = bpp*width;
float a1_9 = (1.0f/9.0f);
float tmp;
for (int i=size2+bpp; i<size-size2-bpp; i++) {
tmp = a1_9 * ((float) image [i-size2-bpp] + (float) image [i-size2] + (float) image [i-size2+bpp] + (float) image [i-bpp] + (float) image [i] + (float) image [i+bpp] + (float)image [i+size2-bpp] + (float) image [i+size2] + (float) image [i+size2+bpp]);
    image [i] = char (tmp);
    float temp = (float) image [i];
}

我打印了赛车截图一次交互的值,对应位置一百万的值,得到了这个:

Image values are: -56 -57 -57 9 -43 -41 108 108 109 
tmp it is: 8.88889
temp it is: 8

乍一看,这些值似乎是正确的(手头做平均数),所以我不太了解出了什么问题。任何帮助将不胜感激。

【问题讨论】:

  • 我看不出有缺陷和原创之间的区别。
  • 您可以尝试在任何地方使用unsigned char 而不是char。给懒人的提示:使用typedef unsigned char byte;。你也不要说imagecharunsigned char的数组,这可能很重要......
  • 鲁宾逊,有很多颜色缺陷。在赛车的大量紫色瑕疵和三角形中,不同颜色的圆线切割了三角形的一部分
  • 是的,它是一个char数组rodrigo,我必须使用char,因为ifstream的read成员函数只能使用char指针。
  • char 通常是有符号类型,从 -128 到 127 而不是 0 到 255。您需要先将其转换为 unsigned char,然后再将其转换为 float/int 和然后到最后unsigned char。或者,您可以将image 声明为unsigned char 的数组,并在调用ifstream 时将其转换为char*

标签: c++ image-processing rgb mean


【解决方案1】:

2 关于你的算法的想法:

1.) 您使用的是 RGB 颜色空间?那为什么你的图像值是负的?您也不必转换为浮点数。只需将整数值相加并除以 9。这要快得多,最后你还是将它转换回 char 所以它应该是相同的结果。

2.) 您在每个迭代步骤中覆盖您的图像,这意味着在过滤器模式中:

-------
|1|2|3|
-------
|4|5|6|
-------
|7|8|9|
-------

值 1、2、3 和 4 已经平滑,5 是根据 5 个未平滑像素和 4 个平滑像素计算得出的。 我建议创建一个新的空白图像并将结果(temp)存储在新图像中,同时从原始图像读取 - 即不要尝试使用输出图像处理 "in-place"与输入图像相同。

【讨论】:

  • 好答案 - 我希望你不介意我的小修改/澄清。
  • 呃,很多拼写错误:-D 现在好多了。谢谢
  • 我真的不明白为什么会出现负值。如果我将它们设为正值,或者将它们转换为零,那么图像就会完全错误。
  • 我最初是在制作 float a1_9 = 1/9,而不是在 a1_9 中存储 0 的 1.0f/9.0f。我认为这个问题与浮点数乘以一个字符有关,并做了所有这些转换工作来避免它。之后,我意识到这不是问题,而是以这种方式留下了代码。反正我把图片位置前面的所有浮点数都去掉了,把tmp改成char,但是并没有造成什么变化。
  • 我做了第二个数组的东西,在我上传的两张图片中我没有注意到差异,但在另一张图片中,全黑背景和一些像素的白点,图像得到了“少”错了。我会尽快更新这两张图片并将链接放在第一条消息中。
【解决方案2】:

我在 BGR 图像格式上实现了 3x3 模糊滤镜。

重要提示:

  • 在对 BRG(或 RGB)格式的图像进行过滤时,必须单独过滤每个颜色通道。
  • 在执行过滤器(使用卷积)时,重要的是使用“就地”计算 - 避免源被目标元素覆盖。
  • 乘以 (1/9) 比除以 9 更有效。
  • 在整数转换前添加 0.5 可用于舍入正值。
  • 通过扩展、缩放和移位执行 (1/9) 缩放的定点实现。示例:avg = (sum*scale + rounding) >> 15; [当比例 = (1/9)*2^15]。

我假设内存中的像素顺序是 b,g,r,b,g,r...(b 在字节 0 中)。

过滤BRG(或RGB)格式的图像时,必须分别过滤每个颜色通道!

分色说明:

bgrbgrbgr
bgrbgrbgr
bgrbgrbgr

分开到:

bbb ggg rrr
bbb ggg rrr
bbb ggg rrr

模糊滤镜内核是:
1/9、1/9、1/9
1/9、1/9、1/9
1/9、1/9、1/9
如前所述,过滤器应分别应用于蓝色、绿色和红色。

您可以将每个 3x3 像素相加,然后将结果除以 9(或者最好像您一样乘以 1/9)。
我使用了乘以 1/9 的定点实现,而不是转换为浮点数。
定点实现用于提高性能(这里不是那么重要,因为代码没有优化)。
我习惯于演示该技术...

我使用了一些代码重复,以强调 RGB 颜色分离。
我还处理了图像边界(使用对称镜像)。

这是我的代码:

//Filter image I with filter kernel:
//   1/9  1/9  1/9
//   1/9  1/9  1/9
//   1/9  1/9  1/9
//I - Input image in pixel ordered BGR format
//image_width - Number of columns of I
//image_height - Number of rows of I
//J - Destination "smoothed" image in BGR format.

//I and J is pixel ordered BGR color format (size in bytes is image_width*image_height*3):
//BRGBRGBRGBRGBR
//BRGBRGBRGBRGBR
//BRGBRGBRGBRGBR
//BRGBRGBRGBRGBR
//
//Limitations:
//I and J must be two separate arrays (in place computation is not supported).
void BgrSmoothing(const unsigned char I[],
                  int image_width,
                  int image_height,
                  unsigned char J[])
{
    const int scale = (int)((1.0/9.0)*(1 << 15) + 0.5); //1/9 expanded by 2^15 (add 0.5 for rounding).
    const int rounding_ofs = (1 << 14);                 //0.5 expanded by 2^14

    int x, y;
    const unsigned char *I0;    //Points beginning of row y-1 (in source image I).
    const unsigned char *I1;    //Points beginning of row y   (in source image I).
    const unsigned char *I2;    //Points beginning of row y+1 (in source image I).
    int x0, x1, x2;             //x0 - pixel to the left of x1, x1 - center, x2 - pixel to the right of x1

    unsigned char *J1;          //Points beginning of row y (in destination image J).

    //3x3 source blue pixels, 3x3 source green pixels, 3x3 source red pixels.
    unsigned char b00, b01, b02,  g00, g01, g02,  r00, r01, r02;
    unsigned char b10, b11, b12,  g10, g11, g12,  r10, r11, r12;
    unsigned char b20, b21, b22,  g20, g21, g22,  r20, r21, r22;

    unsigned char b, g, r;  //Destination blue, green and red pixels.

    for (y = 0; y < image_height; y++)
    {
        if (y == 0)
            I0 = I;                         //Handle first row: use row 0 instead of row -1 (row -1 exceeds image bounds).
        else
            I0 = &I[(y-1)*image_width*3];   //Pointer to beginning of source row above row y in image I.

        I1 = &I[y*image_width*3];           //Pointer to beginning of source row y in image I.

        if (y == image_height-1)
            I2 = &I[y*image_width*3];       //Handle last row: use row image_height-1 instead of row image_height (row image_height exceeds image bounds).
        else
            I2 = &I[(y+1)*image_width*3];   //Pointer to beginning of source row below row y in image I.

        J1 = &J[y*image_width*3];           //Pointer to beginning of destination row in image J.

        //Handle first pixel:
        for (x = 0; x < image_width; x++)
        {
            //Multiply x by 3, to convert pixel index to byte index (in BGR forst each pixel is 3 bytes).
            x0 = (x == 0) ? (0) : (x-1)*3;                              //Handle x0 coordinate of first pixel in the row.
            x1 = x*3;
            x2 = (x == image_width-1) ? (image_width-1)*3 : (x+1)*3;    //Handle x2 coordinate of last pixel in the row.

            //Load 3x3 blue pixels:
            b00 = I0[x0]; b01 = I0[x1], b02 = I0[x2];
            b10 = I1[x0]; b11 = I1[x1], b12 = I1[x2];
            b20 = I2[x0]; b21 = I2[x1], b22 = I2[x2];

            //Load 3x3 green pixels:
            g00 = I0[x0+1]; g01 = I0[x1+1], g02 = I0[x2+1];
            g10 = I1[x0+1]; g11 = I1[x1+1], g12 = I1[x2+1];
            g20 = I2[x0+1]; g21 = I2[x1+1], g22 = I2[x2+1];

            //Load 3x3 red pixels:
            r00 = I0[x0+2]; r01 = I0[x1+2], r02 = I0[x2+2];
            r10 = I1[x0+2]; r11 = I1[x1+2], r12 = I1[x2+2];
            r20 = I2[x0+2]; r21 = I2[x1+2], r22 = I2[x2+2];

            //Sum all 9 blue elements, all 9 green elements and all 9 red elements (convert to int to avoid overflow).
            int sum_b = (int)b00+(int)b01+(int)b02+(int)b10+(int)b11+(int)b12+(int)b20+(int)b21+(int)b22;
            int sum_g = (int)g00+(int)g01+(int)g02+(int)g10+(int)g11+(int)g12+(int)g20+(int)g21+(int)g22;
            int sum_r = (int)r00+(int)r01+(int)r02+(int)r10+(int)r11+(int)r12+(int)r20+(int)r21+(int)r22;

            //b = round(sum_b*(1/9)).
            //Because b i positive round(b) = floor(b+0.5).
            //Use following computation instead: b = floor((sum_b*(1.0/9.0)*2^15 + 2^14) / 2^15)
            b = (unsigned char)((sum_b*scale + rounding_ofs) >> 15);    //Destination blue pixel.
            g = (unsigned char)((sum_g*scale + rounding_ofs) >> 15);    //Destination green pixel.
            r = (unsigned char)((sum_r*scale + rounding_ofs) >> 15);    //Destination red pixel.

            //Store b,g,r elements to destination row J1.
            J1[x1]      = b;
            J1[x1+1]    = g;
            J1[x1+2]    = r;
        }
    }
}

输入图片:

输出图片:

【讨论】:

  • 我不明白加 0.5 和定点的东西。无论如何,正如我在上面的评论中提到的,我现在正在用字符做所有的事情,它并没有改变什么。我在 Jounathaen cmets 之后做的不同数组的东西,我已经在做分别处理每个颜色通道。
  • @user2752471 当我看到你的输出图像时,我以为你有一个分离颜色的错误。我还向其他阅读我的答案的人强调了这一点。
  • @user2752471 四舍五入加0.5很容易理解:round(x) = floor(x + 0.5)。因为整数转换与正值的下限相同,所以可以使用:round(x) = (unsigned)(x + 0.5)。对于定点 - 将其扩展 2^15,你得到: (x + 0.5) = (x + 0.5)*(2^15/2^15) = (x*2^15 + 0.5*2^15) *2^15 = (x*2^15 + 2^14) / 2^15(所以需要加上 2^14 进行四舍五入)。
  • @user2752471 使用整数乘以 (1/9)(不转换为浮点数)很棘手:诀窍是先扩展,然后再缩减。通过乘以 2^k 扩展,并使用右移 k 减少(我选择 k = 15): x*0.11 = x*(0.11*2^15/2^15) = (x*0.11*2^15) /2^15 = (x*(0.11*2^15)) >> 15
猜你喜欢
  • 1970-01-01
  • 2013-07-30
  • 1970-01-01
  • 1970-01-01
  • 2011-04-24
  • 1970-01-01
  • 1970-01-01
  • 2012-03-09
  • 1970-01-01
相关资源
最近更新 更多