im2col関数



Im2col Function



1、 im2col関数

画像input_num = 1
画像チャネルinput_channel = 1
画像の高さinput_h = 4
画像幅input_w = 4
カーネル高kernel_h = 3
カーネル幅kernel_w = 3
stride = 1; pad = 0;
畳み込み後、出力画像の計算式は次のようになります。
output_h =(input_h-kernel_h)/ stride + 1
output_w =(input_w-kernel_w)/ stride + 1

下の図に示すように、(注:画像のデータは画像の色の値を表すものではありません)
画像



元の画像(画像a)は、左から右、上から下のプロセスに従い、(a)のサイズは3です。 3(カーネルサイズが3であるため) 3)マトリックスは右図の列に描画されます(図b)。具体的なプロセスを次の図に示します。
画像

2:マルチチャネルim2col
3つのチャネル(R、G、B)があるとします。画像チャネルinput_channel = 3
画像
メモリへの画像の記憶は、最初に第1のチャネルのデータが連続的に記憶され、次に第2のチャネルのデータが記憶され、最後に第3のチャネルのデータが記憶される。以下に示すように:
画像
マルチチャネルim2colのプロセスを次の図に示します。
画像



メモリ内のストレージは、最初にim2colの最初のチャネル、次にim2colの2番目のチャネル、最後にim2colの3番目のチャネルです。各チャネルのim2colデータもメモリに継続的に保存されます。以下に示すように:

画像

3:カーネル
次の図に示すように、画像の各チャネルはカーネルチャネルに対応します(注:簡単な計算のために、カーネルの値を1に設定します。また、この値はカラー値を表していません)。
画像



カーネルのチャネルデータもメモリに継続的に保存されます。したがって、上記のカーネルイメージは次の図のように表すこともできます。
画像

4:行列乗算sgemm
caffeでは、画像とカーネルの行列乗算はkernel * imgです。つまり、行列の乗算で
M = 1、
N = output_h * output_w
K = input_channels * kernel_h * kernel_w

以下に示すように:

画像

画像データは継続的に保存されるため、出力画像は次の図のようになります[output_h * output_w] = [2 * 2]。
画像
5:マルチチャンネル画像出力
カフェでの画像とカーネルの行列乗算:
M = output_channels、
N = output_h * output_w
K = input_channels * kernel_h * kernel_w

以下に示すように:
画像
同様に、複数の出力チャンネルの画像データは連続して保存されるため、出力画像も下図のようになります[output_channels output_h * output_w】=【3 2 * 2】、
画像

6、実装手順

#define _CRT_SECURE_NO_WARNINGS #include #include #include int conv_out_height(h, pad, size, stride) { return (h + 2 * pad - size) / stride + 1 } int conv_out_width(w, pad, size, stride) { return (w + 2 * pad - size) / stride + 1 } int im2col_get_pixel(int *im, int height, int width, int channels, int row, int col, int channel, int pad) void im2col_cpu(int* data_im, int channels, int height, int width, int ksize, int stride, int pad, int* data_col) { int c, h, w //The height and width of the output feature map int height_col = (height + 2 * pad - ksize) / stride + 1 int width_col = (width + 2 * pad - ksize) / stride + 1 //Number of convolution kernel parameters int channels_col = channels * ksize * ksize //Traverse all convolution kernel elements for (c = 0 c < channels_col ++c) { //The position of the convolution kernel element in the memory can be located through the following 3 values int w_offset = c % ksize//Convolution kernel abscissa w = c% convolution kernel size int h_offset = (c / ksize) % ksize//Convolution kernel ordinate h = (c / convolution kernel size)% convolution kernel size int c_im = c / ksize / ksize//Convolution kernel channel c_im = c/convolution kernel size/convolution kernel size also its corresponding input image channel, the convolution kernel of each channel only convolves the image of the corresponding channel //Image pixel value corresponding to each convolution kernel element traversed for (h = 0 h < height_col ++h) { for (w = 0 w < width_col ++w) { //Indicates that the element with coordinates (h_offset, w_offset) on the c_im channel convolution kernel corresponds to the coordinates (im_row, im_col) of the element in the c_im input channel image for example, the first channel convolution kernel in the figure above One element corresponds to the four elements (0,1,4,5) on the first input channel of the image, and the four coordinates are respectively (0,0) when the first channel of the input image (c_im=0) , (0,1), (1,0), (1,1) int im_row = h_offset + h * stride//The row of image elements when each convolution kernel element convolves the image int im_col = w_offset + w * stride//Column of image elements when each convolution kernel element convolves the image //Each convolution kernel element corresponds to out_w*out_h pixel values, so the number of pixels corresponding to the entire convolution kernel should be: //Output feature map height x output feature map width x convolution kernel size x convolution kernel size x convolution kernel channel //The storage in the memory is the first channel of im2col first, then the second channel of im2col, and finally the third channel of im2col. The im2col data of each channel is also continuously stored in the memory. //The following directory col_index is stored in the order of channels, first the image pixels corresponding to the first element of the convolution kernel of the first channel are stored, and then stored in order then the second channel is placed, and the last one is placed last Channel int col_index = (c * height_col + h) * width_col + w //According to the coordinates of the original image, sequentially stored in the memory data_col[col_index] = im2col_get_pixel(data_im, height, width, channels, im_row, im_col, c_im, pad) } } } } int main(int argc, char* argv[]) { int *data_im = NULL int *data_col = NULL int channels = 3, height = 4, width = 4 int ksize = 3, stride = 1, pad = 0 int out_w, out_h int workspace_size int inputs = height * width * channels data_im = (int*)malloc(inputs * sizeof(int)) if (!data_im) { printf('malloc error ') exit(EXIT_FAILURE) } out_w = conv_out_width(width, pad, ksize, stride) out_h = conv_out_width(height, pad, ksize, stride) workspace_size = out_h * out_w * ksize * ksize * channels data_col = (int*)malloc(workspace_size * sizeof(int)) if (!data_col) { printf('malloc error ') exit(EXIT_FAILURE) } //init image for (int i = 0 i<inputs i++) data_im[i] = i im2col_cpu(data_im, channels, height, width, ksize, stride, pad, data_col) printf('data_im: ') for (int i = 0 i<inputs i++) { printf('%-3d', data_im[i]) if( (i+1) % 4 == 0) printf(' ') } printf(' data_col: ') for (int i = 0 i<workspace_size i++) { printf('%-3d', data_col[i]) if( (i+1) % 4 == 0) printf(' ') } printf(' ') free(data_im) free(data_col) system('pause') exit(EXIT_SUCCESS) }