【darknet源码解析-09】col2im.h 和 col2im.c 解析

本系列为darknet源码解析, 本次解析src/col2im.h 与 src/col2im_cpu.c 两个. 这两个其实与之前所解析src/im2col.h 和 src/im2col.c的逆过程, col2im就是将im2col重排的图片data_col恢复到正常的图像矩阵排列。

col2img.h 中包含的代码如下:主要就是一个函数col2im.h定义,在这里我们也不涉及到gpu那块,先讲解cpu这块。

#ifndef COL2IM_H
#define COL2IM_H
 
void col2im_cpu(float* data_col,
        int channels, int height, int width,
        int ksize, int stride, int pad, float* data_im);
 
#ifdef GPU
void col2im_gpu(float *data_col,
        int channels, int height, int width,
        int ksize, int stride, int pad, float *data_im);
#endif
#endif
col2im.c 的详细分析如下:

#include <stdio.h>
#include <math.h>
 
/**
 * 将输入图像im的channel通道上的第row行,col列像素灰度值加上val(直接修改im的值,因此im相当于是返回值)
 * @param im 输入图像
 * @param height 输入图像的高度
 * @param width 宽度
 * @param channels 通道数
 * @param row 需要加上val的像素所在的行数(padding之后的行数,因此需要减去pad才能得到真正在im中的行数)
 * @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)
{
    // 减去pad才能或者padding之前的所在行数和列数
    row -= pad;
    col -= pad;
 
    // 边界检查:超过边界,直接返回
    if (row < 0 || col < 0 ||
        row >= height || col >= width) return;
 
    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 行数9,列数9
 * @param channels 当前层输入图像的通道数 1
 * @param height 当前层输入图像的行数 5
 * @param width  当前层输入图像的列数 5
 * @param ksize  当前层卷积层尺寸 3
 * @param stride 当前层卷积步幅 2
 * @param pad    当前层对输入图像卷积时补0的长度 1
 * @param data_im 经过col2im_cpu()重新恢复之后得到的输出矩阵,也即上一层的敏感图 l.c * l.h * l.w
 */
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;
    int height_col = (height + 2*pad - ksize) / stride + 1;
    int width_col = (width + 2*pad - ksize) / stride + 1;
 
    int channels_col = channels * ksize * ksize;
    for (c = 0; c < channels_col; ++c) {
        int w_offset = c % ksize;
        int h_offset = (c / ksize) % ksize;
        int c_im = c / ksize / ksize;
        for (h = 0; h < height_col; ++h) {
            for (w = 0; w < width_col; ++w) {
                int im_row = h_offset + h * stride;
                int im_col = w_offset + w * stride;
                int col_index = (c * height_col + h) * width_col + w;
                double val = data_col[col_index];
                col2im_add_pixel(data_im, height, width, channels,
                        im_row, im_col, c_im, pad, val);
            }
        }
    }
}
感觉直接讲解代码还是比较不直观,下面我们举个例子来说明col2im到底是怎样工作的.【此例子是im2col的逆过程,请先看im2col那个例子】其实col2im就是将im2col重排的图片data_col恢复到正常的图像矩阵排列。

输入:data_col是一个9*9,单通道的特征图,图片的高度为5,宽度为5,卷积大小为3*3,卷积步幅stride为2,补0的个数pad为1,【黑色部分实际是im2col时候data2im高度宽度,以及卷积参数】则传输参数如下:

data_col={0,0,0,0,7,9,0,17,19,0,0,0,6,8,10,16,18,20,0,0,0,7,9,0,17,19,0,
          0,2,4,0,12,14,0,22,24,1,3,5,11,13,15,21,23,25,2,4,0,12,14,0,22,24,0,
          0,7,9,0,17,19,0,0,0,6,8,10,16,18,20,0,0,0,7,9,0,17,19,0,0,0,0};
channels = 1, height = 5, width = 5, ksize = 3, stride = 2, pad = 1;
根据data_im输入特征图大小,求得每次卷积后得到特征图大小,注意:是data_im图片后的大小。因为由im2col过程我们知道,每一次将卷积后得到特征图映射到data_col中的一行,而现在我们需要做逆过程,也就是将data_col的一行逆映射回到data_im中。

height_col = (height+2*pad-ksize)/stride+1=(5+2*1-3)/2+1=3
width_col = (height+2*pad-ksize)/stride+1=(5+2*1-3)/2+1=3
 data_col 矩阵到底有多少行需要逆映射回data_im中呢,data_col实际存储方式是一维数组,计算方法还是从data_im到data_col的im2col转换来计算,计算如下:

channels_col = channels * ksize * ksize = 1 * 3 * 3 = 9
那么data_col中每一行有多少元素呢,实际上就是卷积层的大小,对不对!!!

ksize * ksize = 3 * 3 = 9
从而得知data_col实际上的逻辑结构如下,是9*9的二维矩阵。如下图所示:

 那么接下来,我们就要知道data_im矩阵每个元素从输入矩阵data_col怎么样转换过来。如下图所示:

接下来,我们需要将输入矩阵data_col中的所有元素逐行映射到data_im中,注意:data_im中原本是有元素值的,这里我们只考虑映射回来的值,即只计算delta。

C = 0, h = 0,1,2, w = 0, 1, 2,的计算过程如下:

 data_col中第一行的逆映射结果,如下图所示:

C = 1, h = 0,1,2, w = 0, 1, 2,的计算过程如下:

 data_col中第二行的逆映射结果,如下图所示:

大家看看有没有找出规律。。。下面给出每一行的逆映射结果:

 data_col中第三行的逆映射结果,如下图所示: 

  data_col中第四行的逆映射结果,如下图所示: 

 data_col中第五行的逆映射结果,如下图所示: 

 data_col中第六行的逆映射结果,如下图所示: 

 data_col中第七行的逆映射结果,如下图所示: 

 data_col中第八行的逆映射结果,如下图所示: 

 data_col中第九行的逆映射结果,如下图所示: 

由上可知,最终data_col经过9次逆映射结果得到新data_im的增量如下:

 
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/caicaiatnbu/article/details/102626135

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值