【转】PCA算法学习_1(OpenCV中PCA实现人脸降维)

前言:

  PCA是大家经常用来减少数据集的维数,同时保留数据集中对方差贡献最大的特征来达到简化数据集的目的。本文通过使用PCA来提取人脸中的特征脸这个例子,来熟悉下在oepncv中怎样使用PCA这个类。

  开发环境:ubuntu12.04+Qt4.8.2+QtCreator2.5.1+opencv2.4.2

  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人脸特征。

  实验主要部分代码即注释(附录有实验工程code下载链接):

pcaface.h:

#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

pcaface.cpp:

#include "pcaface.h"
#include "ui_pcaface.h"
#include <QString>
#include <iostream>
#include <stdio.h>

using namespace std;

PCAFace::PCAFace(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::PCAFace)
{
    ui->setupUi(this);
    src_face1 = imread("./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 ss;
        string num;
        ss<<i;//将整数i读入字符串流
        ss>>num;//将字符串流中的数据传入num,这2句代码即把数字转换成字符
        string image_name = ("./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内的内容归一化到0~255,归一化后的类型为但通道整型
Mat PCAFace::normalize(const Mat& src) {
    Mat srcnorm;
    cv::normalize(src, srcnorm, 0, 255, NORM_MINMAX, CV_8UC1);
    return srcnorm;
}

void PCAFace::on_startButton_clicked()
{
    //先显示3张原图
    ui->face1Browser->append("<img src=./images/1.pgm>");
    ui->face2Browser->append("<img src=./images/7.pgm>");
    ui->face3Browser->append("<img src=./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中的每一行代表输入数据协方差矩阵一个特征向量,且是按照该协方差矩阵的特征值进行排序的
    pca_face1 = normalize(pca.eigenvectors.row(0)).reshape(1, src[0].rows);//第一个主成分脸
    imwrite("./result/pca_face1.jpg", pca_face1);//显示主成分特征脸1
    ui->face7Browser->append("<img src=./result/pca_face1.jpg>");

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

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

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

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

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

void PCAFace::on_closeButton_clicked()
{
    close();
}

main.cpp:

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

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    PCAFace w;
    w.show();

    return a.exec();
}

  实验总结:

  通过本次实验,对Opencv中的PCA这个类的使用有了一定的了解。

时间: 2024-08-06 19:45:25

【转】PCA算法学习_1(OpenCV中PCA实现人脸降维)的相关文章

数据挖掘算法学习(四)PCA算法

算法简介 主成分分析(PrincipalComponentAnalysis,简称PCA)是一种常用的基于变量协方差矩阵对信息进行处理.压缩和抽提的有效方法.主要用于对特征进行降维. 算法假设 数据的概率分布满足高斯分布或是指数型的概率分布.方差高的向量视为主元. 算法输入 包含n条记录的数据集 算法输出 降维或压缩后的数据集 算法思想 ?1.计算所有样本的均值m和协方差矩阵S: ?2.计算S的特征值,并由大到小排序: ?3.选择前n'个特征值对应的特征矢量作成一个变换矩阵E=[e1,e2, -,

目标跟踪学习笔记_1(opencv中meanshift和camshift例子的应用)

在这一节中,主要讲目标跟踪的一个重要的算法Camshift,因为它是连续自使用的meanShift,所以这2个函数opencv中都有,且都很重要.为了让大家先达到一个感性认识.这节主要是看懂和运行opencv中给的sample并稍加修改. Camshift函数的原型为:RotatedRect CamShift(InputArray probImage, Rect& window, TermCriteria criteria). 其中probImage为输入图像直方图的反向投影图,window为要

[OpenCV-Python] OpenCV 中图像特征提取与描述 部分 V (一)

部分 V图像特征提取与描述 29 理解图像特征 目标本节我会试着帮你理解什么是图像特征,为什么图像特征很重要,为什么角点很重要等.29.1 解释 我相信你们大多数人都玩过拼图游戏吧.首先你们拿到一张图片的一堆碎片,要做的就是把这些碎片以正确的方式排列起来从而重建这幅图像.问题是,你怎样做到的呢?如果把你做游戏的原理写成计算机程序,那计算机就也会玩拼图游戏了.如果计算机可以玩拼图,我们就可以给计算机一大堆自然图片,然后就可以让计算机把它拼成一张大图了.如果计算机可以自动拼接自然图片,那我们是不是可

经典算法学习——链表实现冒泡排序

我在之前一篇博客<经典算法学习--冒泡排序>中简单实现了使用数组进行冒泡排序.这篇博客我们将来实现使用链表如何排序,其实整体的思路是一样的.示例代码上传至: https://github.com/chenyufeng1991/BubbleSortLinkedList . 算法描述如下: (1)比较相邻的前后两个数据,如果前面数据大于后面的数据,就将两个数据交换: (2)这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就到了最后一个位置,也就是下标为N-1的位置(沉到了水底).

用opencv实现的PCA算法,非API调用

理论參考文献:但此文没有代码实现.这里自己实现一下,让理解更为深刻 问题:如果在IR中我们建立的文档-词项矩阵中,有两个词项为"learn"和"study",在传统的向量空间模型中,觉得两者独立. 然而从语义的角度来讲.两者是相似的.并且两者出现频率也类似,是不是能够合成为一个特征呢? <模型选择和规则化>谈到的特征选择的问题.就是要剔除的特征主要是和类标签无关的特征.比方"学生的名字"就和他的"成绩"无关,使用的

OpenCV 中使用 PCA

对于PCA,一直都是有个概念,没有实际使用过,今天终于实际使用了一把,发现PCA还是挺神奇的. 在OPENCV中使用PCA非常简单,只要几条语句就可以了. 1.初始化数据 //每一行表示一个样本 CvMat* pData = cvCreateMat( 总的样本数, 每个样本的维数, CV_32FC1 ); CvMat* pMean = cvCreateMat(1, 样本的维数, CV_32FC1); //pEigVals中的每个数表示一个特征值 CvMat* pEigVals = cvCreat

PCA算法理解及代码实现

github:PCA代码实现.PCA应用 本文算法均使用python3实现 1. 数据降维 ??在实际生产生活中,我们所获得的数据集在特征上往往具有很高的维度,对高维度的数据进行处理时消耗的时间很大,并且过多的特征变量也会妨碍查找规律的建立.如何在最大程度上保留数据集的信息量的前提下进行数据维度的降低,是我们需要解决的问题. ??对数据进行降维有以下优点: ??(1)使得数据集更易使用 ??(2)降低很多算法的计算开销 ??(3)去除噪声 ??(4)使得结果易懂 ??降维技术作为数据预处理的一部

OpenCV中feature2D学习——FAST特征点检测

在前面的文章<OpenCV中feature2D学习--SURF和SIFT算子实现特征点检测>中讲了利用SIFT和SURF算子进行特征点检测,这里尝试使用FAST算子来进行特征点检测. FAST的全名是:Features from Accelerated Segment Test,主要特点值计算速度快,比已知的其他特征点检测算法要快很多倍,可用于计算机视觉应用的实时场景.目前以其高计算效率(computational performance).高可重复性(highrepeatability)成为

机器学习实战ByMatlab(二)PCA算法

PCA 算法也叫主成分分析(principal components analysis),主要是用于数据降维的. 为什么要进行数据降维?因为实际情况中我们的训练数据会存在特征过多或者是特征累赘的问题,比如: 一个关于汽车的样本数据,一个特征是"km/h的最大速度特征",另一个是"英里每小时"的最大速度特征,很显然这两个特征具有很强的相关性 拿到一个样本,特征非常多,样本缺很少,这样的数据用回归去你和将非常困难,很容易导致过度拟合 PCA算法就是用来解决这种问题的,其