Opencv攝像頭實時人臉識別


  • Introduction

網上存在很多人臉識別的文章,這篇文章是我的一個作業,重在通過攝像頭實時采集人臉信息,進行人臉檢測和人臉識別,並將識別結果顯示在左上角。

利用 OpenCV 實現一個實時的人臉識別系統,人臉庫采用 ORL FaceDatabase (網上下載) ,另外在數據庫中增加了作業中自帶的20張照片和自己利用攝像頭采集到的10張照片,系統利用攝像頭實時的采集到場景圖像,從中檢測出人臉用方框標出,並利用提供的數據庫進行人臉識別,並在圖像左上角顯示相匹配的數據庫圖片。

  • Method

算法流程分兩步,分別是人臉檢測和人臉識別。人臉檢測使用的是 ViolaJones 人臉檢測方法,利用樣本的 Haar-like 特征進行分類器訓練,得到級聯boosted 分類器,加載訓練好的人臉分類器,利用分類器在視頻幀中查找人臉區域;人臉識別利用了局部二進制模式直方圖。

  • Haar-like 特征

Haar-like 特征如下圖所示

 

圖1 Haar-like 特征

  • LBPH

 

人臉識別常用的方法有三種,Eigenfaces、Fisherfaces 和 LBPH;對於高維的圖像空間,我們首先應該進行降維操作。LBP 不把圖像看做高維的矢量,而是通過物體的局部特征來描述。將每個像素和其相鄰像素對比形成局部的結構,把該像素看做中心,並以該值對鄰接像素做閾值處理,如果臨界像素的亮度大於該像素則為 1 否則為 0,這樣每個像素點都可以用一個二進制數來表示,比如一個使用 3*3 臨界點的 LBP 操作如下圖所示:

圖2 LBP

  • Implementation
  • 識別訓練

利用准備好的數據庫進行識別訓練:首先我們利用Opencv安裝文件中的python腳本create_csv.py建立CSV文件,文件中每條記錄如:orl/s13/2.pgm;12,分號之前是圖片所存路徑,而分號之后是圖片的標簽號,每一組圖片對應着唯一的標簽號;之后利用代碼中的train_data和read_csv函數對數據集進行訓練。使用到的 OpenCV 類和函數有:FaceRecognizer,createLBPHFaceRecognizer

  • 人臉檢測

運用Opencv安裝文件中的haarcascade_frontalface_alt.xml文件,使用分類器在視頻幀中查找人臉區域,並用綠色方框標出。用到的 OpenCV 類和函數有:CascadeClassifier,detectMultiScale。

  • 人臉識別

讀取訓練好的 yaml文件,對每個監測到的區域的圖像分類,並在視頻幀人臉區域上方顯示分類結果(分類結果顯示為標簽和可信度),在左上角顯示縮略圖。用到的 OpenCV 函數主要有:predict.

  • Code

看到評論,大家需要config.h,抱歉事情多添加有些晚,我放在下面了,有什么問題歡迎交流~

 

#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"

#include <iostream>
#include <fstream>
#include <sstream>
#include <string.h>

char *FACES_TXT_PATH = "face.txt";
char *HARR_XML_PATH = "haarcascade_frontalface_alt.xml";
char *FACES_MODEL = "face.yaml";
char *POTRAITS ="potraits.jpg";
int DEVICE_ID = 0;

 

主文件內容:

 

  1 /*頭文件:*/
  2 #include "opencv2/core/core.hpp"
  3 #include "opencv2/contrib/contrib.hpp"
  4 #include "opencv2/highgui/highgui.hpp"
  5 #include "opencv2/imgproc/imgproc.hpp"
  6 #include "opencv2/objdetect/objdetect.hpp"
  7 
  8 #include <iostream>
  9 #include <fstream>
 10 #include <sstream>
 11 #include <string.h>
 12 
 13 char *FACES_TXT_PATH = "face.txt";
 14 char *HARR_XML_PATH = "haarcascade_frontalface_alt.xml";
 15 char *FACES_MODEL = "face.yaml";
 16 char *POTRAITS ="potraits.jpg";
 17 int DEVICE_ID = 0;
 18 
 19 /*主文件*/
 20 #include "config.h"
 21 
 22 using namespace cv;
 23 using namespace std;
 24 int FACE_WIDHT=92;
 25 int FACE_HEIGHT=112;
 26 int POTRITE_WIDTH = 100;
 27 int POTRITE_HEIGHT = 100;
 28 
 29 static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
 30     std::ifstream file(filename.c_str(), ifstream::in);
 31     if (!file) {
 32         string error_message = "找不到文件,請核對路徑";
 33         CV_Error(CV_StsBadArg, error_message);
 34     }
 35     string line, path, classlabel;
 36     while (getline(file, line)) {
 37         stringstream liness(line);
 38         getline(liness, path, separator);
 39         getline(liness, classlabel);
 40         if(!path.empty() && !classlabel.empty()) {
 41             images.push_back(imread(path, 0));
 42             labels.push_back(atoi(classlabel.c_str()));
 43         }
 44     }
 45 
 46 }
 47 
 48 /*利用csv文件讀取數據集並訓練對應模型*/
 49 void train_data(String fn_csv)
 50 {    
 51     vector<Mat> images;
 52     vector<int> labels;
 53     //獲取數據集,如果出錯拋出異常
 54     try {
 55         read_csv(fn_csv, images, labels);        
 56     }
 57     catch (cv::Exception& e) {
 58         cerr << "打開文件失敗 \"" << fn_csv << "\". 原因: " << e.msg << endl;
 59         exit(1);
 60     }
 61 
 62     // 如果訓練集數量不夠退出
 63     if(images.size() <= 1) {
 64         string error_message = "訓練集圖片少於2";
 65         CV_Error(CV_StsError, error_message);
 66     }
 67 
 68     //訓練模型
 69     Ptr<FaceRecognizer> model = createLBPHFaceRecognizer();
 70     model->train(images, labels);
 71     model->save(FACES_MODEL);
 72 }
 73 
 74 void show_portrait(Mat &potrait, Mat &frame) {
 75     int channels = potrait.channels();
 76     int nRows = potrait.rows;
 77     int nCols = potrait.cols*channels;
 78     
 79     uchar *p_p, *p_f;
 80     for(auto i=0; i<nRows; i++) {
 81         p_p = potrait.ptr<uchar>(i);
 82         p_f = frame.ptr<uchar>(i);
 83         for(auto j=0; j<nCols; j++) {
 84             p_f[j*3] = p_p[j];
 85             p_f[j*3+1] = p_p[j+1];
 86             p_f[j*3+2] = p_p[j+2];
 87         }
 88     }
 89     
 90 }
 91 
 92 void makePotraitImages(vector<Mat> potraits) {
 93     int rows = potraits.size()/6;
 94     if(potraits.size()-rows *6>0)rows++;
 95     rows *= POTRITE_HEIGHT;
 96     int cols = 6*POTRITE_HEIGHT;
 97     Mat potrait_s = Mat(rows,cols,CV_8UC3);
 98     rows = POTRITE_HEIGHT;
 99     cols = POTRITE_WIDTH;
100     uchar *p_ps, *p_p;
101     for(auto i=0; i<potraits.size(); i++) {
102         for(auto j=0; j<rows; j++) {
103             p_ps = potrait_s.ptr<uchar>(i/6*POTRITE_HEIGHT+j)+3*(i%6)*POTRITE_WIDTH;
104             p_p = potraits[i].ptr<uchar>(j);
105             for(auto k=0; k<cols; k++) {
106                 p_ps[k*3] = p_p[k];
107                 p_ps[k*3+1] = p_p[k+1];
108                 p_ps[k*3+2] = p_p[k+2];
109             }
110         }
111     }
112     imwrite(POTRAITS, potrait_s);
113 }
114 
115 void loadPortraits(const string& filename, vector<Mat>& images, char separator = ';') {
116     string fn_csv = string(FACES_TXT_PATH);
117     std::ifstream file(fn_csv.c_str(), ifstream::in);
118     if (!file) {
119         string error_message = "找不到文件,請核對路徑.";
120         CV_Error(CV_StsBadArg, error_message);
121     }
122     string line, path, classlabel;
123     int label(0);
124     while (getline(file, line)) {
125         stringstream liness(line);
126         getline(liness, path, separator);
127         getline(liness, classlabel);
128         if(!path.empty() && !classlabel.empty()) {
129             if(atoi(classlabel.c_str()) != label) {
130                 Mat potrait = imread(path, 0);
131                 resize(potrait, potrait,Size(POTRITE_WIDTH, POTRITE_HEIGHT));
132                 images.push_back(potrait);
133                 label = atoi(classlabel.c_str());
134             }
135         }
136     }
137 }
138 
139 int main(int argc, const char *argv[]) {
140     // 保存圖像和對應標簽的向量,要求同一個人的圖像必須對應相同的標簽
141     string fn_csv = string(FACES_TXT_PATH);
142     string fn_haar = string(HARR_XML_PATH);
143 
144     Ptr<FaceRecognizer> model = createLBPHFaceRecognizer();
145     FileStorage model_file(FACES_MODEL, FileStorage::READ);    
146     if(!model_file.isOpened()){
147         cout<<"無法找到模型,訓練中..."<<endl;
148         train_data(fn_csv);//訓練數據集,1表示EigenFace 2表示FisherFace 3表示LBPHFace
149     }
150     model->load(model_file);
151     model_file.release();
152     vector<Mat> potraits;
153     loadPortraits(FACES_MODEL,potraits);
154     makePotraitImages(potraits);
155     CascadeClassifier haar_cascade;
156     haar_cascade.load(fn_haar);
157 
158     VideoCapture cap(DEVICE_ID);
159     if(!cap.isOpened()) {
160         cerr << "設備 " << DEVICE_ID << "無法打開" << endl;
161         return -1;
162     }
163 
164     Mat frame;
165     for(;;) {
166         cap >> frame;
167         if(!frame.data)continue;
168         // 拷貝現有frame
169         Mat original = frame.clone();
170         // 灰度化
171         Mat gray;
172         cvtColor(original, gray, CV_BGR2GRAY);
173         // 識別frame中的人臉
174         vector< Rect_<int> > faces;
175         haar_cascade.detectMultiScale(gray, faces);
176         
177         if(faces.size() != 0)
178         {
179             int max_area_rect=0;
180             for(int i = 0; i < 1; i++) {
181                 if(faces[i].area() > faces[max_area_rect].area()){
182                     max_area_rect = i;
183                 }
184             
185             }
186 
187             // 順序處理
188             Rect face_i = faces[max_area_rect];
189 
190             Mat face = gray(face_i);
191             rectangle(original, face_i, CV_RGB(0, 255,0), 1);
192             int pridicted_label = -1;
193             double predicted_confidence = 0.0;
194             model->predict(face, pridicted_label, predicted_confidence);
195             string result_text = format("Prediction = %d confidence=%f", pridicted_label, predicted_confidence);
196             int text_x = std::max(face_i.tl().x - 10, 0);
197             int text_y = std::max(face_i.tl().y - 10, 0);
198             putText(original,result_text,  Point(text_x, text_y),FONT_HERSHEY_PLAIN, 1.0, CV_RGB(0,255,0), 2.0);
199             if(pridicted_label >0)
200                 show_portrait(potraits[pridicted_label], original);
201         }
202         // 顯示結果:
203         imshow("face_recognizer", original);
204 
205         char key = (char) waitKey(20);
206         if(key == 32)
207             exit(0);;
208     }
209     return 0;
210 }
  • Experiment

圖3 結果展示

圖4 人臉庫拼圖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM