数字图像是一个二维的离散信号,对数字图像做卷积操作其实就是利用卷积核(卷积模板)在图像上滑动,将图像点上的像素灰度值与对应的卷积核上的数值相乘,然后将所有相乘后的值相加作为卷积核中间像素对应的图像上像素的灰度值,并最终滑动完所有图像的过程。
即:
这个图可以清晰的表征出整个卷积过程中一次相乘后相加的结果;该图片选用3*3的卷积核,卷积核内共有9个数值,所以图片右上角公式中一共有9行,每一行都是图像像素值与卷积核上数值相乘,最终结果代替代替原图像中对应位置的值。这样沿着图片一步长为1滑动,每一个滑动后都一次相乘在相加的工作,可以得到最终输出结果。除此之外,卷积核的选择有一些规则:
-
卷积核大小一般为奇数,这样的话它会按照中间的像素点中心对称,所以卷积核一般都是3×3,5×5,7×7。有中心了,也有了半径的称呼,例如5*5大小的核的半径就是2。
-
卷积核所有的元素之和一般都等于1.这是为了原始图像的能量(亮度)守恒。也存在卷积核元素相加不等于1的情况。
若滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮;反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗。
-
对于滤波后的结构,可能出现负数或者大于255的数值。对于这种情况,我们将它们直接截断到0和255之间即可。对于负数,也可以取绝对值。
边界补充问题
上面的图片说明了图像的卷积操作,但是他也反映出一个问题,如上图,原始图片尺寸为7×7,卷积核的大小为3×3,当卷积核沿着图片滑动后只能滑动出一个5×5的图片出来,这就造成了卷积后的图片和卷积前的图片尺寸不一致,这显然不是我们想要的结果,所以为了避免这种情况,需要先对原始图片做边界填充处理。在上面的情况中,我们需要先把原始图像填充为9×9的尺寸。
常用的区域填充方法包括:
用33定义原始图像的尺寸,补充为99的尺寸。

1. 补零
2. 边界复制
3. 镜像
4.块复制
不同卷积核下卷积意义
我们经常能看到的,平滑,模糊,去燥,锐化,边缘提取等等工作,其实都可以通过卷积操作来完成,下面我们一一举例说明一下:
1. 没有任何作用的卷积核
将原像素中间像素值乘1,其余全部乘0,显然像素值不会发生任何变化。
2.平滑均值滤波
该卷积核的作用在于取九个值的平均值代替中间像素值,所以起到的平滑的效果:
3. 高斯平滑
高斯平滑水平和垂直方向呈现高斯分布,更突出了中心点在像素平滑后的权重,相比于均值滤波而言,有着更好的平滑效果。
4. 图像锐化
该卷积利用的其实是图像中的边缘信息有着比周围像素更高的对比度,而经过卷积之后进一步增强了这种对比度,从而使图像显得棱角分明、画面清晰,起到锐化图像的效果。
除了上述卷积核,边缘锐化还可以选择:
5. 梯度Prewitt
水平梯度卷积核:
垂直梯度卷积核:
6. 边缘检测
Soble与上述卷积核不同之处在于,Soble更强调了和边缘相邻的像素点对边缘的影响。
水平梯度:
垂直梯度:
7. 梯度Laplacian
Laplacian也是一种锐化方法,同时也可以做边缘检测,而且边缘检测的应用中并不局限于水平方向或垂直方向,这是Laplacian与soble的区别。下面这张图可以很好的表征出二者的区别:来源于OpenCV官方文档
OpenCV实现
-
函数说明
将图像与内核卷积。该函数将任意线性滤波器应用于图像。支持就地操作。当光圈部分位于图像外部时,该函数会根据指定的边框模式对异常像素值进行插值。
该函数实际上会计算相关性,而不是卷积:
d s t ( x , y ) = ∑ 0 ≤ y ′ < k e r n e l . r o w s 0 ≤ x ′ < k e r n e l . c o l s k e r n e l ( x ′ , y ′ ) ∗ s r c ( x + x ′ − a n c h o r . x , y + y ′ − a n c h o r . y ) dst(x,y)=∑_{0≤y′<kernel.rows}^{0≤x′<kernel.cols}kernel(x′,y′)∗src(x+x′−anchor.x,y+y′−anchor.y) dst(x,y)=0≤y′<kernel.rows∑0≤x′<kernel.colskernel(x′,y′)∗src(x+x′−anchor.x,y+y′−anchor.y)
也就是说,内核不会围绕定位点进行镜像。如果需要真正的卷积,请使用flip翻转内核,并将新锚点设置为(kernel.cols - anchor.x - 1, kernel.rows - anchor.y - 1)。对于足够大的内核(11 x 11或更大),该函数使用基于DFT的算法,对于较小的内核,该函数使用直接算法。
-
函数声明
void filter2D( InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor = Point(-1,-1), double delta = 0, int borderType = BORDER_DEFAULT );
-
函数参数
- src:输入图像。
- dst:输出与src具有相同大小和相同通道数的图像。
- ddepth:目标图像的所需深度。
- kernel:卷积核(或一个相关核),一个单通道浮点矩阵;如果要将不同的内核应用于不同的通道,请使用split将图像分为单独的色彩平面,并分别进行处理。
- anchor: 内核的锚点,指示内核中已过滤点的相对位置;锚点应位于内核内;默认值(-1,-1)表示锚位于内核中心。内核的基准点(anchor),其默认值为(-1,-1)说明位于kernel的中心位置。基准点即kernel中与进行处理的像素点重合的点。
- delta: 在将像素存储到dst之前将其添加到过滤后的像素的可选值。
- borderType: 像素向外逼近的方法,默认值是BORDER_DEFAULT。
-
应用
Mat srcImage = imread("D:/test/src.jpg"); imshow("原图", srcImage); Mat kernel = (Mat_<double>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1); Mat dstImage; filter2D(srcImage, dstImage, srcImage.depth(), kernel); imshow("卷积图", dstImage);
学习: