首页 > IT职业 > IT生活 > Yolo的卷积运算源码图解之im2col.c
yolo卷积源码解读找了好久,但网上好多的解读,有点误人子弟,
之前还找到一片注释写的比源代码还多几倍的,以为我就这样看懂了?
不存在的 QWQ,后来直接自己看懂了源码,却看不懂了注释
无奈.jpg
于是有了下面的自己理解:
#include "im2col.h"#include/* ** 从输入的多通道数组im(存储图像数据)中获取指定行、列、、通道数处的元素值 ** 输入: im 输入,所有数据存成一个一维数组,例如对于3通道的二维图像而言, ** 每一通道按行存储(每一通道所有行并成一行),三通道依次再并成一行 ** height 每一通道的高度(即输入图像的真正的高度,补0之前) ** width 每一通道的宽度(即输入图像的宽度,补0之前) ** channels 输入im的通道数,比如彩色图为3通道,之后每一卷积层的输入的通道数等于上一卷积层卷积核的个数 ** row 要提取的元素所在的行(二维图像补0之后的行数) ** col 要提取的元素所在的列(二维图像补0之后的列数) ** channel 要提取的元素所在的通道 ** pad 图像左右上下各补0的长度(四边补0的长度一样) ** 返回: float类型数据,为im中channel通道,row-pad行,col-pad列处的元素值 ** 注意:在im中并没有存储补0的元素值,因此height,width都是没有补0时输入图像真正的 ** 高、宽;而row与col则是补0之后,元素所在的行列,因此,要准确获取在im中的元素值, ** 首先需要减去pad以获取在im中真实的行列数 */float im2col_get_pixel(float *im, int height, int width, int channels, int row, int col, int channel, int pad){ // 减去补0长度,获取元素真实的行列数 row -= pad; col -= pad; // 如果行列数小于0,则返回0(刚好是补0的效果) if (row = height || col >= width) return 0; // im存储多通道二维图像的数据的格式为:各通道所有行并成一行,再多通道依次并成一行, // 因此width*height*channel首先移位到所在通道的起点位置,加上width*row移位到 // 所在指定通道所在行,再加上col移位到所在列 // im[col + width*(row + height*channel)]=im[col+width*row+width*height*channel] return im[col + width*(row + height*channel)]; }//From Berkeley Vision's Caffe!//https://github.com/BVLC/caffe/blob/master/LICENSE/* 将输入图片转为便于计算的数组格式,可以参考https://petewarden.com/2015/04/20/why-gemm-is-at-the-heart-of-deep-learning/ 进行辅助理解(但执行方式并不同,只是用于概念上的辅助理解),由作者的注释可知,这是直接从caffe移植过来的 输入: data_im 输入图像 channels 输入图像的通道数(对于第一层,一般是颜色图,3通道,中间层通道数为上一层卷积核个数) height 输入图像的高度(行) width 输入图像的宽度(列) ksize 卷积核尺寸 stride 卷积核跨度 pad 四周补0长度 data_col 相当于输出,为进行格式重排后的输入图像数据 注: data_col还是按行排列, 行数为channels*ksize*ksize, 列数为height_col*width_col,即一张特征图总的元素个数, */void im2col_cpu(float* data_im, int channels, int height, int width, int ksize, int stride, int pad, float* data_col) { int c,h,w; // 卷积后的尺寸计算,这里width_col=width int height_col = (height + 2*pad - ksize) / stride + 1; int width_col = (width + 2*pad - ksize) / stride + 1; /// 卷积核大小:ksize*ksize是一个卷积核的大小,通道数channels int channels_col = channels * ksize * ksize; // 获取channels_col个对应像素核 for (c = 0; c 看不懂?,莫方,莫方,看例子:
例子:
假设有输入data_im和卷积核如下:
这里写图片描述
那么卷积核每次划过的像素:
这里写图片描述
可知相关参数:
height=4width=4channels=1//单通道ksize=3pad=1stride=1 //这里假设为1所以:
height_col = (height + 2*pad - ksize) / stride + 1=4 width_col = (width + 2*pad - ksize) / stride + 1=4 channels_col = channels * ksize * ksize=9那么接下来怎么运算呢?
当c==1:
先获取输入(加了pading的)的第一行前4个数:0 0 0 0想下移动Stride行(这里例子取1),再取四位,即:
0 1 2 3按上操作,往下移,取值:
0 5 6 70 9 10 11最终得到
data_col的第一行所有像素值c不断自加,循环,最后得到data_col像素分布,最终结果如下:
这里写图片描述
最终的卷积运算:
由上知:
卷积核的ksize=3,展开后形状为channelsx(ksizexksize), 即1x9,
而data_col形状为channels_colx(height_colxheight_col),即9x16,所以最终yolo会在通过
convolutional_layer.c里的forward_convolutional_layer函数里的gemm函数计算卷积核l.weights和data_col的矩阵乘积,完成卷积操作好绕~
作者:蜜丝特潮
链接:https://www.jianshu.com/p/b723ebf95fb3
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/2260/viewspace-2810649/ ,如需转载,请注明出处,否则将追究法律责任。