C++借助opencv和Eigen实现人脸识别

来源:互联网 发布:蛟龙号发现了什么知乎 编辑:程序博客网 时间:2024/05/22 01:38

   问题描述: 

                     前段时间实现了一个简单的人脸识别,主要是根据PCA和特征人脸法来实现,具体训练集有20个,如下图,测试集为10个,实现从测试集选一张图片,能够从训练库里匹配出最相近的。


  解决思路:

             这个问题听起来很简单,其实也很简单,只是有些地方有些小麻烦,比如图片的读取,CImage方法实在是太老了,所以我选择的是opencv来读取图片;还有C++对矩阵的运算,包括特征值和特征向量的计算,没有Matlab,Python等方便,当然你也可以选择自己编写函数,但是我选择了Eigen库,可以直接进行矩阵的计算,并且有C++接口。
     关于opencv和Eigen的安装,我这里就不多说了,Eigen还好,opencv一定要注意版本和vs版本对应。可以参考这个教程(vs2010+opencv2.4.10) 点击打开链接

                  具体方案:

                      1)首先读取数据,建立数据库

                      2) 进行PCA降维,构建特征脸

                      3) 选取测试图片,与库进行比较,选取距离最小的图片

代码实现:

首先是读取图片,构建数据集,主要就是把读取的数据转到Eigen的矩阵中:
MatrixXd CreateDataBase(){MatrixXd database = MatrixXd::Zero(20,256*384);for(int i=0;i<20;i++){int count = 0;Mat img=imread("Train/" + Int_to_String(i+1)+".jpg");uchar* p;        for (int j = 0; j < 256; j++)        {            p = img.ptr<uchar>(j);            for (int k = 0; k < 384; k++)            {              database(i,count) = (double)p[k];  count++;         }    }}return database;}
这里的MatrixXd是Eigen的动态矩阵写法,Int_to_String是整形到字符串转换函数,是自己编写的,为了防止有人不清楚,代码也贴出(头文件自行添加,主要是sstream):
string Int_to_String(int n){ostringstream stream;stream<<n;return stream.str();}
下一步就是进行PCA降维,构建特征脸:
void computeCov(MatrixXd &X, MatrixXd &C)  {        //C = X.adjoint() * X;      //C = C.array() / X.rows() - 1;  C = X*X.transpose();C = C/256*384;} void computeEig(MatrixXd &C, MatrixXd &vec, MatrixXd &val)  {      SelfAdjointEigenSolver<MatrixXd> eig(C);        vec = eig.eigenvectors();      val = eig.eigenvalues();  }  MatrixXd Choose(MatrixXd &vec, MatrixXd &val)  {  int count2 =0;int temp[20];for(int i=0;i<20;i++){if(val(i) > 1){   temp[count2]=i;   count2++;}}//cout<<count2++<<endl;//cout<<temp[1]<<endl;MatrixXd Lvec = MatrixXd::Zero(count2,20);for(int j=0;j<20;j++){if(temp[j]>=0){for(int k=0;k<20;k++){Lvec(j,k)=vec(temp[j],k);}}}  return Lvec;}  void CountEigenfaces(MatrixXd &A, MatrixXd &Lvec,MatrixXd &Eigenfaces){Eigenfaces = A.transpose()*Lvec.transpose();}

   关于特征脸,不知道的可以去了解了解,不然代码可能看不懂,是人脸识别比较远古的方法,不过用在这个问题下效果还不错。上面主要进行了协方差,特征值,特征向量的计算,以及计算了特征脸矩阵。

      最后一步就是进行匹配识别,分成几步,首先根据特征脸矩阵把训练集的20张图片转化成低纬度的向量组成的矩阵,然后是对测试集的选取和处理,需要注意的是,数据都要经过预处理,至少要去均值,因为计算协方差的时候默认了数据是去完均值的。
      得到测试集和训练集的特征脸表示后,就可以将测试图片与训练集一一匹配,计算距离,(当然是利用降维后的矩阵进行计算),代码如下:
void ComputeTrainCompare(MatrixXd &A, MatrixXd &Eigenfaces, MatrixXd &traincompare){          traincompare = Eigenfaces.transpose()*A.transpose();} MatrixXd ChooseTestImage(int th){    MatrixXd testimg = MatrixXd::Zero(1,256*384);Mat img=imread("Test/" + Int_to_String(th)+".jpg");uchar* p;int count = 0;    for (int j = 0; j < 256; j++)    {       p = img.ptr<uchar>(j);       for (int k = 0; k < 384; k++)       {          testimg(0,count) = (float)p[k];  count++;   }}return testimg;} void ComputeTestCompare(RowVectorXd &m, MatrixXd &testimg,MatrixXd &Eigenfaces,MatrixXd &testcompare){          MatrixXd testimg2;    testimg2 = normlizationMAX_MIN(testimg,256*384,1);testimg2.rowwise() -=m;testcompare = Eigenfaces.transpose()*testimg.transpose();}int ComputeDistance(MatrixXd &testcompare,MatrixXd &traincompare){      double Distance=0.0; double dis=0.0; double minDistance=INF; cout<<"wuqiong" <<minDistance<<endl; int matchno = 33; for (int i = 0; i < 20; i++) { Distance = 0; for (int j = 0; j < 20; j++) { dis +=(traincompare(j,i)-testcompare(j,0)); cout<<"d : "<<dis<<endl; } Distance = sqrt(dis*1.0); if(Distance < minDistance) {              minDistance = Distance;  matchno = i+1; cout<<minDistance<<"      "<<matchno<<endl; }  }  return matchno;}

至此算法已经结束,最后就是按顺序调用函数进行测试就行了,给出一个Demo实例,仅供参考:
#include "Createdatabase.h"#include "Eigenfaces.h"#include "Recognition.h"#include <iostream> #include <math.h> #include <limits.h> #include <opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> using namespace std;using namespace cv;   #include <Eigen\Dense>using namespace Eigen;#define INF 9999999999void main() { MatrixXd d = MatrixXd::Zero(20,256*384);d = CreateDataBase();MatrixXd x;x = normlizationMAX_MIN(d,256*384,20);MatrixXd meanval = x.colwise().mean();      RowVectorXd meanvecRow = meanval; Normalize(x);MatrixXd c,s,vec,val;computeCov(d, c);computeEig(c,vec,val);MatrixXd L;L = Choose(vec,val);MatrixXd ef;    CountEigenfaces(d,L,ef);MatrixXd traincompare;ComputeTrainCompare(d,ef,traincompare);MatrixXd testm;int a;cout<<"please chosse a test image:  \n"<<endl;cin>>a;testm = ChooseTestImage(int(a));MatrixXd testcomp;ComputeTestCompare(meanvecRow, testm,ef,testcomp);int num;num = ComputeDistance(testcomp,traincompare);cout<<"matching image number is :   "<<num<<endl;    system("pause");}

Demo示例中也给出了头文件,可以进行对照。如果是直接复制代码就想执行的话是行不通的,因为代码分段给出,不太完整,而且依赖于版本,我个人还是建议理解代码,只要理解了就很容易排除问题,毕竟这个问题并不复杂。


原创粉丝点击