RoiAlign-计算实例

最近在看MaskRCNN,贴上一个RoiAlign的计算实例。转自知乎

假设特征图大小是1x256x200x272,rois大小是715x5(5列分别是:对应的图片id(这里只有一张图片,所以第一列全为0)、对应roi左上角的x,y坐标、roi右下角的x,y坐标),roi align后的输出大小为715x256x7x7。

对于每一个roi (1个channel):

把roi的坐标调整到合适的scale,这里假设特征图大小是原图的1/4,那么要将roi的坐标乘以1/4。例如,当前roi为[0, 920.11, 276.99, 1049.87, 322.0],将坐标乘以1/4后得到[0, 230.02, 69.24, 262.46, 80.5](not round),将其绘制在特征图上(不考虑通道的话)就是:
在这里插入图片描述
所以这个roi的宽度是,262.46-230.02=32.44,roi的高度是80.5-69.24=11.26。由于输出的长宽大小为7,因此这个roi要被分成7x7的小块,每个小块的高度是11.26/7=1.60,宽度是32.44/7=4.63。假设我们的采样率为2,那么在每个小块内,要采样2x2=4个点。

对于每一个小块:

计算4个采样点的坐标。第一个采样点,y坐标为69.24+01.60+(0.51.60/2)=69.64,也就是在第一个小块的1/4的高度的位置进行采样,x坐标为230.02+04.63+(0.54.63/2)=231.18,也就是在第一个小块的1/4的宽度的位置进行采样;第二个采样点,y坐标与第一个采样点相同,x坐标为230.02+04.63+(1.54.63/2))=233.49,也就是在第一个小块的3/4的宽度的位置进行采样;第三个采样点在小块的3/4高度,1/4的宽度的位置进行采样;第四个采样点在小块的3/4高度,3/4的宽度的位置进行采样。我们得到:
在这里插入图片描述
对每一个采样点:

第一个采样点,也就是(69.64,231.18),我们对其构建一个1x1的小框,小框的左上角y坐标是69,也就是对69.64取floor,x坐标是231,也就是对231.18取floor,小框的右下角的y坐标是69+1=70,x坐标是231+1=232。得到:
在这里插入图片描述
我们可以算出这个采样点到上下左右的距离分别是,0.64,0.36,0.18,0.83,然后我们从采样点位置将这个小框分成4个部分,并计算出每一部分的面积,左上部分面积为0.64*0.18=0.11,右上部分面积为0.53,左下部分面积为0.06,右下部分面积为0.29。到现在,我们得到了小框的4个顶点的坐标,以及4个部分的面积(area0,area1,area2,area3),我们还可以知道在特征图(对应channel)中,对应这4个顶点坐标上的值(设为val0, val1,val2,val3)。
在这里插入图片描述
我们可以通过(bilinear interpolate)计算这个roi的roi align结果的对应channel里,每一个小块的值:

r e s = ( v a l 0 ∗ a r e a 3 + v a l 1 ∗ a r e a 2 + v a l 2 ∗ a r e a 1 + v a l 3 ∗ a r e a 0 ) / 4 res = (val0 * area3 + val1*area2 + val2*area1 + val3*area0) / 4 res=(val0area3+val1area2+val2area1+val3area0)/4

按照以上步骤,我们可以得到所有roi的roi align的结果

如何使用Tensorflow实现RoiAlign?

tf.image.crop_and_resize的行为

tf.image.crop_and_resize(feature_maps, boxes, box_indices=box_ind, crop_size=crop_size), 他是将box的区域裁剪出来, 并按照指定的插值方式, resize到crop_size大小, 这里的box的格式是[y1, x1, y2, x2] / [height -1, width - 1, height - 1, width - 1], 值得注意的是y2, x2是box的ymax和xmax.(在Mask RCNN)中为了slice方便, 使用的是ymax + 1, xmax+1, 因此在crop_and_resize时要减1). 默认的插值方式是bilinear.
举个例子, 给一个2x2的图片: x = tf.constant([[[[1.], [2.]], [[3], [4]]]]), box是box = tf.constant([[0.2, 0.3, 0.2, 0.3]]), 也就是一个点, crop_size是crop_size = tf.constant([1, 1]), 就是只采样一个点, 接着进行crop_and_resize:

box_ind = tf.constant([0])  # 指定crop的featuremap的id
res = tf.image.crop_and_resize(x, box, box_indices=box_ind, crop_size=crop_size)

run res节点之后, 可以得到结果值为1.7, 这里可以笔算一下是不是这个值。

在像素值分别为1,2,3,4的2x2的图里, 采样[0.2, 0.3, 0.2, 0.3]这个点:
在这里插入图片描述
通过bilinear插值计算的结果等于:

(0.2x0.3)x4+(0.2x0.7)x3+(0.3x0.8)x2+(0.7x0.8)x1=1.7

同样的2x2的图, 如果box = tf.constant([[0.3, 0.0, 0.3, 0.0]]), 同样是采样一个点, 计算的结果等于: (0.3 x1) x 3 + (0.7x1) x1 = 1.6

现在试试采样2x2个点:

x = tf.constant([[[[1.], [2.]], [[3], [4]]]])
box = tf.constant([[0.2, 0.3, 0.4, 0.5]])
res = tf.image.crop_and_resize(x, box, box_indices=box_ind, crop_size=crop_size)

run res节点后得到结果: [[[[1.6999999] [1.9 ]] [[2.1 ][2.3 ]]]], (0.2, 0.3)这个点上面计算过它的结果是1.7, 再计算一下(0.4,0.5)这个点经过bilinear插值后的值:0.2x4+0.2x3+0.3x2+0.3x1=2.3, 与crop_and_resize得到的结果相同.

另外, tf.image.crop_and_resize会处理x2<x1, y2<y1或x1<0,y1<0, x2>w - 1,y2>h-1的情况.

实现不同sample ratio的ROI Align

如果要得到一个m x m的roi align的结果, 同时sample ratio为2的话(也就是mask rcnn paper里使用的值), 需要在每个bin中采样n x n个点, 因此共采样n x n x m x m个点, .

举个例子, 如果需要得到一个4x4的roi align output size的结果, 同时sample ratio为2, 也就是我们要将crop的区域, 分成4x4个bin, 每个bin中分成2x2个grid, 接着对各个grid的中心点进行采样, 共采样8x8个点. 要想通过tf.image.crop_and_resize来完成采样, 可以将box设置为左上采样点(也就是左上grid的中心点)和右下采样点所构成的bouding box, 再把crop_size设置为(8,8), 就可以完成8x8个点的采样(如下图所示).
在这里插入图片描述
写成代码就是:

sample_ratio = 2
output_size = (4, 4)

y1, x1, y2, x2 = tf.split(boxes, 4, axis=1)
bin_height = (y2 - y1) / output_size[0]
bin_width = (x2 - x1) / output_size[1]

grid_center_y1 = (y1 + 0.5 * bin_height / sample_ratio)
grid_center_x1 = (x1 + 0.5 * bin_width / sample_ratio)

grid_center_y2 = (y2 - 0.5 * bin_height / sample_ratio)
grid_center_x2 = (x2 - 0.5 * bin_width / sample_ratio)

# Constructed by top-left sample point and bottom-right
# sample point. shape [num_boxes, (y1, x1, y2, x2)]
new_boxes = tf.concat([grid_center_y1, grid_center_x1, grid_center_y2, grid_center_x2], axis=1)

crop_size = tf.constant([output_size[0] * sample_ratio, output_size[1] * sample_ratio])
return tf.image.crop_and_resize(feature_maps, new_boxes, box_indices=box_indices,
                                crop_size=crop_size, method='bilinear')
sampled = tf.image.crop_and_resize(feature_maps, new_boxes, box_indices=box_indices, crop_size=crop_size, method='bilinear')

得到了8x8的采样点的值后, 需要将每个bin的采样点求均值, 这里我们的sample ratio为2:

aligned = tf.nn.avg_pool2d(sampled, sample_ratio, sample_ratio, padding=‘VALID’)
值得注意的是, 如果feature map的dim 0的size为0(在tf.gather_nd的ids没有元素的时候会出现这种情况), tf.nn.avg_pool2d是无法正常计算梯度的. 可以通过实例化一个tf.keras.AvgPool2D对象来使用.

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值