copy不能算偷…copy!..程序员的事,能算偷吗? ——孔乙己
一、简介
目标检测的格局,其实是大同小异的:都是打头一个特征提取器,提取器有多种形态,SIFT——这是二十年前的事了,HOG——这是十几年前的事了,现在人都用CNN了。倘若简陋一点,提取完特征图后,直接设个阈值二值化,便得到结果。想精细一些呢,就要用到非极大值抑制,即NMS。
非极大值抑制,顾名思义,抑制掉不属于极大值的结果,你可知为何是极大值而不是最大值?因为我们需要筛选出的是局部极值,而非全局极值。其实,NMS的作用很简单——去掉检测结果中高度重复的bbox。
二、从Faster-RCNN的NMS说起
NMS的定义和作用我们明白了,该怎么实现它呢?这里不得不提到我最信赖的目标检测算法Faster-RCNN。作者在发表Faster-RCNN论文时,也公开了代码,其中就有NMS部分的代码。他实现的NMS大致流程是这样的:首先,所有的bbox为待定序列,算法需要输出的bbox为结果序列,初始时将待定序列中所有的bbox按分数从高到底排序。开始第一次筛选,将分数最高的bbox存入结果序列, 然后,计算分数最高的bbox与其他所有的bbox的IOU,将IOU大于阈值的bbox从待定序列中排除全部排除,至此,第一轮筛选结束。接着用剩下的bbox列表进行第二轮筛选,这样循环一直到待定序列为空,输出结果序列。
经过这样的算法流程,我们得到的结果是这样的:结果序列中所有的bbox与除自身外的其他bbox的IOU都小于阈值。这是Faster-RCNN的作者所使用的NMS方式。
在很长一段时间里,我对这种NMS的方式及其变种都很信赖,比如Soft-NMS之类的算法,这些算法都是依据bbox直接的IOU来做筛选的——我称之为IOU-NMS。
直到前一段时间,我遇到了一种棘手的情况,用IOU-NMS无法满足我的需求。
三、IOU-NMS无法解决的问题
我在用Faster-RCNN做人脸检测的时候,发现了一些棘手的现象:一些很密集的较小的人脸完全检测不出来。我第一反应是anchor的size太大了,使得网络无法覆盖到这种小样本。于是我将anchor的size降低一倍重新训练,有一些略微的效果,但是还不够好,看来单纯缩小anchor的size并不能完全解决这个问题。
我将目光放到了RPN网络的结果上。FasterRCNN是个two-stage的方法,第一步RPN生成一堆候选框,然后对候选框进行的后处理,最后将符合要求的候选框送到RCNN里面去回归。第一步需要验证的是,我的小目标究竟是在哪一步出问题了呢?是RPN就直接没检测到,还是RCNN的时候被过滤掉呢?我先检查了一下RPN的结果,未经过后处理之前,小目标是存在的,经过后处理之后,小目标就被过滤掉了。我对后处理做一些人工干预,让这些小目标进入RCNN,发现RCNN给它们打的分数都很高。那么问题就明确了,是后处理的过程不当导致这些小目标被排除了。
这里先说下后处理,具体流程是:
1.根据score阈值筛选一遍,低于阈值的全部过滤掉。
2.进行IOU-NMS。
3.按照score排序,取分数最高的N个目标。
那么,到底是哪一步出问题呢?首先,我要确认一下是不是第一步就把小目标筛选掉了,我看了一下,RPN给小目标打的分数普遍比较低,那么,就先去掉第一步,直接做第二步NMS。由于anchor数量是百万级别的,计算IOU花费了大量时间,但是我们这里先不管时间,先验证结果是不是正确。经过了漫长的算法运行之后,结果出来了,小目标能够保存下来,但是经过第三步score排序之后,这些小目标的score又比不过那些大目标,直接被排除在TOPN之外。因此,我把IOU的阈值调得极度严格,这样终于能够将那些小目标保留下来了。
一张人脸检测热力图,图中画红圈的部分分数很低,但却是应该保留的正样本。下方那些响应度很高的大块红色区域,虽然分数很高,但是存在非常多重复检测样本。
这个问题解决之后,接下来要解决运行时间的问题了,计算这上百万的anchor的IOU需要花费好几秒甚至十几秒的时间,这对于实际工程应用是不允许的。如果设置一定的阈值来初筛,又会伤害到上方图像中那些分值低的目标,那么,有没有另外一种简单粗暴的方法来合理有效地替换IOU-NMS呢?
四、NMS的另一种写法
让我们来回想一下,在传统方法里,NMS是怎么做的呢?我以DPM来举例,首先用HOG算法提取特征得到特征图,然后滑动窗口计算模板和特征图的卷积,模板分为主模板和部件模板,这样滑窗计算之后获得一张主目标热力图和多张部件热力图,然后按照距离损失函数加权,将这些热力图融合成一张热力图。接着,对这张热力图做NMS,这里我采用了另一种方式的NMS来替换IOU-NMS,我称它为FEAT-NMS,具体是先将低于阈值的热力点置为零,接下来,计算图像中每个点与其上下左右四个点的大小关系,当一个点的响应值大于其上下左右四个点的值时,才把这个点保留,否则就把这个点置零。注意,这里的置零必须是等所有点都计算完了之后再操作,计算的时候只记录要保留的点的位置,等计算完了之后在根据位置保留需要的点。同时,这里的点的概念通常也不是一个像素点,而是一个2x2或者3x4或更大的像素块,具体大小取决于你要检测的目标大小和尺寸,类似FasterRCNN的anchor的概念。
思考一下,这样的NMS计算,得到的是一个什么样的结果呢?事实上,这样的NMS过程相当于是计算热力图的局部极大值的过程。经过NMS后,可以保证热力图上的每个响应点都是局部极大值,都大于其上下左右的其他四个响应点。最关键的是,这样的算法流程比IOU的计算简便多了,时间消耗大大减小,虽然结果不如IOU-NMS那么高质量,但是对于第一步初筛来说已经够用了。
好像大家都很喜欢用这张图来当demo,我也跑一张吧。
五、总结
两种NMS都能够对筛选目标起到一定的作用,但是它们的特点却不相同。
IOU-NMS:优点在于能够通过设置阈值或是Soft-NMS策略很好地保留正样本,通过IOU-NMS得到的后处理结果是比较高质量的,但是算法运行时间比较长。
FEAT-NMS:简单粗暴,算法运行时间少,结果也比较粗糙。
在硬件资源充足、运行时间不敏感的应用场景下,可以选用IOU-NMS。在运行时间受限的情况下,可以考虑换成FEAT-NMS。当然,也可以结合这两种方法一起使用,先进行FEAT-NMS,将筛选过的结果再进行IOU-NMS,这样的效果就会更加高质量。
PS:其实,整个过程中还有一个很关键的问题,那就是为什么小目标的分数会低于大目标呢?这一点我也不是很确定,只能给出一些猜测:一是小目标的图像信息太少了,网络可能会把特征当做是噪声;二是小目标在生成训练样本的过程中不占优势,我们知道FasterRCNN在生成样本的时候也是通过IOU来筛选的,比如IOU>0.7为正样本,在这个阈值下,小目标(10x10这种级别)所能得到的正样本数量就比大目标要少很多。这个问题后续再深入研究和讨论吧。