目标检测(Object Detection)学习笔记(一)综述



本篇学习笔记内容包括以下内容:

  • 发展现状
  • 关键任务
  • 评价指标
  • 常用数据集
  • 检测流程
  • 检测框架

发展现状

随着深度学习技术的火热发展,目标检测算法也从基于手工特征的传统算法转向了基于深度神经网络的检测技术。从最初 2013 年提出的 R-CNN、OverFeat,到后面的 Fast/Faster R-CNN、SSD、YOLO 系列,以及Mask R-CNN、RefineDet、RFBNet等(见下图,完整论文列表参见1)。短短不到五年时间,基于深度学习的目标检测技术,在网络结构上,从 two-stage 到 one-stage,从 bottom-up only 到 Top-Down,从 single scale network 到 feature pyramid network,从面向 PC 端到面向移动端,都涌现出许多好的算法技术,这些算法在开放目标检测数据集上的检测效果和性能都很出色。

image_1csdgmgeg8q9ajg1pek2pi1elmm.png-248.9kB

关键任务

目标检测任务可分为两个关键的子任务:目标分类和目标定位。目标分类任务负责判断输入图像或所选择图像区域(Proposals)中是否有感兴趣类别的物体出现,输出一系列带分数的标签表明感兴趣类别的物体出现在输入图像或所选择图像区域(Proposals)中的可能性。目标定位任务负责确定输入图像或所选择图像区域(Proposals)中感兴趣类别的物体的位置和范围,输出物体的包围盒、或物体中心、或物体的闭合边界等,通常使用方形包围盒,即Bounding Box用来表示物体的位置信息。

评价指标

IoU

IoU是预测框与ground truth的交集和并集的比值。这个量也被称为Jaccard指数,并于20世纪初由Paul Jaccard首次提出。为了得到交集和并集,我们首先将预测框与ground truth放在一起,如图所示。

image_1csq7cjhalliu431u4h1mv21kfb9.png-8.7kB

目标检测问题同时是一个回归和分类问题。首先,为了评估定位精度,需要计算IoU(Intersection over Union,介于0到1之间),其表示预测框与真实框(ground-truth box)之间的重叠程度。IoU越高,预测框的位置越准确。因而,在评估预测框时,通常会设置一个IoU阈值(如0.5),只有当预测框与真实框的IoU值大于这个阈值时,该预测框才被认定为真阳性(True Positive, TP),反之就是假阳性(False Positive,FP)。

mAP

mAP定义及相关概念

  • mAP: mean Average Precision, 即各类别AP的平均值AP: PR曲线下面积
  • PR曲线: Precision-Recall曲线
  • Precision: TP / (TP + FP)
  • Recall: TP / (TP + FN)
  • TP: IoU>阈值的检测框数量(同一Ground Truth只计算一次)
  • FP: IoU<=阈值的检测框,或者是检测到同一个GT的多余检测框的数量
  • FN: 没有检测到的GT的数量

mAP计算步骤

  • 首先统计检测出的boxes总数(N=TP+FN),并按照confidence排序;
  • 随后根据设定好的IoU阈值(与GT),判断每个区域为TP或FP。如同一个GT检测出多个区域则只取confidence最大的为TP,其他全部算作FP;
  • 依次添加box,计算累计recall,precision,取每个recall值下的最大precision;
  • 计算P-R曲线面积,即该类别下的AP;
  • 计算所有类别,平均,得到mAP。

对于目标检测,mAP一般在某个固定的IoU上计算,但是不同的IoU值会改变TP和FP的比例,从而造成mAP的差异。COCO数据集提供了官方的评估指标,它的AP是计算一系列IoU下(0.5:0.05:0.9,见说明)AP的平均值,这样可以消除IoU导致的AP波动。其实对于PASCAL VOC数据集也是这样,Facebook的Detectron上的有比较清晰的实现。

参考代码(Detectron)

Precision和recall的计算:

# 按照置信度降序排序
sorted_ind = np.argsort(-confidence)
BB = BB[sorted_ind, :]   # 预测框坐标
image_ids = [image_ids[x] for x in sorted_ind] # 各个预测框的对应图片id

# 便利预测框,并统计TPs和FPs
nd = len(image_ids)
tp = np.zeros(nd)
fp = np.zeros(nd)
for d in range(nd):
    R = class_recs[image_ids[d]]
    bb = BB[d, :].astype(float)
    ovmax = -np.inf
    BBGT = R['bbox'].astype(float)  # ground truth

    if BBGT.size > 0:
        # 计算IoU
        # intersection
        ixmin = np.maximum(BBGT[:, 0], bb[0])
        iymin = np.maximum(BBGT[:, 1], bb[1])
        ixmax = np.minimum(BBGT[:, 2], bb[2])
        iymax = np.minimum(BBGT[:, 3], bb[3])
        iw = np.maximum(ixmax - ixmin + 1., 0.)
        ih = np.maximum(iymax - iymin + 1., 0.)
        inters = iw * ih

        # union
        uni = ((bb[2] - bb[0] + 1.) * (bb[3] - bb[1] + 1.) +
               (BBGT[:, 2] - BBGT[:, 0] + 1.) *
               (BBGT[:, 3] - BBGT[:, 1] + 1.) - inters)

        overlaps = inters / uni
        ovmax = np.max(overlaps)
        jmax = np.argmax(overlaps)
    # 取最大的IoU
    if ovmax > ovthresh:  # 是否大于阈值
        if not R['difficult'][jmax]:  # 非difficult物体
            if not R['det'][jmax]:    # 未被检测
                tp[d] = 1.
                R['det'][jmax] = 1    # 标记已被检测
            else:
                fp[d] = 1.
    else:
        fp[d] = 1.

# 计算precision recall
fp = np.cumsum(fp)
tp = np.cumsum(tp)
rec = tp / float(npos)
# avoid divide by zero in case the first detection matches a difficult
# ground truth
prec = tp / np.maximum(tp + fp, np.finfo(np.float64).eps)

这里最终得到一系列的precision和recall值,并且这些值是按照置信度降低排列统计的,可以认为是取不同的置信度阈值(或者rank值)得到的。然后据此可以计算AP:

def voc_ap(rec, prec, use_07_metric=False):
    """Compute VOC AP given precision and recall. If use_07_metric is true, uses
    the VOC 07 11-point method (default:False).
    """
    if use_07_metric:  # 使用07年方法
        # 11 个点
        ap = 0.
        for t in np.arange(0., 1.1, 0.1):
            if np.sum(rec >= t) == 0:
                p = 0
            else:
                p = np.max(prec[rec >= t])  # 插值
            ap = ap + p / 11.
    else:  # 新方式,计算所有点
        # correct AP calculation
        # first append sentinel values at the end
        mrec = np.concatenate(([0.], rec, [1.]))
        mpre = np.concatenate(([0.], prec, [0.]))

        # compute the precision 曲线值(也用了插值)
        for i in range(mpre.size - 1, 0, -1):
            mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])

        # to calculate area under PR curve, look for points
        # where X axis (recall) changes value
        i = np.where(mrec[1:] != mrec[:-1])[0]

        # and sum (\Delta recall) * prec
        ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
    return ap

参考链接

目标检测中的mAP是什么含义?-知乎
Metrics for object detection -github
目标检测模型的评估指标mAP详解(附代码4

FPS

除了检测准确度,目标检测算法的另外一个重要性能指标是速度,只有速度快,才能实现实时检测,这对一些应用场景极其重要。评估速度的常用指标是每秒帧率(Frame Per Second,FPS),即每秒内可以处理的图片数量。当然要对比FPS,你需要在同一硬件上进行。另外也可以使用处理一张图片所需时间来评估检测速度,时间越短,速度越快。

常用数据集

目标检测常用的数据集包括PASCAL VOC,ImageNet,MS COCO等数据集,这些数据集用于研究者测试算法性能或者用于竞赛。

PASCAL VOC

PASCAL VOC(The PASCAL Visual Object Classification)是目标检测,分类,分割等领域一个有名的数据集。从2005到2012年,共举办了8个不同的挑战赛。PASCAL VOC包含约10,000张带有边界框的图片用于训练和验证。但是,PASCAL VOC数据集仅包含20个类别,因此其被看成目标检测问题的一个基准数据集。

ImageNet

ImageNet在2013年放出了包含边界框的目标检测数据集。训练数据集包含500,000张图片,属于200类物体。由于数据集太大,训练所需计算量很大,因而很少使用。同时,由于类别数也比较多,目标检测的难度也相当大。

COCO

另外一个有名的数据集是Microsoft公司建立的MS COCO(Common Objects in COntext)数据集。这个数据集用于多种竞赛:图像标题生成,目标检测,关键点检测和物体分割。对于目标检测任务,COCO共包含80个类别,每年大赛的训练和验证数据集包含超过120,000个图片,超过40,000个测试图片。测试集最近被划分为两类,一类是test-dev数据集用于研究者,一类是test-challenge数据集用于竞赛者。测试集的标签数据没有公开,以避免在测试集上过拟合。

检测流程

基于以上两个子任务,现代目标检测技术的流程基本可以分为如下步骤,

  • 输入图像;
  • Backbone即主干网络,用以学习图片特征,一般复用图像分类网络;
  • Head即出口,从图像特征中学习检测目标,如生成proposal与得到分类评分;
  • Postprocess后处理,使用NMS等手段优化结果。

image_1csi9f0aag8l1i9m14c8dhp2k89.png-84.7kB

检测框架

目标检测框架大致分为两大类,一个是two-stage 框架,先进行proposal区域生成,再进行目标分类;另一个是one-stage, end to end的框架,只使用一个网络中直接输出结果。一般来讲,one-stage 检测器的 recall 较高,但是 localization 会有所折衷;相反, two-stage 检测器的定位能力则较强,但是 recall 较低,主要原因是第二个 stage 可以 refine 框的精度,但是也会误杀一些正样本。之前的普遍观点是 one-stage 检测器往往更快,比如 YOLO、SSD; two-stage 检测器往往更准,比如 Faster R-CNN、FPN,但从最新的进展来看,这两种方法的区别已经不大。