在计算机视觉领域中,人脸检测或者物体检测一直是一个非常受关注的领域,而在人脸检测中,Viola-Jones人脸检测算法可以说是非常经典的一个算法,所有从事人脸检测研究的人,都会熟悉了解这个算法,Viola-Jones算法在2001年的CVPR上提出,因为其高效而快速的检测即使到现在也依然被广泛使用,OpenCV 和 Matlab中都将这个算法写进了函数库可以很方便的直接调用。虽然VJ人脸检测算法最初都是用来检测正面的人脸图像,对于侧脸图像的检测不是很稳健,不过这个算法依然有值得研究的价值。

这个算法包含以下几个重要的部分:
1 利用Haar 特征描述人脸的共有属性;
2 建立了一种称为积分图像的特征,并且基于积分图像,可以快速获取几种不同的矩形特征;
3 利用Adaboost 算法进行训练;
4 建立层级分类器。

利用Haar 特征描述人脸的共有属性 

一般来说,人脸会有一些基本的共性,比如眼睛区域会比脸颊区域要暗很多,鼻子一般属于脸部的高光区域,鼻子会比周围的脸颊要亮很多,一张正脸图像,眼睛,眉毛,鼻子,嘴巴等的相对位置是有规律可循的。Haar 特征考虑的是某一特定位置相邻的矩形区域,把每个矩形区域的像素相加然后再相减,总得来说,基本对应以下几种情形:

[1,1],[1,1],[1,1,1],[1,1,1]
<script type="math/tex; mode=display" id="MathJax-Element-1">[-1 , 1], [1, -1], [1, -1, 1], [-1, 1, -1]</script>
[1111]
<script type="math/tex; mode=display" id="MathJax-Element-2"> \begin{bmatrix} 1 & -1\\ -1 & 1 \end{bmatrix} </script>

我们以前都是针对单一像素做,现在需要针对矩形区域,相当于是把单个像素变成矩形区域,所以要对每个矩形区域先求和,然后再利用上面的算子做运算,这种特征称之为Haar-like 特征。

积分图像与矩形特征 

正如上一节所说,为了计算Haar-like特征,需要对矩形区域的所有像素求和,一个图像所能形成的矩形区域有大有小,如果每个矩形区域都用遍历所有像素再求和的运算方法,无疑这个运算负担将非常巨大,所有VJ人脸检测算法用到了一种非常巧妙的数据结构,称为integral image(积分图像),积分图像的原理非常简单,总得来说,就是对于图像中的任何一点,该点的积分图像值等于位于该点左上角的所有像素之和,表达式如下:

I(x,y)=xxyyf(x,y)
<script type="math/tex; mode=display" id="MathJax-Element-3"> I(x, y)=\sum_{x' \leq x} \sum_{y' \leq y} f(x', y') </script>  

并且积分图像满足如下关系:

I(x,y)=f(x,y)+I(x1,y)+I(x,y1)I(x1,y1)
<script type="math/tex; mode=display" id="MathJax-Element-4">I(x, y) = f(x, y) + I(x-1, y) + I(x, y-1) - I(x-1, y-1)</script>

其中I<script type="math/tex" id="MathJax-Element-5">I</script> 表示积分图像,f<script type="math/tex" id="MathJax-Element-6">f</script> 表示原来的图像,x,y,x,y<script type="math/tex" id="MathJax-Element-7">x, y, x', y'</script>表示像素的位置。所以一张图像的积分图像记录了这张图像上每一个像素点其左上角所有像素的和,如果把一张图像的左上角看做坐标原点,那么上面的表达式就是原点到该像素点之间的所有像素点的离散求和,这可以看成是一种积分,所以这也是积分图像名称的由来。 利用积分图像,我们可以计算一张图像上任意一个矩形区域的像素和,积分图像的计算matlab 有一个库函数integralImage 可以直接调用,

I=intergralImage(img) 

如下图所示,左边的图表示人脸图像,右边的图表示归一化之后积分图像。

这里写图片描述 这里写图片描述  

有了积分图像,就可以很方便的计算图像中任何一个矩形区域的像素和,如下图所示:

这里写图片描述  

为了计算矩形ABCD的像素和,利用积分图像可以表示如下:

Sabcd=I(D)I(B)I(C)+I(A)
<script type="math/tex; mode=display" id="MathJax-Element-54"> S_{abcd} = I(D)-I(B)-I(C)+I(A) </script> 

这意味着,任何一个矩形区域的像素和,都可以由积分图像 I<script type="math/tex" id="MathJax-Element-55">I</script> 上面的四个点来表示。VJ人脸检测算法用到了三种不同的矩形特征,分别是二邻接,三邻接,四邻接矩形,如下图所示:

这里写图片描述

很显然一个矩形可以由四个点来表示,二邻接矩形需要六个点表示,三邻接矩形需要八个点,而四邻接矩形需要九个点。

下面我们来看看,给定一张图像,有多少个矩形特征,VJ 算法里用到的是 24×24<script type="math/tex" id="MathJax-Element-56">24 \times 24</script> 的图像大小,我们可以计算一个24×24<script type="math/tex" id="MathJax-Element-57">24 \times 24</script> 的图像可以产生多少矩形特征。考虑水平方向与垂直方向,二邻接矩形有两种情况1×2<script type="math/tex" id="MathJax-Element-58">1 \times 2</script> 和 2×1<script type="math/tex" id="MathJax-Element-59">2 \times 1</script>,三邻接矩形也有两种情况 1×3<script type="math/tex" id="MathJax-Element-60">1 \times 3</script> 和 3×1<script type="math/tex" id="MathJax-Element-61">3 \times 1</script>,而四邻接矩形只有一种情况2×2<script type="math/tex" id="MathJax-Element-62">2 \times 2</script>。
下面列出每种邻接矩形可能的size大小。
二邻接矩形 (1×2<script type="math/tex" id="MathJax-Element-63">1 \times 2</script>): 1×2<script type="math/tex" id="MathJax-Element-64">1 \times 2</script>, 1×4<script type="math/tex" id="MathJax-Element-65">1 \times 4</script>, 1×6<script type="math/tex" id="MathJax-Element-66">1 \times 6</script>, … 1×24<script type="math/tex" id="MathJax-Element-67">1 \times 24</script> , 2×2<script type="math/tex" id="MathJax-Element-68">2\times 2</script>, 2×4<script type="math/tex" id="MathJax-Element-69">2 \times 4</script>, 2×6<script type="math/tex" id="MathJax-Element-70">2 \times 6</script>, … 2×24<script type="math/tex" id="MathJax-Element-71">2 \times 24</script> … 24×24<script type="math/tex" id="MathJax-Element-72"> 24 \times 24 </script> 矩形的长以2的倍数增加,宽逐渐增加。
三邻接矩形 (1×3<script type="math/tex" id="MathJax-Element-73">1 \times 3</script>): 1×3<script type="math/tex" id="MathJax-Element-74">1 \times 3</script>, 1×6<script type="math/tex" id="MathJax-Element-75">1\times 6</script>, 1×9<script type="math/tex" id="MathJax-Element-76">1 \times 9</script>, … 24×24<script type="math/tex" id="MathJax-Element-77">24 \times 24</script> 矩形的长是以3的倍数增加,宽逐渐增加。
四邻接矩形 (2×2<script type="math/tex" id="MathJax-Element-78">2 \times 2</script>): 2×2<script type="math/tex" id="MathJax-Element-79">2 \times 2</script>, 2×4<script type="math/tex" id="MathJax-Element-80">2 \times 4</script>, 2×8<script type="math/tex" id="MathJax-Element-81">2 \times 8</script>, 2×16<script type="math/tex" id="MathJax-Element-82">2 \times 16 </script>, … 24×24<script type="math/tex" id="MathJax-Element-83">24 \times 24</script> 矩形的长宽都是以2的倍数增加。

根据卷积定理,我们知道一个W×H<script type="math/tex" id="MathJax-Element-84">W \times H</script> 的图像与 m×n<script type="math/tex" id="MathJax-Element-85">m \times n</script> 的filter 做卷积,新生成的图像大小为
(Wm+1)×(Hn+1)<script type="math/tex" id="MathJax-Element-86">(W-m+1) \times (H-n+1)</script>, 新图像的每一个像素其实就是原图一个m×n<script type="math/tex" id="MathJax-Element-87">m \times n</script> 的local patch与 m×n<script type="math/tex" id="MathJax-Element-88">m \times n</script> 的filter 的乘积和。新图像有多少个像素,就对应着原图多少个m×n<script type="math/tex" id="MathJax-Element-89">m \times n</script> 的矩形。

我们用一段代码来求一个24×24<script type="math/tex" id="MathJax-Element-90">24 \times 24</script> 图像会产生多少个矩形特征: 因为 1×2<script type="math/tex" id="MathJax-Element-91">1 \times 2</script> 和 2×1<script type="math/tex" id="MathJax-Element-92">2 \times 1</script> 的情形是一样的,1×3<script type="math/tex" id="MathJax-Element-93">1 \times 3</script> 和 3×1<script type="math/tex" id="MathJax-Element-94">3 \times 1</script> 的情形也是一样,所以我们只要分别计算一种情况,再乘以2就行了。

import numpy as np

a = np.zeros((3, 2), dtype=int)
Count = np.zeros(3, dtype=int)
a[0, :] = [1, 2]
a[1, :] = [1, 3]
a[2, :] = [2, 2]
Img_size = 24

for ii in range(3):
    rec_h = a[ii, 0]
    rec_w = a[ii, 1]
    for xx in range(rec_h, Img_size+1, rec_h):
        for yy in range(rec_w, Img_size+1, rec_w):
            Count[ii] = Count[ii]+(Img_size-xx+1)*(Img_size-yy+1)
    print Count[ii]

Total = Count[0]*2+Count[1]*2+Count[2]
print ("Total: ", Total)

最后可以得到:
1×2<script type="math/tex" id="MathJax-Element-49">1 \times 2 </script>: 43200
1×3<script type="math/tex" id="MathJax-Element-50">1 \times 3 </script>: 27600
2×2<script type="math/tex" id="MathJax-Element-51">2 \times 2</script>: 20736
所以最终总的矩形特征为 43200×2+27600×2+20736=162336<script type="math/tex" id="MathJax-Element-52">43200 \times 2 + 27600 \times 2 + 20736 = 162336</script>
可以看到,一个 24×24<script type="math/tex" id="MathJax-Element-53">24 \times 24</script> 的图像最终会产生162336个矩形特征,这个维度远远高于图像本身的维度。不可能将所有的矩形特征都用,所有需要做特征选择,下一篇,我们将探讨如何利用AdaBoost来做特征选择与训练。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐