目标检测,即 Object Detection
目标检测的任务是找出图像中所有感兴趣的目标,并确定它们的类别和位置。
目标检测的位置信息⼀般由两种格式(以图片左上⻆为原点(0,0)):
(xmin, ymin, xmax, ymax)
(x_center, y_center, w, h)
目标检测的应用场景
目标检测具有巨⼤的实用价值和应用前景。
经典的目标检测数据集有两种,PASCAL VOC数据集 和 MS COCO数据集。
下载地址
PASCAL VOC是目标检测领域的经典数据集。PASCAL VOC包含约10,000张带有边界框的图片用于训练和验证。PASCAL VOC数据集是目标检测问题的一个基准数据集,很多模型都是在此数据集上得到的,常用的是VOC2007和VOC2012两个版本数据,共20个类别,分别是:
人: 人
动物: 鸟,猫,牛,狗,马,羊
交通工具: 飞机,自行车,船,公共汽车,汽车,摩托车,火车
室内: 瓶子,椅子,餐桌,盆栽,沙发,电视/显示器
整个数据的目录结构如下所示:
IOU(intersection over union,交并比)是目标检测算法中用来评价2个矩形框之间相似度的指标:
I O U = 两个矩形框相交的面积 两个矩形框相并的面积 IOU = \frac{两个矩形框相交的面积}{两个矩形框相并的面积} IOU=两个矩形框相并的面积两个矩形框相交的面积
如下图所示:
再通过⼀个例⼦看下在⽬标检测中的应用:
其中上图蓝⾊框框为检测结果,红⾊框框为真实标注。
那我们就可以通过预测结果与真实结果之间的交并⽐来衡量两者之间的相似度。⼀般情况下对于检测框的判定都会存在⼀个阈值,也就是 IOU 的阈值,⼀般可以设置当 IOU 的值⼤于 0.5 的时候,则可认为检测到目标物体。
实现方法:
import numpy as np
def Iou(box1, box2, wh=False):
if wh == False:
# 使用极坐标形式:(xmin, ymin, xmax, ymax)
xmin1, ymin1, xmax1, ymax1 = box1
xmin2, ymin2, xmax2, ymax2 = box2
else:
# 使用中心坐标形式:(x_center, y_center, w, h)
# 第一个框
xmin1, ymin1 = int(box1[0] - box1[2] / 2.0), int(box1[1] - box1[3] / 2.0)
xmax1, ymax1 = int(box1[0] + box1[2] / 2.0), int(box1[1] + box1[3] / 2.0)
# 第二个框
xmin2, ymin2 = int(box2[0] - box2[2] / 2.0), int(box2[1] - box1[3] / 2.0)
xmax2, ymax2 = int(box2[0] + box2[2] / 2.0), int(box2[1] + box1[3] / 2.0)
# 获取交集的左上角、左下角坐标
xx1 = np.max(xmin1, xmin2)
yy1 = np.max(ymin1, ymin2)
xx2 = np.min(xmax1, xmax2)
yy2 = np.min(ymax1, ymax2)
# 计算交集的面积
inner_area = np.max(0, xx2 - xx1) * np.max(0, yy2 - yy1)
# 计算并集的面积
area1 = (xmax1-xmin1) * (ymax1-ymin1)
area2 = (xmax2-xmin2) * (ymax2-ymin2)
union_area = area1 + area2 - inner_area
# IOU
# 防止分母为零,故在分母处加一个极小的值,对最终的结果影响不大
IOU = inner_area / (union_area + 1e-6)
return IOU
假设我们检测结果如下所示,并展示在图像上:
import matplotlib.pyplot as plt
# 真实框
true_box = [100, 35, 398, 400]
# 预测框
pre_box = [40, 150, 355, 398]
img = plt.imread("dog.jpeg")
fig = plt.imshow(img)
# 将真实框绘制在图像上
# (true_box[0],true_box[1])左上角坐标
fig.axes.add_patch(
plt.Rectangle((true_box[0],true_box[1]),width=true_box[2]-true_box[0],height=true_box[3]-true_box[1],fill=False,edgecolor="blue", linewidth=2))
# 将预测框绘制在图像上
# (pre_box[0],pre_box[1])左上角坐标
fig.axes.add_patch(
plt.Rectangle((pre_box[0],pre_box[1]),width=pre_box[2]-pre_box[0],
height=pre_box[3]-pre_box[1],fill=False,edgecolor="red", linewidth=2))
计算 IOU:
Iou(true_box,pre_box)
# 0.5114435907762924
前面的IOU只是衡量了预测的位置与真实位置之间的相似度,而没有考虑分类结果。
目标检测问题中的每个图片都可能包含⼀些不同类别的物体,需要评估模型的物体 分类(准确率)和 定位(IOU)性能,评估这两者需要 mAP,即 Mean Average Precision,mAP是多个分类任务的 AP 的平均值,而AP(average precision)是PR曲线下的⾯积。在目标检测中,mAP是主要的衡量指标。
复习混淆矩阵:
查准率、查全率
查准率、查全率绘制的曲线称为 P-R 曲线
先定义两个公式,⼀个是 Precision,⼀个是 Recall,与上⾯的公式相
同,扩展开来,⽤另外⼀种形式进⾏展示,其中 all detctions 代表
所有预测框的数量, all ground truths 代表所有 GT(真实框) 的数量。
AP 是计算某⼀类 P-R 曲线下的面积,mAP 则是计算所有类别 P-R 曲线下面积的平均值。
非极⼤值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素。例如在行人检测中,滑动窗⼝经提取特征,经分类器分类识别后,每个窗口都会得到⼀个分数。但是滑动窗⼝会导致很多窗口与其他窗口存在包含或者大部分交叉的情况。这时就需要用到NMS来选取那些邻域⾥分数最⾼(是行人的概率最大),并且抑制那些分数低的窗口。 NMS在计算机视觉领域有着非常重要的应⽤,如视频目标跟踪、数据挖掘、3D重建、目标识别以及纹理分析等 。
在⽬标检测中,NMS的⽬的就是要去除冗余的检测框,保留最好的⼀个,如下图所示:
NMS的原理是对于预测框的列表B及其对应的置信度S,选择具有最⼤ score 的检测框M,将其从B集合中移除并加⼊到最终的检测结果D中。通常将B中剩余检测框中与M的 IOU ⼤于阈值的框从B中移除,重复这个过程,直到B为空。
使⽤流程如下图所示:
通过⼀个例⼦理解 NMS的使⽤⽅法,假设定位⻋辆,算法就找出了⼀系列的矩形框,我们需要判别哪些矩形框是没⽤的,需要使⽤NMS的⽅法来实现。
假设现在检测窗⼝有:A、B、C、D、E 5个候选框,接下来进⾏迭代计算:
最终结果为在这个5个中检测出了两个⽬标为A和B。
单类别的NMS的实现⽅法如下所示:
import numpy as np
def nms(boxes, score, threshold):
# 无候选框返回空
if len(boxes) == 0:
return [], []
boxes = np.array(boxes)
score = np.array(score)
x1 = boxes[:, 0]
y1 = boxes[:, 1]
x2 = boxes[:, 2]
y2 = boxes[:, 3]
# 最终筛选的框和分数
picked_boxes = []
picked_score = []
# 对置信度进行排序(默认从小到大),获取排序后的下标
order = np.argsort(score)
areas = (x2 - x1) * (y2 - y1)
while order.size > 0:
# 获取score最大值的索引
index = order[-1]
picked_boxes.append(boxes[index])
picked_score.append(score[index])
# 获取当前置信度最⼤的候选框与其他任意候选框的相交⾯积
# np.maximum(X, Y) 用于逐元素比较两个array的大小。
# 这里用到了numpy的广播机制
x11 = np.maximum(x1[index], x1[order[:-1]])
y11 = np.maximum(y1[index], y1[order[:-1]])
x22 = np.minimum(x2[index], x2[order[:-1]])
y22 = np.minimum(y2[index], y2[order[:-1]])
# 计算相交的⾯积,不重叠时⾯积为0
w = np.maximum(0.0, x22 - x11)
h = np.maximum(0.0, y22 - y11)
inner_area = w * h
# IOU
IOU = inner_area / (areas[index] + areas[order[:-1]] - inner_area)
# 删除冗余框
keep_boxes = np.where(IOU < threshold)
# 更新order
order = order[keep_boxes]
return picked_boxes, picked_score
boxes = [(187, 82, 337, 317), (150, 67, 305, 282), (246, 121, 368, 304)]
score = [0.9, 0.65, 0.8]
threshold = 0.3
picked_boxes, picked_score = nms(boxes, score, threshold)
print('阈值threshold为:', threshold)
print('NMS后得到的bbox是:', picked_boxes)
print('NMS后得到的bbox的confidences是:', picked_score)
numpy中的广播机制
目标检测算法主要分为 two-stage(两阶段)和 one-stage(单阶段)两类
先由算法⽣成⼀系列作为样本的候选框,再通过卷积神经网络进行样本分类。如下图所示,主要通过⼀个卷积神经网络来完成目标检测过程,其提取的是CNN卷积特征,进⾏候选区域的筛选和目标检测两部分。网络的准确度⾼、速度相对较慢。
two-stage算法的代表是RCNN系列:R-CNN 到 Faster R-CNN⽹络。
直接通过主干网络给出目标的类别和位置信息,没有使⽤候选区域的筛选网路,这种算法速度快,适合实时任务,但是精度相对Two-stage目标检测网络降低了很多。
one-stage算法的代表是: YOLO系列:YOLOv1、YOLOv2、YOLOv3、SSD等。