1. 程式人生 > >openCV人臉識別三種演算法實現(官網翻譯)

openCV人臉識別三種演算法實現(官網翻譯)



怎樣使用OpenCV進行人臉識別

友情提示,要看懂程式碼前,你得先知道OpenCV的安裝和配置,會用C++,用過一些OpenCV函式。基本的影象處理和矩陣知識也是需要的。[gm:我是簫鳴的註釋]由於我僅僅是翻譯,對於六級才過的我,肯定有一些翻譯錯的或者不當的地方,所以請大家糾錯。

OpenCV2.4開始,加入了新的類FaceRecognizer,我們可以使用它便捷地進行人臉識別實驗。本文既介紹程式碼使用,又介紹演算法原理。(他寫的原始碼,我們可以在OpenCVopencv\modules\contrib\doc\facerec\src下找到,當然也可以在他的github中找到,如果你想研究原始碼,自然可以去看看,不復雜

)

目前支援的演算法有

下面所有的例子中的程式碼在OpenCV安裝目錄下的samples/cpp下面都能找到,所有的程式碼商用或者學習都是免費的。

對人類來說,人臉識別很容易。文獻[Tu06]告訴我們,僅僅是才三天的嬰兒已經可以區分周圍熟悉的人臉了。那麼對於計算機來說,到底有多難?其實,迄今為止,我們對於人類自己為何可以區分不同的人所知甚少。是人臉內部特徵(眼睛、鼻子、嘴巴)還是外部特徵(頭型、髮際線)對於人類識別更有效?我們怎麼分析一張影象,大腦是如何對它編碼的?David HubelTorsten Wiesel向我們展示,我們的大腦針對不同的場景,如線、邊、角或者運動這些區域性特徵有專門的神經細胞作出反應。顯然我們沒有把世界看成零散的塊塊,我們的視覺皮層必須以某種方式把不同的資訊來源轉化成有用的模式。自動人臉識別就是如何從一幅影象中提取有意義的特徵,把它們放入一種有用的表示方式,然後對他們進行一些分類。基於幾何特徵的人臉的人臉識別可能是最直觀的方法來識別人臉。第一個自動人臉識別系統在

[Kanade73]中又描述:標記點(眼睛、耳朵、鼻子等的位置)用來構造一個特徵向量(點與點之間的距離、角度等)。通過計算測試和訓練影象的特徵向量的歐氏距離來進行識別。這樣的方法對於光照變化很穩健,但也有巨大的缺點:標記點的確定是很複雜的,即使是使用最先進的演算法。一些幾何特徵人臉識別近期工作在文獻[Bru92]中有描述。一個22維的特徵向量被用在一個大資料庫上,單靠幾何特徵不能提供足夠的資訊用於人臉識別。

特徵臉方法在文獻[TP91]中有描述,他描述了一個全面的方法來識別人臉:面部影象是一個點,這個點是從高維影象空間找到它在低維空間的表示,這樣分類變得很簡單。低維子空間低維是使用主元分析(Principal Component Analysis,PCA)

找到的,它可以找擁有最大方差的那個軸。雖然這樣的轉換是從最佳重建角度考慮的,但是他沒有把標籤問題考慮進去。[gm:讀懂這段需要一些機器學習知識]。想象一個情況,如果變化是基於外部來源,比如光照。軸的最大方差不一定包含任何有鑑別性的資訊,因此此時的分類是不可能的。因此,一個使用線性鑑別(Linear Discriminant Analysis,LDA)的特定類投影方法被提出來解決人臉識別問題[BHK97]。其中一個基本的想法就是,使類內方差最小的同時,使類外方差最大。

近年來,各種區域性特徵提取方法出現。為了避免輸入的影象的高維資料,僅僅使用的區域性特徵描述影象的方法被提出,提取的特徵(很有希望的)對於區域性遮擋、光照變化、小樣本等情況更強健。有關區域性特徵提取的方法有蓋伯小波(Gabor Waelets)([Wiskott97],離散傅立葉變換(Discrete Cosinus Transform,DCT)([Messer06],區域性二值模式(Local Binary Patterns,LBP)([AHP04])。使用什麼方法來提取時域空間的區域性特徵依舊是一個開放性的研究問題,因為空間資訊是潛在有用的資訊。

我們先獲取一些資料來進行實驗吧。我不想在這裡做一個幼稚的例子。我們在研究人臉識別,所以我們需要一個真的人臉影象!你可以自己建立自己的資料集,也可以從這裡(http://face-rec.org/databases/)下載一個。

AT&T Facedatabase又稱ORL人臉資料庫,40個人,每人10張照片。照片在不同時間、不同光照、不同表情(睜眼閉眼、笑或者不笑)、不同人臉細節(戴眼鏡或者不戴眼鏡)下采集。所有的影象都在一個黑暗均勻的背景下采集的,正面豎直人臉(有些有有輕微旋轉)

Yale Facedatabase A ORL資料庫對於初始化測試比較適合,但它是一個簡單的資料庫,特徵臉已經可以達到97%的識別率,所以你使用其他方法很難得到更好的提升。Yale人臉資料庫是一個對於初始實驗更好的資料庫,因為識別問題更復雜。這個資料庫包括15個人(14個男人,1個女人),每一個都有11個灰度影象,大小是320*243畫素。資料庫中有光照變化(中心光照、左側光照、右側光照)、表情變化(開心、正常、悲傷、瞌睡、驚訝、眨眼)、眼鏡(戴眼鏡或者沒戴)

壞訊息是它不可以公開下載,可能因為原來的伺服器壞了。但我們可以找到一些映象(比如 the MIT)但我不能保證它的完整性。如果你需要自己剪裁和校準影象,可以閱讀我的筆記(bytefish.de/blog/fisherfaces)

Extended Yale Facedatabase B 此資料庫包含38個人的2414張圖片,並且是剪裁好的。這個資料庫重點是測試特徵提取是否對光照變化強健,因為影象的表情、遮擋等都沒變化。我認為這個資料庫太大,不適合這篇文章的實驗,我建議使用ORL資料庫。

一.一.1. 準備資料

我們從網上下了資料,下了我們需要在程式中讀取它,我決定使用CSV檔案讀取它。一個CSV檔案包含檔名,緊跟一個標籤。

/path/to/image.ext;0

假設/path/to/image.ext是影象,就像你在windows下的c:/faces/person0/image0.jpg。最後我們給它一個標籤0。這個標籤類似代表這個人的名字,所以同一個人的照片的標籤都一樣。我們對下載的ORL資料庫進行標識,可以獲取到如下結果:

./at/s1/1.pgm;0

./at/s1/2.pgm;0

...

./at/s2/1.pgm;1

./at/s2/2.pgm;1

...

./at/s40/1.pgm;39

./at/s40/2.pgm;39

想象我已經把影象解壓縮在D:/data/at下面,而CSV檔案在D:/data/at.txt。下面你根據自己的情況修改替換即可。一旦你成功建立CSV檔案,就可以像這樣執行示例程式:

facerec_demo.exe D:/data/at.txt

1.3.2 Creating the CSV File

你不需要手工來建立一個CSV檔案,我已經寫了一個Python程式來做這事。

[gm:說一個我實現的方法

如果你會cmd命令,或者稱DOS命令,那麼你開啟命令控制檯。假設我們的圖片放在J:下的Faces資料夾下,可以輸入如下語句:

J:\Faces\ORL>dir /b/s *.bmp > at.txt

然後你開啟at.txt檔案可能看到如下內容(後面的01..標籤是自己加的)

。。。。

J:\Faces\ORL\s1\1.bmp;0

J:\Faces\ORL\s1\10.bmp;0

J:\Faces\ORL\s1\2.bmp;0

J:\Faces\ORL\s1\3.bmp;0

J:\Faces\ORL\s1\4.bmp;0

J:\Faces\ORL\s1\5.bmp;0

J:\Faces\ORL\s1\6.bmp;0

J:\Faces\ORL\s1\7.bmp;0

J:\Faces\ORL\s1\8.bmp;0

J:\Faces\ORL\s1\9.bmp;0

J:\Faces\ORL\s10\1.bmp;1

J:\Faces\ORL\s10\10.bmp;1

J:\Faces\ORL\s10\2.bmp;1

J:\Faces\ORL\s10\3.bmp;1

J:\Faces\ORL\s10\4.bmp;1

J:\Faces\ORL\s10\5.bmp;1

J:\Faces\ORL\s10\6.bmp;1

。。。。

自然還有c++程式設計等方法可以做得更好,看這篇文章反響,如果很多人需要,我就把這部分的程式碼寫出來。(遍歷多個資料夾,標上標籤)

]

特徵臉Eigenfaces

我們講過,影象表示的問題是他的高維問題。二維灰度影象p*q大小,是一個m=qp維的向量空間,所以一個100*100畫素大小的影象就是10000維的影象空間。問題是,是不是所有的維數空間對我們來說都有用?我們可以做一個決定,如果資料有任何差異,我們可以通過尋找主元來知道主要資訊。主成分分析(Principal Component Analysis,PCA)Karl Pearson(1901)獨立發表的,而Harold Hotelling(1933)把一些可能相關的變數轉換成一個更小的不相關的子集。想法是,一個高維資料集經常被相關變量表示,因此只有一些的維上資料才是有意義的,包含最多的資訊。PCA方法尋找資料中擁有最大方差的方向,被稱為主成分。

表示一個隨機特徵,其中.

1. 計算均值向量

2. 計算協方差矩陣S

3. 計算的特徵值和對應的特徵向量

4. 對特徵值進行遞減排序,特徵向量和它順序一致. K個主成分也就是k個最大的特徵值對應的特徵向量。

xK個主成份:

其中11.

PCA基的重構:

其中.

然後特徵臉通過下面的方式進行人臉識別:

A. 把所有的訓練資料投影到PCA子空間

B. 把待識別影象投影到PCA子空間

C. 找到訓練資料投影后的向量和待識別影象投影后的向量最近的那個。

還有一個問題有待解決。比如我們有400張圖片,每張100*100畫素大小,那麼PCA需要解決協方差矩陣的求解,而X的大小是10000*400,那麼我們會得到10000*10000大小的矩陣,這需要大概0.8GB的記憶體。解決這個問題不容易,所以我們需要另一個計策。就是轉置一下再求,特徵向量不變化。文獻[Duda01]中有描述。

[gm:這個PCA還是自己搜著看吧,這裡的講的不清楚,不適合初學者看]

給出示例程式原始碼

#include "opencv2/core/core.hpp"

#include "opencv2/contrib/contrib.hpp"

#include "opencv2/highgui/highgui.hpp"

#include <iostream>

#include <fstream>

#include <sstream>

usingnamespacecv;

usingnamespacestd;

staticMatnorm_0_255(InputArray_src){

Matsrc=_src.getMat();

// 建立和返回一個歸一化後的影象矩陣:

Matdst;

switch(src.channels()){

case1:

cv::normalize(_src,dst,0,255,NORM_MINMAX,CV_8UC1);

break;

case3:

cv::normalize(_src,dst,0,255,NORM_MINMAX,CV_8UC3);

break;

default:

src.copyTo(dst);

break;

}

returndst;

}

//使用CSV檔案去讀影象和標籤,主要使用stringstream和getline方法

staticvoidread_csv(conststring&filename,vector<Mat>&images,vector<int>&labels,charseparator=';'){

std::ifstreamfile(filename.c_str(),ifstream::in);

if(!file){

stringerror_message="No valid input file was given, please check the given filename.";

CV_Error(CV_StsBadArg,error_message);

}

stringline,path,classlabel;

while(getline(file,line)){

stringstreamliness(line);

getline(liness,path,separator);

getline(liness,classlabel);

if(!path.empty()&&!classlabel.empty()){

images.push_back(imread(path,0));

labels.push_back(atoi(classlabel.c_str()));

}

}

}

intmain(intargc,constchar*argv[]){

// 檢測合法的命令,顯示用法

// 如果沒有引數輸入則退出!.

if(argc<2){

cout<<"usage: "<<argv[0]<<" <csv.ext> <output_folder> "<<endl;

exit(1);

}

stringoutput_folder;

if(argc==3){

output_folder=string(argv[2]);

}

//讀取你的CSV檔案路徑.

stringfn_csv=string(argv[1]);

// 2個容器來存放影象資料和對應的標籤

vector<Mat>images;

vector<int>labels;

// 讀取資料如果檔案不合法就會出錯

// 輸入的檔名已經有了.

try{

read_csv(fn_csv,images,labels);

}catch(cv::Exception&e){

cerr<<"Error opening file \""<<fn_csv<<"\". Reason: "<<e.msg<<endl;

// 檔案有問題,我們啥也做不了了,退出了

exit(1);

}

// 如果沒有讀取到足夠圖片,我們也得退出.

if(images.size()<=1){

stringerror_message="This demo needs at least 2 images to work. Please add more images to your data set!";

CV_Error(CV_StsError,error_message);

}

// 得到第一張照片的高度在下面對影象

// 變形到他們原始大小時需要

intheight=images[0].rows;

// 下面的幾行程式碼僅僅是從你的資料集中移除最後一張圖片

//[gm:自然這裡需要根據自己的需要修改,他這裡簡化了很多問題]

MattestSample=images[images.size()-1];

inttestLabel=labels[labels.size()-1];

images.pop_back();

labels.pop_back();

// 下面幾行建立了一個特徵臉模型用於人臉識別,

// 通過CSV檔案讀取的影象和標籤訓練它。

// T這裡是一個完整的PCA變換

//如果你只想保留10個主成分,使用如下程式碼

//      cv::createEigenFaceRecognizer(10);

//

// 如果你還希望使用置信度閾值來初始化,使用以下語句:

//      cv::createEigenFaceRecognizer(10, 123.0);

//

// 如果你使用所有特徵並且使用一個閾值,使用以下語句:

//      cv::createEigenFaceRecognizer(0, 123.0);

//

Ptr<FaceRecognizer>model=createEigenFaceRecognizer();

model->train(images,labels);

// 下面對測試影象進行預測,predictedLabel是預測標籤結果

intpredictedLabel=model->predict(testSample);

//

// 還有一種呼叫方式,可以獲取結果同時得到閾值:

//      int predictedLabel = -1;

//      double confidence = 0.0;

//      model->predict(testSample, predictedLabel, confidence);

//

stringresult_message=format("Predicted class = %d / Actual class = %d.",predictedLabel,testLabel);

cout<<result_message<<endl;

// 這裡是如何獲取特徵臉模型的特徵值的例子,使用了getMat方法:

Mateigenvalues=model->getMat("eigenvalues");

// 同樣可以獲取特徵向量:

MatW=model->getMat("eigenvectors");

// 得到訓練影象的均值向量

Matmean=model->getMat("mean");

// 現實還是儲存:

if(argc==2){

imshow("mean",norm_0_255(mean.reshape(1,images[0].rows)));

}else{

imwrite(format("%s/mean.png",output_folder.c_str()),norm_0_255(mean.reshape(1,images[0].rows)));

}

// 現實還是儲存特徵臉:

for(inti=0;i<min(10,W.cols);i++){

stringmsg=format("Eigenvalue #%d = %.5f",i,eigenvalues.at<double>(i));

cout<<msg<<endl;

// 得到第 #i個特徵

Matev=W.col(i).clone();

//把它變成原始大小,為了把資料顯示歸一化到0~255.

Matgrayscale=norm_0_255

相關推薦

openCV人臉識別演算法實現翻譯

 怎樣使用OpenCV進行人臉識別 友情提示,要看懂程式碼前,你得先知道OpenCV的安裝和配置,會用C++,用過一些OpenCV函式。基本的影象處理和矩陣知識也是需要的。[gm:我是簫鳴的註釋]由於我僅僅是翻譯,對於六級才過的我,肯定有一些翻譯錯的或

OpenCV邊緣檢測演算法canny、sobel、laplacian

Canny演算法 #include<opencv2\opencv.hpp> #include<opencv2\highgui\highgui.hpp> using namespace std; using namespace cv; //邊緣檢測 int mai

最短路和差分約束演算法實現 Til the Cows Come Home

題目訓練連結(密碼hpuacm): https://vjudge.net/contest/246705 我會分別用 迪傑斯特拉  優先佇列和鏈式前向星優化過的迪傑斯特拉  SPFA演算法 三種方法講一下例題。 此外上述三種演算法是求單源最短路問題, 這裡還會

【轉載】快速排序(演算法實現和非遞迴實現)

原文地址 python實現: import random a = [4,1,7,6,9,2,2,3,5,7,8,9,3,1,2,3,4,5,8,0,3,5] b = [4,1,7,6,9,2,8,0,3,5] def twoPointerSort(nums,le

基於Qt的OpenCV人臉識別()

上篇完成了Qt呼叫OpenCV的攝像頭,接下來要做的是基於拍攝到的圖片進行人臉檢測。使用OpenCV的級聯檢測器HAAR人臉檢測,首先在Qt的初始化函式中載入人臉模型: //初始化 void Widget::Init() { //載入人臉檢測模型 if(!faceDetect

快速排序(演算法實現和非遞迴實現)

快速排序(Quick Sort)是對氣泡排序的一種改進,基本思想是選取一個記錄作為樞軸,經過一趟排序,將整段序列分為兩個部分,其中一部分的值都小於樞軸,另一部分都大於樞軸。然後繼續對這兩部分繼續進行排序,從而使整個序列達到有序。 遞迴實現: void Q

A*演算法實現圖形化表示——C++描述

概要   A*演算法是一種啟發式尋路演算法,BFS是一種盲目的無目標的搜尋演算法,相比於BFS,A*演算法根據適應度構建優先佇列,根據適應度值可以很好的向目標點移動,具體詳情,請看搜尋相關文件,我在只是實現了在無障礙的情況下的A*演算法,有障礙的情況類似。 開發環境   visual studio 20

通俗理解卡爾曼濾波及其演算法實現帶例項解析

1.簡介(Brief Introduction) 在學習卡爾曼濾波器之前,首先看看為什麼叫“卡爾曼”。跟其他著名的理論(例如傅立葉變換,泰勒級數等等)一樣,卡爾曼也是一個人的名字,而跟他們不同的是,他是個現代人! 卡爾曼全名Rudolf Emil Kalman,匈牙利數學家,1930年出生於

Reduce端join演算法實現 - 訂單跟商品

程式碼地址: https://gitee.com/tanghongping/hadoopMapReduce/tree/master/src/com/thp/bigdata/rjon 現在有兩張表 1.訂單表 2.商品表 訂單資料表t_order: id

啟用pycharm!方法!2018-11-25

方法一:查詢啟用碼(註冊碼) 這樣要浪費很多時間,百度一下你會發現:很多啟用碼要麼過期了要麼被官方遮蔽了; 谷歌一下就知道啟用找不到這樣一大串啟用碼。 放棄這種愚蠢的方法,儘管我之前就是這種方法的!   方法二:申請官方學生使用授權 這種方法的前提是得有EDU

素數求法總結 素數詳解

#include<stdio.h> #include<string.h> #include<math.h> // 第一種 利用素數 分佈規律 int ispri

梯度下降法的形式BGD批量梯度下降、SGD隨機梯度下降以及MBGD小批量梯度下降

在應用機器學習演算法時,我們通常採用梯度下降法來對採用的演算法進行訓練。其實,常用的梯度下降法還具體包含有三種不同的形式,它們也各自有著不同的優缺點。   下面我們以線性迴歸演算法來對三種梯度下降法進行比較。   一般線性迴歸函式的假設函式為:          

Tomcat Connector執行模式BIO, NIO, APR的比較和優化

                  Java Blocking Connector   Java Nio Blocking Connector   APR/native Connector                              BIO                         NIO

看懂二叉樹的遍歷比較容易理解

轉載:http://blog.csdn.net/soundwave_/article/details/53120766二叉樹的遍歷分為以下三種:先序遍歷:遍歷順序規則為【根左右】中序遍歷:遍歷順序規則為【左根右】後序遍歷:遍歷順序規則為【左右根】什麼是【根左右】?就是先遍歷根

VMware虛擬機器聯網方式圖文詳細解說

VMware三種網路模式聯網 首先說一下VMware的幾個虛擬裝置 安裝了VMware虛擬機器後,會在網路連線對話方塊中多出兩個虛擬網絡卡,如圖: VMnet0:用於虛擬橋接網路下的虛擬交換機 VMnet1:用於虛擬Host-Only網路下的虛擬交換機 VMnet8:用於

python實戰之IO多路複用別名:事件驅動,模式:sellect,poll,epoll,Python的selectors模組

IO多路複用前需瞭解 通常,我們寫伺服器處理模型的程式時,有以下幾種模型: (1)每收到一個請求,建立一個新的程序,來處理該請求; (2)每收到一個請求,建立一個新的執行緒,來處理該請求; (3)每收到一個請求,放入一個事件列表,讓主程序通過非阻塞I/O方式來處理請求 上面的幾種

pollard's p-1演算法實現使用GMP庫

pollard’s p-1演算法實現(使用GMP庫) 隔了好久,今天終於更新了自己的部落格。前面偷懶有點過分了,我會陸續把之前積累的一點東西放到部落格上來 演算法描述 The basic algorithm can be written as foll

設計模式-工廠模式-附Java原始碼

建議結合示例原始碼理解 1 簡單工廠模式 簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它可以根據引數的不同返回不同類的例項,被建立的例項通常都具有共同的父類。因為在簡單工廠模式中用於建立例項的方法是靜態(

UITableView設定單元格選中後只顯示一個打勾的簡單方法僅供參考

1、第一種方法:先定位到最後一行,若選中最後一行直接退出,否則用遞迴改變上次選中的狀態,重新設定本次選中的狀態。 - (UITableViewCell*)tableView:(UITableView*)tableViewcellForRowAtIndexPath:(NSIn

Prim演算法實現詳細偽碼

S集存放最小生成樹的頂點。 lowcost[j] :j->s集的最小權值 closest[j]: j在S集中的鄰接頂點 T集合:存放最短路 struct enode{     int weight;//邊權     int u,v;//相關聯的2點 }; 實現要點: