[R-FCN问题记录]输入层/变量im_info作用详解

R-FCN问题记录
前段时间用到了目标检测网络R-FCN,对于整体框架有了大概的了解.
然后被问了关于网络输入层im_info的问题:输入层有data层为什么还要有个im_info层,两者的区别在哪,作用是什么?在此记录下过程。
在test的prototxt中确实有两个输入层:

input: “data”
input_shape {
dim: 1
dim: 3
dim: 224
dim: 224
}

input: “im_info
input_shape {
dim: 1
dim: 3
}

首先在prototxt.txt中查看了im_info的去处:

layer {
name: ‘proposal’
type: ‘Python’
bottom: ‘rpn_cls_prob_reshape’
bottom: ‘rpn_bbox_pred’
bottom: ‘im_info’
top: ‘rois’
python_param {
module: ‘rpn.proposal_layer’
layer: ‘ProposalLayer’
param_str: “‘feat_stride’: 16” }
}

发现im_info层仅用于proposal层的输入,查看proposal层的python实现代码proposal_layer.py:

im_info = bottom[2].data[0, :]
if DEBUG:
    print 'im_size: ({}, {})'.format(im_info[0], im_info[1])
    print 'scale: {}'.format(im_info[2])

这里的赋值是将proposal层的第三个bottom层的data信息给到im_info变量中,也就是im_info层的信息.
下面的debug模式表明可以输出im_info[0],im_info[1],im_info[2],然而im_info层的初始输入参数只有两个:input_shape {dim: 1 dim: 3}
所以继续往上查看im_info变量的输入,测试时通常调用lib/fast_rcnn/test.py,在test.py中搜索了im_info变量:

if cfg.TEST.HAS_RPN:
    im_blob = blobs['data']
    blobs['im_info'] = np.array([[im_blob.shape[2],im_blob.shape[3], im_scales[0]]], dtype=np.float32)

可以看到变量im_info包含三个值,其中两个是从blobs[‘data’]传递过来的:im_blob.shape[2]和im_blob.shape[3],第三个值为im_scales[0].
继续查看im_scales变量:

blobs, im_scales = _get_blobs(im, boxes)

从内部函数_get_blobs()中获得:

def _get_blobs(im, rois):
    blobs = {'data' : None, 'rois' : None}
    blobs['data'], im_scale_factors = _get_image_blob(im)
    if not cfg.TEST.HAS_RPN:
        blobs['rois'] = _get_rois_blob(rois, im_scale_factors)
    return blobs, im_scale_factors

继续查看im_scale_factors变量,从函数_get_image_blob()处获得:

im_scale_factors = []
for target_size in cfg.TEST.SCALES:
    im_scale = float(target_size) / float(im_size_min)
    # Prevent the biggest axis from being more than MAX_SIZE
    if np.round(im_scale * im_size_max) > cfg.TEST.MAX_SIZE:
        im_scale = float(cfg.TEST.MAX_SIZE) / float(im_size_max)
    im_scale_factors.append(im_scale)

return blob, np.array(im_scale_factors)

可以看到im_scale_factors是一个数组,里面存放的值是im_scale.
在config.py文件中有参数设定,TEST.SCALES存放了图片最短边的长度,可以是一个数组. TEST.MAX_SIZE存放了图片最长边的长度.

# Each scale is the pixel size of an image's shortest side
__C.TEST.SCALES = (600,)
# Max pixel size of the longest side of a scaled input image
__C.TEST.MAX_SIZE = 1000

所以im_scale的含义是:
进入网络时图片假设resize参数设定(config.py)为600 * 1000,图片A真实大小为size = 1080 * 1920,那么im_scale = float(600) / float(1080),并且如果np.round(im_scale * im_size_max) > cfg.TEST.MAX_SIZE,那么im_scale = float(1000) / float(1920),即im_scale = min{长边比,短边比}
回到test.py,得到缩放比例后,将原始图片按照该比例缩放:

im = cv2.resize(im_orig, None, None, fx=im_scale, fy=im_scale, interpolation=cv2.INTER_LINEAR)

然后将该图片存入blob:

    processed_ims.append(im)
# Create a blob to hold the input images
blob = im_list_to_blob(processed_ims)

最后使用im_info存储resize后图片的宽高和缩放比例:

blobs['im_info'] = np.array([[im_blob.shape[2],im_blob.shape[3],
im_scales[0]]], dtype=np.float32)

以图片A为例,resize参数设定为600 * 1000,图片A真实大小为size = 1080 * 1920,im_scale计算出来为im_scale = 1000 / 1920 ≈ 0.521,因此图片进入网络时resize为(1080 * im_scale) * (1920 * im_scale) = 562.5 * 1000大小.
在命令行中将im_info变量输出能得到:

im_blob.shape[2] = 562.5
im_blob.shape[3] = 1000
im_scales[0] = 0.52083333

因此实际上im_info层的意义是存储了每张图片进入网络后原图片大小被resize至设定图片大小的比例和resize后图片的宽高.
使用是在proposal层:

im_info = bottom[2].data[0, :]
...
min_size = cfg[cfg_key].RPN_MIN_SIZE
...
#clip predicted boxes to image
proposals = clip_boxes(proposals, im_info[:2])
#remove predicted boxes with either height or width < threshold
#convert min_size to input image scale stored in im_info[2])
keep = _filter_boxes(proposals, min_size * im_info[2])
proposals = proposals[keep, :]
scores = scores[keep]

im_info[:2]为resize后的宽高,将得到的proposals侯选框进行修剪,去除超出图片边界的框.
im_info[2]是scale比例,用于去除proposals中比设定的最小min_size乘以对应比例后还小的侯选框,例如__C.TEST.RPN_MIN_SIZE = 16,则去除proposals中长或宽比16 * 0.521 ≈ 8.3 还要小的侯选框.
ps:去除方法可以查看lib/fast_rcnn/bbox_transform.py中的clip_boxes函数定义和proposal.py中的_filter_boxes函数定义.


后续:im_scales可以是多个短边值组成的数组,但im_info直接使用了im_scales[0].
对于这个问题刚开始没想通,后发现test.py中:

if cfg.DEDUP_BOXES > 0 and not cfg.TEST.HAS_RPN:
    v = np.array([1, 1e3, 1e6, 1e9, 1e12])
    hashes = np.round(blobs['rois'] * cfg.DEDUP_BOXES).dot(v)
    _, index, inv_index = np.unique(hashes, return_index=True,
                                    return_inverse=True)
    blobs['rois'] = blobs['rois'][index, :]
    boxes = boxes[index, :]
if cfg.TEST.HAS_RPN:
    forward_kwargs['im_info'] = blobs['im_info'].astype(np.float32, copy=False)
else:
    forward_kwargs['rois'] = blobs['rois'].astype(np.float32, copy=False)

并且有:

if cfg.TEST.HAS_RPN:
    assert len(im_scales) == 1, "Only single-image batch implemented"
    rois = net.blobs['rois'].data.copy()
    # unscale back to raw image space
    boxes = rois[:, 1:5] / im_scales[0]

所以当config.py中参数__C.TEST.HAS_RPN = True时,len(im_scales)只能为1,也就是说使用RPN生成侯选框时只能有一个size值;
不使用RPN的话是R-FCN的另一种ohem模式,而非end-to-end模式,此时使用rois层作为输入,可以支持多个size图片同时进入网络,models里ohem模式的test.pt中输入层如下:

input: “data”
input_shape {
dim: 1
dim: 3
dim: 224
dim: 224
}

input: “rois
input_shape {
dim: 1 # to be changed on-the-fly to num ROIs
dim: 5 # [batch ind, x1, y1, x2, y2] zero-based indexing
}

对于im_info层的详解大致如上,牵扯到挺多细节点,对于源码的理解和钻研还不够,有理解不对的地方请多多指教~.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值