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

2023-05-16

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

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • HTTP请求头Authorization

    今天部署了一个Authorization项目 xff0c 由于改了auth服务器客户端id和密码 xff0c 而前端请求header没有修改 xff0c 登录时一直弹框要求输入用户名和密码 xff0c 输入后却无效 xff0c 只好改前端代
  • c语言编程规范第三部分

    3 头文件应向稳定的方向包含 头文件的包含关系是一种依赖 xff0c 一般来说 xff0c 应当让不稳定的模块依赖稳定的模块 xff0c 从而当不稳定的模块发生变化时 xff0c 不会影响 xff08 编译 xff09 稳定的模块 就我们的
  • 看完《C++ primer》后,我都收获了什么?

    文章目录 1 语言只是一个工具2 光学会一门语言还不够3 结语 迫于读研和未来就业的压力 xff0c 我研一上自学了C 43 43 的这门语言 xff0c 自我感觉比较认真的看完了 C 43 43 primer 这本书的大部分章节 xff0
  • Ubuntu18.04 ROS Melodic版本安装gazebo_ros_pkgs包

    疫情期间宅在家没法做实验 xff0c 只能上手gazebo做仿真 xff0c 导入实验室机器人的相关文件后 xff0c 打算看看效果 xff0c 运行代码 roslaunch probot gazebo probot anno gazebo
  • vim如何批量注释

    1 ctrl 43 v 进入 2 按箭头 选中要注释的首行 3 按下大写 i 进入插入模式 4输入 按esc 就可以看到如下内容
  • 还弄不明白【委托和事件】么?看这一篇就足够了!!!

    这两个概念长时间不用了 xff0c 今天看到CAD二次开发中用到了自定义事件 xff0c 有点迷糊了 xff0c 索性再整理一下加深印象 xff01 一说到委托和事件 xff0c 他们总是绑定在一起的不可分割 xff01 可能你会说啊 xf
  • 三种简单排序(冒泡、插入、选择)的比较和图解

    冒泡排序 这种排序方式是最容易理解的 xff0c 主体思想就是 xff1a 指针重复地走访过要排序的数列 xff0c 一次比较两个元素 xff0c 如果他们的顺序错误就把他们交换过来 走访数列的工作是重复地进行直到没有再需要交换 xff0c
  • ModuleNotFoundError: No module named 'XXX'的解决办法

    类似问题一 ModuleNotFoundError No module named 39 captcha 39 pycharm打开的项目 xff0c 在虚拟环境里通过终端命令python manage py runserver运行pytho
  • 微信小程序实现搜索功能以及效果(超详细)

    我们先来看一下实现哪些功能 1 搜索历史记录以及清空历史记录 2 热门搜索推荐以及更新推荐内容 3 根据输入框输入的内容来自动搜索相关查询 后台逻辑是模糊查询 后台就先不扯了 这里我用的是自己定义的虚拟数据 暂时没用后台接口 可能有点问题
  • 微信小程序实现收货地址城市选择效果(添加收货地址)

    先来张效果图 这里主要是城市选择效果 请忽视其他 不要吐槽 谢谢 接下来看一下代码吧 wxml lt pages my my add address index wxml gt lt view class 61 34 redact addr
  • uni-app实现商城多商家购物车功能(超详细, 附带源码)

    我们先来看一下效果 有什么不懂可以直接下方留言 先来看代码 lt template gt lt view class 61 34 cart 34 gt lt 购物车为空 S gt lt view v if 61 34 cartList le
  • 微信小程序web-view的使用教程

    最近公司有需求 xff0c 需要点击小程序首页banner xff0c 跳转到别人的h5页面 首先是域名的问题 xff1a 步骤 xff1a 先登录小程序开发平台 xff0c 将页面需要跳转的域名写上去 xff0c 注意了 xff0c 域名
  • uni-app实现上传图片裁剪效果(附源码)

    我们先来看一下效果 封装一个组件在components下创建一个 文件夹随意命名 xff0c 这里我是uni img cropper uni img cropper vue lt template gt lt view class 61 3
  • js 一维数组和二维数组实现足迹、浏览记录等场景

    再开发过程中 xff0c 再没有接口提供的情况下来实现浏览记录或者搜索记录等场景 我们可以利用本地缓存实现 xff0c 废话不多说 xff0c 直接上代码 xff1a 多维数组 64 param Array arr 数组 64 param
  • Markdown使用(超详细)

    xff08 HBuilderX xff09 掌握md及HBuilderX对md的强大支持 如果没有点右键设置自动换行 xff0c 可按Alt 43 滚轮横向滚动查看 很多人只把markdown用于网络文章发表 xff0c 这糟蹋了markd
  • 宫格导航 (自定义更灵活,超详细)

    先来看一下效果 调用方法 lt 页面调用 gt lt nav grid list 61 34 menu 34 64 click 61 34 34 gt lt nav grid gt 数据 export default data return
  • Firefox和Chromedriver驱动下载及安装步骤

    Mozilla Firefox Mozilla Firefox 中文名称 34 火狐 34 是由一个自由及开放源代码的网页浏览器 使用Gecko排版引擎 支持多种操作系统 如 Windows Mac OS X 及GNU Linux等 该浏览
  • 流媒体选择Nginx是福还是祸?

    流媒体选择Nginx是福还是祸 xff1f CDN xff0c 视频云 xff0c 已经 僧多粥少 视频直播的持续升温 xff0c 无意间也让带宽生意的争夺变得异常残酷 一时间 xff0c 各种云计算 CDN 视频云提供商都在视频尤其是直播
  • xpath去除换行\空格

    使用xpath获取文本内容 有空格或者换行就用normalize space 方法 例 intro li 61 div xpath 39 normalize space div 64 class 61 34 bookinfo 34 p te
  • Java 静态域和静态方法

    main方法都被标记为static修饰符 xff0c 本文讨论一下该修饰符含义 静态域和静态方法 一 静态域静态常量 二 静态方法三 工厂方法 一 静态域 若把域定义为static xff0c 则每个类中只有一个这样的域 而每一个对象对于所

随机推荐