1. 程式人生 > >C/C++ 影象處理(18)------人臉檢測

C/C++ 影象處理(18)------人臉檢測

人臉識別包括人臉檢測和識別兩個部分

一般的邏輯是先檢測人臉位置,然後再識別。

具體的流程是

準備要識別的人臉資料->檢測人臉->學習人臉特徵並生成模型

檢測人臉->對比檢測出來的人臉和模型的相識度->給出識別結果

本篇文章用OpenCV實現了這兩個過程,具體的程式碼有參考網上的程式碼,如有雷同,絕非巧合

具體的檢測和識別原理不在本篇文章的範疇之內,望見諒

人臉檢測

#include <time.h>
#include <opencv2/opencv.hpp>
#include <vector>//容器標頭檔案 
using namespace std; using namespace cv; //關鍵函式 /* void detectMultiScale( const Mat& image, CV_OUT vector<Rect>& objects, double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, Size minSize = Size(), Size maxSize = Size() ); */ //引數1:image--待檢測圖片,一般為灰度影象加快檢測速度;
//引數2:objects--被檢測物體的矩形框向量組; //引數3:scaleFactor--表示在前後兩次相繼的掃描中,搜尋視窗的比例係數。預設為1.1即每次搜尋視窗依次擴大10%; //引數4:minNeighbors--表示構成檢測目標的相鄰矩形的最小個數(預設為3個)。 //如果組成檢測目標的小矩形的個數和小於 min_neighbors - 1 都會被排除。 //如果min_neighbors 為 0, 則函式不做任何操作就返回所有的被檢候選矩形框 //引數5:flags--要麼使用預設值,要麼使用CV_HAAR_DO_CANNY_PRUNING //CV_HAAR_DO_CANNY_PRUNING--函式將會使用Canny邊緣檢測來排除邊緣過多或過少的區域,因為這些區域通常不會是人臉所在區域;
//引數6、7:minSize和maxSize用來限制得到的目標區域的範圍。 void getfaceimg()//人臉影象獲取 { CascadeClassifier ccf;//建立臉部物件 string cascadeName = "haarcascade_frontalface_alt2.xml";//人臉檢測模型,請在OpenCV資料夾中搜索 ccf.load(cascadeName);//讀取opencv人臉檢測模型 vector<Rect> faces;//容器,存放檢測到的人臉 long time = clock(); string path; for (size_t i = 0; i < 5; i++) { path = to_string(i + 1); path.append(".jpg"); Mat img = imread(path, 0); //imshow("原圖", img); equalizeHist(img, img);//直方圖均衡化 //imshow("直方圖均衡化", img); //人臉檢測 ccf.detectMultiScale(img, faces, 1.1, 3, CV_HAAR_DO_CANNY_PRUNING, Size(50, 50), Size(500, 500));//人臉檢測 for (vector<Rect>::const_iterator iter = faces.begin(); iter != faces.end(); iter++) { rectangle(img, *iter, Scalar(0), 2, 8); //用矩形圈出人臉 } //擷取儲存臉部影象 Mat faceimg; for (vector<Rect>::const_iterator iter = faces.begin(); iter != faces.end(); iter++) { faceimg = img(*iter); imshow("臉部影象", faceimg); imwrite("faceImg//" + path, faceimg); } imshow("檢測結果", img); waitKey(1000); } cout << "花費時間:" << clock() - time << "ms" << endl; } void main() { getfaceimg();//人臉影象獲取 }

這裡寫圖片描述

分類器訓練

//人臉分類器訓練,資料庫用的ORL人臉資料庫,並加入了要識別的人臉放在"S0"資料夾中
void TrainFaceImg()
{
    //定義儲存圖片和標籤的向量容器
    std::vector<Mat> images;
    std::vector<int> labels;
    //讀取樣本
    for (size_t s = 0; s <= 40; s++)
    {
        String path = "att_faces//s"+ to_string(s)+"//";
        for (size_t i = 1; i <= 10; i++)
        {
            String imgpath = path + to_string(i) + ".pgm";
            Mat trainfaceimg = imread(imgpath, CV_LOAD_IMAGE_GRAYSCALE);
            resize(trainfaceimg, trainfaceimg, Size(128, 128));
            images.push_back(trainfaceimg);//加入影象                              
            labels.push_back(s);//加入標籤
        }
    }
    //三種人臉識別方法,只用其中的一種也可以
    Ptr<FaceRecognizer> faceClass = createEigenFaceRecognizer();
    Ptr<FaceRecognizer> fisherClass = createFisherFaceRecognizer();
    Ptr<FaceRecognizer> lpbhClass = createLBPHFaceRecognizer();
    //訓練
    faceClass->train(images, labels);
    fisherClass->train(images, labels);
    lpbhClass->train(images, labels);
    //儲存訓練的分類器
    faceClass->save("faceClass.xml");
    fisherClass->save("fisherClass.xml");
    lpbhClass->save("lpbhClass.xml");
    cout << "分類器訓練完成"<< endl;
}

人臉識別(注意要放進去識別的圖片是先經過檢測、裁剪和尺度變換後的圖片)

void FaceDetect()
{
    Ptr<FaceRecognizer> faceClass = createEigenFaceRecognizer();
    Ptr<FaceRecognizer> fisherClass = createFisherFaceRecognizer();
    Ptr<FaceRecognizer> lpbhClass = createLBPHFaceRecognizer();
    //載入分類器
    faceClass->load("faceClass.xml");
    fisherClass->load("fisherClass.xml");
    lpbhClass->load("lpbhClass.xml");
    //使用訓練好的分類器進行預測。
    Mat detectimg = imread("faceImg//14.pgm", 0);
    resize(detectimg, detectimg, Size(128, 128));
    //預測樣本並獲取標籤和置信度
    int faceResult = faceClass->predict(detectimg);
    cout << String("faceClass標籤類別:") << faceResult << endl;;
    int fisherResult = -1;
    double fisherConfidence = 0.0;
    fisherClass->predict(detectimg, fisherResult, fisherConfidence);
    cout << String("fisherClass標籤類別:") << fisherResult << String("置信度:") << fisherConfidence << endl;
    int lpbhResult = lpbhClass->predict(detectimg);
    cout << String("lpbhResult標籤類別:") << lpbhResult << endl;;
}

通過上面的步驟基本上就完成了一個普通的人臉識別流程,但其缺點是準確率還不是很高,要提高準確率可以考慮用深度學習的方法,後面研究到的話會寫出來跟大家交流。