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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shuiliusheng/article/details/79949113

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

  1. 卷积和池化的具体解释(比较清晰直观):
    http://www.hackcv.com/index.php/archives/104/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

  2. 图像卷积的实现

    • 图像卷积的原理图解
      这里写图片描述
    • 具体实现——卷积模板(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];
        }
    }
    }
  3. 最大池化的实现

    • 最大池化的原理
      这里写图片描述

    • 具体实现

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

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

猜你喜欢

转载自blog.csdn.net/shuiliusheng/article/details/79949113