昨天和别人讨论起人脸识别,因此顺便用opencv写个检测程序重温一下。人脸检测并不是十分高深的技术,现在的智能手机拍摄大多都能检测人脸,也有不少视频软件能够动态捕捉人脸。而这些简单的正脸识别也大多使用已经训练成熟的特征数据。

opencv提供了大量已经训练好了的特征数据供使用,它的特征库基于Viola-Jones检测器,并且在此检测器基础上用了Haar-like特征做了扩展,opencv中的所有特征数据都通过这个检测器来训练,也叫做“Haar分类器”。

opencv下载:
http://opencv.org/

特征数据库:
http://opencvlibrary.svn.sourceforge.net/viewvc/opencvlibrary/trunk/opencv/data/haarcascades/

在opencv库的data目录下带有所有的xml格式的特征数据文件。使用opencv检测人脸的C++代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <string>
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
static CvScalar colors[] =
{
{{0,0,255}},
{{0,128,255}},
{{0,255,255}},
{{0,255,0}},
{{255,128,0}},
{{255,255,0}},
{{255,0,0}},
{{255,0,255}}
};
static const std::string kWindowName = "face_recognition";
class FaceRecognition
{
public:
FaceRecognition()
: memStorage_(NULL), haarClassifierCascade_(NULL)
{
}
~FaceRecognition()
{
}
public:
void setHaarcascade(const std::string& filename)
{
haarCascadeFilename_ = filename;
}
void setPicture(const std::string& filename)
{
pictureFilename_ = filename;
}
bool run()
{
//加载特征
haarClassifierCascade_ = static_cast<CvHaarClassifierCascade*>(cvLoad(haarCascadeFilename_.c_str(), NULL, NULL, NULL));
//创建内存储存器
memStorage_ = cvCreateMemStorage(0);
//加载要识别的图片
IplImage* image = cvLoadImage(pictureFilename_.c_str(), CV_LOAD_IMAGE_COLOR);
if (image == NULL)
{
std::cout << "load image failed." << std::endl;
return false;
}
//创建窗口
cvNamedWindow(kWindowName.c_str(), CV_WINDOW_AUTOSIZE);
float scale = 1.3f;
IplImage* gray = cvCreateImage(cvSize(image->width, image->height), 8, 1);
IplImage* small_img = cvCreateImage(cvSize(cvRound(image->width / scale), cvRound(image->height / scale)), 8, 1);
//转为灰度图
cvCvtColor(image, gray, CV_BGR2GRAY);
cvResize(gray, small_img, CV_INTER_LINEAR);
//使灰度图象直方图均衡化
cvEqualizeHist(small_img, small_img);
//干掉分配的内存
cvClearMemStorage(memStorage_);
//根据特征库查找图像中所有匹配的人脸
CvSeq* faces = cvHaarDetectObjects( small_img, haarClassifierCascade_, memStorage_, 1.1, 2, 0, cvSize(30, 30));
//检测所有人脸
for(int i = 0; i < faces->total; i++)
{
CvRect faceRect = *(CvRect*)cvGetSeqElem(faces, i);
cvRectangle(image, cvPoint(faceRect.x * scale, faceRect.y * scale),
cvPoint((faceRect.x + faceRect.width) * scale,
(faceRect.y + faceRect.height) * scale),
colors[i % 8], 2, 8, 0);
}
//释放资源
cvReleaseImage(&gray);
cvReleaseImage(&small_img);
//把图像显示到窗口
cvShowImage(kWindowName.c_str(), image);
cvWaitKey(0);
cvReleaseImage(&image);
//销毁窗口
cvDestroyWindow(kWindowName.c_str());
return true;
}
private:
std::string haarCascadeFilename_;
std::string pictureFilename_;
CvMemStorage* memStorage_;
CvHaarClassifierCascade* haarClassifierCascade_;
};
int main(int argc, char* argv[])
{
FaceRecognition faceRecognition;
faceRecognition.setHaarcascade("haarcascade_mcs_nose.xml");
faceRecognition.setPicture("smile.jpg");
faceRecognition.run();
return 0;
}

以下是一些效果图:

haar_single
单人正脸识别,用了haarcascade_frontalface_alt2.xml这个特征库,识别率很高。如果是人类正脸识别率大概在90%以上。另外不要问我这个美女是谁。

haar_multi_people
多人人脸识别,也是用了haarcascade_frontalface_alt2.xml这个特征库,识别率一样,而且试过在一群人中进行人脸匹配,效果不错,速度也很快。另外这三个是我的好基友,分别是万能、渊叔和猫仔。

haar_nose
最后贴一张小美的相片,我知道她肯定不会看这篇文章,所以我不担心被骂。如图所示,这是一张鼻子识别的图片,识别率很高。但除此之外,opencv提供的眼镜识别,微笑识别,嘴巴识别都没那么理想。对于五官识别起来依然有些难度,比如说嘴巴和眼睛在一定情况特征是非常相似的,因此在匹配单眼的时候可能也会把嘴巴给匹配到了。

另外,即使是半侧脸,用正脸特征数据的识别效果也很好。但是我用自己的相片发现脸以及五官都识别不出来,谁能告诉我这是为什么。。。。