目标检测技术从阶段上分为两种,一阶段和二阶段。
二阶段的核心思想是:首先提出proposal框,通过第一阶段的网络回归出目标框的大致位置、大小以及是前景的概率,第二阶段是通过另一个网络回归出目标框的位置、大小及类别。
一阶段网络的核心:对于输入图像,通过网络直接回归出目标大小、位置和类别
当某个类别的样本数特别多的时候,训练出来的网络对该类的检测精度往往会比较高,而当某一类的训练样本数较少的时候,模型对该类目标的检测精度就会有所下降。
对于一阶段目标检测来说,它既要做定位又要做分类,最后几层中1X1的卷积层的loss都是混合在一起,没有明确的分工。
对于二阶段目标检测来说,在RPN网络结构中进行了前景和背景的分类和检测,这个过程与一阶段的目标检测直接一上来就进行分类和检测要简单的多,有了前景和背景的区分,就可以选择性的挑选样本,这样正负样本就更加的均衡,然后重点对一些参数进行分类训练,训练的分类难度会比一阶段目标检测直接做混合分类和预测框回归要来的简单很多。
但是一阶段检测算法要比二阶段检测算法快。
RPN获得多个anchors的时候,会使用一个NMS,在进行回归 操作的时候,预测框和标签的IOU同回归后预测框和标签的IOU相比,一般会有较大的变化,但是NMS使用的时候用的是回归前的置信度,这样就会导致一些回归后高IOU的预测框被删除。这就使得回归前的置信度并不能完全表征回归后的IOU大小。这样就会导致算法精度的下降,在第一次使用NMS时候这种情况会比较明显,第二次使用的时候就会好很多。因此,一阶段只使用一次NMS是会对精度有影响的,而二阶段目标检测中会在RPN之后进行一个更为精细的回归,在该处也会用到NMS,此时检测的精度就会好很多。
遮挡本身也可以分为两种类型,一种是由于非目标造成的遮挡,一种是由于也是需要检测的目标造成的遮挡。这两种遮挡分别被叫做occlusion和crowded。
对于前一种类型遮挡,很难有针对性的办法去解决,最好的办法也就是使用更多的数据和更强的feature。可以从训练数据入手。加掩膜,加扰动,提高算法对遮挡的应对能力。
对于第二种遮挡,提出了Repulsion Loss
FPN是在卷积神经网络中图像金字塔的应用。图像金字塔在多尺度识别中有重要的作用,尤其是小目标检测。顶层特征上采样后和底层特征融合,每层独立预测。
FPN的设计动机:
利用预训练模型进行迁移微调(fine-tuning),预训练模型通常在特征上拥有很好的语义表达。此时,只需将模型在小数据集上进行微调就能取得不错的效果。这也是目前大部分小数据集常用的训练方式。视觉领域内,通常会ImageNet上训练完成的模型。自然语言处理领域,也有BERT模型等预训练模型可以使用。
单样本或者少样本学习(one-shot,few-shot learning),这种方式适用于样本类别远远大于样本数量的情况等极端数据集。例如有1000个类别,每个类别只提供1-5个样本。少样本学习同样也需要借助预训练模型,但有别于微调的在于,微调通常仍然在学习不同类别的语义,而少样本学习通常需要学习样本之间的距离度量。例如孪生网络(Siamese Neural Networks)就是通过训练两个同种结构的网络来判别输入的两张图片是否属于同一类。3. 以上两种是常用训练小样本数据集的方式。此外,也有些常用的方式:数据集增强、正则或者半监督学习等方式来解决小样本数据集的训练问题。
import numpy as np
def ComputeIOU(boxA,boxB):
# 计算相交框的坐标
# bbox[0][1] 左上角坐标
# bbox[2][3] 右下角坐标
x1 = np.max([boxA[0],boxB[0]])
y1 = np.max([boxA[1], boxB[1]])
x2 = np.min([boxA[2], boxB[2]])
y2 = np.min([boxA[3], boxB[3]])
# 计算交区域 并区域 及IOU
S_A = (boxA[2]-boxA[0]+1)*(boxA[3]-boxA[1]+1)
S_B = (boxB[2]-boxB[0]+1)*(boxB[3]-boxB[1]+1)
interArea = np.max([x2-x1+1, 0])*np.max([y2-y1+1,0]) ##一定要和0比较大小,如果是负数就说明压根不相交
unionArea = S_A + S_B - interArea
iou = interArea/unionArea
return iou
boxA = [1,1,3,3]
boxB = [2,2,4,4]
IOU = ComputeIOU(boxA, boxB)
import numpy as np
def nms(dets,iou_thred,cfd_thred):
if len(dets) == 0:
return []
bboxes = np.array(dets)
# 对整个bboxes排序
bboxes = bboxes[np.argsort(bboxes[:,4])]
pick_bboxes = []
while bboxes.shape[0] and bboxes[-1,-1] >= cfd_thred:
bbox = bboxes[-1]
x1 = np.maximum(bbox[0],bboxes[:-1,0])
y1 = np.maximum(bbox[1], bboxes[:-1,1])
x2 = np.minimum(bbox[2], bboxes[:-1,2])
y2 = np.minimum(bbox[3], bboxes[:-1,3])
inters = np.maximum(x2-x1+1, 0) * np.maximum(y2-y1+1, 0)
unions = (bbox[2]-bbox[0]+1)*(bbox[3]-bbox[1]+1) + (bboxes[:-1,2]-bboxes[:-1,0]+1)*(bboxes[:-1,3]-bboxes[:-1,1]+1) - inters
ious = inters/unions
keep_indices = np.where(ious<iou_thred)
bboxes = bboxes[keep_indices] ## indices一定不包括自己
pick_bboxes.append(bbox)
return np.asarray(pick_bboxes)
dets = [[187, 82, 337, 317, 0.9], [150, 67, 305, 282, 0.75], [246, 121, 368, 304, 0.8]]
dets_nms = nms(dets, 0.5, 0.3)
print(dets_nms)