这个项目主要包含三部分:人脸检测、特征提取、性别分类:
![](https://box.kancloud.cn/2016-02-25_56ce6384b066d.png)
这篇博客中我们重点介绍OpenCv的人脸检测函数。这篇博客我们先不提MFC,而是在win32控制台下编写一段人脸检测的程序。
一、开启摄像头
我们先讲解如何通过摄像头来采集图像,这听起来更有实际意义。
1、新建工程并配置OpenCv(注意工程类型选择win32控制台应用程序):
![](https://box.kancloud.cn/2016-02-25_56ce6384be108.png)
2、包含头文件
OpenCv2.x版本包含头文件非常方便,一句话搞定:
~~~
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
~~~
谈到包含头文件,这里有一个地方需要详细说一下,就是OpenCv2.x之所以操作简洁,是因为其将各个模块的头文件全部置于“opencv.hpp”这个文件中了,右键打开opencv.hpp文档,你会发现如下内容:
![](https://box.kancloud.cn/2016-02-25_56ce6384d66a7.png)
3、初始化一个摄像头捕捉器
首先,需要建立一个摄像头捕捉器,并将其与当前设备中的摄像头相关联:
~~~
/***********初始化一个摄像头捕捉器***********/
CvCapture* capture = cvCreateCameraCapture(0);
cvNamedWindow("Camera");
~~~
注意以"cv"开头的结构体和函数名都是隶属于OpenCv1.x版本中的内容,不过OpenCv2.x是完全兼容1.x版本的,而且貌似在2.x版本并未对摄像头相关函数进行重写,因此这里暂且延用1.x中的代码。
4、调用摄像头步骤画面并显示
首先,给出代码,稍后解释:
~~~
IplImage* cameraImage = NULL;
while ((cameraImage = cvQueryFrame(capture)) != NULL)
{
cvShowImage("Camera",cameraImage);
cvWaitKey(1);
}
~~~
显然cvQueryFrame()函数的作用是在当前的时间点从摄像头抓取的视频流中截出一帧,这里将其赋值给变量camearImage(IplImage*类型,因为这是1.0的代码),若其非空,则显示在屏幕上。注意这里必须添加延时函数cvWaitKey(单位是毫秒),哪怕只延时一毫秒否则将无法正常显示摄像头输出。
按下Ctrl+F5,程序正常运行:
![](https://box.kancloud.cn/2016-02-25_56ce6384ecfef.png)
二、人脸检测
OpenCv2.x版本中封装的人脸检测函数基于AdaBoost(级联分类器)人脸检测算法,当然这里我们无需深入了解算法相关的知识,因为Intel已经将需要用到的、训练好的人脸检测器(分类器)放在了安装文件里:
![](https://box.kancloud.cn/2016-02-25_56ce63852e8f9.png)
1、准备工作
调用人脸检测函数前需要做一些准备工作,分别是初始化所需内存、初始化检测器指针、设置检测器路径:
~~~
static CvMemStorage* storage = NULL;
static CvHaarClassifierCascade* cascade = NULL;
const char* cascadePath = "D:\\opencv\\sources\\data\
\\haarcascades\\haarcascade_frontalface_alt_tree.xml";
~~~
这里有两个问题需要强调:
(1)从路径中可以看出,检测器位于安装目录下的source文件夹下的data文件夹下的haarcascades文件夹中。
(2)在C++表示路径是要用双斜杠,因为第一个斜杠会默认为是转义字符,对第二个斜杠进行转义。
(3)这三个变量均为全局变量。
2、图像灰度化
由于这里用到的人脸检测函数主要针对于灰度图像,因此需要将摄像头采集的彩色图像灰度化:
~~~
/**********灰度化***********/
IplImage* grayImage = cvCreateImage(cvSize(cameraImage->width,cameraImage->height),8,1);
cvCvtColor(cameraImage,grayImage,CV_BGR2GRAY);
~~~
这里涉及到如何通过cvCreatImage创建一个空的8位无符号整型单通道图,即需要通过一个cvSize结构体来指定图像初始的尺寸,这点在opencv2.x得到了很大改良(Mat类的加入)。
3、调用人脸检测函数
首先,创建一块内存区域,并加载相应的检测器(这个在主循环外完成即可):
~~~
storage = cvCreateMemStorage(0);
cascade = (CvHaarClassifierCascade*)cvLoad(cascadePath);
~~~
然后,清空指定位置内存块,调用人脸检测函数:
~~~
/**********人脸检测***********/
cvClearMemStorage(storage);
CvSeq* objects = cvHaarDetectObjects(grayImage,cascade,storage,1.1,2,0,cvSize(30,30));
~~~
cvhaardetectobjects函数的参数较为复杂,具体参数设置参见:[cvhaardetectobjects参数设置](http://blog.sina.com.cn/s/blog_4c78d3fb0100u8lv.html)。我们这里需要了解的就是这个函数的返回参数是一系列检测结果序列,每个检测结果实际上就是一个矩形结构体对象。
4、绘制人脸区域矩形框
接下来一一绘制检测到的矩形结果:
~~~
/**********绘制检测结果***********/
for (int i = 0; i < (objects ? objects->total : 0); i++)
{
CvRect* rect = (CvRect*)cvGetSeqElem(objects,i);
cvRectangle(cameraImage,cvPoint(rect->x,rect->y),
cvPoint(rect->x + rect->width,rect->y + rect->height),cvScalar(0.0,255));
}
cvShowImage("Camera",cameraImage);
~~~
注意这里需要把之前测试摄像头程序中的图片显示语句注释掉,否则前后在显示图像时会发生覆盖,不能正常看到图像的检测结果:
![](https://box.kancloud.cn/2016-02-25_56ce63854468d.png)
5、总程序
这里给出摄像头人脸检测的总程序:
~~~
// Camera.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
static CvMemStorage* storage = NULL;
static CvHaarClassifierCascade* cascade = NULL;
const char* cascadePath = "D:\\opencv\\sources\\data\
\\haarcascades\\haarcascade_frontalface_alt_tree.xml";
int _tmain(int argc, _TCHAR* argv[])
{
/***********初始化一个摄像头捕捉器***********/
CvCapture* capture = cvCreateCameraCapture(0);
cvNamedWindow("Camera");
/***********初始化人脸检测相关变量***********/
IplImage* cameraImage = NULL;
storage = cvCreateMemStorage(0);
cascade = (CvHaarClassifierCascade*)cvLoad(cascadePath);
while ((cameraImage = cvQueryFrame(capture)) != NULL)
{
//cvShowImage("Camera",cameraImage);
cvWaitKey(1);
/**********灰度化***********/
IplImage* grayImage = cvCreateImage(cvSize(cameraImage->width,cameraImage->height),8,1);
cvCvtColor(cameraImage,grayImage,CV_BGR2GRAY);
/**********人脸检测***********/
cvClearMemStorage(storage);
CvSeq* objects = cvHaarDetectObjects(grayImage,cascade,storage,1.1,2,0,cvSize(30,30));
/**********绘制检测结果***********/
for (int i = 0; i < (objects ? objects->total : 0); i++)
{
CvRect* rect = (CvRect*)cvGetSeqElem(objects,i);
cvRectangle(cameraImage,cvPoint(rect->x,rect->y),
cvPoint(rect->x + rect->width,rect->y + rect->height),cvScalar(0.0,255));
}
cvShowImage("Camera",cameraImage);
}
return 0;
}
~~~