简单的写一下卷积到底是一个什么计算过程。
假设有一个卷积核h,就一般为3*3的矩阵:
有一个待处理矩阵x:
h*x的计算过程分为三步
第一步,将卷积核翻转180°,也就是成为了
第二步,将卷积核h的中心对准x的第一个元素,然后对应元素相乘后相加,没有元素的地方补0。
这样结果Y中的第一个元素值Y11=1*0+2*0+1*0+0*0+0*1+0*2+-1*0+-2*5+-1*6=-16
第三步每个元素都像这样计算出来就可以得到一个输出矩阵,就是卷积结果
……………………
像这样计算,其他过程略了。
最后结果
注意:
我这里是用0补全原矩阵的,但我们不一定选择0。在Opencv的cvFilter2D函数中,就没有使用0来补全矩阵,而是用了边缘拷贝的方式,下一篇我会介绍Opencv的CvFilter2D函数卷积运算过程。
根据以上过程,java代码如下
public class test {
public double[][] datas;//数据元素
public int rows;//行数
public int cols;//列数
public test(int r, int c){
datas = new double[r][c];//创建一个二维数组对象,行和列共同构成一个数据元素
this.rows = r;
this.cols = c;
}
//卷积运算
public test conv(test Kernel){
int width =Kernel.rows/2;//核函数的1/2宽度
int length=Kernel.cols/2;//核函数的1/2宽度
test temp = new test(this.rows+2*width,this.cols+2*length);//构建一个新的矩阵,用于卷积计算————核矩阵为偶数?如何处理
//构建一个矩阵,用来进行计算
for(int i = width; i<this.rows+width;i++){//矩阵要乘的行数
for(int j = length;j<this.cols+length;j++){//每一行要乘的列数
temp.datas[i][j]=datas[i-width][j-length];//构建上下边缘为空的矩阵,上下距离width length
}
}
//循环计算结果矩阵中的值
for(int i = width; i<this.rows+width;i++){//矩阵要乘的行数
for(int j = length;j<this.cols+length;j++){//每一行要乘的列数
this.datas[i-width][j-length]=mutiKernel(Kernel,temp,i,j,width,length);//计算数值
}
}
return this;
}
//使用核函数计算结果矩阵中每一个位置的值,
public int mutiKernel(test Kernel,test temp,int i,int j,int width,int length) {
int number =0;
for(int m = 0; m<Kernel.rows;m++){//矩阵要乘的行数
for(int n = 0;n<Kernel.cols;n++){//每一行要乘的列数
number+=Kernel.datas[m][n]*temp.datas[i-width+m][j-length+n];//循环计算结果
}
}
return number;
}
public void display(){//打印矩阵
if(this == null) return;
for(int i = 0;i<this.rows;i++){
for(int j = 0;j<this.cols;j++){
System.out.print(this.datas[i][j]+"\t");
}
System.out.println();
}
}
public test turn(){//翻转矩阵 180度
double temp=0;
//水平翻转
for (int i = 0; i < this.rows / 2; i++) {
for (int j = 0; j < this.cols; j++) {
temp = this.datas[i][j];
this.datas[i][j] = this.datas[this.rows - 1 - i][j];
this.datas[this.rows - 1 - i][j] = temp;
}
}
//垂直翻转
for (int i = 0; i < this.rows; i++) {
for (int j = 0; j < this.cols / 2; j++) {
temp = this.datas[i][j];
this.datas[i][j] = this.datas[i][this.cols - 1 - j];
this.datas[i][this.cols - 1 - j] = temp;
}
}
return this;
}
public static void main(String[] args) {
//输入数值
System.out.println("第一个矩阵元素。。。");
test m = new test(3,3);
for(int i = 0; i<3;i++){
m.datas[1][i] = 0;
}
m.datas[0][0]=-1;
m.datas[0][1]=-2;
m.datas[0][2]=-1;
m.datas[2][0]=1;
m.datas[2][1]=2;
m.datas[2][2]=1;
m.display();
//核函数翻转
m.turn();
test m1 = new test(4,4);
int count=1;
System.out.println("第二个矩阵元素。。。");
for(int i = 0; i<4;i++){
for(int j = 0; j<4;j++){
m1.datas[i][j] = count++;
}
}
m1.display();
System.out.println("卷积的结果是: ");
//核矩阵 Kernel=m
m1.conv(m).display();
}
}