Object Detection | 目标检测


目标检测指的是给定一个输入的图像,我们希望模型可以分析这个图像里究竟有哪些物体,并能够定位这些物体在整个图像中的位置,对于图像中的每一个像素,能够分析其属于哪一个物体。它包含两部分,一部分是对图像内容的识别,也就是分类问题,另一个是标记目标在图像中的位置,也就是定位的问题。因此,神经网络的输出也包含了更多的信息,除了包含物体的类别外,还需要包含矩形框的位置信息。

Sliding Windonw Detection

一种容易想到的目标检测方式是使用滑动窗口,我们用一个矩形窗口依次滑过图片中的每个区域,每个窗口通过一个已经训练好分类模型(比如Resnet,GoogLnet等)进行图片识别,如下图所示

这种方式的问题在于计算量太大,对于每个窗口都需要单独计算。举例来说,上图中的窗口大小为一个14*14*3,现在这个窗口向左,右和右下各滑动一次,假设步长为2,则需要进行四次模型运算,得到4个结果

在参考文献[1]中提到了减少计算量一个方案是将原来神经网路中的FC层变成卷积层,如下图所示

还是上图中的滑动窗口,滑动步长为2,则4次滑动运算只需要一次即可完成

此时得到结果是一个2*2*4的矩阵,包含了当前位置,右边,下边和右下共4组计算结果。实际上这种方式是将上面4次单独计算合并成了一次。推而广之,假如我们有一个更大的的滑动窗口(28*28*3),一次运算,我们可以得到64组运算结果

再进一步扩大,我们可以让一个图片等同于一个滑动窗口,因此只进行一次模型运算即可

YOLO

上述算法虽然解决了计算效率问题,但是没有解决对目标矩形的定位问题,例如,上面算法中,我们完全有可能碰到这种情况,即没有任何一个窗口能完全覆盖检测目标,如下图所示

解决这个问题,参考文献[2],即YOLO算法提供一个不错的思路。YOLO将一张图片分割成$n$*$n$的格子,如下图中$n=3$,即9个格子

在数据标注的时候,我们将待检测目标的中心赋予某一个box,比如上图中的黄点和绿点。然后对该box用下面的一个向量表示

其中,$p_c$表示该box中是否有待检测的目标,如果有$p_c$为1,否则为0,$(b_x, b_y, b_h, b_w)$表示目标矩形,其中每个box的左上角为(0,0),右下角为(1,1)。$(b_x,b_y)$表示目标的中心点,$(b_h, b_w)$表示矩形框的高和宽相对于该box的百分比,如下图所示

最后$c_1, c_2,c_3$表示目标类别,比如上图中$c_1$表示行人,$c_2$表示车辆,$c_3$表示摩托车,则上图中黄色box的$y$值为

因此,YOLO模型的输出便是9个上述目标向量,即3*3*8的矩阵。由于存在$(b_x, b_y, b_h, b_w)$的值,使得YOLO可以精确的计算出bounding box的位置,注意到 $(b_x,b_y)$必须在格子内,因此它们值在0到1之间,但是$(b_h,b_w)$可以大于1,因为可能存在目标物体比当前box大的情况。实际应用中,往往将一张图片分成19*19的格子,输出变为19*19*8,这降低了多个目标被分配到同一个格子的概率。

Evaluating Your Algorithm

我们该如何衡量目标检测的准确率呢,比如下图中目标矩形为红色,而实际检测结果却为紫色矩形。

此时我们要引入一个指标叫做Intersection Over Union (IoU)。它的计算方式为用两个矩形的Intersection部分除以它们的union部分,得到的比值作为准确率。如果IoU的值大于0.5,则认为识别的区域是正确的。当然0.5这个值可以根据实际情况进行调节。

Non-Max Suppression

实际应用中的另一个问题是对于图片中某个目标,我们的模型可能有多个box输出的$p_c$值都大于0.1,即都认为自己得到的是正确的bounding box,如下图所示

上图中左边是我们的输入图像,根据YOLO算法,将其分为19*19个格子,右边是模型的输出,可见有多个$p_c$值符合条件的矩形,这时我们要用一种叫做Non-Max Suppression的算法来选出唯一的bounding box,步骤如下

  1. 首先去掉$p_c$值小于0.6的bounding boxes
  2. 在剩下的box中,选取$p_x$最大的
  3. 在剩下的box中去掉那些IoU值大于0.5的
  4. 重复第二步

在第一步中,我们常用score代替$p_c$作为筛选条件,Score的计算方式如下图

上图中,我们假设有一组box1的预测结果,其中$p_1$为$0.6$,说明box1有60%的几率有目标。我们用这个值乘以$c_1…c_80$,例如$Score_{c_3} = 0.6 \times 0.73 = 0.44$。然后再这个80个类别中找出score最大的类别标记在box1上

Anchor Boxes

前面我们每个box只负责一个目标的检测,但有时候会出现多个目标的中心点集中在同一个box里的情况,如下图所示

此时我们的标注$y$需要包含两个box的信息

我们可以用前8个元素表示第一个anchor box(图中垂直的),后8个元素表示第二个anchor box(图中水平的),因此模型的输出变成了3*3*16。以图中标注的两个矩形为例,则该box的$y$如下

那么如果这个box中有三个目标呢?目前这种情况很少见,YOLO还不能很好的处理这种情况。实际上同一个box中出现两个目标的情况也比较少见。

YOLO Recap

在引入了Anchor Box之后,一个完整的YOLO模型如下图所示

上面模型中的输入为(608 * 608 * 3)的图片,输出为(19 * 19 * 5 * 85 )的矩阵。可以该模型使用了5个Anchor Box,每个box的$y$除了包含$p_c$和$(b_x,b_y,b_h,b_w)$外,还有80个类别。

R-CNN和其相关模型

除了YOLO模型外,还有一些模型可以做目标识别,比较有名的就是R-CNN以它相关的变种。下面简略介绍一下其实现的思路

Fast R-CNN

我们需要首先为图像生成一系列RoIs(Region of Interests),每一个RoI都是一个bounding box。以Fast R-CNN为例,我们需要为每张图片准备大概2000个。然后用卷积神经网络提取图片中的feature。论文中使用的VGG16。例如,一张(1, 3, 512, 512)的图片经过CNN网络后得到一组(512, 3, 16, 16)的feature map。

接下来我们要对从feature maps中提取RoI,由于我们的feature map的大小已经从(512, 512)变成了(16, 16), 我们的RoI区域也要等比例缩小,例如一个(x:296, y:192, h:145, w:200)的bounding box,在feature map中将变成(x:9.25, y:6, h:4.53, w:6.25 )如下图所示

此时我们发现bbox出现了小数,我们需要其quantize成整数,但是这会损失一部分数据,如下图三所示

接下来我们要对RoI中的像素进行RoI Pooling,其目的是将各种大小不一的bbox映射成长度统一的vector以便进行后面的FC。具体做法是使用max pooling,还是上面的例子,经过quantization后,我们的bbox变成了4x6的,接下来我们用max pooling把它映射成3x3的,如下图所示

上图中我们发现最下面一行数据也被丢弃掉了。通过RoI Pooling我们可以得到一组($roi,512,3,3的feature map,这也是后面FC层的输入,最终通过两层FC我们得到了两个输出结果,一个是classification,表明RoI中的Object类别,另一个是RoI的bbox,用来标识Object

Faster R-CNN

相比Fast R-CNN,Faster R-CNN的主要改变是将RoI的提取整合进了网络

Mask R-CNN

Mask R-CNN是基于Faster R-CNN的架构,引入了Instant Segmentation。它除了输出目标物体的类型和bbox意外,还输出一个segmeation mask,其结构如下图所示

由于Mask R-CNN需要生成像素级别的mask,前面提到的RoI Pooling由于损失太多data因此精度大大降低。为了解决这个问题Mask R-CNN对上面RoI pooling的改进,提出了RoI Align。我们下面来重点介绍这个算法

前面已经知道RoI Pooling的两次quantization损失了很多data,RoI Align通过使用双线性二次插值弥补了这一点。还是以前面例子来说明,下图是我们前面的bbox,我们的目标还是对其进行3x3的RoI Pooling操作

和之前不同的是,我们此时不对x,y,w,h进行取整,而是应用双线性插值,具体做法是将RoI区域划分成3x3的格子,然后计算4个点的位置如下图所示

接下来我们便可以使用下面公式对上面的4个点进行双线性插值计算,其中Q值为每个点对应的像素值

下面我们以左上角(9.44, 6.50)为例,看一下双线性插值是如何计算的。公式里的x,y分别对应(9.44, 6.50)。其中(x1, x2, y1, y2)分别对应四个点邻接cell的中心值,以(9.44, 6.50)为例,和它最近的cell的中心点为(9.50, 6.50),因此x19.5,y16.50; 左下角点邻接cell的中心值为(9.50, 7.50),由于x1没有发生变化还是9.5,而y2变成了7.5; 以此类推,我们可以得到右上和右下两个cell的中心点分别为 (10.50, 6.50)(10.50, 7.50),此时x1,x2,y1,y2的值均已确定,我们可以套用上面公式计算出(x,y)点处的值为0.14

按照上述方式我们可以为剩余三个点计算其双线性插值结果,如下图所示

当我们结算完这个4个点的双线性插值结果,我们便可以用max pooling得到该区域的RoI Align的结果。

重复上述步骤我们可以得到这RoI Region的结果,如下图所示

理解了RoI Align之后,我们便可以和Fast R-CNN一样对所有的feature map进行RoI Align的操作,得到一组($roi,512,3,3)的feature map,作为后续layer的输入

和RoI Pooling相比,RoI Align利用了更多RoI Region周围的像素信息(上图中左边绿色部分),因此可以得到更准确的结果

Resources