非极大值抑制(Non-Maximum Suppression,NMS[1])可以正面地表述为局部最大搜索(Local Maximum Search), 其中局部最大值大于其所有邻居 (不包括其自身)。本质上是搜索局部极大值,抑制非极大值元素。
NMS 在计算机视觉领域有着非常重要的应用,如视频目标跟踪、数据挖掘、3D重建、目标识别以及纹理分析等。[2]
2.1 1D NMS
Straightforward Implementation
NMS 的简单实现由两个嵌套循环组成, 其中外部循环遍历所有像素, 内部循环针对外部循环的所有邻居测试其候选项。一旦邻居强度超过当前候选, 内部循环就会中止。显然, 该算法需要对每个像素做 2n 次比较, 而不需要提前中止。在最坏的情况下, 中止不能降低复杂性。这可以通过将算法应用于强度趋势来看出。由于一侧的像素总是小于候选像素, 因此内部循环会在第 (n + 1) 迭代处中止, 从而使每个像素有 n + 1 次比较。因此, 比较像素的最坏情况数是 。关于时间复杂度分析的具体内容可查看原论文[1]。下面我以一张图片来说明 NMS:
import numpy as np
from matplotlib import pyplot as plt
np.set_printoptions(2) # 修改了 NumPy 的打印精度
# 指定默认字体, 为在 Matplotlib 中显示中文,设置特殊字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号 '-' 显示为方块的问题
%matplotlib inline
img_name = '../images/catdog.jpg'
img = plt.imread(img_name)
载入图片之后,我们先讨论 3-Neighborhood。
3-近邻
我们先将原图片拉直为一个向量,下面我们定义 NMS:
I = img.flatten()[:30]
w = len(I)
maximumat = []
i = 1
while i + 1 < w:
if I[i] > I[i+1] and I[i] >= I[i-1]: # I[i] 是极大值
maximumat.append(i)
else:
i += 1
while i + 1 < w and I[i] <= I[i+1]:
i += 1
if i + 1 < w:
maximumat.append(i)
i += 2
plt.plot(I, 'y--')
plt.scatter(maximumat, I[maximumat])
plt.axis('off')
for _x, _y in enumerate(I):
plt.text(_x, _y, str(_x))
为了更加形象,下面我们将展示 NMS 处理前后的图像:
I = img.flatten()
w = len(I)
maximumat = []
i = 1
while i + 1 < w:
if I[i] > I[i+1] and I[i] >= I[i-1]:
maximumat.append(i)
else:
i += 1
while i + 1 < w and I[i] <= I[i+1]:
i += 1
if i + 1 < w:
maximumat.append(i)
i += 2
K = img[:].flatten()
K[maximumat] = 0
img1 = K.reshape(img.shape) # 去除极大值点后组成的图像
img2 = img - img1 # 极大值点组成的图像
plt.figure(figsize=(10, 10))
plt.subplot(131)
plt.imshow(img)
plt.title('原图')
plt.axis('off')
plt.subplot(132)
plt.imshow(img1)
plt.title('去除极大值点图')
plt.axis('off')
plt.subplot(133)
plt.imshow(img2)
plt.title('极大值点图')
plt.axis('off')
plt.show()
2.2 动态块算法:(2n+1)-近邻
def nms(y, n):
'''
参数
===========
y:: 一维数组
n:: 近邻数
返回
==========
N-近邻 的 NMS
'''
i = 0
maximumat = []
w = len(y)
while 2 * n + i < w:
if I[i+n] > max(I[i+n+1:i+2*n]) and I[i+n] >= max(I[i:i+n]):
maximumat.append(i+n)
else:
i += 1
while 2 * n + i < w and I[i+n] < max(I[i+n+1:i+2*n]):
i += 1
if 2 * n + i < w:
maximumat.append(i+n)
i += n
return maximumat
为了方便我将可视化的代码放入函数:
def plot(img, img1, img2):
plt.figure(figsize=(10, 10))
plt.subplot(131)
plt.imshow(img)
plt.title('原图')
plt.axis('off')
plt.subplot(132)
plt.imshow(img1)
plt.title('去除极大值点图')
plt.axis('off')
plt.subplot(133)
plt.imshow(img2)
plt.title('极大值点图')
plt.axis('off')
plt.show()
import numpy as np
from matplotlib import pyplot as plt
np.set_printoptions(2) # 修改了 NumPy 的打印精度
# 指定默认字体, 为在 Matplotlib 中显示中文,设置特殊字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号 '-' 显示为方块的问题
%matplotlib inline
img_name = '../images/catdog.jpg'
img = plt.imread(img_name)
I = img.flatten()
n = 3
maximumat = nms(I, n)
K = img[:].flatten()
K[maximumat] = 0
img1 = K.reshape(img.shape) # 去除极大值点后组成的图像
img2 = img - img1 # 极大值点组成的图像
plot(img, img1, img2)
I = img.flatten()
n = 100
maximumat = nms(I, n)
K = img[:].flatten()
K[maximumat] = 0
img1 = K.reshape(img.shape) # 去除极大值点后组成的图像
img2 = img - img1 # 极大值点组成的图像
plot(img, img1, img2)
-
Neubeck A, Van Gool L. Efficient Non-Maximum Suppression[C]. international conference on pattern recognition, 2006: 850-855. ↩ ↩
-
非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素,可以理解为局部最大搜索。这个局部代表的是一个邻域,邻域有两个参数可变,一是邻域的维数,二是邻域的大小。这里不讨论通用的 NMS 算法(参考论文《Efficient Non-Maximum Suppression》 对 1 维和 2 维数据的 NMS 实现),而是用于目标检测中提取分数最高的窗口的。例如在行人检测中,滑动窗口经提取特征,经分类器分类识别后,每个窗口都会得到一个分数。但是滑动窗口会导致很多窗口与其他窗口存在包含或者大部分交叉的情况。这时就需要用到 NMS 来选取那些邻域里分数最高(是行人的概率最大),并且抑制那些分数低的窗口。@http://www.cnblogs.com/makefile/p/nms.html © 康行天下 ↩