人脸识别(一):Ubuntu Python安装dlib C++ library
人脸识别(二):如何使用 dlib 实现简单的人脸识别功能
人脸识别(三):使用face_recognition库实现人脸识别,python实现
人脸识别(四):人脸识别理论、原理、分类、概括,请针对性学习所需算法,不要全学。
目录
1. adaboost由来
2. Haar特征
2.1 矩形特征个数
2.2 积分图
3. Adaboost算法流程
3.1 弱分类器训练
3.2. 强分类器训练
4. 使用Opencv实现人脸检测
看了很多的文章,认为liulina603和nk_wavelet对adaboost算法讲解的比较详细,查阅相关资料后,本文将介绍haar+adaboost算法理论和具体实现,多处参考以上两位博主。
adaboost方法是Boosting方法的提升版,Boosting方法又起源于PCA学习模型。1984年Valiant提出PCA模型,1990年,SChapire就首先构造出一种多项式级的算法,将弱学习算法提升为强学习算法,就是最初的Boosting算法。1993年,Drucker和Schapire首次以神经网络作为弱学习器,利用Boosting算法解决实际问题。1994年,Kearns和Valiant证明,在Valiant的PAC(Probably ApproximatelyCorrect)模型中,只要数据足够多,就可以将弱学习算法通过集成的方式提高到任意精度。1995年,Freund在Kearns和Valiant证明的基础上提出了一种效率更高的Boosting算法,即现在的Adaboost模型,对Boosting算法有巨大提升。
PCA(Probably Approximately Correct)模型是计算学习理论中常用的模型,PAC学习的实质就是在样本训练的基础上,使算法的输出以概率接近未知的目标概念。PAC学习模型是考虑样本复杂度(指学习器收敛到成功假设时至少所需的训练样本数)及计算复杂度(指学习器收敛到成功假设时所需的计算量)的一个基本框架,成功的学习被定义为形式化的概率理论。
Boosting 原意为提升、加强,现在一般指的是将弱学习算法提升为强学习算法的一类算法。boosting模型就是学习一系列的分类器,每个分类器对其前一个分类器产生的错误给予更大的重视,并增加导致错误分类的样本权值,重新对该样本训练分类器后,再学习下一个分类器。该训练过程重复到设定的次数后停止,最终分类器从这一系列的分类器中得出。Boosting是一种把若干个若分类器结合到一个强分类器中,从而大大提高检测性能的方法。强分类器对数据进行分类,是通过弱分类器的多数投票机制进行的。
Adaboost(Adaptive Boosting)是Boosting家族的代表算法之一,不需要任何关于弱学习器性能的先验知识,而且和Boosting算法具有同样的效率,所以在提出之后得到了广泛的应用。
Adaboost算法已被证明是一种有效而实用的Boosting算法,其算法原理是通过调整样本权重和弱分类器权值,从训练出的弱分类器中筛选出权值系数最小的弱分类器组合成一个最终强分类器。基于训练集训练弱分类器,每次下一个弱分类器都是在样本的不同权值集上训练获得的。每个样本被分类的难易度决定权重,而分类的难易度是经过前面步骤中的分类器的输出估计得到的。
Adaboost算法在样本训练集使用过程中,对其中的关键分类特征集进行多次挑选,逐步训练分量弱分类器,用适当的阈值选择最佳弱分类器,最后将每次迭代训练选出的最佳弱分类器构建为强分类器。其中,级联分类器的设计模式为在尽量保证感兴趣图像输出率的同时,减少非感兴趣图像的输出率,随着迭代次数不断增加,所有的非感兴趣图像样本都不能通过,而感兴趣样本始终保持尽可能通过为止。
首先需要了解若分类器、强分类器和级联分类器。弱分类器:没有专门的定义,可以理解为对分类的效果较弱,正确率较低。强分类器:即分类学习正确率较高,有显著的分类分类效果。级联分类器:将多个分类器连在一起组成一个强分类器,再由多个强分类器级联在一起组成一个标准分类器。
Adaboost是一种基于级联分类模型的分类器,级联分类模型可以用下图表示:
所有样本进入第一个强分类器,若为正例则进入下一个强分类器,否则,直接作为负例输出,以此类推,直到最后的强分类器输出正例,作为最终的输出结果。
有些强分类器可能包含10个弱分类器,有些则包含20个弱分类器,一般情况下一个级联用的强分类器包含20个左右的弱分类器,然后在将10个强分类器级联起来,就构成了一个级联强分类器,这个级联强分类器中总共包括200弱分类器。因为每一个强分类器对负样本的判别准确度非常高,所以一旦发现检测到的目标位负样本,就不在继续调用下面的强分类器,减少了很多的检测时间。因为一幅图像中待检测的区域很多都是负样本,这样由级联分类器在分类器的初期就抛弃了很多负样本的复杂检测,所以级联分类器的速度是非常快的;只有正样本才会送到下一个强分类器进行再次检验,这样就保证了最后输出的正样本的伪正(false positive)的可能性非常低。
级联结构分类器由多个弱分类器组成,每一级都比前一级复杂。每个分类器可以让几乎所有的正例通过,同时滤除大部分负例。这样每一级的待检测正例就比前一级少,排除了大量的非检测目标,可大大提高检测速度。
其次,Adaboost是一种迭代算法。初始时,所有训练样本的权重都被设为,在此样本分布下训练出一个弱分类器。在第(,T为迭代次数)次迭代中,样本的权重由第n-1次迭代的结果而定。在每次迭代的最后,都有一个调整权重的过程,被分类错误的样本将得到更高的权重。这样分错的样本就被突出出来,得到一个新的样本分布。在新的样本分布下,再次对弱分类器进行训练,得到下一个新的弱分类器。经过T次循环,得到T个弱分类器,把这T个弱分类器按照一定的权重叠加起来,就得到最终的强分类器。
Aadboost 算法系统具有较高的检测速率,且不易出现过适应现象。但是该算法在实现过程中为取得更高的检测精度则需要较大的训练样本集,在每次迭代过程中,训练一个弱分类器则对应该样本集中的每一个样本,每个样本具有很多特征,因此从庞大的特征中训练得到最优弱分类器的计算量增大。
Harr-like特征是Viola等提出的一种简单矩形特征,因其类似于Harr小波而得名,它反映了图像局部的灰度化。影响AdaBoost检测训练算法速度很重要的两方面是特征的选取和特征值的计算。脸部的一些特征可以由矩形特征(特征模板)简单地描绘。如下图示范:
上图中两个矩形特征,表示出人脸的某些特征。比如中间一幅表示眼睛区域的颜色比脸颊区域的颜色深,右边一幅表示鼻梁两侧比鼻梁的颜色要深。同样,其他目标,如眼睛等,也可以用一些矩形特征来表示。在给定有限的数据情况下,基于特征的检测能够编码特定区域的状态,而且基于特征的系统比基于像素的系统要快得多。矩形特征对一些简单的图形结构,比如边缘、线段,比较敏感,但是其只能描述特定走向(水平、垂直、对角)的结构,因此比较粗略。如上图,脸部一些特征能够由矩形特征简单地描绘,例如,通常眼睛要比脸颊颜色更深;鼻梁两侧要比鼻梁颜色要深;嘴巴要比周围颜色更深。
对于一个 24×24 检测器,其内的矩形模板决定的矩形特征数量超过160,000个,必须通过特定算法甄选合适的矩形特征,并将其组合成强分类器才能检测人脸。
常用的矩形特征有三种:两矩形特征、三矩形特征、四矩形特征,如图:
由图表可以看出,两矩形特征反映的是边缘特征,三矩形特征反映的是线性特征、四矩形特征反映的是特定方向特征。LienhartR.等对Haar-like矩形特征库作了进一步扩展,加入了旋转角的矩形特征。扩展后的特征大致分为4种类型:边缘特征、线特征环、中心环绕特征和对角线特征:如下图
特征模板的特征值定义为:模板内白色矩形像素和减去黑色矩形像素和。
接下来就需要求解特征个数和特征值
如图所示的一个m*m大小的子窗口,可以计算在这么大的子窗口内存在多少个矩形特征。特征模板可以在子窗口内以“任意”尺寸“任意”放置,每一种形态称为一个特征。找出子窗口所有特征,是进行弱分类训练的基础。
对于 mm×mm 子窗口,我们只需要确定了矩形左上顶点 和右下顶点,即可以确定一个矩形。如果这个矩形还必须满足下面两个条件(称为条件,满足条件的矩形称为条件矩形,即在x方向和y方向可以被s和t整除。这样就可以矩形模板的在不同的尺寸,都可以将图片完整划分成多个矩形。
如下图特征模板和对应的(s,t)条件:
模板1对应的是(1,2)条件,所以,x方向可以被1整除,y方向可以被2整除就可以,这样就把(50x100)图片划分成多个50*50个小格;再将模板按照倍数放大(横纵倍数可以不同)=====》(1,4),这样又可以把这个(50x100)的图片划分成50*25个小格,以此类推,直到不能再放大模板位置。这样得到的每个矩形小格都代表每种特征,此时所有小格的个数就是矩形特征的个数。
下面以24×24子窗口和一下五种模板为例,具体计算其特征总数量:
下面列出了,在不同子窗口大小内,特征的总数量:
在获取了矩形特征后,要计算矩形特征的特征值。Viola等人提出了利用积分图求特征值的方法。积分图的概念可用下图表示:
在上图中,表示点A(x,y)的积分图,指的是从(1,1)到A(x,y)的图像像素之和。
如上图,SAT(1)表示A区域的像素之和,SAT(2)表示A+B区域的像素之和,SAT(3)表示A+C区域的像素之和,SAT(4)表示A+B+C+D区域的像素之和。
所以D区域的像素之和 。所以,一个区域的像素值,可以由区域的积分图计算。使用模板将图像划分网格后,就可以利用积分图计算每个区域的像素值,这中方法比计算像素值要快很多。
如下图,使用模板中的2号模板(s,t)为(2,1)将图像划分网格:
A和B区域代表着举行模板,所以A的像素值使用积分图计算:;B区域的像素值为:。那么AB区域的特征模板的特征值为A区域像素值减去B区域像素值:。
所以,矩形特征的特征值,至于矩形端点的积分图有关,与坐标无关。计算端点的积分图,再加减运算,就可以得到特征值,运算速度大大提高,检测速增加。
如上述,adaboost采用迭代的方法,用不同训练集训练同一个弱分类器,然后再把不同训练集得到的弱分类器集合在一起,构成一个强分类器,组后再把几组强分类器联在一起构成一个分类器。
弱分类器数学结构如下:
其中f为特征,θ为阈值,p指示不等号的方向,x该表一个检测子窗口。对每个特征f,训练一个弱分类器h(x,f,p,θ),就是确定f的最优阈值,使得这个弱分类器 h(x,f,p,θ)对所有训练样本的分类误差最小。
弱分类器训练的过程大致分为如下几步:
1)对每个特征f,计算所有训练样本的特征值;
2)将特征值排序;
3)对排好序的每个元素计算:
3.1)全部正例的权重和T+;
3.2)全部负例的权重和T−;
3.3)该元素前正例的权重和S+;
3.4)该元素前负例的权重和S−.
4)选取当前元素的特征值和它前面的一个特征值之间的数作为阈值,所得到的弱分类器就在当前元素处把样本分开 —— 也就是说这个阈值对应的弱分类器将当前元素前的所有元素分为人脸(或非人脸),而把当前元素后(含)的所有元素分为非人脸(或人脸)。该阈值的分类误差为:
于是,通过把这个排序表从头到尾扫描一遍就可以为弱分类器选择使分类误差最小的阈值(最优阈值),也就是选取了一个最佳弱分类器。
在训练强分类器中,,其中实例空间,而实例空间,。AdaBoost算法的目的就是从训练数据中学习一系列弱分类器,然后将这些弱分类器组合成一个强分类器。AdaBoost算法流程如下:
简单来说,AdaBoost有很多优点:
● AdaBoost是一种有很高精度的分类器;
● 可以使用各种方法构建子分类器,AdaBoost算法提供的是框架;
● 当使用简单分类器时,计算出的结果是可以理解的。而且弱分类器构造极其简单;
● 简单,不用做特征筛选;
● 不用担心过拟合。
OpenCV自带的AdaBoost程序能够根据用户输入的正样本集与负样本集训练分类器,常用于人脸检测,行人检测等。它的默认特征采用了Haar,不支持其它特征。
这次实现的是人脸检测,首先是需要安装opencv库,可以从此下载opencv官方,安装。
找到opencv\sources\data\haarcascades中的haarcascade_frontalface_alt.xml文件,此文件是opencv中已经训练好的人脸adaboost联级分类器,只需要解析此文件即可对图像进行检测是否存在人脸,以及检测框的位置。可以将此文件复制到项目路径下。导入分类器的方法是使用CascaClassifter函数,即:
cv2.CascadeClassifier('./haarcascade_frontalface_alt.xml')
使用face_Cascade.detectMultiScale(image,scaleFactor,minNeighbors,flags,minSize,maxSize)进行识别,
例:faceRects = face_Cascade.detectMultiScale(image, 1.05, 2, cv2.CASCADE_SCALE_IMAGE, minSize_1)
image:是待检测图像
scaleFactor:前后两次相继的扫描中,搜索窗口的比例系数
minNeighbors:构成检测目标的相邻矩形的最小个数。
minSize:目标的最小尺寸
maxSize:目标的最大尺寸
下面是使用haar+adaboost实现的人脸识别案例:
先上效果:
再上代码部分:
# coding:utf-8
import cv2
import numpy as np
[x, y, w, h] = [0, 0, 0, 0]
path = './face_images/2008_002506.jpg'
face_Cascade = cv2.CascadeClassifier("./haarcascades/haarcascade_frontalface_alt.xml")
frame = cv2.imread(path)
size = frame.shape[:2]
image = np.zeros(size, dtype=np.float32)
image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 直方图均衡
image = cv2.equalizeHist(image)
im_h, im_w = size
minSize_1 = (im_w // 10, im_h // 10)
faceRects = face_Cascade.detectMultiScale(image, 1.05, 2, cv2.CASCADE_SCALE_IMAGE, minSize_1)
if len(faceRects) > 0:
for faceRect in faceRects:
x, y, w, h = faceRect
cv2.rectangle(frame, (x, y), (x + w, y + h), [255, 255, 0], 2)
cv2.imshow("detection", frame)
cv2.waitKey(0)
print('okay')
同时还可以改造成视频内的人脸检测,如下:
# coding:utf-8
import cv2
import numpy as np
[x, y, w, h] = [0, 0, 0, 0]
video_capture = cv2.VideoCapture("./video/hamilton_clip.mp4")
# video_capture = cv2.VideoCapture(0)
face_Cascade = cv2.CascadeClassifier("./haarcascades/haarcascade_frontalface_alt.xml")
cv2.namedWindow("Face Detection System")
while video_capture.isOpened():
ret, frame = video_capture.read()
if not ret:
break
size = frame.shape[:2]
image = np.zeros(size, dtype=np.float32)
image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 直方图均衡
image = cv2.equalizeHist(image)
im_h, im_w = size
minSize_1 = (im_w // 8, im_h // 8)
faceRects = face_Cascade.detectMultiScale(image, 1.05, 2, cv2.CASCADE_SCALE_IMAGE, minSize_1)
if len(faceRects) > 0:
for faceRect in faceRects:
x, y, w, h = faceRect
cv2.rectangle(frame, (x, y), (x + w, y + h), [0, 255, 0], 2)
cv2.imshow("Face Detection System", frame)
key = cv2.waitKey(5)
if key == int(30):
break
video_capture.release()
cv2.destroyWindow("Face Detection System")