版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shuiliusheng/article/details/79949113
Convolution and Max Pooling of CNN (卷积和池化的实现)
卷积和池化的具体解释(比较清晰直观):
http://www.hackcv.com/index.php/archives/104/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io图像卷积的实现
- 图像卷积的原理图解
- 具体实现——卷积模板(Filter)
double filter0[3][3]={{0,0,0},{0,1,0},{0,0,0}}; double filter1[3][3]={{1/16.0,1/16.0,1/16.0},{1/16.0,8/16.0,1/16.0},{1/16.0,1/16.0,1/16.0}}; double filter2[3][3]={{-1,0,0},{0,1,0},{0,0,0}}; double filter3[3][3]={{0,-1,0},{0,1,0},{0,0,0}}; double filter4[3][3]={{0,0,0},{-1,0,1},{0,0,0}}; double filter5[3][3]={{1.0/9,1.0/9,1.0/9},{1.0/9,1.0/9,1.0/9},{1.0/9,1.0/9,1.0/9}}; double filter6[3][3]={{0,0,0},{-1,2,-1},{0,0,0}}; double filter7[3][3]={{0,1,0},{1,-4,1},{0,1,0}}; double filter8[3][3]={{0,0,-1},{0,1,0},{0,0,0}}; double filter9[3][3]={{0,0,0},{-1,1,0},{0,0,0}}; double filter10[3][3]={{0,-1,0},{0,0,0},{0,1,0}}; double filter11[3][3]={{1/16.0,2/16.0,1/16.0},{2/16.0,4/16.0,2/16.0},{1/16.0,2/16.0,1/16.0}}; double filter12[3][3]={{0,-1,0},{0,2,0},{0,-1,0}}; double filter13[3][3]={{1,1,1},{1,-7,1},{1,1,1}}; double filter14[3][3]={{0,0,0},{0,1,-1},{0,0,0}}; double filter15[3][3]={{0,0,0},{0,1,0},{-1,0,0}}; double filter16[3][3]={{-1,0,0},{0,0,0},{0,0,1}}; double filter17[3][3]={{1/16.0,1/16.0,1/16.0},{2/16.0,6/16.0,2/16.0},{1/16.0,1/16.0,1/16.0}}; double filter18[3][3]={{-1,0,0},{0,2,0},{0,0,-1}}; double filter19[3][3]={{-1,-1,-1},{-1,8,-1},{-1,-1,-1}}; double filter20[3][3]={{0,0,0},{0,1,0},{0,-1,0}}; double filter21[3][3]={{0,0,0},{0,1,0},{0,0,-1}}; double filter22[3][3]={{0,0,-1},{0,0,0},{1,0,0}}; double filter23[3][3]={{1/16.0,2/16.0,1/16.0},{1/16.0,6/16.0,1/16.0},{1/16.0,2/16.0,1/16.0}}; double filter24[3][3]={{0,0,-1},{0,2,0},{-1,0,0}}; double filter25[3][3]={{0,-1,0},{-1,5,-1},{0,-1,0}};
- 具体实现——输入输出数据格式
typedef struct{ int W;//图像的宽 int H;//图像的高 int num;//图像个数 byte ***data;//具体数据 }Data;
- 具体实现——卷积的实现
/* 功能:卷积 输入: Data input:要进行卷积的图像数据的总和 out_num:指定卷积之后图像的个数 start_filter:指定从哪一个filter开始进行卷积 border:true代表保留原图像的边界,图像大小不变;false忽略边界,图像大小发生变化 输出: Data &output:卷积之后的图像的总和 */ bool convolution(Data input,Data &output,int out_num,int start_filter,bool border) { if(!init_output(output,input,out_num,MAX_FILTERS,border)) return false; for(int i=0;i<input.num;i++) { for(int j=0;j<out_num;j++) { int filter=(j+start_filter)%MAX_FILTERS; if(border) convolution_once_border(input.data[i],output.data[j], input.W,input.H,filter); else convolution_once_noborder(input.data[i],output.data[j], input.W,input.H,filter); } } return true; } /* 功能:初始化output */ bool init_output(Data &output,Data input,int out_num,int use_filter,bool border) { cout <<"Conv: "; if(input.num*use_filter<out_num) { cout <<"Inputs :"<<input.num<<" | "; cout <<"Filters:"<<use_filter<<" | "; cout <<"Max outputs:"<<input.num*use_filter<<endl; cout <<"Require outputs:"<<out_num<<endl; return false; } if(!border) { if(input.W<5||input.H<5) { cout <<"Use border mode, Output Image too small:"<< input.W-2<<" "<<input.H-2<<endl; return false; } output.W=input.W-2; output.H=input.H-2; } else { output.W=input.W; output.H=input.H; } output.num=out_num; printf("Input Image:%d*%d*%d | Output Image:%d*%d*%d\n", input.num,input.W,input.H,output.num,output.W,output.H); malloc_data(output); return true; } /* 功能:为output的data字段分配实际的空间 */ void malloc_data(Data &output) { output.data=(byte ***)malloc(sizeof(byte **)*output.num); for(int i=0;i<output.num;i++) { output.data[i]=(byte **)malloc(sizeof(byte *)*output.H); for(int j=0;j<output.H;j++) output.data[i][j]=(byte*)malloc(sizeof(byte)*output.W); } } /* 功能:考虑边界的单张图像的卷积操作 输入: byte **input:单张图像的数据 int W,int H:图像的长宽 int filter:卷积要使用的filter 输出: byte **output */ void convolution_once_border(byte **input,byte **output,int W,int H,int filter) { double m[3][3]={0}; for(int r=0;r<H;r++) { for(int c=0;c<W;c++) { //拷贝出3*3大小的矩阵数据用于卷积 copy_metrix(input,r-1,c-1,W,H,m); //卷积:矩阵乘法 byte t=(byte)mult_metrix(m,Filters[filter]); output[r][c]=t; } } for(int r=0;r<H;r++) { output[r][0]=output[r][1]; output[r][W-1]=output[r][W-2]; } for(int c=0;c<W;c++) { output[0][c]=output[1][c]; output[H-1][c]=output[H-2][c]; } } /* 功能:不考虑边界的单张图像的卷积操作 输入: byte **input:单张图像的数据 int W,int H:图像的长宽 int filter:卷积要使用的filter 输出: byte **output */ oid convolution_once_noborder(byte **input,byte **output,int W,int H,int filter) { double m[3][3]; for(int r=1;r<H-1;r++) { for(int c=1;c<W-1;c++) { copy_metrix(input,r-1,c-1,W,H,m); output[r-1][c-1]=(byte)mult_metrix(m,Filters[filter]); } } } /* 功能:拷贝用于卷积运算的3*3矩阵 输入: byte **input:单张图像的数据 int r,int c:3*3矩阵的(0,0)点的实际位置 int W,int H:图像的长宽 输出: double m[3][3] */ void copy_metrix(byte **input,int r,int c,int W,int H,double m[3][3]) { for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { int t1=r+i; int t2=c+j; if(t1<0)t1=0; if(t2<0)t2=0; if(t1>=H)t1=H-1; if(t2>=W)t2=W-1; m[i][j]=input[t1][t2]; } } }
- 图像卷积的原理图解
最大池化的实现
最大池化的原理
具体实现
/* 功能:最大池化 输入: Data input:要进行池化的图像数据的总和 int step:池化的步长,上图中的窗口大小 输出: Data &output:池化之后的图像的总和 */ bool max_pooling(Data input,Data &output,int step) { if(!init_output(input,output,step)) return false; for(int i=0;i<input.num;i++) { max_pooling_once(input.data[i],output.data[i], input.W,input.H,output.W,output.H,step); } return true; } /* 功能:初始化output */ bool init_output(Data input,Data &output,int step) { cout <<"Pooling:"; if(input.W/step+1<3||input.H/step+1<3) { cout <<"The Output Image too small:"<<input.W/step+1<<" "<<input.H/step+1<<endl; return false; } output.num=input.num; output.H=input.H/step+1; output.W=input.W/step+1; printf("Input Image:%d*%d*%d | Output Image:%d*%d*%d\n",input.num,input.W,input.H,output.num,output.W,output.H); malloc_data(output); return true; } /* 功能:单张图像的最大池化操作 输入: byte **input:单张图像的数据 int W,int H:图像的长宽 int dW,int dH:目的图像的长宽 int step:池化步长 输出: byte **output */ void max_pooling_once(byte **input,byte **output,int W,int H,int dW,int dH,int step) { byte *m=(byte *)malloc(sizeof(byte)*step*step); for(int r=0;r<dH;r++) { for(int c=0;c<dW;c++) { copy_array(input,r*step,c*step,W,H,step,m); output[r][c]=find_max(m,step); } } } // 功能:拷贝池化窗口内的数据到数组内 void copy_array(byte **input,int r,int c,int W,int H,int step,byte *m) { for(int i=0;i<step;i++) { for(int j=0;j<step;j++) { int t1=r+i; int t2=c+j; if(t1>=H)t1=H-1; if(t2>=W)t2=W-1; m[i*step+j]=input[t1][t2]; } } } /* 功能:寻找数组中的最大值 */ byte find_max(byte *m,int step) { int l=step*step; int max=0; for(int i=0;i<l;i++) if(m[i]>max) max=m[i]; return max; }
示例结果
输出数据 Raw Image Size (516*415) convert to (500*400) Conv: Input Image:1*500*400 | Output Image:18*500*400 Pooling:Input Image:18*500*400 | Output Image:18*126*101 Conv: Input Image:18*126*101 | Output Image:36*126*101 Pooling:Input Image:36*126*101 | Output Image:36*32*26 Conv: Input Image:36*32*26 | Output Image:161*32*26 Pooling:Input Image:161*32*26 | Output Image:161*17*14 Conv: Input Image:161*17*14 | Output Image:528*17*14 Pooling:Input Image:528*17*14 | Output Image:528*9*8
github:https://github.com/Shuiliusheng/2018/tree/master/C-_for_NN/Feature-Convolution_and_Pool