Dlib+OpenCV深度學(xué)習(xí)人臉識(shí)別的方法示例
前言
人臉識(shí)別在LWF(Labeled Faces in the Wild)數(shù)據(jù)集上人臉識(shí)別率現(xiàn)在已經(jīng)99.7%以上,這個(gè)識(shí)別率確實(shí)非常高了,但是真實(shí)的環(huán)境中的準(zhǔn)確率有多少呢?我沒有這方面的數(shù)據(jù),但是可以確信的是真實(shí)環(huán)境中的識(shí)別率并沒有那么樂觀。現(xiàn)在雖然有一些商業(yè)應(yīng)用如員工人臉識(shí)別管理系統(tǒng)、海關(guān)身份驗(yàn)證系統(tǒng)、甚至是銀行人臉識(shí)別功能,但是我們可以仔細(xì)想想員工人臉識(shí)別管理,海關(guān)身份證系統(tǒng)的應(yīng)用場(chǎng)景對(duì)身份的驗(yàn)證功能其實(shí)并沒有商家吹噓的那么重要,打個(gè)比方說員工上班的時(shí)候刷臉如果失敗了會(huì)怎樣,是不是重新識(shí)別一下,如果還是誤識(shí)別,或是識(shí)別不出,是不是就干脆刷卡或是其他方式登記上班,然后罵一句他娘的,本人那么帥居然沒識(shí)別出來!那銀行柜員機(jī)上人臉識(shí)別系統(tǒng)呢,你看它敢不敢讓你連密碼也不輸直接刷臉轉(zhuǎn)賬,是不是關(guān)掉了人臉識(shí)別、指紋識(shí)別機(jī)器還可以正常運(yùn)作。所以說真實(shí)環(huán)境中在各種光照因素、年齡因素、網(wǎng)紅因素(化妝)、甚至是作弊因素等各種因素條件下的識(shí)別率有多少只有產(chǎn)品廠家自己知道,我相信每個(gè)廠家針對(duì)這些情況都有做優(yōu)化,比如外圍硬件的輔助,針對(duì)特定場(chǎng)景的各種約束等等,通過各個(gè)廠家自己在各個(gè)方面對(duì)系統(tǒng)的優(yōu)化,是可以提升自身產(chǎn)品的綜合體驗(yàn)的。
前面扯遠(yuǎn)了,本文的目的是實(shí)現(xiàn)一個(gè)人臉識(shí)別的最簡(jiǎn)單實(shí)際應(yīng)用,即用攝像頭捕捉動(dòng)態(tài)人臉,然后和已經(jīng)存儲(chǔ)在數(shù)據(jù)庫中的128D人臉特征進(jìn)行比較識(shí)別出相應(yīng)的人臉信息(名字等)。工程是基于VS2015+簡(jiǎn)單的MFC對(duì)話框?qū)崿F(xiàn)的,代碼存放在:http://git.oschina.net/wjiang/face_recognition
在這個(gè)系統(tǒng)中我預(yù)先存儲(chǔ)了下面幾位明星的正面頭像的128D人臉特征,當(dāng)然你可以存儲(chǔ)和導(dǎo)入更多的人臉。

然后經(jīng)過人臉檢測(cè)、人臉圖像處理,和人臉識(shí)別等步驟識(shí)別出相應(yīng)的人臉信息,識(shí)別效果如下(怕大家被丑到所以用了明星的圖片,沒有用真實(shí)的人臉 – 沒有做活體檢測(cè)):


當(dāng)然這只是一個(gè)簡(jiǎn)單的應(yīng)用,真正用到生產(chǎn)的系統(tǒng),還需運(yùn)用活體檢測(cè)等技術(shù),防止運(yùn)用照片或是手機(jī)視頻等方式欺騙過人臉識(shí)別系統(tǒng),安全級(jí)別要求更高的應(yīng)用領(lǐng)域例如支付、轉(zhuǎn)賬等系統(tǒng)活體檢測(cè)可能仍不夠安全,這時(shí)還可以通過人臉識(shí)別+驗(yàn)證密碼等方式加強(qiáng)安全性能。
人臉數(shù)據(jù)庫導(dǎo)入
人臉數(shù)據(jù)導(dǎo)入,也就是說我在系統(tǒng)啟動(dòng)之初,需要導(dǎo)入我的人臉數(shù)據(jù)庫,也就是前面的那些明星的正面照。裝載的開始階段,因?yàn)橐獧z測(cè)靜態(tài)人臉圖片的人臉部位,首先需要用dlib的人臉檢測(cè)器,用get_frontal_face_detector()獲得。然后需要將68點(diǎn)人臉標(biāo)記模型導(dǎo)入shape_predictor sp,目的就是要對(duì)其人臉到一個(gè)標(biāo)準(zhǔn)的姿勢(shì),接著就是裝載DNN模型。然后取每張人臉照片的特征,并將特征和姓名等相關(guān)的信息放入FACE_DESC結(jié)構(gòu)中,最后將每張人臉信息結(jié)構(gòu)放入face_desc_vec容器中,這里我只裝載了9個(gè)明星的人臉信息。
int FACE_RECOGNITION::load_db_faces(void)
{
intrc = -1;
longhFile = 0;
struct_finddata_tfileinfo;
frontal_face_detectordetector =get_frontal_face_detector();
// We will also use a face landmarking model to align faces to a standard pose: (see face_landmark_detection_excpp for an introduction)
deserialize("shape_predictor_68_face_landmarksdat") >>sp;
// And finally we load the DNN responsible for face recognition
deserialize("dlib_face_recognition_resnet_model_vdat") >>net;
if ((hFile =_findfirst("\\faces\\*jpg", &fileinfo)) != -1)
{
do
{
if ((fileinfoattrib &_A_ARCH))
{
if (strcmp(fileinfoname,"") != 0 && strcmp(fileinfoname,"") != 0)
{
if (!strcmp(strstr(fileinfoname,"") + 1 , "jpg"))
{
cout <<"This file is an image file!" <<fileinfoname <<endl;
matrix<rgb_pixel>img;
charpath[260];
sprintf_s(path,"\\faces\\%s",fileinfoname);
load_image(img,path);
image_windowwin(img);
for (autoface :detector(img))
{
autoshape =sp(img,face);
matrix<rgb_pixel>face_chip;
extract_image_chip(img,get_face_chip_details(shape, 150, 25),face_chip);
//Record the all this face's information
FACE_DESCsigle_face;
sigle_faceface_chip =face_chip;
sigle_facename =fileinfoname;
std::vector<matrix<rgb_pixel>>face_chip_vec;
std::vector<matrix<float, 0, 1>>face_all;
face_chip_vecpush_back(move(face_chip));
//Asks the DNN to convert each face image in faces into a 128D vector
face_all =net(face_chip_vec);
//Get the feature of this person
std::vector<matrix<float, 0, 1>>::iteratoriter_begin = face_allbegin(),
iter_end =face_allend();
if (face_allsize() > 1)break;
sigle_faceface_feature = *iter_begin;
//all the person description into vector
face_desc_vecpush_back(sigle_face);
winadd_overlay(face);
}
}
else
{
cout <<"This file is not image file!" <<fileinfoname <<endl;
}
}
}
else
{
//filespush_back(passign(path)append("\\")append(fileinfoname));
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
returnrc;
}
人臉檢測(cè)
人臉檢測(cè)在人臉識(shí)別的應(yīng)用系統(tǒng)中我認(rèn)為是至關(guān)重要的一環(huán),因?yàn)槿四槞z測(cè)的好壞直接影響最終的識(shí)別率,如果在人臉檢測(cè)階段能做到盡量好的話,系統(tǒng)的識(shí)別率會(huì)有一個(gè)比較大的提升。下面的是人臉檢測(cè)的具體代碼實(shí)現(xiàn)(很簡(jiǎn)陋莫怪),嘗試了用Dlib人臉檢測(cè),OpenCV人臉檢測(cè),還有于仕琪的libfacedetection,比較發(fā)現(xiàn)于仕琪的libfacedetection是做人臉檢測(cè)最好的一個(gè),速度快,并且檢測(cè)圖像效果也很好。
intcapture_face(Matframe,Mat&out)
{
Matgray;
Matface;
intrc = -1;
if (frame.empty() || !frame.data)return -1;
cvtColor(frame,gray,CV_BGR2GRAY);
int *pResults =NULL;
unsignedchar *pBuffer = (unsignedchar *)malloc(DETECT_BUFFER_SIZE);
if (!pBuffer)
{
fprintf(stderr,"Can not alloc buffer.\n");
return -1;
}
//pResults = facedetect_frontal_tmp((unsigned char*)(gray.ptr(0)), gray.cols, gray.rows, gray.step,
// 1.2f, 5, 24);
pResults =facedetect_multiview_reinforce(pBuffer, (unsignedchar*)(gray.ptr(0)),gray.cols,gray.rows, (int)gray.step,
1.2f, 2, 48, 0, 1);
//printf("%d faces detected.\n", (pResults ? *pResults : 0));//重復(fù)運(yùn)行
//print the detection results
if (pResults !=NULL)
{
for (inti = 0;i < (pResults ? *pResults : 0);i++)
{
short *p = ((short*)(pResults + 1)) + 6 *i;
intx =p[0];
inty =p[1];
intw =p[2];
inth =p[3];
intneighbors =p[4];
Rect_<float>face_rect =Rect_<float>(x,y,w, h);
face =frame(face_rect);
printf("face_rect=[%d, %d, %d, %d], neighbors=%d\n",x,y, w,h,neighbors);
Pointleft(x,y);
Pointright(x +w,y + h);
cv::rectangle(frame,left,right, Scalar(230, 255, 0), 4);
}
//imshow("frame", frame);
if (face.empty() || !face.data)
{
face_detect_count = 0;
return -1;
}
if (face_detect_count++ > 30)
{
imshow("face",face);
out =face.clone();
return 0;
}
}
else
{
//face is moving, and reset the detect count
face_detect_count = 0;
}
returnrc;
}
人臉識(shí)別
通過人臉檢測(cè)函數(shù)capture_face()經(jīng)過處理之后臨時(shí)保存在工程目錄下的cap.jpg,用get_face_chip_details()函數(shù)將檢測(cè)到的目標(biāo)圖片標(biāo)準(zhǔn)化為150*150像素大小,并對(duì)人臉進(jìn)行旋轉(zhuǎn)居中,用extract_image_chip()取得圖像的一個(gè)拷貝,然后將其存儲(chǔ)到自己的圖片face_chip中,把的到face_chip放入vect_faces容器中,傳送給深度神經(jīng)網(wǎng)絡(luò)net,得到捕捉到人臉圖片的128D向量特征。最后在事先導(dǎo)入的人臉數(shù)據(jù)庫中遍歷與此特征最相近的人臉即可識(shí)別出相應(yīng)的人臉信息。
這種模式的應(yīng)用,也就是我們所說的1:N應(yīng)用,1對(duì)N是比較考驗(yàn)系統(tǒng)運(yùn)算能力的,舉個(gè)例子,現(xiàn)在支付寶賬戶應(yīng)該已經(jīng)是上億級(jí)別的用戶,如果你在就餐的時(shí)候選擇使用支付寶人臉支付,也許在半個(gè)小時(shí)內(nèi)服務(wù)器也沒有找你的臉,這下就悲催,當(dāng)然在真實(shí)應(yīng)用場(chǎng)景可能是還需要你輸入你的名字,這下可能就快多了,畢竟全國(guó)可能和你重名的也就了不的幾千上萬個(gè)吧,一搜索,人臉識(shí)別再一驗(yàn)證即可。
前面的這些還沒有考慮安全的因素,比如說雙胞胎啊,化妝?。ňW(wǎng)紅的年代啊),還有年齡的因素,環(huán)境的因素還包括光照、角度等導(dǎo)致的誤識(shí)別或是識(shí)別不出,識(shí)別不出的情況還好,如果是誤識(shí)別對(duì)于支付等對(duì)于安全性要求極其嚴(yán)苛的應(yīng)用來說簡(jiǎn)直就是災(zāi)難。所以人臉識(shí)別還有很大的局限性 – 額,好像扯遠(yuǎn)了。
matrix<rgb_pixel> face_cap;
//save the capture in the project directory
load_image(face_cap, ".\\cap.jpg");
//Display the raw image on the screen
image_window win1(face_cap);
frontal_face_detector detector = get_frontal_face_detector();
std::vector<matrix<rgb_pixel>> vect_faces;
for (auto face : detector(face_cap))
{
auto shape = face_recognize.sp(face_cap, face);
matrix<rgb_pixel> face_chip;
extract_image_chip(face_cap, get_face_chip_details(shape, 150, 0.25), face_chip);
vect_faces.push_back(move(face_chip));
win1.add_overlay(face);
}
if (vect_faces.size() != 1)
{
cout <<"Capture face error! face number "<< vect_faces.size() << endl;
cap.release();
goto CAPTURE;
}
//Use DNN and get the capture face's feature with 128D vector
std::vector<matrix<float, 0, 1>> face_cap_desc = face_recognize.net(vect_faces);
//Browse the face feature from the database, and find the match one
std::pair<double,std::string> candidate_face;
std::vector<double> len_vec;
std::vector<std::pair<double, std::string>> candi_face_vec;
candi_face_vec.reserve(256);
for (size_t i = 0; i < face_recognize.face_desc_vec.size(); ++i)
{
auto len = length(face_cap_desc[0] - face_recognize.face_desc_vec[i].face_feature);
if (len < 0.45)
{
len_vec.push_back(len);
candidate_face.first = len;
candidate_face.second = face_recognize.face_desc_vec[i].name.c_str();
candi_face_vec.push_back(candidate_face);
#ifdef _FACE_RECOGNIZE_DEBUG
char buffer[256] = {0};
sprintf_s(buffer, "Candidate face %s Euclid length %f",
face_recognize.face_desc_vec[i].name.c_str(),
len);
MessageBox(CString(buffer), NULL, MB_YESNO);
#endif
}
else
{
cout << "This face from database is not match the capture face, continue!" << endl;
}
}
//Find the most similar face
if (len_vec.size() != 0)
{
shellSort(len_vec);
int i(0);
for (i = 0; i != len_vec.size(); i++)
{
if (len_vec[0] == candi_face_vec[i].first)
break;
}
char buffer[256] = { 0 };
sprintf_s(buffer, "The face is %s -- Euclid length %f",
candi_face_vec[i].second.c_str(), candi_face_vec[i].first);
if (MessageBox(CString(buffer), NULL, MB_YESNO) == IDNO)
{
face_record();
}
}
else
{
if (MessageBox(CString("Not the similar face been found"), NULL, MB_YESNO) == IDYES)
{
face_record();
}
}
face_detect_count = 0;
frame.release();
face.release();
異常處理
當(dāng)人臉或是物體快速的在攝像頭前活動(dòng)時(shí),會(huì)導(dǎo)致系統(tǒng)異常拋出,異常提示如下:

對(duì)于這個(gè)問題,我們可以先用C++捕獲異常的工具,try和catch工具來捕獲異常:
Mat frame;
Mat face;
VideoCapture cap(0);
if (!cap.isOpened()) {
AfxMessageBox(_T("Please check your USB camera's interface num."));
}
try
{
while (1)
{
check_close(cap);
cap >> frame;
if (!frame.empty())
{
if (capture_face(frame, face) == 0)
{
//convert to IplImage format and then save with .jpg format
IplImage face_Img;
face_Img = IplImage(face);
//save the capture face to the project directory
cvSaveImage("./cap.jpg", &face_Img);
break;
}
imshow("view", frame);
}
int c = waitKey(10);
if ((char)c == 'c') { break; }
}
}
catch (exception& e)
{
cout << "\nexception thrown!" << endl;
cout << e.what() << endl;
#ifdef _CAPTURE_DEBUG
MessageBox(CString(e.what()), NULL, MB_YESNO);
#endif
goto CAPTURE;
}
在catch中將捕獲到的異常信息打印出來:

可以看到,可能是由于攝像頭捕獲響應(yīng)速率跟不上的原因,在cap >>frame;的時(shí)候得到的frame出現(xiàn)了格式錯(cuò)誤,如上圖的對(duì)話框所示error(-215) 0 < roi.x,也就是說opencv感興趣區(qū)域的x坐標(biāo)出現(xiàn)了一個(gè)負(fù)數(shù),而這顯然必須是要非負(fù)數(shù)的地方出現(xiàn)了一個(gè)負(fù)數(shù)的輸入,導(dǎo)致OpenCV異常拋出。
沒關(guān)系我們我們不理會(huì)這個(gè)異常的frame輸入就可以,在異常拋出的catch屏蔽掉對(duì)話框的顯示,我們即可流暢的采集圖像。不理會(huì)這個(gè)錯(cuò)誤的幀輸入也就是說直接丟棄這一幀。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Python OpenCV機(jī)器學(xué)習(xí)之圖像識(shí)別詳解
- Python OpenCV實(shí)戰(zhàn)之與機(jī)器學(xué)習(xí)的碰撞
- OpenCV機(jī)器學(xué)習(xí)MeanShift算法筆記分享
- 基于深度學(xué)習(xí)和OpenCV實(shí)現(xiàn)目標(biāo)檢測(cè)
- Python-OpenCV深度學(xué)習(xí)入門示例詳解
- 使用Python中OpenCV和深度學(xué)習(xí)進(jìn)行全面嵌套邊緣檢測(cè)
- opencv深入淺出了解機(jī)器學(xué)習(xí)和深度學(xué)習(xí)
相關(guān)文章
關(guān)于不懂Chromedriver如何配置環(huán)境變量問題解決方法
這篇文章主要介紹了關(guān)于不懂Chromedriver如何配置環(huán)境變量問題解決方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-06-06
pandas 數(shù)據(jù)實(shí)現(xiàn)行間計(jì)算的方法
今天小編就為大家分享一篇pandas 數(shù)據(jù)實(shí)現(xiàn)行間計(jì)算的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-06-06
python list多級(jí)排序知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家分享的是關(guān)于python list多級(jí)排序的相關(guān)知識(shí)點(diǎn)內(nèi)容,有需要的朋友們學(xué)習(xí)下。2019-10-10
如何在Python函數(shù)執(zhí)行前后增加額外的行為
有的時(shí)候會(huì)需要在函數(shù)前后添點(diǎn)額外的功能(比如過濾、計(jì)時(shí)等)時(shí),以前總是首先想到裝飾器。最近學(xué)習(xí)了Python的上下文管理器,所以本文就給大家介紹了如何在Python函數(shù)執(zhí)行前后增加額外的行為,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-10-10
Python利用matplotlib繪制約數(shù)個(gè)數(shù)統(tǒng)計(jì)圖示例
這篇文章主要介紹了Python利用matplotlib繪制約數(shù)個(gè)數(shù)統(tǒng)計(jì)圖,結(jié)合實(shí)例形式詳細(xì)分析了Python使用matplotlib進(jìn)行統(tǒng)計(jì)圖繪制的相關(guān)操作技巧,需要的朋友可以參考下2019-11-11
解決Django Static內(nèi)容不能加載顯示的問題
今天小編就為大家分享一篇解決Django Static內(nèi)容不能加載顯示的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-07-07

