Opencv人脸识别项目简介

来源:互联网 发布:网络新技术专题报告 编辑:程序博客网 时间:2024/06/05 11:39

项目要求

  • 使用opencv实现对人脸库的主成分提取(不使用PCA类),完成特征模型保存
  • 对一张测试照片进行识别,找到图片库中和测试图片最像的图

配置说明

  • Opencv3.0
  • VS2015
  • Win10

配置过程网上太多了,就不做过多解释了,可以参照某个教程来做。主要的也就几步,下载Opencv,配Path,配置VC++目录的包含目录和库目录,配置链接器附加项的附加依赖项。

人脸库

  • AT&T人脸库, 40人x10张 (本次Proj使用)
  • 其他人脸库

结果

  • 训练集是AT&T人脸库40x8(两张做测试用)
  • 左图是输入的测试人脸,右图两张是人脸库中的匹配到后还原人脸
测试人脸 匹配结果 匹配结果(前50%特征向量) 测试人脸 匹配结果 弱

前言

  • 为什么要做PCA (principle component analysis)

    主成分分析做的就是给了一堆很高维的数据,我们需要把它变成低维的数据,变成低维数据有一个好,最直观的就是数据量下来了。那图片举例,比如1000个样本,100x100的分辨率,以前1000x10000的数据,每个点就算是1Btye的数据,要10M。我把它映射到500维(取前50%特征向量)的空间,变成1000x500的数据,一下子降低到了500K,一下子降低了一个量级,更关键的是计算量瞬间也降了一个档次。一些场景如嵌入式设备上(如ZKteco的考勤机),计算和空间都是很奢侈的东西,我们的PCA就发挥作用了。

  • 怎么做数据的降维
    为什么要做降维原因还有很多很多,有兴趣的可以去查查,不在这里讲的主要原因是我自己也不知道。那么接下来就要分析怎么降维,降维大概的意思就是把一个数据点降到低维的数据点,比如XY二维的点映射到一维的话,如果是映射到x轴,那么所有点点乘个(1, 0)就好了;如果是三维的点降到一维那么点乘(1, 0, 0)就好了,降到二维点乘(1, 0, 0);(0, 1, 0)就好了,也就是相当于原来的数据矩阵乘了一个变换矩阵。

    问题来了,这个变换矩阵要怎么设定呢?是不是直接所有的都映射到X,Y,Z…轴上就好了呢?当然不是!比如下面有张数据点图表示男女的身高体重图

    黑点表示男生,红点表示女生
    X轴表示体重,Y轴表示身高

img

如果判断都是按照体重来划分的话,m1很可能就被判断成了女生,fm1则被判断成了男生。“虽然我体重轻一点,但是我身高比较高啊,我应该被划分成男生”。讲道理的话按照那条蓝线来分比较科学,为什么科学啊,因为比较符合图的分布,为什么符合分布啊?….这个时候有人就弄了一个PCA做降维的准则,有人说:“我觉得使得新的数据集方差最大的那种分法比较好。”大家想想,是这么个道理,方差比较小,大家挤成一团,很多数据点很容易重叠,也不容易区分。

数学推导

更加细致的推导可以去参考其他大学的pca课程ppt,如JHU这里有pca处理人脸步骤的详解,CMU的ppt则对整个过程的数学推导有详细的证明,CMU的ML课程主页还有更多的资源,是一个很好的学习的地方
看他们的ppt对PCA的理解帮助更大,如果想简单了解下直接看下我的证明也可以

假设数据集是

新的数据集是

原来的数据是p维,新的数据是k维,对于新数据的第一维数据

我们需要选择 使得最大

因此,目标变成了

用拉格朗日乘子法求值

(看不懂的话可以查The Matrix Cookbook)

因此最大的方差就是最大的特征值,对应的

是最大特征值对应的特征向量。
再来求

这个时候

需要满足要求使得

同理可得它就是第二大的特征根,我们要找投影方差最大的K个向量,也就是协方差矩阵的前K大特征值对应的特征向量。
所以为了保存最多的信息,数据变换到K维空间我们的变换矩阵就是

通过

进行数据的还原


讲了那么多我们队PCA的大概求解过程也了解了,那么,具体用在人脸识别的分析上面是否又是如此呢?有点小小的不一样。
我们这时候不是图像的灰度值来算协方差矩阵,而是

为什么呢,因为用offset值算的协方差矩阵,重构图像后的error是最小的。具体的推导我实在是懒得打了,把CMU课程上的两张推导图放上来,大家将就着看好了。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述


计算小Tips

我们在计算协方差矩阵的时候,会发现特征值没法算,为什么呢,一个图像一万维,10000x10000的矩阵特征值不是算爆炸了吗?你会发现自己opencv的函数半天输出不出结果,这个时候如果人脸训练样本比较少的话,假设

可以算

的特征向量,然后乘以 A,就可以得到要求得协方差矩阵的特征向量,证明如下(摘自JHU的ppt)
这里写图片描述
这再次说明了一个问题,多看看别人学校的课程公开的资源,用google随便一搜很多,或者是coursera,比很多博客上的靠谱全面太多了,囧


源码

有时间我再挑代码重点解释下吧,大家上面理解了,下面的大家应该基本都能够看懂,原谅我翔一样的代码,看个高兴就好了…人脸库是上面提到的ATT/T,上面有链接,可以自己去下

#include "opencv2/opencv.hpp"#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>cv::String root_folder = "att_faces";int population = 10;int gesture = 8;double EigenPerc = 0.5;/* --load model data-- */int img_rows;cv::Mat LoadNewSpaceData, loadEigenVector, loadDataMean; // datamean is a mean face (a vector)/* ---------------- */void showFace(cv::String windowName, cv ::Mat faceVector, int rows) {    faceVector = faceVector.reshape(1, rows);    faceVector.convertTo(faceVector, CV_8U);    cv::imshow(windowName, faceVector); }void saveModel() {      cv::Mat dataMat; // original image data     int img_rows; // will be wriiten to model file    //read image data    for (int man = 1; man <= population; man++)        for (int pos = 1; pos <= gesture; pos++) {            char filename[50];            sprintf(filename, "att_faces/s%d/%d.pgm", man, pos);            cv::Mat img = cv::imread(filename);            cv::cvtColor(img, img, CV_RGB2GRAY);            img_rows = img.rows;            img = img.reshape(1, 1);                        dataMat.push_back(img);                 }    dataMat.convertTo(dataMat, CV_32F);    //calc covariance and dataMean (mean matrix will be written to model file)    cv::Mat  cov, dataMean, dataMatOff, temp, temp12;    cv::reduce(dataMat, dataMean, 0, CV_REDUCE_SUM);    dataMean = dataMean / dataMat.rows;    cv::repeat(dataMean, dataMat.rows, 1, temp12); //tips: source and destination array can't be the same    cv::subtract(dataMat, temp12, dataMatOff);     cov = dataMatOff * dataMatOff.t();    // Calculate eigenVectors of the corvariance matrix of original image matrix    cv::Mat eigenVal, eigenVec;    cv::Mat eigenVecFloat;    cv::eigen(cov, eigenVal, eigenVec);    eigenVec = eigenVec.t();            eigenVec.convertTo(eigenVecFloat, CV_32F);    cv::Mat realEigenVec = dataMatOff.t() * eigenVecFloat;    /* normalize eigenvector */    cv::Mat temp1, temp2, temp3;    cv::pow(realEigenVec, 2, temp1);    cv::reduce(temp1, temp2, 0, CV_REDUCE_SUM);    cv::sqrt(temp2, temp2);    cv::repeat(temp2, temp1.rows, 1, temp3);    cv::divide(realEigenVec, temp3, realEigenVec);          cv::Mat newSpaceData, newFace;    int cutcols = (int)(EigenPerc * realEigenVec.cols);    if (cutcols == 0) cutcols = 1;    newSpaceData = dataMatOff * realEigenVec.colRange(0, cutcols);    std::cout << "cols: "<< cutcols << std::endl;    /*    newFace = newSpaceData * (realEigenVec.colRange(0, cutcols).t());       newFace = newFace + dataMean;*/    cv::FileStorage fsw("model.yml", cv::FileStorage::WRITE);    fsw << "imageRows" << img_rows;    fsw << "newSpaceData" << newSpaceData << "EigenVector" << realEigenVec.colRange(0, cutcols)        << "dataMean" << dataMean;    fsw.release();    return; }void loadModel() {    cv::FileStorage fsr("model.yml", cv::FileStorage::READ);    img_rows = (int)fsr["imageRows"];    fsr["newSpaceData"] >> LoadNewSpaceData;    fsr["EigenVector"] >> loadEigenVector;    fsr["dataMean"] >> loadDataMean;    fsr.release();    cv::Mat newFace = LoadNewSpaceData * loadEigenVector.t();    cv::Mat expandDataMean = cv::repeat(loadDataMean, newFace.rows, 1);    newFace = newFace + expandDataMean;    //show face    showFace("model_face", newFace.rowRange(0, 1), img_rows);   }void checkFace(cv::Mat testFace) {    cv::Mat testNewSpaceFace;    testFace.convertTo(testFace, CV_32F);    testNewSpaceFace = (testFace - loadDataMean) * loadEigenVector;    // find a nearest face from newSpaceDataLoad    float minDis = 3.402823466e+38F;    cv::Mat matchFace;    for (int i = 0; i < LoadNewSpaceData.rows; i++) {        //std::cout << testNewSpaceFace;        cv::Mat off = (testNewSpaceFace - LoadNewSpaceData.rowRange(i, i + 1));        cv::Mat val = off * off.t();        val.convertTo(val, CV_32F);        float dis;        dis = val.at<float>(0, 0);        if (dis < minDis) {            minDis = dis;            matchFace = LoadNewSpaceData.rowRange(i, i + 1) * loadEigenVector.t();        }           }    cv::Mat face8U;    matchFace = matchFace + loadDataMean;    matchFace.convertTo(face8U, CV_8U);    std::cout << minDis;    showFace("matched_face", face8U, img_rows);    cv::waitKey(0);}int main() {    saveModel();    loadModel();    cv::namedWindow("original_face");    cv::namedWindow("matched_face");    cv::namedWindow("model_face");    cv::Mat testFace = cv::imread("test.pgm");    imshow("original_face", testFace);    cv::cvtColor(testFace, testFace, CV_RGB2GRAY);    testFace = testFace.reshape(1, 1);    checkFace(testFace);    return 0;}

CV的教材及参考资料

  • Richard Szeliski, Computer Vision: Algorithms and Applications, 2010 Draft PDF, Sep.3 2010
  • Simon J.D. Prince, Computer Vision: Models, Learning, and Inference, Cambridge University Press, Draft PDF, Jul.7 2012
  • David A. Forsyth, Jean Ponce, Computer Vision: A Modern Approach (2nd Edition)
  • Gary Bradski, Adrian Kaebler, Learning OpenCV, O’Reilly, 2008.
  • Rafael C. Gonzalez, Richard E. Woods, Digital Image Processing, 2nd or 3rd, Prentice Hall,电子工业出版社(影印版,第二版2004年2月,第三版2010年1月)
  • Richard O.Duda, Peter E.Hart, David G.Stork, Pattern Classification (2nd Edition), John Wiley, 机械工业出版社(影印版,中译版,2003年9月)

  • 矩阵速查手册 the matrix cookbook,学习DM的时候Prof强力推荐的书,矩阵求导不知道怎么求的话可以看这本书
  • numerical recipes in c++
0 0
原创粉丝点击