Opencv人脸识别项目简介
来源:互联网 发布:网络新技术专题报告 编辑:程序博客网 时间:2024/06/05 11:39
项目要求
- 使用opencv实现对人脸库的主成分提取(不使用PCA类),完成特征模型保存
- 对一张测试照片进行识别,找到图片库中和测试图片最像的图
配置说明
- Opencv3.0
- VS2015
- Win10
配置过程网上太多了,就不做过多解释了,可以参照某个教程来做。主要的也就几步,下载Opencv,配Path,配置VC++目录的包含目录和库目录,配置链接器附加项的附加依赖项。
人脸库
- AT&T人脸库, 40人x10张 (本次Proj使用)
- 其他人脸库
结果
- 训练集是AT&T人脸库40x8(两张做测试用)
- 左图是输入的测试人脸,右图两张是人脸库中的匹配到后还原人脸
前言
为什么要做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轴表示身高
如果判断都是按照体重来划分的话,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++
- Opencv人脸识别项目简介
- 四川大学 opencv 人脸识别 手势识别 研究项目
- OpenCV人脸识别
- opencv人脸识别
- openCV人脸识别
- Opencv 人脸识别
- opencv人脸识别
- Opencv 人脸识别
- opencv 人脸识别
- OpenCv人脸识别
- openCV人脸识别
- OpenCV人脸识别
- 人脸识别opencv
- openCV人脸识别
- opencv人脸识别
- 图像项目-基于opencv的人脸识别
- android 结合 opencv项目(NDK、OpenCV、android,官方Demo人脸识别)
- OpenCV的行人识别&人脸识别
- Windows下pip安装包报错:Microsoft Visual C++ 9.0 is required Unable to find vcvarsall.bat
- Android SQLite数据库存储
- java检测linux系统中所有端口的ip地址
- linux shell数据重定向(输入重定向与输出重定向)详细分析
- [Java开发之路](12)JDOM和DOM4J解析XML文档
- Opencv人脸识别项目简介
- 不用百度地图,不用高德地图,使用原生的android api也可以定位并做相应的优化
- 解析Excel时列号数字转换为字母
- 从第一个字符串中删除第二个字符串中出现的所有字符
- linux下网络连接socket统计工具ss学习小结
- BZOJ 1009 KMP思想 + DP + 矩阵快速幂
- XML操作
- [HDU 4263]Red/Blue Spanning Tree[kruskal]
- 数据结构学习笔记——单链表