用C语言实现卷积操作(包含单通道单卷积和多通道多卷积以及维度的转换)

单通单卷积

卷积其实就是个点成相加的过程
原理图如下:
在这里插入图片描述
关键代码实现:

//单通道单卷积
#define IMG_SIZE 5
#define W_SIZE   3
#define OUT_SIZE 3   // (f-w+2p)/s + 1 = (5-3+0)/1+1 = 3
int conv(float img[IMG_SIZE][IMG_SIZE],float w[W_SIZE][W_SIZE],float out[OUT_SIZE][OUT_SIZE])
{
    float tmp=0.0;
    for(int k=0;k<=IMG_SIZE - W_SIZE;k++)  //特征平面的行  列平移 行卷积
    {

        for(int r=0;r<=IMG_SIZE - W_SIZE;r++) //特征平面的列  行平移  列卷积
        {
            tmp = 0.0;
            //单次卷积 点对点相乘 然后相加
            for(int i=0;i<W_SIZE;i++) //卷积的行
            {
                for(int j=0;j<W_SIZE;j++) //卷积的列
                {
                    tmp +=  img[i+k][j+r]*w[i][j];
                }
            }
            out[k][r] = tmp;
        }
    }
    return 0;
}

运行结果:

在这里插入图片描述

多通道多卷积

原理示意图:
在这里插入图片描述

关键代码实现如下:

//多通道多卷积
#define IMG_SIZE 7
#define IMG_CH   3
#define CONV_KERNEL_SIZE 3
#define CONV_KERNEL_CH 3
#define CONV_KERNEL_NUM 2
#define OUT_SIZE 5
#define OUT_CH 2 

/*
注意:这里把通道数放后面 是为了与后续 tensorflow 提取的参数保持一致 

如:
    # 进行卷积计算
    # x表示输入的描述表示4阶张量,类似[1,2,2,3], 由于使用一张图 于是可以写成[2][3][3]
    # 第一维参数表示一次性读取的图片数量
    # 第2、3维表示图片的分辨率,最后一维表示图片的通道数

    # w表示卷积核的表述,同样是4维张量
    # 第1、2维表示卷积核的分辨率,第3维表示图片的通道数
    # 第4维表示卷积核的个数
*/


/*
img : [7][7][3]      输入图像大小   7*7  有3个通道数
w   : [3][3][3][2]   权重大小       3*3  有3个通道数 一共有2个卷积核
out : [5][5][2]      输出图像大小   5*5  有2个通道数 (输出通道数等于卷积核的数量)

*/
int convs(  float img[IMG_SIZE][IMG_SIZE][IMG_CH],
            float w[CONV_KERNEL_SIZE][CONV_KERNEL_SIZE][CONV_KERNEL_CH][CONV_KERNEL_NUM],
            float out[OUT_SIZE][OUT_SIZE][OUT_CH])
{
    float tmp=0.0,tmp1=0.0;
    for(int m=0;m<CONV_KERNEL_NUM;m++)
    {
        for(int k=0;k<=IMG_SIZE - W_SIZE;k++)  //特征平面的行  列平移 行卷积
        {

            for(int r=0;r<=IMG_SIZE - W_SIZE;r++) //特征平面的列  行平移  列卷积
            {
                //多通道累加
                tmp1 = 0.0;
                for(int n=0;n<CONV_KERNEL_CH;n++)
                {
                    tmp = 0.0;
                    //单次卷积 点对点相乘 然后相加
                    for(int i=0;i<W_SIZE;i++) //卷积的行
                    {
                        for(int j=0;j<W_SIZE;j++) //卷积的列
                        {
                            tmp +=  img[i+k][j+r][n]*w[i][j][n][m];
                        }
                    }
                    //累加多个特征平面的卷积结果
                    tmp1 += tmp;
                }
                out[k][r][m] = tmp1;
            }
        }
    }
}

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

维度转换(多维转一维)

//实现多维转一维
#define IN_SIZE 3
#define IN_CH 3
#define OUT_LEN (IN_SIZE*IN_SIZE*IN_CH)
int convert(float in[IN_SIZE][IN_SIZE][IN_CH],float out[OUT_LEN])
{
    // float *p = out;
    for(int k=0;k<IN_CH;k++)
    {
        for(int i=0;i<IN_SIZE;i++)
        {
            for(int j=0;j<IN_SIZE;j++)
            {
                #if 0
                *p = in[i][j][k];
                #else 
                out[k*IN_SIZE*IN_SIZE+i*IN_SIZE+j] = in[i][j][k];
                #endif
            }
        }
    }
    return 0;
}

运行结果:
在这里插入图片描述

完整代码实现:

#include <stdio.h>

//单通道单卷积
#define IMG_SIZE 5
#define W_SIZE   3
#define OUT_SIZE 3   // (f-w+2p)/s + 1 = (5-3+0)/1+1 = 3
int conv(float img[IMG_SIZE][IMG_SIZE],float w[W_SIZE][W_SIZE],float out[OUT_SIZE][OUT_SIZE])
{
    float tmp=0.0;
    for(int k=0;k<=IMG_SIZE - W_SIZE;k++)  //特征平面的行  列平移 行卷积
    {

        for(int r=0;r<=IMG_SIZE - W_SIZE;r++) //特征平面的列  行平移  列卷积
        {
            tmp = 0.0;
            //单次卷积 点对点相乘 然后相加
            for(int i=0;i<W_SIZE;i++) //卷积的行
            {
                for(int j=0;j<W_SIZE;j++) //卷积的列
                {
                    tmp +=  img[i+k][j+r]*w[i][j];
                }
            }
            out[k][r] = tmp;
        }
    }
    return 0;
}
int conv_test()
{

    float img_in[IMG_SIZE][IMG_SIZE] = { 1,1,1,0,0,
								    0,1,1,1,0,
								    0,0,1,1,1,
								    0,0,1,1,0,
								    0,1,1,0,0};
								
	float conv_w[W_SIZE][W_SIZE] = {  1,0,1,
									  0,1,0,
									  1,0,1};

	float img_out[OUT_SIZE][OUT_SIZE] = {0};

    //调用卷积函数 
    conv(img_in,conv_w,img_out);

    int i,j;
    printf("Input picture:\n");
    for(i=0;i<5;i++)
    {
        for(j=0;j<5;j++)
        {
            printf("%.1f ",img_in[i][j]);
        }
        printf("\n");
    }

    printf("Input weight:\n");
    for(i=0;i<3;i++)
    {
        for(j=0;j<3;j++)
        {
            printf("%.1f ",conv_w[i][j]);
        }
        printf("\n");
    }

    printf("Output picture:\n");
    for(i=0;i<3;i++)
    {
        for(j=0;j<3;j++)
        {
            printf("%.1f ",img_out[i][j]);
        }
        printf("\n");
    }
    return 0;
}

//多通道多卷积
#define IMG_SIZE 7
#define IMG_CH   3
#define CONV_KERNEL_SIZE 3
#define CONV_KERNEL_CH 3
#define CONV_KERNEL_NUM 2
#define OUT_SIZE 5
#define OUT_CH 2 

/*
注意:这里把通道数放后面 是为了与后续 tensorflow 提取的参数保持一致 

如:
    # 进行卷积计算
    # x表示输入的描述表示4阶张量,类似[1,2,2,3], 由于使用一张图 于是可以写成[2][3][3]
    # 第一维参数表示一次性读取的图片数量
    # 第2、3维表示图片的分辨率,最后一维表示图片的通道数

    # w表示卷积核的表述,同样是4维张量
    # 第1、2维表示卷积核的分辨率,第3维表示图片的通道数
    # 第4维表示卷积核的个数
*/


/*
img : [7][7][3]      输入图像大小   7*7  有3个通道数
w   : [3][3][3][2]   权重大小       3*3  有3个通道数 一共有2个卷积核
out : [5][5][2]      输出图像大小   5*5  有2个通道数 (输出通道数等于卷积核的数量)

*/
int convs(  float img[IMG_SIZE][IMG_SIZE][IMG_CH],
            float w[CONV_KERNEL_SIZE][CONV_KERNEL_SIZE][CONV_KERNEL_CH][CONV_KERNEL_NUM],
            float out[OUT_SIZE][OUT_SIZE][OUT_CH])
{
    float tmp=0.0,tmp1=0.0;
    for(int m=0;m<CONV_KERNEL_NUM;m++)
    {
        for(int k=0;k<=IMG_SIZE - W_SIZE;k++)  //特征平面的行  列平移 行卷积
        {

            for(int r=0;r<=IMG_SIZE - W_SIZE;r++) //特征平面的列  行平移  列卷积
            {
                //多通道累加
                tmp1 = 0.0;
                for(int n=0;n<CONV_KERNEL_CH;n++)
                {
                    tmp = 0.0;
                    //单次卷积 点对点相乘 然后相加
                    for(int i=0;i<W_SIZE;i++) //卷积的行
                    {
                        for(int j=0;j<W_SIZE;j++) //卷积的列
                        {
                            tmp +=  img[i+k][j+r][n]*w[i][j][n][m];
                        }
                    }
                    //累加多个特征平面的卷积结果
                    tmp1 += tmp;
                }
                out[k][r][m] = tmp1;
            }
        }
    }
}


int convs_test()
{
    float input[IMG_CH][IMG_SIZE][IMG_SIZE] = {
	0,0,0,0,0,0,0,
	0,1,1,2,2,1,0,
	0,1,1,1,2,1,0,
	0,2,1,1,0,2,0,
	0,2,1,0,1,2,0,
	0,2,1,2,2,2,0,
	0,0,0,0,0,0,0,
	
	0,0,0,0,0,0,0,
	0,0,1,2,0,1,0,
	0,2,2,1,1,0,0,
	0,2,1,0,0,2,0,
	0,1,0,0,0,2,0,
	0,0,1,0,1,2,0,
	0,0,0,0,0,0,0,
	
	0,0,0,0,0,0,0,
	0,2,2,0,1,2,0,
	0,0,0,2,1,2,0,
	0,2,1,0,2,1,0,
	0,1,1,0,0,0,0,
	0,0,0,1,1,1,0,
	0,0,0,0,0,0,0,};

    

    
	
	float conv_w[CONV_KERNEL_NUM][CONV_KERNEL_CH][CONV_KERNEL_SIZE][CONV_KERNEL_SIZE] = {
	1,1,1,
	-1,-1,0,
	-1,1,0,
	
	-1,-1,1,
	-1,1,0,
	-1,1,0,
	
	1,0,-1,
	0,0,0,
	1,-1,-1,
	
	0,0,-1,
	-1,1,1,
	0,0,0,
	
	0,0,1,
	1,0,1,
	0,-1,-1,
	
	-1,1,1,
	0,1,1,
	1,-1,1};

  
    //由于输入的图像是正规的数据排列 ,但是函数调用是图像数据需要做预处理
    float input1[IMG_SIZE][IMG_SIZE][IMG_CH] = {0};
    float conv_w1[CONV_KERNEL_SIZE][CONV_KERNEL_SIZE][CONV_KERNEL_CH][CONV_KERNEL_NUM] = {0};
    float img_out[OUT_SIZE][OUT_SIZE][OUT_CH] = {0};
  
    int i,j,k,r,m,n;

    printf("Input picture\n");
    for(k =0;k<IMG_CH;k++)
    {
        for(i=0;i<IMG_SIZE;i++)
        {
            for(j=0;j<IMG_SIZE;j++)
            {
                input1[i][j][k] = input[k][i][j];
                printf("%.1f  ",input1[i][j][k]);
            }
            printf("\n");
        }
        printf("\n");
    }
    printf("\n");

    printf("CONV_KERNEL:\n");
    for(r=0;r<CONV_KERNEL_NUM;r++)
    {
        for(k=0;k<CONV_KERNEL_CH;k++)
        {
            for(i=0;i<CONV_KERNEL_SIZE;i++)
            {
                for(j=0;j<CONV_KERNEL_SIZE;j++)
                {
                    conv_w1[i][j][k][r] = conv_w[r][k][i][j];
                    printf("%.1f  ",conv_w1[i][j][k][r]);
                }
                printf("\n");
            }
            printf("\n");
        }
    }
    printf("\n");

    convs(input1,conv_w1,img_out);

    printf("Output picture\n");
    for(k=0;k<OUT_CH;k++)
    {
        for(i=0;i<OUT_SIZE;i++)
        {
            for(j=0;j<OUT_SIZE;j++)
            {
                printf("%.1f   ",img_out[i][j][k]);
            }
            printf("\n");
        }
        printf("\n");
    }
    printf("\n");
    return 0;

}

//实现多维转一维
#define IN_SIZE 3
#define IN_CH 3
#define OUT_LEN (IN_SIZE*IN_SIZE*IN_CH)
int convert(float in[IN_SIZE][IN_SIZE][IN_CH],float out[OUT_LEN])
{
    // float *p = out;
    for(int k=0;k<IN_CH;k++)
    {
        for(int i=0;i<IN_SIZE;i++)
        {
            for(int j=0;j<IN_SIZE;j++)
            {
                #if 0
                *p = in[i][j][k];
                #else 
                out[k*IN_SIZE*IN_SIZE+i*IN_SIZE+j] = in[i][j][k];
                #endif
            }
        }
    }
    return 0;
}

int convert_test()
{
    float input[IN_CH][IN_SIZE][IN_SIZE] = {
	1,1,1,
	-1,-1,0,
	-1,1,0,
	
	-1,-1,1,
	-1,1,0,
	-1,1,0,
	
	1,0,-1,
	0,0,0,
	1,-1,-1,
	};

    float input1[IN_SIZE][IN_SIZE][IN_CH] = {0};
    float output[OUT_LEN] ={0};
    int i,j,k,r,m,n;
    float *p = output;
    printf("Input picture\n");
    for(k=0;k<IN_CH;k++)
    {
        for(i=0;i<IN_SIZE;i++)
        {
            for(j=0;j<IN_SIZE;j++)
            {
                input1[i][j][k] = input[k][i][j];
                printf("%.1f  ",input1[i][j][k]);
            }
            printf("\n");
        }
        printf("\n");
    }
    printf("\n");

    convert(input1,output);

    printf("Output picture\n");
    for(k=0;k<IN_CH;k++)
    {
        for(i=0;i<IN_SIZE;i++)
        {
            for(j=0;j<IN_SIZE;j++)
            {
                // printf("%.1f  ",*p);
                // p++;
                // printf("%.1f   ",output[i][j][k]);
                printf("%.1f  ",output[k*IN_SIZE*IN_SIZE+i*IN_SIZE+j]);
                // printf("%.1f  ",output[k*IN_SIZE*IN_SIZE+i*IN_SIZE+j]);
            }
        }
    }
    return 0;

}

int main()
{
    //conv_test();
    //convs_test();
    convert_test();
    return 0;

}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值