# Convolution and Max Pooling of CNN (卷积和池化的实现)

1. 图像卷积的实现

• 图像卷积的原理图解
• 具体实现——卷积模板（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];
}
}
}
2. 最大池化的实现

• 最大池化的原理

• 具体实现

/*
功能：最大池化
输入：
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;
}
3. 示例结果

输出数据
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