2019年1月21日

Fast RCNN: RoI Pooling 原理及代码

RoI Pooling 层

初始化参数

  • pooled_weight_ RoI池化层宽网格数(即论文中W=7)
  • pooled_height_ RoI池化层高网格数(即论文中H=7)
  • spatial_scale_ 从输入图像的坐标到特征图像坐标的缩放值

Forward函数流程

输入:(特征图data,位置信息rois) * Batch个

从Batch中取一张特征图datas的一个通道data,从此图对应的rois中取一个roi,得到(id,x,y,w,h)

  1. 缩放roi使其对应到特征图中位置(四舍五入为整数)得到(Rx,Ry,Rw,Rh) round(roi * spatial_scale_)
  2. 通过Rw,Rh得到每个bin的长宽BINw,BINh(四舍五入为整数) round(Rw / pooled_weight_)
  3. 遍历BIN中的特征值,将最大值作为输出,并记录此值对应在特征图的位置

示意图

RoI Pooling.png

Caffe代码

摘自 jiongnima

//cpu前传函数
template <typename Dtype>
void ROIPoolingLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
    const Dtype* bottom_data = bottom[0][/latex]gt;cpu_data();//bottom_data表示共享卷积层输出的特征图
    const Dtype* bottom_rois = bottom[1][/latex]gt;cpu_data();//bottom_rois表示rois的信息,一共5维,第一维表示rois在训练batch中的图片索引,后四维表示rois的坐标信息
    // Number of ROIs
    int num_rois = bottom[1][/latex]gt;num();//roi的总数
    int batch_size = bottom[0][/latex]gt;num();//batch_size表示一次训练中输入的图片数量,因为roi并不是都存在于同一张图片上
    int top_count = top[0][/latex]gt;count();//top_count表示top[0]的全部容量(N*C*H*W)
    Dtype* top_data = top[0][/latex]gt;mutable_cpu_data();//使用top_data指针索引top[0]
    caffe_set(top_count, Dtype(-FLT_MAX), top_data);//初始化top_data(即top[0]),将其初始值(共top_count个)置为最小值(-FLT_MAX)
    int* argmax_data = max_idx_.mutable_cpu_data();//使用argmax_data指针索引max_idx_
    caffe_set(top_count, -1, argmax_data);//初始化argmax_data(即max_idx_),将其初始值(共top_count个)置为-1
    // For each ROI R = [batch_index x1 y1 x2 y2]: max pool over R (rois的五维数据的说明)
    for (int n = 0; n < num_rois; ++n) {//第一个for循环,遍历rois
        int roi_batch_ind = bottom_rois[0];//找到该roi对应的训练图片在训练batch中的索引
        int roi_start_w = round(bottom_rois[1] * spatial_scale_);//得到该roi的x1(左上角x坐标)在共享卷积层输出的特征图上面的位置(注意使用了round取整)
        int roi_start_h = round(bottom_rois[2] * spatial_scale_);//得到该roi的y1(左上角y坐标)在共享卷积层输出的特征图上面的位置(注意使用了round取整)
        int roi_end_w = round(bottom_rois[3] * spatial_scale_);//得到该roi的x2(右下角x坐标)在共享卷积层输出的特征图上面的位置(注意使用了round取整)
        int roi_end_h = round(bottom_rois[4] * spatial_scale_);//得到该roi的y2(右下角x坐标)在共享卷积层输出的特征图上面的位置(注意使用了round取整)
        //下面两行代码核验该roi必须要出现在训练batch的某一张图片中
    CHECK_GE(roi_batch_ind, 0);//核验roi_batch_ind是否大于等于零
        CHECK_LT(roi_batch_ind, batch_size);//核验roi_batch_ind是否小于batch_size
        int roi_height = max(roi_end_h - roi_start_h + 1, 1);//得到该roi在共享卷积层输出的特征图上面的高
        int roi_width = max(roi_end_w - roi_start_w + 1, 1);//得到该roi在共享卷积层输出的特征图上面的宽
        const Dtype bin_size_h = static_cast<Dtype>(roi_height) / static_cast<Dtype>(pooled_height_);//得到roi_pool的时候该roi在高方向上被划分的段数
        const Dtype bin_size_w = static_cast<Dtype>(roi_width) / static_cast<Dtype>(pooled_width_);//得到roi_pool的时候该roi在宽方向上被划分的段数
        const Dtype* batch_data = bottom_data + bottom[0][/latex]gt;offset(roi_batch_ind);//找到该roi对应的batch中的那张图片
        for (int c = 0; c < channels_; ++c) {//第二个for循环,遍历channels
            for (int ph = 0; ph < pooled_height_; ++ph) {//第三个for循环,遍历pooled_height_,即pool过后的roi的高
                for (int pw = 0; pw < pooled_width_; ++pw) {//第四个for循环,遍历pooled_width_,即pool过后的roi的宽
                    // Compute pooling region for this output unit:
                    //    start (included) = floor(ph * roi_height / pooled_height_)
                    //    end (excluded) = ceil((ph + 1) * roi_height / pooled_height_)
                    //hstart,wstart,hend,wend构成了在得到roi_pool输出中每一个值的过程中在共享卷积层输出的特征图上面的检索区域的坐标,下称"该值"
                    int hstart = static_cast<int>(floor(static_cast<Dtype>(ph) * bin_size_h));//找到该值对应的检索区域在共享卷积层输出的特征图上面的高开始的偏移坐标
                    int wstart = static_cast<int>(floor(static_cast<Dtype>(pw) * bin_size_w));//找到该值对应的检索区域在共享卷积层输出的特征图上面的宽开始的偏移坐标
                    int hend = static_cast<int>(ceil(static_cast<Dtype>(ph + 1) * bin_size_h));//找到该值对应的检索区域在共享卷积层输出的特征图上面的高结束的偏移坐标
                    int wend = static_cast<int>(ceil(static_cast<Dtype>(pw + 1) * bin_size_w));//找到该值对应的检索区域在共享卷积层输出的特征图上面的宽结束的偏移坐标
                    hstart = min(max(hstart + roi_start_h, 0), height_);//找到该值对应的检索区域在共享卷积层输出的特征图上面的高开始坐标
                    hend = min(max(hend + roi_start_h, 0), height_);//找到该值对应的检索区域在共享卷积层输出的特征图上面的宽开始坐标
                    wstart = min(max(wstart + roi_start_w, 0), width_);//找到该值对应的检索区域在共享卷积层输出的特征图上面的高结束坐标
                    wend = min(max(wend + roi_start_w, 0), width_);//找到该值对应的检索区域在共享卷积层输出的特征图上面的宽结束坐标
                    bool is_empty = (hend <= hstart) || (wend <= wstart);//如果说该roi的尺寸非法,即(hend <= hstart)或者(wend <= wstart),is_empty就为true
                    const int pool_index = ph * pooled_width_ + pw;//找到该值对应的pool_index
                    if (is_empty) {
                        top_data[pool_index] = 0;//如果is_empty为true,那么该值就为0
                        argmax_data[pool_index] = -1;//如果is_empty为true,那么该值对应的max_idx_为-1
                    }
                    for (int h = hstart; h < hend; ++h) {//第五个for循环,遍历该值对应的检索区域的高
                        for (int w = wstart; w < wend; ++w) {//第六个for循环,遍历该值对应的检索区域的宽
                            const int index = h * width_ + w;//找到检索区域中正被比较的值在共享卷积层输出的特征图上面的位置索引
                            if (batch_data[index] > top_data[pool_index]) {
                                top_data[pool_index] = batch_data[index];//该值对应检索区域中的最大值
                                argmax_data[pool_index] = index;//记录该值对应的共享特征图上面的位置
                            }
                        }
                    }
                }
            }//结束第三至第六个for循环
            // Increment all data pointers by one channel
            batch_data += bottom[0][/latex]gt;offset(0, 1);//做完了一个channel,将batch_data索引指针移动到下一个channel继续pool
            top_data += top[0][/latex]gt;offset(0, 1);//做完了一个channel,将输出索引指针移动到下一个channel继续接收pool的输出
            argmax_data += max_idx_.offset(0, 1);//做完了一个channel,将max_idx_索引指针移动到下一个channel继续接收坐标记录
        }//结束第二个for循环
        // Increment ROI data pointer
        bottom_rois += bottom[1][/latex]gt;offset(1);//处理完了一个roi,接着处理下一个roi
    }//结束第一个for循环
}
Share

You may also like...

发表评论

您的电子邮箱地址不会被公开。