纯C++超分辨率重建LapSRN --改编--(五)im2row 和 im2col

实际上,前面已经把所有流程都完成了,但是效果很差,主要是 matconvnet 为 matlab 服务的,而matlab 的数据是按列排列的,相当于经过转置,所以这里找了一个im2col_cpu 来代替im2row_cpu。从名称就可以看出,有点不一样。

哪么,row2im_cpu要不要替换呢,应该不用吧(只要宽和高值调换一下就可以了)。

im2col_cpu:

//---------------------------im2col--------------------------------
//如果参数a的值大于或等于零且低于参数b的值,则函数使用从int到无符号的转换来比较。 
//b参数的类型为signed,并且始终为正,因此其值始终低于0x800 ...其中,参数的输出负值将其转换为高于0x800的值...
//铸件允许使用一个条件而不是两个条件。
inline bool is_a_ge_zero_and_a_lt_b(int a, int b) {//若a大于等于零或小于b,返回true,否则返回false
  return static_cast<unsigned>(a) < static_cast<unsigned>(b);
}

/*
   caffe卷积操作的实现:

卷积层输出 = 权值矩阵 * 输入特征图转化得到的矩阵

权值矩阵尺度 = (卷积组输出通道数) * (卷积组输入通道数*卷积核高*卷积核宽)

输入特征图转化得到的矩阵尺度 = (卷积组输入通道数*卷积核高*卷积核宽) * (卷积层输出单通道特征图高 * 卷积层输出单通道特征图宽)

因此,卷积层输出尺度可以表示为

卷积层输出尺度 = (卷积层输出通道数) * (卷积层输出单通道特征图高 * 卷积层输出单通道特征图宽)


im2col_cpu将c个通道的卷积层输入图像转化为c个通道的矩阵,

矩阵的行值为卷积核高*卷积核宽,
也就是说,矩阵的单列表征了卷积核操作一次处理的小窗口图像信息;

而矩阵的列值为卷积层输出单通道图像高*卷积层输出单通道图像宽,表示一共要处理多少个小窗口。
*/

                                  //  im2col_cpu  接收13个参数
void im2col_cpu(const float* data_im,           //输入数据指针
    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, //卷积核高与宽方向的扩展(倍数)(无扩展为1)
    float* data_col                             //输出矩阵数据指针
    ) {
  const int output_h = (height + 2 * pad_h -
    (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1;//计算卷积层输出图像的高
  const int output_w = (width + 2 * pad_w -
    (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1;//计算卷积层输出图像的宽
  const long channel_size = height * width;//计算卷积层输入单通道图像的数据容量
  /*第一个for循环表示输出的矩阵通道数和卷积层输入图像通道是一样的,每次处理一个输入通道的信息*/
  for (int channel = channels; channel--; data_im += channel_size) {
	/*第二个和第三个for循环表示了输出单通道矩阵的某一列,同时体现了输出单通道矩阵的行数*/
    for (int kernel_row = 0; kernel_row < kernel_h; kernel_row++) {
      for (int kernel_col = 0; kernel_col < kernel_w; kernel_col++) {
        int input_row = -pad_h + kernel_row * dilation_h;//在这里找到卷积核中的某一行在输入图像中的第一个操作区域的行索引
		/*第四个和第五个for循环表示了输出单通道矩阵的某一行,同时体现了输出单通道矩阵的列数*/
        for (int output_rows = output_h; output_rows; output_rows--) {
          if (!is_a_ge_zero_and_a_lt_b(input_row, height)) {//如果计算得到的输入图像的行值索引小于零或者大于输入图像的高(该行为pad)
            for (int output_cols = output_w; output_cols; output_cols--) {
              *(data_col++) = 0;//那么将该行在输出的矩阵上的位置置为0
            }
          } else {
            int input_col = -pad_w + kernel_col * dilation_w;//在这里找到卷积核中的某一列在输入图像中的第一个操作区域的列索引
            for (int output_col = output_w; output_col; output_col--) {
              if (is_a_ge_zero_and_a_lt_b(input_col, width)) {//如果计算得到的输入图像的列值索引大于等于于零或者小于输入图像的宽(该列不是pad)
                *(data_col++) = data_im[input_row * width + input_col];//将输入特征图上对应的区域放到输出矩阵上
              } else {//否则,计算得到的输入图像的列值索引小于零或者大于输入图像的宽(该列为pad)
                *(data_col++) = 0;//将该行该列在输出矩阵上的位置置为0
              }
              input_col += stride_w;//按照宽方向步长遍历卷积核上固定列在输入图像上滑动操作的区域
            }
          }
          input_row += stride_h;//按照高方向步长遍历卷积核上固定行在输入图像上滑动操作的区域
        }
      }
    }
  }
}

然后放到vl_impl_im2row中:

void vl_impl_im2row(
                                 float* stacked,
                                 float const* data,
                                 size_t height, size_t width, size_t depth,
                                 size_t windowHeight, size_t windowWidth,
                                 size_t strideY, size_t strideX,
                                 size_t padTop, size_t padBottom, size_t padLeft, size_t padRight)
{
	im2col_cpu(data,			//const float* data_im,                       //输入数据指针
    depth,						//const int channels,                         //卷积操作处理的一个卷积组的通道数
    height, width,				//const int height, const int width,          //输入图像的高与宽
    windowHeight, windowWidth,	//const int kernel_h, const int kernel_w,     //原始卷积核的高与宽
    padTop, padLeft,			//const int pad_h, const int pad_w,           //输入图像高与宽方向的边界扩展(像数个数)
    strideY, strideX,			//const int stride_h, const int stride_w,     //卷积操作高与宽方向的步长
    1,1,						//const int dilation_h, const int dilation_w, //卷积核高与宽方向的扩展(倍数)(无扩展为1)
    stacked						//float* data_col                             //输出矩阵数据指针
    );
}

效果图:

原图

2倍重建图

4倍重建图

8倍重建图

由于8倍重建中的网络每2倍层数最小,效果不算很好吧。

要亲自测试效果的朋友请到下面网址下载:

https://download.csdn.net/download/juebai123/10599656

另外这个矩阵乘法需要非常大的内存,只能用于很小的图,比如40x40(跟电脑内存大小有关)

所以,把4倍和8倍的去掉,只余2倍的一个程序也放在里面,这样可用于大一点的图。

猜你喜欢

转载自blog.csdn.net/juebai123/article/details/81603108