【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的增量如下:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章