实例分割--CondInst论文分享
论文Conditional Convolutions for Instance Segmentation
代码aim-uofa/AdelaiDet
写在前面
论文出自沈春华团队,精品文章值得推荐。目前现有的实例分割方法主要有两类,一类是以Mask RCNN为代表的使用检测器+roi+mask head的方法,通过先检测,提取roi特征再前背景分割的方式获得实例级的分割结果, 这类方法需要分别对每个物体提取特征,然后逐个通过mask head进行前背景分割,速度比较慢。 另一类是先采用语义分割获得每类的mask, 然后在在分割的mask上采用类似聚类的后处理方式获得每类的不同实例,后处理非常复杂、速度慢,针对遮挡、重叠等问题存在天然缺陷。 实例分割相比于检测的难点在于每个实例的大小不同, 而不能向检测那样直接回归一个固定长度的向量来确定目标。实例分割相比于语义分割的难点在于每个图片实例的个数不确定, 因此不能采用固定通道的mask在原图上直接分割出不同实例。CondInst针对这些困难,从一个新的角度解决实例分割问题, 其核心思想在于以实例为条件,动态的为每个实例生成一个mask head,然后使用生成的mask head在feature map上获得不同实例的分割结果。CondInst具有两个优点:1)通过全卷积网络解决了实例分割,无需进行ROI裁剪和特征对齐。2)由于动态生成的条件卷积的容量大大提高,因此mask head可以非常紧凑(例如3个卷积层,每个仅具有8个通道),从而可以显着加快推理速度。 在COCO数据集上, 性能优于Mask R-CNN。
##亮点
1、CondInst是完全由卷积组成的,并且不依赖于ROI操作。无需调整特征图的大小即可获得具有更高精确度的高分辨率实例mask。
2、与以前的方法不同,CondInst的mask head中的卷积会动态生成并以实例为条件。由于仅要求卷积预测一个实例的掩码,因此极大地减轻了学习要求,从而减轻了卷积的负载。与边界框检测器FCOS相比,CondInst仅需要多花费约10%的计算时间,甚至可以处理每个图像的最大实例数(即100个实例)
方法流程
CondInst建立在FCOS之上,相比于FCOS有两点差异1).增加了一个Fmask分支,Fmask的特征来自于P3层,拼接了每个位置的相对坐标,作为mask head的输入来生成最终的实例mask。2).在FCOS的检测头上增加了controller head,controller head的输出用作mask head的权重。对于每一个实例, controller head的输出肯定不同, 相当于为每个实例定制了一个mask head,这也是为什么叫做动态mask head的原因,当mask head用于全局的Fmask feature上时,就可以区分这个instance和北京,获得实例级分割结果。这种动态生成卷积核的思想来自于CondConv,将一个网络的输出直接作为卷积核的权重。
##动态mask head
controller是一个普通的卷积网络,它在每个位置预测一个m+k维的向量,m为mask head中权重的数量,k为mask head中bias的数量, 则将controller的输出可以分解为几组权重和bias,代码如下:
assert params.dim() == 2
assert len(weight_nums) == len(bias_nums)
assert params.size(1) == sum(weight_nums) + sum(bias_nums)
num_insts = params.size(0)
num_layers = len(weight_nums)
params_splits = list(torch.split_with_sizes(
params, weight_nums + bias_nums, dim=1
))
weight_splits = params_splits[:num_layers]
bias_splits = params_splits[num_layers:]
for l in range(num_layers):
if l < num_layers - 1:
# out_channels x in_channels x 1 x 1
weight_splits[l] = weight_splits[l].reshape(num_insts * channels, -1, 1, 1)
bias_splits[l] = bias_splits[l].reshape(num_insts * channels)
else:
# out_channels x in_channels x 1 x 1
weight_splits[l] = weight_splits[l].reshape(num_insts * 1, -1, 1, 1)
bias_splits[l] = bias_splits[l].reshape(num_insts)
return weight_splits, bias_splits
获得权重之后,将其组成mask head,用于Fmask特征上获得每个实例的分割结果,代码如下:
def mask_heads_forward(self, features, weights, biases, num_insts):
'''
:param features
:param weights: [w0, w1, ...]
:param bias: [b0, b1, ...]
:return:
'''
assert features.dim() == 4
n_layers = len(weights)
x = features
for i, (w, b) in enumerate(zip(weights, biases)):
x = F.conv2d(
x, w, bias=b,
stride=1, padding=0,
groups=num_insts
)
if i < n_layers - 1:
x = F.relu(x)
return x
注意一下,作者子啊代码中选取了置信度比较高的前100 mask head。
Inference
CondInst的inference比较直接,首先是检测部分得到检测的结果,然后采用box-based NMS来去除重复框,最后选出top 100的检测框,只有这部分instances会进行instance mask的预测。由于产生的mask head非常小,所以100个instance的mask预测时间只需要4.5ms,那么CondInst的预测时间仅比原始的FCOS增加了约10%。这里额外要说的一点是CondInst的box预测主要用于NMS,但不会参与instance mask的预测中,而Mask R-CNN是需要box来进行ROI croping。
实验结果
- 点赞
- 收藏
- 关注作者
评论(0)