深度学习FPGA实现基础知识17(图像处理卷积运算 矩阵卷积)
需求说明:深度学习FPGA实现知识储备
内容:第一部分:矩阵的卷积运算详细过程
第二部分:图像处理之卷积理解
第三部分:矩阵卷积转换为矩阵相乘
整理来自:时间的诗
第一部分:矩阵的卷积运算详细过程
来自:http://blog.****.net/frankyzhangc/article/details/6990782
一个矩阵与另一个矩阵的卷积运算大部分运用在图像处理上,例如用一个模板去对一幅图像进行卷积。
把模板(n*n)放在矩阵上(中心对准要处理的元素),用模板的每个元素去乘矩阵中的的元素,累加和等于这个元素例如例子中的第二行第二个元素16= 1*2+1*1+1*3+1*1+1*2+1*1+1*2+1*1+1*2+1*1+1*3的计算,依次计算每个元素的值,如果矩阵的中心在边缘就要将原矩阵进行扩展,例如补0,或者直接规定模板的中心距离边缘(n-1)/2个单位以上。
以下举一个简单的例子,并用Matlab来观察
相关MATALB代码
a=[2 1 3 1;1 2 1 2;2 1 3 2;1 3 1 2];
b=[1 1 1;1 1 1;1 1 1];
c=conv2(a,b,’same’);
d=conv2(a,b,’full’);
fprintf(‘\na = \n’);
disp(a);
fprintf(‘\nb = \n’);
disp(b);
fprintf(‘\nc = \n’);
disp(c);
fprintf(‘\nd = \n’);
disp(d);
MATALB仿真结果
a =
2 1 3 1
1 2 1 2
2 1 3 2
1 3 1 2
b =
1 1 1
1 1 1
1 1 1
c =
6 10 10 7
9 16 16 12
10 15 17 11
7 11 12 8
d =
2 3 6 5 4 1
3 6 10 10 7 3
5 9 16 16 12 5
4 10 15 17 11 6
3 7 11 12 8 4
1 4 5 6 3 2
卷积的计算步骤:
(1) 卷积核绕自己的核心元素顺时针旋转180度(这个千万不要忘了)
(2) 移动卷积核的中心元素,使它位于输入图像待处理像素的正上方
(3) 在旋转后的卷积核中,将输入图像的像素值作为权重相乘
(4) 第三步各结果的和做为该输入像素对应的输出像素
请看用水平和垂直差分算子对矩阵处理后的结果,然后细细体会
a =
2 1 3 1
1 2 1 2
2 1 3 2
1 3 1 2
b =
-1 -1 -1
0 0 0
1 1 1
e =
-1 0 1
-1 0 1
-1 0 1
c =
-3 -4 -5 -3
0 0 -1 -1
-1 -1 -1 0
3 6 6 5
d =
-3 -1 0 4
-4 -2 -1 7
-6 -1 0 5
-4 -1 0 4
第二部分:图像处理之卷积理解
来自:http://blog.****.net/jia20003/article/details/7038938
图像处理之理解卷积
一:什么是卷积
离散卷积的数学公式可以表示为如下形式:
f(x) = - 其中C(k)代表卷积操作数,g(i)代表样本数据, f(x)代表输出结果。
举例如下:
假设g(i)是一个一维的函数,而且代表的样本数为G = [1,2,3,4,5,6,7,8,9]
假设C(k)是一个一维的卷积操作数, 操作数为C=[-1,0,1]
则输出结果f(x)可以表示为 F=[1,2,2,2,2,2,2,2,1] //边界数据未处理
以上只是一维的情况下,当对一幅二维数字图像加以卷积时,其数学意义可以解释如下:
源图像是作为输入源数据,处理以后要的图像是卷积输出结果,卷积操作数作为Filter
在XY两个方向上对源图像的每个像素点实施卷积操作。如图所示:
粉红色的方格每次在X/Y前进一个像素方格,就会产生一个新的输出像素,图中深蓝色的代
表要输出的像素方格,走完全部的像素方格,就得到了所有输出像素。
图中,粉红色的矩阵表示卷积操作数矩阵,黑色表示源图像– 每个方格代表一个像素点。
二:卷积在数字图像处理中应用
一副数字图像可以看作一个二维空间的离散函数可以表示为f(x, y), 假设有对于二维卷积操
作函数C(u, v) ,则会产生输出图像g(x, y) = f(x, y) *C(u,v), 利用卷积可以实现对图像模糊处理,边缘检测,产生轧花效果的图像。
一个简单的数字图像卷积处理流程可以如下:
1. 读取源图像像素
2. 应用卷积操作数矩阵产生目标图像
3. 对目标图像进行归一化处理
4. 处理边界像素
三:一个纯Java的卷积模糊图像效果
四:关键代码解释
完成对像素点RGB颜色的卷积计算代码如下:
// red color
out3DData[row][col][1] =in3DData[row][col][1] +
in3DData[row-1][col][1] +
in3DData[row+1][col][1] +
in3DData[row][col-1][1] +
in3DData[row-1][col-1][1] +
in3DData[row+1][col-1][1] +
in3DData[row][col+1][1] +
in3DData[row-1][col+1][1] +
in3DData[row+1][col+1][1];
// green color
out3DData[row][col][2] =in3DData[row][col][2] +
in3DData[row-1][col][2] +
in3DData[row+1][col][2] +
in3DData[row][col-1][2] +
in3DData[row-1][col-1][2] +
in3DData[row+1][col-1][2] +
in3DData[row][col+1][2] +
in3DData[row-1][col+1][2] +
in3DData[row+1][col+1][2];
// blue color
out3DData[row][col][3] =in3DData[row][col][3] +
in3DData[row-1][col][3] +
in3DData[row+1][col][3] +
in3DData[row][col-1][3] +
in3DData[row-1][col-1][3] +
in3DData[row+1][col-1][3] +
in3DData[row][col+1][3] +
in3DData[row-1][col+1][3] +
in3DData[row+1][col+1][3];
计算归一化因子以及对卷积结果归一化处理的代码如下:
// find the peak data frominput and output pixel data.
int inpeak = 0;
int outPeak = 0;
for(int row=0; row<srcH; row++) {
for(int col=0; col<srcW; col++) {
if(inpeak < in3DData[row][col][1]) {
inpeak = in3DData[row][col][1];
}
if(inpeak < in3DData[row][col][2]) {
inpeak = in3DData[row][col][2];
}
if(inpeak < in3DData[row][col][3]) {
inpeak = in3DData[row][col][3];
}
if(outPeak < out3DData[row][col][1]) {
outPeak = out3DData[row][col][1];
}
if(outPeak < out3DData[row][col][2]) {
outPeak = out3DData[row][col][2];
}
if(outPeak < out3DData[row][col][3]) {
outPeak = out3DData[row][col][3];
}
}
}
// normalization
double outputScale = ((double) inpeak) / ((double)outPeak);
for(int row=0; row<srcH; row++) {
for(int col=0; col<srcW; col++) {
out3DData[row][col][1] = (int)(outputScale * out3DData[row][col][1]);
out3DData[row][col][2] = (int)(outputScale * out3DData[row][col][2]);
out3DData[row][col][3] = (int)(outputScale * out3DData[row][col][3]);
}
}
五:本文没有提及的内容 –边界像素处理
没有处理边缘像素,对边缘像素的处理,有两个可以参考的方法
其一是直接填充法– 超出边界部分的以边界像素填充。
其二是线性插值法– 超出边界部分的以 i/row的像素填充。
第三部分:矩阵卷积转换为矩阵相乘
来自:http://blog.****.net/anan1205/article/details/12313593
两个矩阵卷积转化为矩阵相乘形式——Matlab应用(这里考虑二维矩阵,在图像中对应)两个图像模糊(边缘)操作,假设矩阵A、B,A代表源图像,B代表卷积模板,那么B的取值决定最后运算的结果。
Matlab中的应用函数——conv2(二维卷积,一维对应conv)
函数给出的公式定义为:
同一维数据卷积一样,它的实质在于将卷积模板图像翻转(旋转180),这里等同于一维信号的翻转,然后将卷积模板依次从上到下、从左到右滑动,计算在模板与原始图像交集元素的乘积和,该和就作为卷积以后的数值。
为了验证后续矩阵卷积转化为矩阵相乘,这里给出的conv2的实例描述:
假设矩阵A(4*3)、B(2*3)如下:
首先,B需要旋转180,
命令旋转2次90即可:
B = rot90(rot90(B));或者B = rot90(h,2); 结果为:
其次:命令conv2函数:
C = conv2(A,B,‘shape’),该函数的具体操作图示:
依次计算直至结束,结果数据为:
shape的取值有三种,full代表返回卷积以后的全部数据,size为(mA+mB-1,nA+nB-1)的数据;same代表返回卷积以后的原图size (mA,nA)的部分数据;valid返回size为(mA-mB+1,nA-nB+1)的数据,指的是模板元素全部参加运算的结果数据,即源图像和模板的交集为模板。
矩阵卷积转化为矩阵相乘,网上也有很多方法,通俗化表示为:
A×B = B1*A1;
需要针对原始数据与模板数据做变换,变换过程如下:
首先进行周期延拓,补零:
M = mA+mB-1 = 5; N = nA+nB-1 = 5,对应卷积以后full数据大小。
那么初次换换的A和B为:
其次对A1和B1分别进行变换
转化B1——针对B1以及转换矩阵方法为:
将B1中的每一行向量依次按照B转化为一个方形矩阵Ba~Be,然后针对于每一个方形矩阵按照B矩阵组合成一个新的矩阵B1。B1矩阵的大小为((mA+mB-1)*(nA+nB-1),(mA+mB-1)*(nA+nB-1))。
转化A1——堆叠向量式
将上个步骤转换的A1按照行向量顺寻依次转化为一个列向量,那么列向量的大小为((mA+mB-1)*(nA+nB-1),1)大小。
针对实例:具体代码为:
周期延拓:
转化A——>A1
-
[m1,n1] = size(A); [m2,n2] = size(B); -
m=m1+m2-1;n=n1+n2-1; -
AA = padarray(A,[m2-1,n2-1],’post’);%%%补零 -
BB = padarray(B,[m1-1,n1-1],’post’);%%%补零 -
AA =AA’; -
A1 = AA(:);%%%%
转化B——>B1
- B2(1,:) = BB(1,:);
- for i =2:m
- B2(i,:) = BB(m-i+2,:);
- end %%%矩阵a ~ e的重新赋值
- B4 = zeros(n,n);%%%%%%%每一行转化的方阵
- B1 = zeros(m*n,m*n);%%%%%最后的矩阵
- for i =1:m%%%%%%%%几维向量
- B = B2(i,:);
- if sum(sum(abs(B))==0)
- B4 = zeros(n,n);
- else
- for j = 1:n%%%%%%%元素
- for k =0:n-1%%%%%%%%位置(搞定一行向量转化为方阵的形式)
- t = mod(j+k,n);
- if t==0
- t = n;
- end %%%end if
- B4(t,k+1) = B(j);
- end %%%end for
- end %%%end for
- for k =0:m-1%%%%%%%%每一个转换矩阵在大矩阵中的位置编号(搞定小方阵在大阵中的位置转化为大方阵的形式)
- t = mod(i+k,m);
- if t==0
- t = m;
- end %%%end if
- B1(k*n+1:(k+1)*n,(t-1)*n+1:t*n) = B4;
- end %%%end for
- end %%%end if else
- end %%%end for
结果数据转化:
-
Result = B1*A1; -
Result = reshape(Result,n,m); -
Result = Result’;
得到的结果等同于conv2的数据结果:
-
dd_data = [1,2,4,5,6;6,8,9,1,3;6,8,9,2,4;7,3,5,7,5;1,5,8,9,3]; % 5 * 5 -
f_k = [3,4,5;6,7,8;3,2,1]; % 3 * 3 -
dd_data_f_k = conv2(dd_data,f_k,’full’); % matlab 函数接口 -
dd_data1 = padarray(dd_data,[2 2],’both’); % 扩充原始数据 -
v_dd_data = im2col(dd_data1,[3 3]);% 块数据向量化 -
f_k1 = f_k(:); -
f_k1 = f_k1(end : -1 :1); -
f_k1 = f_k1‘; % 卷积核的表示 -
dd_data_f_k1 = f_k1 * v_dd_data; % 卷积转化为相乘 -
dd_data_f_k1 = reshape(dd_data_f_k1,[7 7]); % 转化为结果数据