我的问题可能听起来很愚蠢,但请理解我不会在大学/学院学习,教授可以给我很好的帮助。我是自学,几乎没有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];
}
}
}
答案 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(这是所有值的总和),如果你想在图像上得到一个标准化结果(标准化强度)。