OpenCV中人脸识别代码实现

声明:本文代码来源于http://www.cognotics.com/opencv/servo_2007_series/,实现平台为Linux+OpenCV,共分为两部分:人脸检测与人脸识别。本文为后半部分的代码,关于第一部分请参见http://blog.csdn.net/liudekuan/article/details/8560251。不多言,以下给出代码及相关注解

#include <stdio.h>
#include <string.h>
#include "cv.h"
#include "cvaux.h"
#include "highgui.h"

using namespace cv;

//globle variables
int nTrainFaces 		= 0;	// number of trainning images
int nEigens 			= 0;	// number of eigenvalues
IplImage** faceImgArr 		= 0; 	// array of face images
CvMat* personNumTruthMat 	= 0;	// array of person numbers
IplImage* pAvgTrainImg 		= 0;	// the average image
IplImage** eigenVectArr 	= 0;	// eigenvectors
CvMat* eigenValMat 		= 0;	// eigenvalues
CvMat* projectedTrainFaceMat 	= 0;	// projected training faces

 Function prototypes
void learn();
void recognize();
void doPCA();
void storeTrainingData();
int loadTrainingData(CvMat** pTrainPersonNumMat);
int findNearestNeighbor(float* projectedTestFace);
int loadFaceImgArray(char* filename);
void printUsage();

int main( int argc, char** argv )
{
    if((argc != 2) && (argc != 3)){
	printUsage();
	return -1;
    }

    if( !strcmp(argv[1], "train" )){
	learn();
    } else if( !strcmp(argv[1], "test") ){
	recognize();
    } else {
	printf("Unknown command: %s\n", argv[1]);
    }
    return 0;
}

void printUsage(){
    printf("Usage: eigenface <command>\n",
	"  Valid commands are\n"
	"    train\n"
	"    test\n"
    );
}

void learn(){
    int i;

    // load training data
    nTrainFaces = loadFaceImgArray("train.txt");
    if( nTrainFaces < 2){
	fprintf(
	    stderr,
	    "Need 2 or more training faces\n"
  	    "Input file contains only %d\n",
	    nTrainFaces   
	);
	return;
    }

    // do PCA on the training faces
    doPCA();

    // project the training images onto the PCA subspace
    projectedTrainFaceMat = cvCreateMat(nTrainFaces, nEigens, CV_32FC1);
    for(i = 0; i < nTrainFaces; i ++){
	cvEigenDecomposite(
	    faceImgArr[i],
	    nEigens,
	    eigenVectArr,
	    0, 0,
	    pAvgTrainImg,
	    projectedTrainFaceMat->data.fl + i*nEigens
	);
    }

    // store the recognition data as an xml file
    storeTrainingData();
}

int loadFaceImgArray(char* filename){
    FILE* imgListFile = 0;
    char imgFilename[512];
    int iFace, nFaces = 0;

    // open the input file
    imgListFile = fopen(filename, "r");
    
    // count the number of faces
    while( fgets(imgFilename, 512, imgListFile) ) ++ nFaces;
    rewind(imgListFile);

    // allocate the face-image array and person number matrix
    faceImgArr = (IplImage **)cvAlloc( nFaces*sizeof(IplImage *) );
    personNumTruthMat = cvCreateMat( 1, nFaces, CV_32SC1 );

    // store the face images in an array
    for(iFace=0; iFace<nFaces; iFace++){
	//read person number and name of image file
  	fscanf(imgListFile, "%d %s", personNumTruthMat->data.i+iFace, imgFilename);

	// load the face image
	faceImgArr[iFace] = cvLoadImage(imgFilename, CV_LOAD_IMAGE_GRAYSCALE);
    }

    fclose(imgListFile);

    return nFaces;
}

void doPCA(){
    int i;
    CvTermCriteria calcLimit;
    CvSize faceImgSize;

    // set the number of eigenvalues to use
    nEigens = nTrainFaces - 1;

    // allocate the eigenvector images
    faceImgSize.width = faceImgArr[0]->width;
    faceImgSize.height = faceImgArr[0]->height;
    eigenVectArr = (IplImage**)cvAlloc(sizeof(IplImage*) * nEigens);
    for(i=0; i<nEigens; i++){
	eigenVectArr[i] = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1);
    }

    // allocate the eigenvalue array
    eigenValMat = cvCreateMat( 1, nEigens, CV_32FC1 );

    // allocate the averaged image
    pAvgTrainImg = cvCreateImage(faceImgSize, IPL_DEPTH_32F, 1);

    // set the PCA termination criterion
    calcLimit = cvTermCriteria( CV_TERMCRIT_ITER, nEigens, 1);

    // compute average image, eigenvalues, and eigenvectors
    cvCalcEigenObjects(
	nTrainFaces,
	(void*)faceImgArr,
	(void*)eigenVectArr,
	CV_EIGOBJ_NO_CALLBACK,
	0,
	0,
	&calcLimit,
	pAvgTrainImg,
	eigenValMat->data.fl
    );
}

void storeTrainingData(){
    CvFileStorage* fileStorage;
    int i;
   
    // create a file-storage interface
    fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_WRITE);

    // store all the data
    cvWriteInt( fileStorage, "nEigens", nEigens);
    cvWriteInt( fileStorage, "nTrainFaces", nTrainFaces );
    cvWrite(fileStorage, "trainPersonNumMat", personNumTruthMat, cvAttrList(0, 0));
    cvWrite(fileStorage, "eigenValMat", eigenValMat, cvAttrList(0,0));
    cvWrite(fileStorage, "projectedTrainFaceMat", projectedTrainFaceMat, cvAttrList(0,0));
    cvWrite(fileStorage, "avgTrainImg", pAvgTrainImg, cvAttrList(0,0));

    for(i=0; i<nEigens; i++){
	char varname[200];
	sprintf( varname, "eigenVect_%d", i);
	cvWrite(fileStorage, varname, eigenVectArr[i], cvAttrList(0,0));
    }

    //release the file-storage interface
    cvReleaseFileStorage( &fileStorage );
}

void recognize(){
    int i, nTestFaces = 0;		// the number of test images
    CvMat* trainPersonNumMat = 0;	// the person numbers during training    
    float* projectedTestFace = 0;	

    // load test images and ground truth for person number
    nTestFaces = loadFaceImgArray("test.txt");    
    printf("%d test faces loaded\n", nTestFaces);
    
    // load the saved training data
    if( !loadTrainingData( &trainPersonNumMat ) ) return;

    // project the test images onto the PCA subspace
    projectedTestFace = (float*)cvAlloc( nEigens*sizeof(float) );
    for(i=0; i<nTestFaces; i++){
	int iNearest, nearest, truth;

 	// project the test image onto PCA subspace
	cvEigenDecomposite(
	    faceImgArr[i],
	    nEigens,
	    eigenVectArr,
            0, 0,
	    pAvgTrainImg,
	    projectedTestFace
	);

	iNearest = findNearestNeighbor(projectedTestFace);
	truth = personNumTruthMat->data.i[i];
	nearest = trainPersonNumMat->data.i[iNearest];

	printf("nearest = %d, Truth = %d\n", nearest, truth);
    }
}

int loadTrainingData(CvMat** pTrainPersonNumMat){
    CvFileStorage* fileStorage;
    int i;
 
    // create a file-storage interface
    fileStorage = cvOpenFileStorage( "facedata.xml", 0, CV_STORAGE_READ );
    if( !fileStorage ){
	fprintf(stderr, "Can't open facedata.xml\n");
	return 0;
    }

    nEigens = cvReadIntByName(fileStorage, 0, "nEigens", 0);
    nTrainFaces = cvReadIntByName(fileStorage, 0, "nTrainFaces", 0);
    *pTrainPersonNumMat = (CvMat*)cvReadByName(fileStorage, 0, "trainPersonNumMat", 0);
    eigenValMat = (CvMat*)cvReadByName(fileStorage, 0, "eigenValMat", 0);
    projectedTrainFaceMat = (CvMat*)cvReadByName(fileStorage, 0, "projectedTrainFaceMat", 0);
    pAvgTrainImg = (IplImage*)cvReadByName(fileStorage, 0, "avgTrainImg", 0);
    eigenVectArr = (IplImage**)cvAlloc(nTrainFaces*sizeof(IplImage*));
    for(i=0; i<nEigens; i++){
	char varname[200];
	sprintf( varname, "eigenVect_%d", i );
	eigenVectArr[i] = (IplImage*)cvReadByName(fileStorage, 0, varname, 0);
    }

    // release the file-storage interface
    cvReleaseFileStorage( &fileStorage );

    return 1;
}

int findNearestNeighbor(float* projectedTestFace){
    double leastDistSq = DBL_MAX;
    int i, iTrain, iNearest = 0;

    for(iTrain=0; iTrain<nTrainFaces; iTrain++){
	double distSq = 0;

	for(i=0; i<nEigens; i++){
	    float d_i = projectedTestFace[i] -
		projectedTrainFaceMat->data.fl[iTrain*nEigens + i];
	    distSq += d_i*d_i;
	}

	if(distSq < leastDistSq){
	    leastDistSq = distSq;
	    iNearest = iTrain;
	}
    }

    return iNearest;
}

代码解析:

        OpenCV实现了基于PCA的特征脸人脸识别方法,相关理论可参考Paul Viola和Michael Jones于2001年发表的《Rapid Object Detection using a Boosted Cascade of SimpleFeatures》。整个算法又分为样本训练和人脸识别两个过程,在上述代码中,分别通过函数learn()与recognize()来实现。在样本训练阶段,将样本库中的人脸图像转换为特征向量表示,并投影到PCA子空间,最终将这些向量数据保存到中间文件facedata.xml中。而在识别阶段,同样将待识别的人脸图像使用PCA子空间的向量表示,通过计算待识别图像的向量与样本中的向量之间的距离,寻找其中最相近的人脸图像,作为识别结果。

        需要说明的是,在代码中训练样本图像及待识别的图像分别通过文本文件train.txt和test.txt记录,所用图像均为pgm格式,train.txt及test.txt中的内容如下所示:

其中,在这两个文件中,每一行记录一幅人脸图像。每条记录开始的数字表示人的序号,紧跟其后的则是此照片的存储路径。显然,此例在训练样本时只使用了序号为1,2,4三个人的第一幅照片,而识别时则对这三个人的多幅人脸图像进行了识别。其最终的识别结果如下所示:

nearest表示最相似的样本图像的序号,而Truth则表示待识别图像的序号。当二者相同时,表示正确识别,否则识别错误。


       

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值