在过去十年中,图像分类是一个快速发展的领域,卷积神经网络(CNNs)和其他深度学习技术的使用也在快速增长。然然而,在CNNs成为主流之前,另一种技术被广泛使用并继续使用:Viola-Jones。
CNN是个单独的分类器,它查看完整的图像并应用矩阵运算来获得分类,而Viola-Jones采用的是集成方法。这意味着Viola-Jones使用了许多不同的分类器,每个分类器查看图像的不同部分。每个单独的分类器比最终的分类器更弱(更不准确,产生更多的误报等),因为它接收的信息更少。但是,当将每个分类器的结果组合在一起时,它们会产生一个强分类器。
由于算法的性质,Viola-Jones方法仅限于二进制分类任务(例如对象检测),并且训练周期非常长。但是,由于每个弱分类器只需要少量的参数,并且有足够的弱分类器,因此它的误报率很低,因此它可以快速地对图像进行分类。
特征和积分图像
Viola-Jones做出的重要贡献之一是在图像识别中使用了一组简单的特征。在大多数任务中,像素值是输入到算法中的特征。但是,Viola和Jones引入了以下新特征。
A和B是两个矩形特征,C是三个矩形特征,D是四个矩形特征。
从无阴影矩形中像素的和减去阴影矩形中像素的和。很容易看到,即使是小图像,也有很多特征(对于24 x 24的图像,有超过160,000个特征)。由于该算法需要遍历所有特征,因此必须非常有效地计算这些特征。为了做到这一点,Viola和Jones引入了积分图像。积分图像由以下递归关系定义。
s(x,y)是点(x,y)处的累积行和,ii(x,y)是同一点处的积分图像值,并且i(x,y)是该点的像素值。这个关系式的意思是,在点(x, y)处的积分图像是当前像素的左上方所有像素之和。这使得计算矩形区域内像素的和很容易,如下所示。
区域D中的像素之和是ii(4)+ ii(1)-ii(2)-ii(3),即四个数组引用。让我们通过创建一个辅助函数来开始实现算法,该函数计算给定图像的积分图像(表示为2D numpy数组)。
import numpy as np #Don't forget to import numpydef integral_image(image): ii = np.zeros(image.shape) s = np.zeros(image.shape) for y in range(len(image)): for x in range(len(image[y])): s[y][x] = s[y-1][x] + image[y][x] if y-1 >= 0 else image[y][x] ii[y][x] = ii[y][x-1]+s[y][x] if x-1 >= 0 else s[y][x] return ii
这个函数只是实现了上面定义的递归关系。由于s(x,-1)= 0,当y-1 <0时,s(x,y)= i(x,y),同样对于ii(-1,y)。接下来,让我们定义一个辅助类来存储矩形区域,以便以后计算特征值。
class RectangleRegion: def __init__(self, x, y, width, height): self.x = x self.y = y self.width = width self.height = heightdef compute_feature(self, ii): return ii[self.y+self.height][self.x+self.width] + ii[self.y][self.x] - (ii[self.y+self.height][self.x]+ii[self.y][self.x+self.width])
Viola-Jones
现在我们已经设置了辅助类和函数,我们可以开始创建Viola-Jones算法。让我们从定义ViolaJones类开始。我们的算法将使用的唯一超参数是特征的数量(也就是弱分类器的数量)
class ViolaJones: def __init__(self, T = 10): self.T = T
在训练中,Viola-Jones使用Adaboost的变体。增强的概念是针对每个后续的弱分类器来纠正先前分类器的错误。为此,它为每个训练示例分配权重,训练分类器,选择最佳分类器,并根据分类器的误差更新权重。标记不正确的例子将获得更大的权重,因此它们将被选择的下一个分类器正确分类。
增强的