這個項目主要包含三部分:人臉檢測、特征提取、性別分類:
這篇博客中我們重點介紹OpenCv的人臉檢測函數。這篇博客我們先不提MFC,而是在win32控制台下編寫一段人臉檢測的程序。
一、開啟攝像頭
我們先講解如何通過攝像頭來采集圖像,這聽起來更有實際意義。
1、新建工程並配置OpenCv(注意工程類型選擇win32控制台應用程序):
2、包含頭文件
OpenCv2.x版本包含頭文件非常方便,一句話搞定:
#includeusing 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參數設置。我們這裡需要了解的就是這個函數的返回參數是一系列檢測結果序列,每個檢測結果實際上就是一個矩形結構體對象。
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" #includeusing 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; }