初学者对图像过滤的尝试

A beginner's attempt on image filtering

本文关键字:过滤 图像 初学者      更新时间:2023-10-16

我的问题可能听起来很愚蠢,但请理解我不是在大学/学院学习,教授可以给我像样的帮助。我是自学的,对我来说几乎不需要3-4个月的编码时间。因此,我请求所有用户耐心等待。

我试图编码一个简单的过滤器函数,我定义了一个kernel,一个3x3的2D数组。我创建了另一个矩阵load,在那里我想存储我的图像数组的像素值。我面临两个主要问题。对于第一个问题,我完全困惑了。我读了这个关于如何在不使用任何内置高斯函数的情况下对图像进行高斯模糊的精彩回答?,它提到:

对于像素11,你需要加载像素0,1,2,10,11,12,20,21、22 .

,然后将像素0乘以3x3的左上角模糊滤波器。像素1在中上方,像素2在右上方,像素3在右上方,像素10,中间偏左,依此类推

我想知道具有3个通道的IplImage如何在我的load矩阵中存储相应的像素[如上面的链接所述],因为我感到困惑的是有3个值[R G B],所以我应该将什么与什么相乘?

还如何确保像素不出界?因为一旦我们接近图像的边缘,像素值可能会超出边界。

void filter(const IplImage *img) 
{
    unsigned char kernel[][3]  = { 1,  2,  1, 
                                   2,  1,  2, 
                                   1,  2,  1 , };
    unsigned char load[][3] = { 0 };
    int rows=img->height,cols=img->width,row,col;
    uchar* temp_ptr=0 ;
    for( row = 0; row < rows; ++row) 
    {
            for ( col = 0; col < cols; ++col) 
            {
                CvPoint pt = {row,col};
                temp_ptr  = &((uchar*)(img->imageData + (img->widthStep*row)))[col*3];
            }
    }
}

对于您的RGB问题:您将内核分别应用于每个通道。也就是说,你基本上把这三个通道当作三个独立的单色图片,并对每个通道进行模糊处理。

确保像素不越界在原则上是很简单的:当你访问像素时,你首先测试像素是否越界,如果是,不要访问它。然而,有趣的问题是该怎么做。一种可能是不将过滤器应用于内核将超过图像的边界点,但这意味着要留下一个未模糊的边界(除非您可以承受损失一个像素的边界,在这种情况下,这可能是最好的解决方案)。另一种解决方案是为外部像素假定某种颜色(例如白色)。第三种选择,我认为在这里是最好的,就是不使用这些点,而是使用只包含掩码的边界像素的简化内核。注意,在这种情况下,您还需要调整规范化因子。

顺便说一句,你确定你的内核描述了一个模糊?我希望中间值是最大的模糊

1)通过线性系统过滤图像相当于我们所说的卷积。这很容易理解,它是图像与对称核(中心对称)的逐项乘法。如果你的核是对称的,就像高斯核一样,那么它就是一个简单的逐项乘法。

你把你的内核移动到图像上,正如你所说的,可能会有越界异常…有两个选择,或者你不过滤边框(大多数情况下),或者你插值结果(如果你刚开始处理图像,这可能太困难了,所以,让我们做一些简单的事情)。

算法看起来是这样的,请注意,col和行从1开始,以宽度-1和高度-1结束,以避免越界问题…如果你有一个更大的内核,你会做同样的宽度-kernelSize/2 kernelSize是一个奇数3x3,…7 x7……等。

kernel[9] = {1/13, 2/13, 1/13, 2/13, 1/13, 2/13, 1/13, 2/13, 1/13};
for(row=1;row<img->height-1;row++)
{
     for(col=1;row<img->width-1;col++)
     {
           img->data[row*img->width+3*col+0] = ... ; //B Channel
           img->data[row*img->width+3*col+1] = ... ; //G Channel
           img->data[row*img->width+3*col+2] = ... ; //R Channel
      }
}

代替…或者你用一个循环来计算卷积然后替换。根据循环的结果(如果有时需要更大的内核(例如9x9),这是最灵活的解决方案),或者您手工编写:

double convB = kernel[0]*img->data[(row-1)*img->width+3*(col-1)+0]
+ kernel[1]*img->data[(row-1)*img->width+3*(col)+0]
+ kernel[2]*img->data[(row-1)*img->width+3*(col+1)+0]
+ kernel[3]*img->data[(row)*img->width+3*(col-1)+0]
+ kernel[4]*img->data[(row)*img->width+3*(col)+0]
+ kernel[5]*img->data[(row)*img->width+3*(col+1)+0]
+ kernel[6]*img->data[(row+1)*img->width+3*(col-1)+0]
+ kernel[7]*img->data[(row+1)*img->width+3*(col)+0]
+ kernel[8]*img->data[(row+1)*img->width+3*(col+1)+0];

2)对于G和R通道,您可以使用相同的+1和+2而不是括号[]中的+0来定位正确的内存位置;

3)你应该使用一个归一化的内核,这就是为什么我使用值x 1/13(这是所有值的总和),如果你想在你的图像上得到一个归一化的结果(具有归一化的强度)。