opencv中已經有人臉識別的功能了,所以來看看整個流程以及具體實現吧。
人臉識別:就是給一個已知人臉貼上一個標籤。
上面這句簡單的話,其實包含的信息量好大啊,這說明首先你要知道這是一張臉,然後你還要識別出來這個臉屬於哪個的。
所以人臉識別具體分爲以下幾個步驟:
1,人臉檢測:從一張圖中定位出人臉的位置,只關心這個區域是不是人臉,並不關心這個人是誰。
2,人臉預處理:對人臉做一些預處理,包括人臉圖像大小的調整,去噪,亮度對比度均衡,彩色轉灰度等,爲了下一步得到更好地結果。
3,收集人臉並且學習: 收集已經預處理的統一大小的人臉圖像,然後建立學習模型,使用這些數據學習怎樣識別出人臉。
4,人臉識別:通過上面的學習模型,對人臉圖像識別出待檢測圖像跟數據庫中哪張人臉是最接近的。3是模型的訓練,4就是模型的測試
一,人臉檢測
人臉檢測首先就是2001 Viola &Jones 發表的Haar-based cascade classifier for object detection, 2002 Lienhart &Maydt對該方法進行了速度和穩定度的改進,正臉正確率可達差不多95%。並且Lienhart 寫了opencv可用的檢測器可以檢測正面人臉,側臉,眼,鼻,嘴,公司logo等等。之後opencv2.0又擴展了Ahonen,
Hadid and Pietikäinen in 2006發表的基於LBP特徵的檢測器,LBP要比類Haar特徵檢測器要快很多,並且沒有版權問題。
類Haar特徵人臉檢測器的基本思想是:如果你觀察大多數的人臉區域,會發現眼睛區域的顏色應該比額頭和臉頰區域的顏色深,並且嘴巴區域比臉頰區域顏色深等等。它典型的要運行20個階段這種類似的比較來決定是否是人臉,並且要在每張圖像的每個可能位置對每個可能是人臉的尺寸進行檢查,所以經常需要對一張圖像進行上千次的檢查。LBP特徵人臉檢測器與類Haar類似,但是它使用像素密度對比的直方圖,比如邊緣,角點以及平坦區域。
這兩個檢測器都可以使用XML文件中存儲的信息來自動的用大量圖像訓練從而檢測出人臉,典型的,這些級聯分類器檢測器都要使用1000個人臉圖像和1000個非人臉圖像來訓練,這個訓練過程對於多核桌面系統要消耗大量的時間(LBP幾個小時,但是類Harr要一週)。opencv中有訓練好的Haar和LBP檢測器可以使用,可以把級聯分類器XML文件加載到物體檢測器中,來分別檢測正臉,側臉,眼,鼻,嘴。
分別存儲在data\haarcascades data\lbpcascades中,但是opencv中的LBP級聯檢測器訓練的不如Haar,所以如果想要效果好的話就要自己訓練LBP,或者使用類Haar。
main.cpp
#include "detectFace.h"
int main(){
CascadeClassifier facedetector;
string facecascadeXML = "F:\\opencv\\data\\lbpcascades\\lbpcascade_frontalface.xml";
try{
facedetector.load(facecascadeXML);
}
catch (Exception e){}
if (facedetector.empty()){
cerr<<"ERROR: could not open facedetector :" <<facecascadeXML<<endl;
exit(1);
}
VideoCapture camera;
camera.open(0);
if (!camera.isOpened()){
cerr<<"ERROR: could not open camera "<<endl;
exit(1);
}
camera.set(CV_CAP_PROP_FRAME_HEIGHT,480);
camera.set(CV_CAP_PROP_FRAME_WIDTH,640);
while(true){
Mat cameraFrame;
camera>>cameraFrame;
if (cameraFrame.empty()){
cerr<<"ERROR: could not open cameraframe" <<endl;
exit(1);
}
Rect detectObject;
detectLargestObject(cameraFrame,facedetector,detectObject);
rectangle(cameraFrame,Point(detectObject.x,detectObject.y),Point(detectObject.x +detectObject.width,detectObject.y+detectObject.height),Scalar(0,0,255));
imshow("detect",cameraFrame);
waitKey(20);
}
}
detectFace.cpp
#include "detectFace.h" // Easily detect faces or eyes (using LBP or Haar Cascades).
// Search for objects such as faces in the image using the given parameters, storing the multiple cv::Rects into 'objects'.
// Can use Haar cascades or LBP cascades for Face Detection, or even eye, mouth, or car detection.
// Input is temporarily shrunk to 'scaledWidth' for much faster detection, since 200 is enough to find faces.
void detectObjectsCustom(const Mat &img, CascadeClassifier &cascade, vector<Rect> &objects, int scaledWidth, int flags, Size minFeatureSize, float searchScaleFactor, int minNeighbors)
{
// If the input image is not grayscale, then convert the BGR or BGRA color image to grayscale.
Mat gray;
if (img.channels() == 3) {
cvtColor(img, gray, CV_BGR2GRAY);
}
else if (img.channels() == 4) {
cvtColor(img, gray, CV_BGRA2GRAY);
}
else {
// Access the input image directly, since it is already grayscale.
gray = img;
}
// Possibly shrink the image, to run much faster.
Mat inputImg;
float scale = img.cols / (float)scaledWidth;
if (img.cols > scaledWidth) {
// Shrink the image while keeping the same aspect ratio.
int scaledHeight = cvRound(img.rows / scale);
resize(gray, inputImg, Size(scaledWidth, scaledHeight));
}
else {
// Access the input image directly, since it is already small.
inputImg = gray;
}
// Standardize the brightness and contrast to improve dark images.
Mat equalizedImg;
equalizeHist(inputImg, equalizedImg);
// Detect objects in the small grayscale image.
cascade.detectMultiScale(equalizedImg, objects, searchScaleFactor, minNeighbors, flags, minFeatureSize);
// Enlarge the results if the image was temporarily shrunk before detection.
if (img.cols > scaledWidth) {
for (int i = 0; i < (int)objects.size(); i++ ) {
objects[i].x = cvRound(objects[i].x * scale);
objects[i].y = cvRound(objects[i].y * scale);
objects[i].width = cvRound(objects[i].width * scale);
objects[i].height = cvRound(objects[i].height * scale);
}
}
// Make sure the object is completely within the image, in case it was on a border.
for (int i = 0; i < (int)objects.size(); i++ ) {
if (objects[i].x < 0)
objects[i].x = 0;
if (objects[i].y < 0)
objects[i].y = 0;
if (objects[i].x + objects[i].width > img.cols)
objects[i].x = img.cols - objects[i].width;
if (objects[i].y + objects[i].height > img.rows)
objects[i].y = img.rows - objects[i].height;
}
// Return with the detected face rectangles stored in "objects".
}
// Search for just a single object in the image, such as the largest face, storing the result into 'largestObject'.
// Can use Haar cascades or LBP cascades for Face Detection, or even eye, mouth, or car detection.
// Input is temporarily shrunk to 'scaledWidth' for much faster detection, since 200 is enough to find faces.
// Note: detectLargestObject() should be faster than detectManyObjects().
void detectLargestObject(const Mat &img, CascadeClassifier &cascade, Rect &largestObject, int scaledWidth)
{
// Only search for just 1 object (the biggest in the image).
int flags = CASCADE_FIND_BIGGEST_OBJECT;// | CASCADE_DO_ROUGH_SEARCH;
// Smallest object size.
Size minFeatureSize = Size(20, 20);
// How detailed should the search be. Must be larger than 1.0.
float searchScaleFactor = 1.1f;
// How much the detections should be filtered out. This should depend on how bad false detections are to your system.
// minNeighbors=2 means lots of good+bad detections, and minNeighbors=6 means only good detections are given but some are missed.
int minNeighbors = 4;
// Perform Object or Face Detection, looking for just 1 object (the biggest in the image).
vector<Rect> objects;
detectObjectsCustom(img, cascade, objects, scaledWidth, flags, minFeatureSize, searchScaleFactor, minNeighbors);
if (objects.size() > 0) {
// Return the only detected object.
largestObject = (Rect)objects.at(0);
}
else {
// Return an invalid rect.
largestObject = Rect(-1,-1,-1,-1);
}
}
效果圖
光線條件不好的情況下,檢測結果很差,開燈之後改善很多,我用的是電腦自帶的攝像機,檢測結果以及實時性都一般吧。