深度学习之卷积与卷积计算
一、卷积介绍
在泛函分析中,卷积(又称叠积(convolution)、褶积或旋积),是透过两个函数 f 和 g 生成第三个函数的一种数学算子,表征函数 f 与经过翻转和平移的 g 的乘积函数所围成的曲边梯形的面积。如果将参加卷积的一个函数看作区间的指示函数,卷积还可以被看作是“移动平均”的推广。
% Create folding of two rectangular impulses
clear
X=-2.6:0.001:3;
F1=abs(X)<=0.5;
F2=abs(X)<=0.5;
clf
mkdir('tmp');
[tmp zero_offset] = min(abs(X));
SyncFrames=[1 round(18.67*(1:numel(X)))];
integral=nan(size(X));
frame=1;
for offset_i=1:numel(X);
offset=X(offset_i);
shift=offset_i-zero_offset;
F2_shifted = circshift(F2, [0 shift]);
product = F2_shifted.*F1;
integral(offset_i) = sum(product)/numel(X)*(X(end)-X(1));
if offset_i==SyncFrames(frame)
frame=frame+1;
area(X, product, 'facecolor', 'yellow');
hold on
plot(X, F1, 'b', X, F2_shifted, 'r', X, integral, 'k', [offset offset], [0 2], 'k:')
hold off
axis image
axis([-2.1 2.1 0 1.1])
xlabel('\tau & t');
grid on
legend('Area under f(\tau)g(t-\tau)', 'f(\tau)', 'g(t-\tau)', '(f\astg)(t)');
print('-dpng','-r72',sprintf('tmp/conv_box_box_%06d.png', offset_i));
drawnow
end
end
system('"C:\Program Files\ImageMagick-6.6.3-Q16\convert.exe" -layers Optimize -delay 5 tmp/conv_box_box_*.png conv_box_box.gif');
delete('tmp/*');
rmdir('tmp');
卷积
图示是两个方形脉冲信号波的卷积。其中函数"g"首先对反射,接着平移"t",成为。那么重叠部分的面积就相当于"t"处的卷积,其中横坐标代表待变量 以及新函数的自变量"t"。
详细说明可参考:维基百科或者百度
image.png image.png卷积定理及其证明
卷积定理是傅立叶变换满足的一个重要性质。卷积定理指出,函数卷积的傅立叶变换是函数傅立叶变换的乘积。换言之,一个域中的卷积对应于另一个域中的乘积,例如,时域中的卷积对应于频域中的乘积。
image这一定理对拉普拉斯变换、Z变换等各种傅立叶变换的变体同样成立。需要注意的是,以上写法只对特定形式的变换正确,因为变换可能由其它方式正规化,从而使得上面的关系式中出现其它的常数因子。
下面我们来证明时域卷积定理,频域卷积定理的证明与此类似,读者可以自行证明。
证明:将卷积的定义
傅立叶变换的作用在频域对信号进行分析,我们可以把时域的信号看做是若干正弦波的线性叠加,傅立叶变换的作用正是求得这些信号的幅值和相位。
既然固定的时域信号是若干固定正弦信号的叠加,在不改变幅值的情况下,在时间轴上移动信号,也就相当于同时移动若干正弦信号,这些正弦信号的相位改变、但幅值不变,反映在频域上就是傅立叶变换结果的模不变、而相位改变。所以,时移性质其实就表明当一个信号沿时间轴平移后,各频率成份的大小不发生改变,但相位发生变化。
既然这里提到了傅立叶变换的性质,这里我们还将补充一些关于帕塞瓦尔定理的有关内容。该定理最早是由法国数学家帕塞瓦尔(Marc-Antoine Parseval)在1799年推导出的一个关于级数的理论,该定理随后被应用于傅立叶级数。帕塞瓦尔定理的表述是这样的:
image image综上所述,原结论得证。
前面我们也介绍过复数形式的傅立叶级数,下面我们来推导与复数形式傅立叶变换相对应的帕塞瓦尔等式。这里再次给出傅立叶级数的复数形式表达式,具体推导过程请读者参阅前文
帕塞瓦尔定理把一个信号的能量或功率的计算和频谱函数或频谱联系起来了,它表明一个信号所含有的能量(功率)恒等于此信号在完备正交函数集中各分量能量(功率)之和。换言之,能量信号的总能量等于各个频率分量单独贡献出来的能量的连续和;而周期性功率信号的平均功率等于各个频率分量单独贡献出来的功率之和。
二、卷积计算
假设有一个卷积核H,就一般为3*3的矩阵:
image有一个待处理矩阵X:
imageH*X的计算过程分为三步
第一步,将卷积核翻转180°,也就是成为了
image第二步,将卷积核h的中心对准x的第一个元素,然后对应元素相乘后相加,没有元素的地方补0。
image这样结果Y中的第一个元素值Y11=10+20+10+00+01+02+-10+-25+-1*6=-16
第三步每个元素都像这样计算出来就可以得到一个输出矩阵,就是卷积结果
image像这样计算,其他过程略了。
最后结果
image注意:
我这里是用0补全原矩阵的,但我们不一定选择0。在Opencv的cvFilter2D函数中,就没有使用0来补全矩阵,而是用了边缘拷贝的方式。
三、cvFilter2D卷积函数的计算过程分析
cvFilter2D卷积函数的形式如下:
void cvFilter2D( const CvArr* src, CvArr* dst,const CvMat* kernel, CvPoint anchor=cvPoint(-1,-1));
src
输入图像.
dst
输出图像.
kernel
卷积核, 单通道浮点矩阵. 如果想要应用不同的核于不同的通道,先用 cvSplit 函数分解图像到单个色彩通道上,然后单独处理。
anchor
核的锚点表示一个被滤波的点在核内的位置。 锚点应该处于核内部。缺省值 (-1,-1) 表示锚点在核中心。
函数 cvFilter2D 对图像进行线性滤波,支持 In-place 操作。当核运算部分超出输入图像时,函数从最近邻的图像内部象素差值得到边界外面的象素值。
先上代码,
#include<opencv2\opencv.hpp>
for (i = 0; i<m->rows; i++)
for (j = 0; j<m->cols; j++)
double ImgPixelVal = cvGetReal2D(m, i, j);
cout << ImgPixelVal << " ";
CvMat Ma = cvMat(3, 3, CV_32FC1, A);//核矩阵
CvMat Mb = cvMat(3, 3, CV_32FC1, B);//输入
CvMat *C = cvCreateMat(3, 3, CV_32FC1);
cvFilter2D(&Mb, C, &Ma, cvPoint(1, 1));
输出的结果
image为了不让数据有偶然性,所以卷积核很古怪,大家不要在意,我们只是要研究他的计算过程。
首先我们发现91 这个值就是 19+28+37+44+55+66-17-28-1*9=91。即对应的元素相乘后,求和。而且卷积核没有旋转180°
然后我们可以看到如果用我上一篇介绍的边缘用0补全,是得不到这个结果的,所以我们肯定Opencv不是用0补全。
那下面的问题就是Opencv到底是怎么补全的
问号里面该填什么
image???
image???
之后,我们就猜测是不是边缘复制,
image ------> image通过计算后发现,果然是这样。19+29+38+49+59+68-14-24-1*5=163
其他也逐一验证 。
结论:
1.Opencv中,卷积核不会进行180°的旋转
2.CvFilter2D是边缘拷贝,通过边缘拷贝补全矩阵进行计算。
库文件下载地址:
opencv-4-3-0文献资料
opencv-4.3.0开源计算机视觉库