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

   问题描述: 

                     前段时间实现了一个简单的人脸识别,主要是根据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 9999999999



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


  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值