OpenCV人臉檢測與人臉識别
tkorays(http://blog.csdn.net/tkorays)
成為領袖,而不是依賴别人的指揮。
用OpenCV做人臉檢測與人臉識别是一件比較簡單的事情,因為OpenCV的庫已經為我們做了很多工作,我們甚至不需要太了解原理,調用就行了。
注意,人臉檢測(Detection)和人臉識别(Recognition)不是同一個概念。所謂“檢測”是将一張圖檔或視訊的一幀裡的人臉找出來,隻是找出,并不需要知道他(她)是誰。而人臉“識别”,重在識别出人臉是誰。是以,人臉識别過程中常常是先找出人臉,再去判斷人臉屬于誰的。
這篇部落格我們使我們的代碼盡量簡化,能使讀者一看就會。接下來,我們就一步步來介紹吧。
人臉檢測
準備素材
首先,我們準備好素材,找一些包含人臉的圖檔,這裡我們随便拍幾張就可以了。
當然有很多人臉庫可以利用,CSDN上有篇Blog介紹了一些人臉庫,可以直接下載下傳用:http://blog.csdn.net/onlyyouandme/article/details/5596915。
檢測原理
這裡我們使用OpenCV的級聯(Cascade)分類器來做人臉檢測。所謂級聯分類器,即将多個弱分類器組合起來得到一個性能比較好的分類器。如果你學過機器學習,那麼你對這個肯定不會陌生。
既然是分類器,首先肯定是要學習的,這裡我們不需要做這一步的,因為OpenCV已經為我們訓練好了人臉的分類器,我們要做的是加載這個級聯分類器分類器檔案。當然,級聯分類器不止是做人臉檢測的,你還可以使用它來檢測其他物體,詳情可以參考OpenCV官方教程“級聯分類器訓練”:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/user_guide/ug_traincascade.html。
主要使用的是CascadeClassifier這個類,以及load、detectMultiScale函數。
首先加載已經訓練好的分類器,參數儲存在一個檔案裡,如OpenCV提供的用于人臉檢測的分類器檔案haarcascade_frontalface_default.xml等,這些檔案可以在OpenCV安裝檔案的data目錄裡面找到。
然後傳入圖檔檢測,得到檢測結果(包含人臉的一個矩形區域)。
代碼參考
/*
* 一個最簡單的人臉檢測實作
* @author tkorays<[email protected]>
*/
// 靜态連接配接庫包含
// 可以從 http://github.com/tkorays/cxxlib 擷取
#include "../cxxlib/cv_lib.h"
#pragma comment(lib, cvLIB("core"))
#pragma comment(lib, cvLIB("highgui"))
#pragma comment(lib, cvLIB("imgproc"))
#pragma comment(lib, cvLIB("objdetect"))
// -----------------------------------------
#include <string>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\objdetect\objdetect.hpp>
using namespace std;
using namespace cv;
#define FACE_CASCADE_FILE "E:/haarcascade_frontalface_default.xml"
#define FACE_IMG_FILE "E:/1.jpg"
int main() {
Mat img = imread(FACE_IMG_FILE,CV_LOAD_IMAGE_GRAYSCALE); // 讀取為灰階圖檔
vector<Rect> result;
CascadeClassifier face_detection;
face_detection.load(FACE_CASCADE_FILE); // 加載級聯分類器
// 檢測人臉,參數說明:
// (原始圖檔,結果,尺度因子,最小鄰居數,檢測方法,最小檢測視窗大小)
face_detection.detectMultiScale(img, result, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(30, 30));
// 看檢測出人臉的個數,人臉在圖檔中位置儲存在vector的每個Rect中
cout << "檢測出人臉數: " << result.size() << endl;
return 0;
}
人臉識别
準備素材
人臉識别要準備的素材要稍微多點了,因為這裡面我們要對分類器進行訓練了。OpenCV并沒有提供識别張三和李四人臉的分類器資料,我們需要自己對分類器訓練,讓它對不同人臉進行分類。
這裡我們隻識别兩個人,不妨每個人臉提供5張照片吧,要提高識别精度你可以多提供一些照片,但是訓練時間也就變長了。
人臉圖檔要求同樣大小,隻包括人臉部分,不包括其他部分。這個你可以使用OpenCV來做,也可以用一些截圖工具(記住要同樣大小的)。
圖1 人臉
識别原理
原理,這裡面沒有什麼可以多說的,如果真的要說會說很多。
我們用到的主要就是FaceRecognizer這個類,這個類沒有構造函數,必須通過函數調用來初始化。用于初始化的有三個函數:
圖2 人臉識别函數
Create[Eigen|Fisher|LBPH]FaceRecognizer,三個函數對應着三種識别方法,如果你了解模式識别之類的東西,那你肯定知道他們是什麼,如果不了解也沒關系,除非你要深入研究,否則這裡會用就可以了。
然後就是訓練與識别了,詳細資訊見代碼。
代碼參考
/*
* 一個最簡單的人臉識别實作
* @author tkorays<[email protected]>
*/
// 靜态連接配接庫包含
// 可以從 http://github.com/tkorays/cxxlib 擷取
#include "../cxxlib/cv_lib.h"
#pragma comment(lib, cvLIB("core"))
#pragma comment(lib, cvLIB("contrib"))
#pragma comment(lib, cvLIB("highgui"))
#pragma comment(lib, cvLIB("imgproc"))
#pragma comment(lib, cvLIB("objdetect"))
// -----------------------------------------
#include <string>
#include <iostream>
#include <sstream>
#include <opencv2\core\core.hpp>
#include <opencv2\contrib\contrib.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\objdetect\objdetect.hpp>
using namespace std;
using namespace cv;
string FACE_FILE_PATH = "E:/BigData/face_data/";
int main() {
// 準備資料
vector<Mat> faces;
vector<int> labels;
stringstream ss;
string s_index;
// 讀取兩個人的人臉資料
// 特征臉以及fisher方法要求為灰階
for (size_t i = 0; i < 5; i++) {
ss << i;
ss >> s_index;
faces.push_back(imread(FACE_FILE_PATH + "PEOPLE_1/small/" + s_index + ".jpg",CV_LOAD_IMAGE_GRAYSCALE));
labels.push_back(1);
}
for (size_t i = 0; i < 5; i++) {
ss << i;
ss >> s_index;
faces.push_back(imread(FACE_FILE_PATH + "PEOPLE_2/small/" + s_index + ".jpg", CV_LOAD_IMAGE_GRAYSCALE));
labels.push_back(2);
}
// --------------------------------------------------------------------------
// 訓練
// 建立人臉識别類,這裡可以選擇特征臉,fisher,以及LBPH這三種方法
// createEigenFaceRecognizer,createFisherFaceRecognizer,createLBPHFaceRecognizer
Ptr<FaceRecognizer> face_recog = createEigenFaceRecognizer(20);
face_recog->train(faces, labels); // 傳入人臉圖檔以及标簽
int face_id = face_recog->predict(imread(FACE_FILE_PATH + "PEOPLE_1/small/" + "6.jpg", CV_LOAD_IMAGE_GRAYSCALE));
// 識别
cout << face_id << endl;
return 0;
}
注意,這裡我們訓練完後可以将訓練好的一些參數儲存起來(FaceRecognizer::save),以便下次使用不要再加載圖檔訓練,而是像上面的級聯分類器一樣直接加載xml檔案就可以工作了。
建議
有不會用的函數不妨去OpenCV論壇的文檔上去搜尋,本部落格隻能讓你入門,要深入研究還需要自己去挖掘:http://www.opencv.org.cn/opencvdoc/2.3.2/html/index.html
圖3 學會搜尋
By tkorays(http://blog.csdn.net/tkorays)