参考文献依然是放前面:https://blog.csdn.net/caicaiatnbu/category_9096319.html
darknet版本: https://github.com/AlexeyAB/darknet,与原始的版本还是有一点区别的。
因为第一次读源码,我就直接按照参考文献的顺序来了,到时候再查漏补缺,加油!
【只分析原理,弄明白具体的功能,就不去看gpu了,等以后有时间再补.cu文件】
首先你要弄明白一个原理:im2col和col2im的原理,链接如下
https://blog.csdn.net/u013066730/article/details/86489139?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.nonecase
其次,要先去看一下im2col的代码,链接如下:
https://blog.csdn.net/weixin_38715903/article/details/105978855
最后开始看代码
1.col2im.h
#ifndef COL2IM_H
#define COL2IM_H
#ifdef __cplusplus
extern "C" {
#endif
void col2im_cpu(float* data_col,
int channels, int height, int width,
int ksize, int stride, int pad, float* data_im);
void col2im_cpu_ext(const float* data_col, const int channels,
const int height, const int width, const int kernel_h, const int kernel_w,
const int pad_h, const int pad_w,
const int stride_h, const int stride_w,
const int dilation_h, const int dilation_w,
float* data_im);
#ifdef GPU
void col2im_ongpu(float *data_col,
int channels, int height, int width,
int ksize, int stride, int pad, float *data_im);
void col2im_gpu_ext(const float* data_col, const int channels,
const int height, const int width, const int kernel_h, const int kernel_w,
const int pad_h, const int pad_w, const int stride_h,
const int stride_w, const int dilation_h, const int dilation_w,
float* data_im);
#endif
#ifdef __cplusplus
}
#endif
#endif
2.col2im.c
#include
#include
#include
#include "col2im.h"
/**
* 将输入图像im的channel通道上的第row行,col列像素灰度值加上val(直接修改im的值,因此im相当于是返回值)
* @param im 输入图像
* @param height 输入图像的高度
* @param width 宽度
* @param channels 通道数
* @param row 需要加上val的像素所在的行数
* @param col 需要加上val的像素所在的列数
* @param channel 需要加上val的像素所在的通道数
* @param pad 补0的长度
* @param val 像素灰度值
*/
void col2im_add_pixel(float *im, int height, int width, int channels,
int row, int col, int channel, int pad, float val)
{
//只在img部分上修改像素值,所以要减去pad的值,获得padding之前的宽高值
row -= pad;
col -= pad;
//限定大小满足条件
if (row < 0 || col < 0 ||
row >= height || col >= width) return;
//对图片上的每一个值进行像素val的添加
im[col + width*(row + height*channel)] += val;
}
//This one might be too, can't remember.
/**
* 此函数与im2col_cpu函数的流程相反,目的是将im2col_cpu()函数重排得到的图片data_col恢复至正常的图像矩阵形式,并与data_im相加,
* 最终data_im相当于输出值, 需要注意的是, data_im的尺寸是在函数外确定的,且并没有显示的将data_col转为
*
* @param data_col backward_convolutional_layer()中计算得到包含上一层所有敏感度信息的矩阵,
* 行数 l.n * l.size * l.size (l.代表本层)
* 列数l.out_h * l.out_w
* @param channels 当前层输入图像的通道数
* @param height 当前层输入图像的行数
* @param width 当前层输入图像的列数
* @param ksize 当前层卷积层尺寸
* @param stride 当前层卷积步幅
* @param pad 当前层对输入图像卷积时补0的长度
* @param data_im 经过col2im_cpu()重新恢复之后得到的输出矩阵,也即上一层的敏感图 l.c * l.h * l.w
*/
//明确一下:col矩阵式为了方便进行卷积操作而转换图片得到的。
//https://blog.csdn.net/weixin_38715903/article/details/105978855 解析
//col矩阵直接与卷积核相乘得到的是相应的卷积输出,每一列就是相应的一组点乘操作的数据
//现在目的是为了将得到的特征或是梯度,反映射至原始输入特征上去
void col2im_cpu(float* data_col,
int channels, int height, int width,
int ksize, int stride, int pad, float* data_im)
{
int c,h,w;
//padding之后的尺寸计算,也就是我们需要填内容的im经过卷积操作之后得到的特征大小
//也就是得到进行卷积操作时,分别在输入特征图上,向右向下的位移次数
//以im:[5,5] kernel:[3,3] stride[2] pad[0]为例,最后的输出是[2,2]
//也可以知道,kernel分别向右向下移动了[2,2]次
int height_col = (height + 2*pad - ksize) / stride + 1;
int width_col = (width + 2*pad - ksize) / stride + 1;
int channels_col = channels * ksize * ksize;//整体col的行数
for (c = 0; c < channels_col; ++c) {
//获得在im中对应的位置,比如说3*3的卷积,就是找出col对应在这九个位置中的哪一个
//比如说col的第一行是im中9个位置的(0,0)位,那么w_offset=0,h_offset=0
int w_offset = c % ksize;
int h_offset = (c / ksize) % ksize;
//找出它是属于im中哪一个channels的【c/9】
int c_im = c / ksize / ksize;
for (h = 0; h < height_col; ++h) {
for (w = 0; w < width_col; ++w) {
//找到映射到im中对应的几何像素点,行,列的坐标
//如果看懂了im2col的原理,这里应该比较容易懂,我就不解释了
int im_row = h_offset + h * stride;
int im_col = w_offset + w * stride;
//为了取出每一个col中的特征值,先计算特征值存放在col一维数组中的位置
int col_index = (c * height_col + h) * width_col + w;
float val = data_col[col_index];
//得到相应的像素值,把我们在col中的像素值抽取出来,放到im中去与im中原有值相加
col2im_add_pixel(data_im, height, width, channels,
im_row, im_col, c_im, pad, val);
}
}
}
}
// ----------------------------------------
//构建Y矩阵空间,并赋初值为0
//如果想要的初值不为0,就对Y逐值赋值
void caffe_set(const int N, const float alpha, float* Y) {
if (alpha == 0) {
memset(Y, 0, sizeof(float) * N); // NOLINT(caffe/alt_fn)
return;
}
int i;
for (i = 0; i < N; ++i) {
Y[i] = alpha;
}
}
//判断a