初学者尝试图像过滤

时间:2011-10-22 19:01:42

标签: c++ opencv

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

我正在尝试编写一个简单的过滤函数,其中我定义了一个kernel,一个3x3 2D数组。我创建另一个矩阵load,我想存储我的Image数组的像素值。我面临两个主要问题。对于第一个问题,我完全感到困惑和困惑。我读到了这个关于How do I gaussian blur an image without using any in-built gaussian functions?的精彩答案,它提到了:

  

对于像素11,您需要加载像素0,1,2,10,11,12,20,   21,22。

     

然后将像素0乘以3x3的左上部分   模糊过滤器。像素1由顶部中间,像素2,像素3由右上角,   左中角的像素10,依此类推。

我想知道一个有3个频道的IplImage如何在我的load矩阵中存储相应像素[如上面的链接中所述],因为我感到困惑的是3个值[RGB],所以我应该用什么来加倍?

另外如何确保像素不会超出范围?因为一旦我们靠近图像的边缘,像素值可能超出界限。

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];

            }
    }

}

2 个答案:

答案 0 :(得分:2)

对于您的RGB问题:您将内核分别应用于每个频道。也就是说,您基本上将三个通道视为三个独立的单色图像并将每个通道模糊化。

原则上确保像素不超出范围很简单:当您访问像素时,首先要测试像素是否超出界限,如果是,则不要访问它。然而,有趣的问题是如何做。一种可能性就是不将滤镜应用到内核超出图像的边界点,但这意味着留下一个不模糊的边框(除非你能承受失去一个像素的边界,在这种情况下,这可能是最好的解决方案)。另一种解决方案是假设外部像素(例如白色)的某种颜色。我认为第三个选项是最好的选择,就是不使用这些点,而是使用仅包含掩码的入边像素的简化内核。请注意,在这种情况下,您还需要调整标准化因子。

顺便说一下,你确定你的内核描述了模糊吗?我原本期望中间值是模糊的最大值。

答案 1 :(得分:0)

1)通过线性系统过滤图像等同于我们称之为卷积的图像。它很容易实现,它是一个术语,通过术语乘以对称内核(中心对称)。如果你的内核是对称的,就像高斯内核那样,那么它就是一个简单的术语乘法。

您将内核移动到图像上,正如您所说,可能存在超出范围的异常...... 两个选项,或者你不过滤边框(主要是这种情况)或者你插入结果(如果你从图像处理开始这可能太难了,让我们做一些简单的事情。)

算法看起来像,注意col和行从1开始并以width-1和height-1结束以避免出界问题...如果你有一个更大的内核,你会做同样的width-kernelSize / 2 kernelSize是一个奇数3x3,... 7x7 ......等等。

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
      }
}

而不是......或者你使用循环来计算卷积,然后用循环的结果替换...(如果有时你需要更大的内核(ex 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(这是所有值的总和),如果你想在图像上得到一个标准化结果(标准化强度)。