Chinaunix首页 | 论坛 | 博客

Lzy

  • 博客访问: 214901
  • 博文数量: 56
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 675
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-27 15:50
文章分类

全部博文(56)

文章存档

2014年(56)

我的朋友

分类: C/C++

2014-05-23 18:47:04

PCA数学理论:

  关于PCA的理论,资料很多,公式也一大把,本人功底有限,理论方面这里就不列出了。下面主要从应用的角度大概来讲讲具体怎么实现数据集的降维。

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

  2. 求该矩阵的协防差矩阵(关于协方差的介绍可以参考我的博文:一些知识点的初步理解_4(协方差矩阵,ing...))。

  3. 求步骤2中得到的协方差矩阵的特征值和特征向量。

  4. 将求出的特征向量按照特征值的大小进行组合形成一个映射矩阵,并根据指定的PCA保留的特征个数取出映射矩阵的前n行或者前n列作为最终的映射矩阵。

  5. 用步骤4的映射矩阵对原始数据进行映射,达到数据降维的目的。

 

  实验说明:

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

  Mat Mat::reshape(int cn, int rows=0) const

  该函数是改变Mat的尺寸,即保持尺寸大小=行数*列数*通道数 不变。其中第一个参数为变换后Mat的通道数,如果为0,代表变换前后通道数不变。第二个参数为变换后Mat的行数,如果为0也是代表变换前后通道数不变。但是该函数本身不复制数据(这点不是很理解,调用一个Matreshape,如果我们不把调用后的Mat做为返回值去用,难道此时调用前的Mat一点变化都没有?)。

 

  void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 ) const

  该函数其实是对原Mat的每一个值做一个线性变换。参数1为目的矩阵,参数2为目d矩阵的类型,参数34变换的系数,看完下面的公式就明白了:

  

 

  PCA::PCA(InputArray data, InputArray mean, int flags, int maxComponents=0)

  该构造函数的第一个参数为要进行PCA变换的输入Mat;参数2为该Mat的均值向量;参数3为输入矩阵数据的存储方式,如果其值为CV_PCA_DATA_AS_ROW则说明输入Mat的每一行代表一个样本,同理当其值为CV_PCA_DATA_AS_COL时,代表输入矩阵的每一列为一个样本;最后一个参数为该PCA计算时保留的最大主成分的个数。如果是缺省值,则表示所有的成分都保留。

 

  Mat PCA::project(InputArray vec) const

  该函数的作用是将输入数据vec(该数据是用来提取PCA特征的原始数据)投影到PCA主成分空间中去,返回每一个样本主成分特征组成的矩阵。因为经过PCA处理后,原始数据的维数降低了,因此原始数据集中的每一个样本的维数都变了,由改变后的样本集就组成了本函数的返回值。

 

  Mat PCA::backProject(InputArray vec) const

  一般调用backProject()函数前需调用project()函数,因为backProject()函数的参数vec为经过PCA投影降维过后的矩阵。 因此backProject()函数的作用就是用vec来重构原始数据集(关于该函数的本质数学实现暂时还不是很了解)。

  另外PCA类中还有几个成员变量,mean,eigenvectors, eigenvalues等分别对应着原始数据的均值,协方差矩阵的特征值和特征向量。

 

  实验结果:

  本次实验是用4个人人脸图像,其中每个人分别有5张,共计20张人脸图片。用这些图片组成原始数据集来提取他们的PCA主特征脸。该20张图片如下所示:

  

 

  当运行软件后,单击start按钮,该程序的结果显示如下:

   

  其中第一行的3张人脸分别为20张原图中的3张,这里取的是3个不同人的。

  第二行中显示的3张人脸分别为第一行中人脸经过PCA投影后,又方向投影过来的人脸图像,仔细观察可以看到第二行的人脸图像整体比第一行的亮度上要亮些,且细节上也有所不同。

  3行的人脸图为取的原始数据协方差矩阵特征向量的最前面3个,因此这3个人脸为最具代表人脸特征的3PCA人脸特征。


点击(此处)折叠或打开

  1. #ifndef PCAFACE_H
  2. #define PCAFACE_H
  3. #include <opencv2/core/core.hpp>
  4. #include <opencv2/highgui/highgui.hpp>
  5. #include <opencv2/imgproc/imgproc.hpp>

  6. using namespace cv;

  7. #include <QDialog>

  8. namespace Ui {
  9. class PCAFace;
  10. }

  11. class PCAFace : public QDialog
  12. {
  13.     Q_OBJECT
  14.     
  15. public:
  16.     explicit PCAFace(QWidget *parent = 0);
  17.     ~PCAFace();

  18.     Mat normalize(const Mat& src);

  19.     
  20. protected:
  21.     void changeEvent(QEvent *e);
  22.     
  23. private slots:
  24.     void on_startButton_clicked();

  25.     void on_closeButton_clicked();

  26. private:
  27.     Ui::PCAFace *ui;
  28.     Mat src_face1, src_face2, src_face3;
  29.     Mat project_face1, project_face2, project_face3;
  30.     Mat dst;
  31.     Mat pca_face1, pca_face2, pca_face3;
  32.     vector<Mat> src;
  33.     int total;
  34. };

  35. #endif // PCAFACE_H

点击(此处)折叠或打开

  1. #include <QApplication>
  2. #include "pcaface.h"

  3. int main(int argc, char *argv[])
  4. {
  5.     QApplication a(argc, argv);
  6.     PCAFace w;
  7.     w.show();
  8.     
  9.     return a.exec();
  10. }


点击(此处)折叠或打开

  1. #include "pcaface.h"
  2. #include "ui_pcaface.h"
  3. #include <QString>
  4. #include <iostream>
  5. #include <stdio.h>

  6. using namespace std;

  7. PCAFace::PCAFace(QWidget *parent) :
  8.     QDialog(parent),
  9.     ui(new Ui::PCAFace)
  10. {
  11.     ui->setupUi(this);
  12.     src_face1 = imread("./images/1.pgm", 0);
  13.     //下面的代码为设置图片显示区域自适应图片的大小
  14.     ui->face1Browser->setFixedHeight(src_face1.rows+1);
  15.     ui->face1Browser->setFixedWidth(src_face1.cols+1);
  16.     ui->face2Browser->setFixedHeight(src_face1.rows+1);
  17.     ui->face2Browser->setFixedWidth(src_face1.cols+1);
  18.     ui->face3Browser->setFixedHeight(src_face1.rows+1);
  19.     ui->face3Browser->setFixedWidth(src_face1.cols+1);

  20.     ui->face4Browser->setFixedHeight(src_face1.rows+1);
  21.     ui->face4Browser->setFixedWidth(src_face1.cols+1);
  22.     ui->face5Browser->setFixedHeight(src_face1.rows+1);
  23.     ui->face5Browser->setFixedWidth(src_face1.cols+1);
  24.     ui->face6Browser->setFixedHeight(src_face1.rows+1);
  25.     ui->face6Browser->setFixedWidth(src_face1.cols+1);

  26.     ui->face7Browser->setFixedHeight(src_face1.rows+1);
  27.     ui->face7Browser->setFixedWidth(src_face1.cols+1);
  28.     ui->face8Browser->setFixedHeight(src_face1.rows+1);
  29.     ui->face8Browser->setFixedWidth(src_face1.cols+1);
  30.     ui->face9Browser->setFixedHeight(src_face1.rows+1);
  31.     ui->face9Browser->setFixedWidth(src_face1.cols+1);

  32.     for(int i = 1; i <= 15; i++)
  33.     {
  34.         stringstream ss;
  35.         string num;
  36.         ss<<i;//将整数i读入字符串流
  37.         ss>>num;//将字符串流中的数据传入num,这2句代码即把数字转换成字符
  38.         string image_name = ("./images/" + num + ".pgm");//需要读取的图片全名
  39.         src.push_back(imread(image_name, 0));
  40.     }
  41.     total= src[0].rows*src[0].cols;
  42. }

  43. PCAFace::~PCAFace()
  44. {
  45.     delete ui;
  46. }

  47. void PCAFace::changeEvent(QEvent *e)
  48. {
  49.     QDialog::changeEvent(e);
  50.     switch (e->type()) {
  51.     case QEvent::LanguageChange:
  52.         ui->retranslateUi(this);
  53.         break;
  54.     default:
  55.         break;
  56.     }
  57. }

  58. //将Mat内的内容归一化到0~255,归一化后的类型为但通道整型
  59. Mat PCAFace::normalize(const Mat& src) {
  60.     Mat srcnorm;
  61.     cv::normalize(src, srcnorm, 0, 255, NORM_MINMAX, CV_8UC1);
  62.     return srcnorm;
  63. }


  64. void PCAFace::on_startButton_clicked()
  65. {
  66.     //先显示3张原图
  67.     ui->face1Browser->append("");
  68.     ui->face2Browser->append("");
  69.     ui->face3Browser->append("");

  70.     //mat数组用来存放读取进来的所有图片的数据,其中mat的每一列对应1张图片,该实现在下面的for函数中
  71.     Mat mat(total, src.size(), CV_32FC1);
  72.     for(int i = 0; i < src.size(); i++)
  73.     {
  74.         Mat col_tmp = mat.col(i);
  75.         src[i].reshape(1, total).col(0).convertTo(col_tmp, CV_32FC1, 1/255.);
  76.     }
  77.     int number_principal_compent = 12;//保留最大的主成分数
  78.     //构造pca数据结构
  79.     PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, number_principal_compent);
  80.     //pca.eigenvectors中的每一行代表输入数据协方差矩阵一个特征向量,且是按照该协方差矩阵的特征值进行排序的
  81.     pca_face1 = normalize(pca.eigenvectors.row(0)).reshape(1, src[0].rows);//第一个主成分脸
  82.     imwrite("./result/pca_face1.jpg", pca_face1);//显示主成分特征脸1
  83.     ui->face7Browser->append("");

  84.     pca_face2 = normalize(pca.eigenvectors.row(1)).reshape(1, src[0].rows);//第二个主成分脸
  85.     imwrite("./result/pca_face2.jpg", pca_face2);//显示主成分特征脸2
  86.     ui->face8Browser->append("");

  87.     pca_face3 = normalize(pca.eigenvectors.row(2)).reshape(1, src[0].rows);//第三个主成分脸
  88.     imwrite("./result/pca_face3.jpg", pca_face3);//显示主成分特征脸3
  89.     ui->face9Browser->append("");

  90.     //将原始数据通过PCA方向投影,即通过特征向量的前面几个作用后的数据,因此这里的dst的尺寸变小了
  91.     dst = pca.project(mat);
  92.     //通过方向投影重构原始人脸图像(其本质暂时还没完全弄明白)
  93.     project_face1 = normalize(pca.backProject(dst).col(0)).reshape(1, src[0].rows);
  94.     imwrite("./result/project_face1.jpg", project_face1);
  95.     ui->face4Browser->append("");

  96.     project_face2 = normalize(pca.backProject(dst).col(6)).reshape(1, src[0].rows);
  97.     imwrite("./result/project_face2.jpg", project_face2);
  98.     ui->face5Browser->append("");

  99.     project_face3 = normalize(pca.backProject(dst).col(13)).reshape(1, src[0].rows);
  100.     imwrite("./result/project_face3.jpg", project_face3);
  101.     ui->face6Browser->append("");
  102. }

  103. void PCAFace::on_closeButton_clicked()
  104. {
  105.     close();
  106. }

点击(此处)折叠或打开

  1. #include <QApplication>
  2. #include "pcaface.h"

  3. int main(int argc, char *argv[])
  4. {
  5.     QApplication a(argc, argv);
  6.     PCAFace w;
  7.     w.show();
  8.     
  9.     return a.exec();
  10. }



阅读(1028) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~