转载:http://blog.csdn.net/chaipp0607/article/details/72236892

        https://www.zhihu.com/question/22298352



   
 在图像处理领域,我们经常能听到滤波,卷积之类的词,其实他们都可以看做一种图像的卷积操作,相对应的卷积核,卷积模板,滤波器,滤波模板,扫描窗其实也都是同一个东西。下面我们进一步讨论图像中的卷积操作核卷积的意义。

1、数字信号处理中卷积理解

   
卷积一词最开始出现在信号与线性系统中,信号与线性系统中讨论的就是信号经过一个线性系统以后发生的变化。由于现实情况中常常是一个信号前一时刻的输出影响着这一时刻的输出,所在一般利用系统的单位响应与系统的输入求卷积,以求得系统的输出信号(当然要求这个系统是线性时不变的)。 
卷积的定义: 卷积是两个变量在某范围内相乘后求和的结果。如果卷积的变量是序列x(n)和h(n),则卷积的结果: 


                     


作者:果程C
链接:https://www.zhihu.com/question/22298352/answer/50940942
来源:知乎


对于初学者,我推荐用复利的例子来理解卷积可能更好理解一些:

小明存入100元钱,年利率是5%,按复利计算(即将每一年所获利息加入本金,以计算下一年的利息),那么在五年之后他能拿到的钱数是,如下表所示:


将这笔钱存入银行的一年之后,小明又往银行中存入了100元钱,年利率仍为5%,那么这笔钱按复利计算,到了第五年,将收回的钱数是
,我们将这一结果作为新的一行加入上面的表格中:

以此类推,如果小明每年都往银行中存入新的100元钱,那么这个收益表格将是这样的:
<img
src="https://pic1.zhimg.com/50/cfe98b9d33640fae02a21bf369f0459d_hd.jpg"
data-rawwidth="1296" data-rawheight="284"
class="origin_image zh-lightbox-thumb" width="1296"
data-original="https://pic1.zhimg.com/cfe98b9d33640fae02a21bf369f0459d_r.jpg">
可见,最终小明拿到的钱将等于他各年存入的钱分别计算复利之后得到的钱数的总和,即:

用求和符号来简化这个公式,可以得到:

在上式中,为小明的存钱函数,而为存入银行的每一笔钱的复利计算函数。在这里,小明最终得到的钱就是他的存钱函数和复利计算函数的卷积。
为了更清晰地看到这一点,我们将这个公式推广到连续的情况,也就是说,小明在从到的这一段时间内,每时每刻都往银行里存钱,他的存钱函数为
,而银行也对他存入的每一笔钱按复利公式计算收益:,则小明到时间将得到的总钱数为:

这也就是卷积的表达式了,上式可以记为。

相信通过上面这个例子,大家应该能够很清晰地记住卷积公式了。下面我们再展开说两句:
如果我们将小明的存款函数视为一个信号发生(也就是激励)的过程,而将复利函数视为一个系统对信号的响应函数(也就是响应),那么二者的卷积就可以看做是在
时刻对系统进行观察,得到的观察结果(也就是输出)将是过去产生的所有信号经过系统的「处理/响应」后得到的结果的叠加,这也就是卷积的物理意义了。

作者:鱼腻
链接:https://www.zhihu.com/question/22298352/answer/91131073
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


看了好多关于卷积的答案,看到这个例子才彻底地理解了这个过程~
关于卷积的一个血腥的讲解

比如说你的老板命令你干活,你却到楼下打台球去了,后来被老板发现,他非常气愤,扇了你一巴掌(注意,这就是输入信号,脉冲),于是你的脸上会渐渐地(贱贱地)鼓起来一个包,你的脸就是一个系统,而鼓起来的包就是你的脸对巴掌的响应,好,这样就和信号系统建立起来意义对应的联系。下面还需要一些假设来保证论证的严谨:假定你的脸是线性时不变系统,也就是说,无论什么时候老板打你一巴掌,打在你脸的同一位置(这似乎要求你的脸足够光滑,如果你说你长了很多青春痘,甚至整个脸皮处处连续处处不可导,那难度太大了,我就无话可说了哈哈),你的脸上总是会在相同的时间间隔内鼓起来一个相同高度的包来,并且假定以鼓起来的包的大小作为系统输出。好了,那么,下面可以进入核心内容——卷积了!

如果你每天都到地下去打台球,那么老板每天都要扇你一巴掌,不过当老板打你一巴掌后,你5分钟就消肿了,所以时间长了,你甚至就适应这种生活了……如果有一天,老板忍无可忍,以0.5秒的间隔开始不间断的扇你的过程,这样问题就来了,第一次扇你鼓起来的包还没消肿,第二个巴掌就来了,你脸上的包就可能鼓起来两倍高,老板不断扇你,脉冲不断作用在你脸上,效果不断叠加了,这样这些效果就可以求和了,结果就是你脸上的包的高度随时间变化的一个函数了(注意理解);如果老板再狠一点,频率越来越高,以至于你都辨别不清时间间隔了,那么,求和就变成积分了。可以这样理解,在这个过程中的某一固定的时刻,你的脸上的包的鼓起程度和什么有关呢?和之前每次打你都有关!但是各次的贡献是不一样的,越早打的巴掌,贡献越小,所以这就是说,某一时刻的输出是之前很多次输入乘以各自的衰减系数之后的叠加而形成某一点的输出,然后再把不同时刻的输出点放在一起,形成一个函数,这就是卷积,卷积之后的函数就是你脸上的包的大小随时间变化的函数。本来你的包几分钟就可以消肿,可是如果连续打,几个小时也消不了肿了,这难道不是一种平滑过程么?反映到剑桥大学的公式上,f(a)就是第a个巴掌,g(x-a)就是第a个巴掌在x时刻的作用程度,乘起来再叠加就ok了,大家说是不是这个道理呢?我想这个例子已经非常形象了,你对卷积有了更加具体深刻的了解了吗?

作者:Kaixiang Wang
链接:https://www.zhihu.com/question/22298352/answer/193852554
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

补充一下@镜面狐 <https://www.zhihu.com/people/4842c560072dd4123338058fd29c1a42>和@笑劫戈
<https://www.zhihu.com/people/06391d232050ac227a98820e6e790572>的答案。


想要形象地理解卷积这个数学概念,那么思路当然是找它的“实际应用”,最容易想到的例子就是它对动态系统的输入输出关系的描述了。许多答主提到了“打脸”的例子,非常形象,就不复述了。

以下略纠结,嫌麻烦可以跳过直接看图。

之前我对@镜面狐 <https://www.zhihu.com/people/4842c560072dd4123338058fd29c1a42>
答案的有部分理解是不准确的;现在看来他对 的定义确实有问题(感谢@高启峰
<https://www.zhihu.com/people/c835c3a6c2c4eaf86c48d73d0f6aac23>和@逐光
<https://www.zhihu.com/people/1e97cbc69525d8674a64f124ec52c0e9>指出)。顺便提一下,@镜面狐
<https://www.zhihu.com/people/null>
的答案引用了Franklin的那本经典教材,但是我越来越感觉它讲卷积的那部分并没有写得很清楚,比如它和配图和文字就对不上号,徒增了理解的难度。我尽量梳理一遍吧。

首先是单位脉冲和单位脉冲响应 的定义,大家应该都懂我就不啰嗦了。(注意 的“面积”为 。)再是定义 和 ,当 时趋近于 和 。(同样注意
的高度为 ,面积为 。)

一个输入信号可以拆成很多 的和,即 (所以
@镜面狐 <https://www.zhihu.com/people/4842c560072dd4123338058fd29c1a42>的答案确实少了个 )
输出即
取极限写成积分形式即

注意不要忘了 ,因为我们是要跟单位脉冲比较面积,即 是 的多少倍。


纠结部分完。

有了这个基础,再去理解卷积的公式就没那么intimidating了。放张图,心血来潮随手画的,但愿能顶个卵用。

另外,对于实际系统(因果系统),由于未来的打脸( )不会造成现在的脸肿,所以积分上限设为 就行了;此外我们还一般假设
之前没人打脸并且脸也不肿,所以积分下限设为 就行了。因此。

2、数字图像处理中卷积

       
数字图像是一个二维的离散信号,对数字图像做卷积操作其实就是利用卷积核(卷积模板)在图像上滑动,将图像点上的像素灰度值与对应的卷积核上的数值相乘,然后将所有相乘后的值相加作为卷积核中间像素对应的图像上像素的灰度值,并最终滑动完所有图像的过程。 









     
 这张图可以清晰的表征出整个卷积过程中一次相乘后相加的结果:该图片选用3*3的卷积核,卷积核内共有九个数值,所以图片右上角公式中一共有九行,而每一行都是图像像素值与卷积核上数值相乘,最终结果-8代替了原图像中对应位置处的1。这样沿着图片一步长为1滑动,每一个滑动后都一次相乘再相加的工作,我们就可以得到最终的输出结果。除此之外,卷积核的选择有一些规则: 
       
1)卷积核的大小一般是奇数,这样的话它是按照中间的像素点中心对称的,所以卷积核一般都是3x3,5x5或者7x7。有中心了,也有了半径的称呼,例如5x5大小的核的半径就是2。 
       2)卷积核所有的元素之和一般要等于1,这是为了原始图像的能量(亮度)守恒。其实也有卷积核元素相加不为1的情况,下面就会说到。        
3)如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗。  
      4)对于滤波后的结构,可能会出现负数或者大于255的数值。对这种情况,我们将他们直接截断到0和255之间即可。对于负数,也可以取绝对值。
2.1边界补充问题

        
上面的图片说明了图像的卷积操作,但是他也反映出一个问题,如上图,原始图片尺寸为7*7,卷积核的大小为3*3,当卷积核沿着图片滑动后只能滑动出一个5*5的图片出来,这就造成了卷积后的图片和卷积前的图片尺寸不一致,这显然不是我们想要的结果,所以为了避免这种情况,需要先对原始图片做边界填充处理。在上面的情况中,我们需要先把原始图像填充为9*9的尺寸。 
常用的区域填充方法包括: 
        图是我在word里面画的,所以有很多回车键哈,有些简陋,大家凑合看吧
,意思还是对的,也是为了画图方便,这里就不用5*5的尺寸了,用3*3定义原始图像的尺寸,补充为9*9的尺寸,图片上的颜色只为方便观看,并没有任何其他含义。 


原始图像:




2.1.1补零 





2.2.2边界复制 





2.2.3镜像





2.2.4块复制







以上四种边界补充方法通过看名字和图片就能理解了,不在多做解释。

不同卷积核下卷积意义

我们经常能看到的,平滑,模糊,去燥,锐化,边缘提取等等工作,其实都可以通过卷积操作来完成,下面我们一一举例说明一下: 
(1)一个没有任何作用的卷积核: 
 
将原像素中间像素值乘1,其余全部乘0,显然像素值不会发生任何变化。 
(2)平滑均值滤波: 
选择卷积核: 
 
该卷积核的作用在于取九个值的平均值代替中间像素值,所以起到的平滑的效果: 
该卷积核的作用在于取九个值的平均值代替中间像素值,所以起到的平滑的效果: 
 




(3)高斯平滑: 
卷积核: 
 
高斯平滑水平和垂直方向呈现高斯分布,更突出了中心点在像素平滑后的权重,相比于均值滤波而言,有着更好的平滑效果。 
 
(4)图像锐化: 
卷积核: 
 
该卷积利用的其实是图像中的边缘信息有着比周围像素更高的对比度,而经过卷积之后进一步增强了这种对比度,从而使图像显得棱角分明、画面清晰,起到锐化图像的效果。 
 
除了上述卷积核,边缘锐化还可以选择: 
 
(5)梯度Prewitt: 
水平梯度: 
 
 
垂直梯度: 
 


梯度Prewitt卷积核与Soble卷积核的选定是类似的,都是对水平边缘或垂直边缘有比较好的检测效果。

(6)Soble边缘检测: 
Soble与上述卷积核不同之处在于,Soble更强调了和边缘相邻的像素点对边缘的影响。 
水平梯度: 
 
 
垂直梯度: 
 


以上的水平边缘与垂直边缘检测问题可以参考:Soble算子水平和垂直方向导数问题
<http://blog.csdn.net/chaipp0607/article/details/54348624>

(7)梯度Laplacian:

卷积核: 





Laplacian也是一种锐化方法,同时也可以做边缘检测,而且边缘检测的应用中并不局限于水平方向或垂直方向,这是Laplacian与soble的区别。下面这张图可以很好的表征出二者的区别:
来源于OpenCV官方文档 <http://docs.opencv.org/master/d5/d0f/tutorial_py_gradients.html> 




代码实现

可以利用OpenCV提供的filter2D函数完成对图像进行卷积操作,其函数接口为:
CV_EXPORTS_W void filter2D( InputArray src, OutputArray dst, int ddepth,
InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int
borderType=BORDER_DEFAULT );
* 1
* 2
* 3
* 4
* 5
* 6
* 7
* 8
第一个参数: 输入图像 
第二个参数: 输出图像,和输入图像具有相同的尺寸和通道数量 
第三个参数: 目标图像深度,输入值为-1时,目标图像和原图像深度保持一致。 
第四个参数: 卷积核,是一个矩阵 
第五个参数:内核的基准点(anchor),其默认值为(-1,-1)说明位于kernel的中心位置。基准点即kernel中与进行处理的像素点重合的点。 
第六个参数: 在储存目标图像前可选的添加到像素的值,默认值为0 
第七个参数: 像素向外逼近的方法,默认值是BORDER_DEFAULT。
#include <iostream> #include <iostream> #include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp>
using namespace std; using namespace cv; int main() { Mat srcImage = imread(
"1.jpg"); namedWindow("srcImage", WINDOW_AUTOSIZE); 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); namedWindow("dstImage"
,WINDOW_AUTOSIZE); imshow("卷积图",dstImage); waitKey(0); return 0; }
* 1
* 2
* 3
* 4
* 5
* 6
* 7
* 8
* 9
* 10
* 11
* 12
* 13
* 14
* 15
* 16
* 17
* 18
* 19
* 20
* 21
* 22
* 23
* 24
所以代码的实现就非常简单了,不同的卷积操作只需要改变卷积核kernel 即可。