一、實驗目的:我這里完成的是,將8張人臉圖片(4組,每組兩張)存入庫中,選取1張圖片,程序識別出與其匹配的另一張。
這里介紹分三個步驟完成該工作,①程序讀取攝像頭、拍照
②程序從電腦文檔中讀取圖片
③檢測人臉,並用紅框框出人臉
④使用感知哈希算法匹配最相似的圖片
二、實驗環境: Win 7(x64)、visual studio 2010、openCV-2.4.3
使用語言:C++
三、實驗准備:①安裝好vs2010,本文不予介紹。
②配置opencv :
1'進入官網下載http://opencv.org/ (OpenCv 2.4x是支持Vs2010的,建議根據自己的Vs版本安裝相應的OpenCv的版本)解壓后,目錄如下:
2'打開vs2010,創建項目,這里以我的工程名test為例。右擊項目->屬性->vc++目錄->包含目錄, 添加三個路徑:(在解壓后opencv下的build目錄)
E:\新建文件夾\opencv\build\include
E:\新建文件夾\opencv\build\include\opencv
E:\新建文件夾\opencv\build\include\opencv2
然后點擊庫目錄,添加路徑:E:\新建文件夾\opencv\build\x86\vc10\lib
配置好了后,第二步,點擊鏈接器->輸入->附加依賴項,編輯添加,把E:\新建文件夾\opencv\build\x86\vc10\bin里面左右的文件名稱全導入進去,導入帶d的文件,比如有opencv_calib3d243.dll與opencv_calib3d243d.dll文件,只需添加后者帶d的即可。
至此,openCV配置已經全部完成。接下來編寫代碼。
四、編寫代碼
①為了收集圖片庫,我編寫了直接拍照的功能,按下p進行拍照,並自動命名排序存到文件中。
#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; int main( int argc, char** argv ) { int i=1;//照片名從1開始命名 while(1) { CvCapture *pCapture=cvCreateCameraCapture(-1);//打開攝像頭 Mat image=cvQueryFrame(pCapture); //將攝像頭拍的以圖片形式展現 cvNamedWindow("frame",1); //命名一個窗口 imshow("frame",image); //在窗口顯示圖片 string filename=format("C:\\images\\%d.jpg",i); char key=waitKey(100); switch(key) { case'p': i++; //鍵盤上每按一次p,就拍一張照 imwrite(filename,image); //寫入圖片 imshow("photo",image); //展示照片 waitKey(500); destroyWindow("photo"); break; default:break; } } }
實驗效果:
②讀取圖片,顯示在屏幕上
Mat image=imread("C:\\images\\2.jpg",-1); //使用Mat創建一個對象,-1為打開類型的參數 cvNamedWindow("圖像顯示",1); //命名一個窗口 ,1為顯示參數 imshow("圖像顯示",image); cvWaitKey(0); //很重要,不寫讀不出來
③將讀取的圖片進行檢測出人臉。
首先找到opencv目錄下,識別人臉的類的文件(包裝在data路徑下)
Haarcascades_frontalface_default 包裝的是 檢測人臉,將這個文件拷貝到你的vs工程下。
#include <iostream> #include <string> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/gpu/gpu.hpp> using namespace cv; using namespace std; string xmlPath="C:\\Users\\yu\\Documents\\Visual Studio 2010\\Projects\\加我南\\haarcascade_frontalface_default.xml"; //xmlpath 字符串記錄那個.xml文件的路徑 void detectAndDisplay(Mat image); int main( int argc,char**argv ) { string path="C:\\Users\\yu\\Documents\\Visual Studio 2010\\Projects\\test\\images\\1.jpg";//以檢測圖片1.jpg為例 Mat image =imread(path,-1); CascadeClassifier a; //創建臉部對象 if(!a.load(xmlPath)) //如果讀取文件不出錯,則檢測人臉 { cout<<"無法加載xml文件"<<endl; return 0; } detectAndDisplay(image);// 檢測人臉 return 0; } void detectAndDisplay(Mat image) { CascadeClassifier ccf; //創建臉部對象 ccf.load(xmlPath); //導入opencv自帶檢測的文件 vector<Rect> faces; Mat gray; cvtColor(image,gray,CV_BGR2GRAY); equalizeHist(gray,gray); ccf.detectMultiScale(gray,faces,1.1,3,0,Size(50,50),Size(500,500)); for(vector<Rect>::const_iterator iter=faces.begin();iter!=faces.end();iter++) { rectangle(image,*iter,Scalar(0,0,255),2,8); //畫出臉部矩形 } Mat image1; for(size_t i=0;i<faces.size();i++) { Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2); image1= image(Rect(faces[i].x, faces[i].y, faces[i].width, faces[i].height)); } imshow("1",image); imshow("2",image1); cvWaitKey(0); }
實驗結果如下:
④進行人臉檢測
opencv的FaceRecogizer目前有三個類實現了它,特征臉和fisherface方法最少訓練圖像為兩張,而LBP可以單張圖像訓練,LBP方法原理:
引用:http://blog.csdn.net/lsq2902101015/article/details/49717441
因此,我想到了與LBP算法類似的感知哈希算法。
說下原理過程:
(1)縮小尺寸:去除高頻和細節的最快方法是縮小圖片,將圖片縮小到8x8的尺寸,共64個像素。不用保持縱橫比,只需將其變成8x8的正方形,這樣就可以摒棄不同尺寸、比例帶來的圖片差異
(2)簡化色彩:將8x8的小圖片轉換成灰度圖像。
(3)計算平均值:計算所有64個像素的灰度平均值
(4)比較像素的灰度:將每個像素的灰度,與平均值進行比較,大於或等於就記為1,小於均值,就記為0;
(5)計算hash值,將上面的比價比較結果組合在一起,構成了64位的哈希值,就是這張圖片的指紋。
(6)通過比較兩張圖片的哈希值,計算差值得到漢明距離。漢明距離越小,說明兩張圖片越相似。
引用:http://blog.csdn.net/zouxy09/article/details/17471401
#include <iostream> #include <string> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/gpu/gpu.hpp> using namespace cv; string xmlPath="C:\\Users\\yu\\Documents\\Visual Studio 2010\\Projects\\加我南\\haarcascade_frontalface_default.xml"; string HashValue(Mat &src) //得到圖片的哈希值 //很久之前寫的,現在想不起來了...注釋就先不寫了.....抱歉哈。但是是可以運行的 { string rst(64,'\0'); Mat img; if(src.channels()==3) cvtColor(src,img,CV_BGR2GRAY); else img=src.clone(); resize(img,img,Size(8,8)); uchar *pData; for(int i=0;i<img.rows;i++) { pData=img.ptr<uchar>(i); for(int j=0;j<img.cols;j++) { pData[j]=pData[j]/4; } } int average=mean(img).val[0]; Mat mask=(img>=(uchar)average); int index=0; for(int i=0;i<mask.rows;i++) { pData = mask.ptr<uchar>(i); for(int j=0;j<mask.cols;j++) { if(pData[j]==0) rst[index++]='0'; else rst[index++]='1'; } } return rst; } int HanmingDistance(string &str1,string &str2) //求兩張圖片的漢明距離 { if((str1.size()!=64)||(str2.size()!=64)) return -1; int diff=0; for(int i=0;i<64;i++) { if(str1[i]!=str2[i]) diff++; } return diff; } void detectAndDisplay(Mat image) { CascadeClassifier ccf; ccf.load(xmlPath); vector<Rect> faces; Mat gray; cvtColor(image,gray,CV_BGR2GRAY); equalizeHist(gray,gray); ccf.detectMultiScale(gray,faces,1.1,3,0,Size(50,50),Size(500,500)); for(vector<Rect>::const_iterator iter=faces.begin();iter!=faces.end();iter++) { rectangle(image,*iter,Scalar(0,0,255),2,8); //畫出臉部矩形 } for(size_t i=0;i<faces.size();i++) { Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2); image= image(Rect(faces[i].x, faces[i].y, faces[i].width, faces[i].height)); } } //識別並截取人臉 int main( int argc, char** argv ) { using std::cout; using std::endl; using std::cin; cout<<"請輸入想要選擇的圖片"<<endl; int a,x,i; int diff[9]; cin>>a; const string path1=format("C:\\Users\\yu\\Documents\\Visual Studio 2010\\Projects\\test\\images\\%d.jpg",a); Mat image1,image2; image1=imread(path1,-1); string str1,str2,path2; cvNamedWindow("選擇的圖片",1); /*cvResizeWindow("選擇的圖片",700,500);*/ imshow("選擇的圖片",image1); detectAndDisplay(image1); str1=HashValue(image1); cvWaitKey(0); for(i=1;i<=8;i++)//因為我完成的就是8張圖片的檢測,所以循環值為8 { path2=format("C:\\Users\\yu\\Documents\\Visual Studio 2010\\Projects\\test\\images\\%d.jpg",i); image2=imread(path2,-1); detectAndDisplay(image2); str2=HashValue(image2); diff[i]=HanmingDistance(str1,str2); } int min=1000,t; for(i=1;i<=8;i++) //循環值為8,求與原圖片漢明距離最小的那張圖片 { if(min>diff[i]&&diff[i]!=0) { min=diff[i]; t=i;} //檢測出的標記為t } path2=format("C:\\Users\\yu\\Documents\\Visual Studio 2010\\Projects\\test\\images\\%d.jpg",t); image2=imread(path2,-1);//將圖片t顯示出來 cvNamedWindow("相似的圖片",1); imshow("相似的圖片",image2);//這時顯示的就是最相似的照片 cvWaitKey(0); cin.get(); //吃掉回車符 }
實驗結果如下:
我的照片庫如下:
至此,簡單地完成了檢測並且識別人臉的功能。