1. Haar特征描述原理
1.1 Haar-like特征
Haar(哈尔)特征分为三类:边缘特征、线性特征、中心特征和对角线特征,组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和减去黑色矩形像素和。 Haar特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述特定走向(水平、垂直、对角)的结构。
对于图中的A, B和D这类特征,特征数值计算公式为:v=Σ白-Σ黑,而对于C来说,计算公式如下:v=Σ白-2*Σ黑;之所以将黑色区域像素和乘以2,是为了使两种矩形区域中像素数目一致。我们希望当把矩形放到人脸区域计算出来的特征值和放到非人脸区域计算出来的特征值差别越大越好,这样就可以用来区分人脸和非人脸。 通过改变特征模板的大小和位置,可在图像子窗口中穷举出大量的特征。上图的特征模板称为“特征原型”;特征原型在图像子窗口中扩展(平移伸缩)得到的特征称为“矩形特征”;矩形特征的值称为“特征值”。
上图中两个矩形特征,表示出人脸的某些特征。比如中间一幅表示眼睛区域的颜色比脸颊区域的颜色深,右边一幅表示鼻梁两侧比鼻梁的颜色要深。同样,其他目标,如眼睛等,也可以用一些矩形特征来表示。使用特征比单纯地使用像素点具有很大的优越性,并且速度更快。
矩形特征可位于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数。故类别、大小和位置的变化,使得很小的检测窗口含有非常多的矩形特征,如:在24*24像素大小的检测窗口内矩形特征数量可以达到16万个。这样就有两个问题需要解决了: (1)如何快速计算那么多的特征?—积分图大显神通;
(2)哪些矩形特征才是对分类器分类最有效的?—如通过AdaBoost算法来训练
1.2 Haar分类器训练
Haar分类器训练的五大步骤为:
1、准备人脸、非人脸样本集;
2、计算特征值和积分图;
3、筛选出T个优秀的特征值(即最优弱分类器);
4、把这个T个最优弱分类器传给AdaBoost进行训练。
5、级联,也就是强分类器的强强联手。
在开始前,一定要记住,以20*20窗口为例,就有78,460的特征数量,筛选出T个优秀的特征值(即最优弱分类器),然后把这个T个最优弱分类器传给AdaBoost进行训练得到一个强分类器,最后将强分类器进行级联。
2.opencv代码实现
import cv2
import numpy
as np
haar_front_face_xml
= './data/haarcascade_frontalface_default.xml'
haar_eye_xml
= './data/haarcascade_eye.xml'
def StaticDetect(filename
):
face_cascade
= cv2
.CascadeClassifier
(haar_front_face_xml
)
img
= cv2
.imread
(filename
)
gray_img
= cv2
.cvtColor
(img
, cv2
.COLOR_BGR2GRAY
)
faces
= face_cascade
.detectMultiScale
(gray_img
, 1.3, 5)
for (x
, y
, w
, h
) in faces
:
img
= cv2
.rectangle
(img
, (x
, y
), (x
+ w
, y
+ h
), (255, 0, 0), 2)
cv2
.namedWindow
('Face Detected!')
cv2
.imshow
('Face Detected!', img
)
cv2
.waitKey
(0)
cv2
.destroyAllWindows
()
def DynamicDetect():
'''
打开摄像头,读取帧,检测帧中的人脸,扫描检测到的人脸中的眼睛,对人脸绘制蓝色的矩形框,对人眼绘制绿色的矩形框
'''
face_cascade
= cv2
.CascadeClassifier
(haar_front_face_xml
)
eye_cascade
= cv2
.CascadeClassifier
(haar_eye_xml
)
camera
= cv2
.VideoCapture
(0)
cv2
.namedWindow
('Dynamic')
while True:
ret
, frame
= camera
.read
()
if ret
:
gray_img
= cv2
.cvtColor
(frame
, cv2
.COLOR_BGR2GRAY
)
faces
= face_cascade
.detectMultiScale
(gray_img
, 1.3, 5)
for (x
, y
, w
, h
) in faces
:
cv2
.rectangle
(frame
, (x
, y
), (x
+ w
, y
+ h
), (255, 0, 0), 2)
roi_gray
= gray_img
[y
:y
+ h
, x
:x
+ w
]
eyes
= eye_cascade
.detectMultiScale
(roi_gray
, 1.03, 5, 0, (40, 40))
for (ex
, ey
, ew
, eh
) in eyes
:
cv2
.rectangle
(frame
, (ex
+ x
, ey
+ y
), (x
+ ex
+ ew
, y
+ ey
+ eh
), (0, 255, 0), 2)
cv2
.imshow
('Dynamic', frame
)
if cv2
.waitKey
(100) & 0xff == ord('q'):
break
camera
.release
()
cv2
.destroyAllWindows
()
if __name__
== '__main__':
filename
= './p/02.jpg'
StaticDetect
(filename
)