用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;

}
  • 11
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是 C 语言实现卷积操作的代码和详细注释: ```c #include <stdio.h> #define ROWS 3 // 卷积核的行数 #define COLS 3 // 卷积核的列数 // 定义卷积核 int kernel[ROWS][COLS] = { {1, , -1}, {1, , -1}, {1, , -1} }; // 定义输入矩阵 int input[5][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}, {16, 17, 18, 19, 20}, {21, 22, 23, 24, 25} }; // 定义输出矩阵 int output[3][3]; int main() { // 遍历输出矩阵的每一个元素 for (int i = ; i < 3; i++) { for (int j = ; j < 3; j++) { // 计算卷积结果 int sum = ; for (int k = ; k < ROWS; k++) { for (int l = ; l < COLS; l++) { sum += input[i + k][j + l] * kernel[k][l]; } } output[i][j] = sum; } } // 输出结果 for (int i = ; i < 3; i++) { for (int j = ; j < 3; j++) { printf("%d ", output[i][j]); } printf("\n"); } return ; } ``` 注释: 1. `#include <stdio.h>`:引入标准输入输出库。 2. `#define ROWS 3` 和 `#define COLS 3`:定义卷积核的行数和列数。 3. `int kernel[ROWS][COLS]`:定义卷积核。 4. `int input[5][5]`:定义输入矩阵。 5. `int output[3][3]`:定义输出矩阵。 6. `for (int i = ; i < 3; i++)` 和 `for (int j = ; j < 3; j++)`:遍历输出矩阵的每一个元素。 7. `for (int k = ; k < ROWS; k++)` 和 `for (int l = ; l < COLS; l++)`:遍历卷积核的每一个元素。 8. `sum += input[i + k][j + l] * kernel[k][l]`:计算卷积结果。 9. `output[i][j] = sum`:将卷积结果存入输出矩阵。 10. `printf("%d ", output[i][j])`:输出结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值