程序员人生 网站导航

OpenCV+Qt:基于PCA主成分分析的人脸识别例程

栏目:php教程时间:2015-07-03 08:43:26

在模式辨认领域中,PCA是1种经常使用的数据集降维手段,在此基础上,保存数据集中对方差贡献最大的特点从而进行模式分类。OpenCV中提供PCA的类,因此可以方便地使用PCA来进行人脸辨认研究。在学习了网上的相干实现和代码,在以下开发平台跑通了代码:win8.1+OpenCV2.4.9+Qt5.3.2。

1、基本步骤

关于PCA的1些理论,可参照:http://blog.csdn.net/liyuefeilong/article/details/45126255 以下是实现PCA的基本思路:

1.把原始数据中每一个样本用1个向量表示,然后把所有样本组合起来构成1个矩阵。这里为了不样本单位对后续处理的影响,样本集需要标准化。

2.求样本的散布矩阵。事实上,散布矩阵是样本协方差矩阵的(n⑴)倍,而协方差矩阵则表示不同随机变量之间的相互关系,在图象中则等价为求两个像素之间的关系。这里散布矩阵是实对称矩阵。

3.对第2步中得到的散布矩阵求相应的特点值和特点向量。

4.所谓主成份分析,即需要得到具有最大特点值的特点向量,所以我们需要将特点向量依照特点值由大到小排序并构成1个映照矩阵,并根据指定的PCA保存的特点个数取出映照矩阵的前n行或前n列作为终究的映照矩阵。

5.用第4步的映照矩阵对训练样本数据进行映照,到达数据降维的目的。假定原始的图象数据是m*n的矩阵,只包括主成份的特点向量构成1个n*p的矩阵,其中每列都是1个特点向量。将两个矩阵相乘,便可取得降维以后的图象矩阵m*p,这个矩阵远小于原始的图象数据。

6.同步骤5,读取所有测试集图象,并对其进行降维操作。如果测试集有M幅图象,则降维后的矩阵为M*p。

7.最后,对测试集进行模式辨认。

在本次实验实现的进程中,需要用到opencv的这些函数,下面简单介绍下这些函数。

2、OpenCV中需要用到的几个函数

PCA::PCA(InputArray data, // 输入1个矩阵 InputArray mean, // 输出1个句子 int flags, // 输入矩阵数据的存储方式,有以下两种参数设定 // CV_PCA_DATA_AS_ROW:代表输入矩阵的每行表示1个样本 // CV_PCA_DATA_AS_COL:代表输入矩阵的每列表示1个样本 int maxComponents=0) // 计算PCA时保存的最大主成份的个数
// 该函数将输入数据投影到PCA主成份空间中去 // 返回每个样本主成份特点组成的矩阵 cv::Mat PCA::project(InputArray vec) const
// 调用backProject函数前1般已调用过project()函数 // 其作用可理解为project()函数的逆运算 // 函数的作用就是用vec来重构原始数据集(原理有待进1步了解) cv::Mat PCA::backProject(InputArray vec) const

另外PCA类中还有几个重要的成员变量:

mean // 原始数据的均值 eigenvectors // 散布矩阵(协方差矩阵)的特点值 eigenvalues // 散布矩阵(协方差矩阵)的特点向量

3、相干代码

根据网上提供的代码,修改成可以在开发平台上使用的版本:

#ifndef PCAFACE_H #define PCAFACE_H #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace cv; #include <QDialog> namespace Ui { class PCAFace; } class PCAFace : public QDialog { Q_OBJECT public: explicit PCAFace(QWidget *parent = 0); ~PCAFace(); Mat normalize(const Mat& src); protected: void changeEvent(QEvent *e); private slots: void on_startButton_clicked(); void on_closeButton_clicked(); private: Ui::PCAFace *ui; Mat src_face1, src_face2, src_face3; Mat project_face1, project_face2, project_face3; Mat dst; Mat pca_face1, pca_face2, pca_face3; vector<Mat> src; int total; }; #endif // PCAFACE_H
#include "pcaface.h" #include "ui_pcaface.h" #include <QString> #include <iostream> #include <stdio.h> #include <QDir> #include <QDebug> using namespace std; QDir dir; QString runPath = dir.currentPath(); PCAFace::PCAFace(QWidget *parent) : QDialog(parent), ui(new Ui::PCAFace) { ui->setupUi(this); src_face1 = imread("C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/images/1.pgm", 0); //下面的代码为设置图片显示区域自适应图片的大小 ui->face1Browser->setFixedHeight(src_face1.rows+1); ui->face1Browser->setFixedWidth(src_face1.cols+1); ui->face2Browser->setFixedHeight(src_face1.rows+1); ui->face2Browser->setFixedWidth(src_face1.cols+1); ui->face3Browser->setFixedHeight(src_face1.rows+1); ui->face3Browser->setFixedWidth(src_face1.cols+1); ui->face4Browser->setFixedHeight(src_face1.rows+1); ui->face4Browser->setFixedWidth(src_face1.cols+1); ui->face5Browser->setFixedHeight(src_face1.rows+1); ui->face5Browser->setFixedWidth(src_face1.cols+1); ui->face6Browser->setFixedHeight(src_face1.rows+1); ui->face6Browser->setFixedWidth(src_face1.cols+1); ui->face7Browser->setFixedHeight(src_face1.rows+1); ui->face7Browser->setFixedWidth(src_face1.cols+1); ui->face8Browser->setFixedHeight(src_face1.rows+1); ui->face8Browser->setFixedWidth(src_face1.cols+1); ui->face9Browser->setFixedHeight(src_face1.rows+1); ui->face9Browser->setFixedWidth(src_face1.cols+1); for(int i = 1; i <= 15; i++) { stringstream str; string num; str<<i;// 将整数i读入字符串流 str>>num;// 将字符串流中的数据传入num,这2句代码即把数字转换成字符 string image_name = ("C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/images/" + num + ".pgm");//需要读取的图片全名 src.push_back(imread(image_name, 0)); } total= src[0].rows*src[0].cols; } PCAFace::~PCAFace() { delete ui; } void PCAFace::changeEvent(QEvent *e) { QDialog::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: ui->retranslateUi(this); break; default: break; } } // 将Mat内的内容归1化到0255,归1化后的类型为8位无符号整型单通道 Mat PCAFace::normalize(const Mat& src) { Mat norm_src; cv::normalize(src, norm_src, 0, 255, NORM_MINMAX, CV_8UC1); return norm_src; } void PCAFace::on_startButton_clicked() { //先显示3张原图 ui->face1Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/images/5.pgm>"); ui->face2Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/images/7.pgm>"); ui->face3Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/images/14.pgm>"); //mat数组用来寄存读取进来的所有图片的数据,其中mat的每列对应1张图片,该实现在下面的for函数中 Mat mat(total, src.size(), CV_32FC1); for(int i = 0; i < src.size(); i++) { Mat col_tmp = mat.col(i); src[i].reshape(1, total).col(0).convertTo(col_tmp, CV_32FC1, 1/255.); } int number_principal_compent = 12;//保存最大的主成份数 //构造pca数据结构 PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, number_principal_compent); //pca.eigenvectors中的每行代表输入数据协方差矩阵1个特点向量,且是依照该协方差矩阵的特点值进行排序的 pca_face1 = normalize(pca.eigenvectors.row(0)).reshape(1, src[0].rows);//第1个主成份脸 imwrite("C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/pca_face1.jpg", pca_face1);//显示主成份特点脸1 ui->face7Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/pca_face1.jpg>"); pca_face2 = normalize(pca.eigenvectors.row(1)).reshape(1, src[0].rows);//第2个主成份脸 imwrite("C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/pca_face2.jpg", pca_face2);//显示主成份特点脸2 ui->face8Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/pca_face2.jpg>"); pca_face3 = normalize(pca.eigenvectors.row(2)).reshape(1, src[0].rows);//第3个主成份脸 imwrite("C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/pca_face3.jpg", pca_face3);//显示主成份特点脸3 ui->face9Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/pca_face3.jpg>"); //将原始数据通过PCA方向投影,即通过特点向量的前面几个作用后的数据,因此这里的dst的尺寸变小了 dst = pca.project(mat); //通过方向投影重构原始人脸图象 project_face1 = normalize(pca.backProject(dst).col(0)).reshape(1, src[0].rows); imwrite("C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/project_face1.jpg", project_face1); ui->face4Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/project_face1.jpg>"); project_face2 = normalize(pca.backProject(dst).col(1)).reshape(1, src[0].rows); imwrite("C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/project_face2.jpg", project_face2); ui->face5Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/project_face2.jpg>"); project_face3 = normalize(pca.backProject(dst).col(2)).reshape(1, src[0].rows); imwrite("C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/project_face3.jpg", project_face3); ui->face6Browser->append("<img src=C:/Users/peng__000/Desktop/PR.proj05/OpenCV4PCA/PCA_Face/result/project_face3.jpg>"); } void PCAFace::on_closeButton_clicked() { close(); }
#include <QApplication> #include "pcaface.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); PCAFace w; w.show(); return a.exec(); }

这里写图片描述

其中第1行的3张人脸分别为ORL人脸库其中的3张,分别取3个不同的人。

第2行中显示的3张人脸分别为第1行中人脸经过PCA投影函数Project后,又调用反向投影函数backProject变换回来的人脸图象。

第3行的人脸图为取的原始数据计算散布矩阵的特点向量的最前面3个,使用这3个向量所得到的输出最能代表人脸特点。

最后感谢代码原文介绍:http://www.cnblogs.com/tornadomeet/archive/2012/09/06/2673104.html

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐