這個項目主要包含三部分:人臉檢測、特征提取、性別分類:

這篇博客中我們重點介紹OpenCv的人臉檢測函數。這篇博客我們先不提MFC,而是在win32控制臺下編寫一段人臉檢測的程序。
一、開啟攝像頭
我們先講解如何通過攝像頭來采集圖像,這聽起來更有實際意義。
1、新建工程并配置OpenCv(注意工程類型選擇win32控制臺應用程序):

2、包含頭文件
OpenCv2.x版本包含頭文件非常方便,一句話搞定:
~~~
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
~~~
談到包含頭文件,這里有一個地方需要詳細說一下,就是OpenCv2.x之所以操作簡潔,是因為其將各個模塊的頭文件全部置于“opencv.hpp”這個文件中了,右鍵打開opencv.hpp文檔,你會發現如下內容:

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,程序正常運行:

二、人臉檢測
OpenCv2.x版本中封裝的人臉檢測函數基于AdaBoost(級聯分類器)人臉檢測算法,當然這里我們無需深入了解算法相關的知識,因為Intel已經將需要用到的、訓練好的人臉檢測器(分類器)放在了安裝文件里:

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);
~~~
注意這里需要把之前測試攝像頭程序中的圖片顯示語句注釋掉,否則前后在顯示圖像時會發生覆蓋,不能正常看到圖像的檢測結果:

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;
}
~~~
?