2019.12.10 更新 未完结
3. The Proposed Method
首先,我们在图1中概述了我们的两阶段方法:
在第一阶段,通过添加SF-Net和MDA-Net,可以期望特征图包含更多的特征信息和更少的噪声。为了角度参数的位置灵敏度,此阶段仍使水平框回归。
通过改进的五参数回归和第二阶段中每个提案的旋转非最大抑制(R-NMS)操作,我们可以获得任意旋转下的最终检测结果。
3.1 Finer Sampling and Feature Fusion Network (SF-Net)
在我们的分析中,检测小物体有两个主要障碍:物体特征信息不足和anchor样本不足。 原因是由于使用池化层,因此小对象在深层中丢失了大部分特征信息。 同时,高级特征图的较大采样步幅倾向于直接跳过较小的对象,从而导致采样不足。
3.1.1 Feature fusion
通常认为,低级特征图可以保留小对象的位置信息,而高级特征图可以包含高级语义线索。 特征金字塔网络(FPN)[23],自上而下调制(TDM)[35]和与对象先验网络(RON)的反向连接[21]是常见的特征融合方法,涉及高低级特征图不同形式的组合。
3.1.2 Finer sampling
训练样本不足和不平衡会影响检测性能。 通过引入期望的最大重叠(EMO)得分,[45]中的作者计算出锚点和物体之间的期望的最大联合交叉点(IoU)。 他们发现锚点(SA)的stride越小,获得的EMO得分越高,从统计上讲导致所有对象的平均最大IoU均得到改善。
图2显示了分别跨步16和8进行小物体采样的结果。 可以看出,较小的SA可以对更多高质量的样本进行采样,从而很好地捕获了小物体,这对于检测器训练和推理都非常有帮助。
基于以上分析,我们设计了更精细的采样和特征融合网络(SF-Net),如图3所示。在基于锚点的检测框架中,特征图相对于原始图像缩减了 S A S_A SA倍。换句话说, S A S_A SA的值只能是2的指数倍。SF-Net通过更改特征图的大小来解决此问题,从而使SA的设置更加灵活以允许更多自适应采样。为了减少网络参数,SF-Net仅使用Resnet [16]中的C3和C4进行融合,以平衡语义信息和位置信息,同时忽略其他不太相关的功能。简单来说,SF-Net的第一个通道会对C4进行升采样,以使其SA = S,其中S是预期的锚跨度。第二个通道还将C3上采样到相同的大小。然后,我们将C3传递给起始结构,以扩展C3的接受域并增加语义信息。初始结构包含各种比率卷积核以捕获对象形状的多样性。最后,通过将两个通道逐个元素相加获得新的特征图F3。
表1列出了不同SA下DOTA的检测精度和训练开销。我们发现最佳的SA取决于特定的数据集,尤其是小对象的大小分布。在本文中,为了在精度和速度之间进行权衡,通常将S的值设置为6。
——此处代码在代码 ./libs/networks/resnet.py (test部分)
def resnet_base(img_batch, scope_name, is_training=True):
'''
this code is derived from light-head rcnn.
https://github.com/zengarden/light_head_rcnn
It is convenient to freeze blocks. So we adapt this mode.
'''
if scope_name == 'resnet_v1_50':
middle_num_units = 6
elif scope_name == 'resnet_v1_101':
middle_num_units = 23
else:
raise NotImplementedError('We only support resnet_v1_50 or resnet_v1_101 or mobilenetv2. '
'Check your network name.')
blocks = [resnet_v1_block('block1', base_depth=64, num_units=3, stride=2),
resnet_v1_block('block2', base_depth=128, num_units=4, stride=2),
# use stride 1 for the last conv4 layer.
resnet_v1_block('block3', base_depth=256, num_units=middle_num_units, stride=1)]
# when use fpn, stride list is [1, 2, 2]
with slim.arg_scope(resnet_arg_scope(is_training=False)):
with tf.variable_scope(scope_name, scope_name):
# Do the first few layers manually, because 'SAME' padding can behave inconsistently
# for images of different sizes: sometimes 0, sometimes 1
net = resnet_utils.conv2d_same( img_batch, 64, 7, stride=2, scope='conv1')
net = tf.pad(net, [[0, 0], [1, 1], [1, 1], [0, 0]])
net = slim.max_pool2d( net, [3, 3], stride=2, padding='VALID', scope='pool1')
not_freezed = [False] * cfgs.FIXED_BLOCKS + (4-cfgs.FIXED_BLOCKS)*[True]
# Fixed_Blocks can be 1~3
with slim.arg_scope(resnet_arg_scope(is_training=(is_training and not_freezed[0]))):
C2, end_points_C2 = resnet_v1.resnet_v1(net,
blocks[0:1],
global_pool=False,
include_root_block=False,
scope=scope_name)
with slim.arg_scope(resnet_arg_scope(is_training=(is_training and not_freezed[1]))):
C3, end_points_C3 = resnet_v1.resnet_v1(C2,
blocks[1:2],
global_pool=False,
include_root_block=False,
scope=scope_name)
with slim.arg_scope(resnet_arg_scope(is_training=(is_training and not_freezed[2]))):
C4, _ = resnet_v1.resnet_v1(C3,
blocks[2:3],
global_pool=False,
include_root_block=False,
scope=scope_name)
if cfgs.ADD_FUSION:
C3_shape = tf.shape(end_points_C3['{}/block2/unit_3/bottleneck_v1'.format(scope_name)])
C4 = tf.im