OpenCV中PCA实现人脸降维
前言:
PCA是大家经常用来减少数据集的维数,同时保留数据集中对方差贡献最大的特征来达到简化数据集的目的。本文通过使用PCA来提取人脸中的特征脸这个例子,来熟悉下在oepncv中怎样使用PCA这个类。
开发环境:ubuntu12.04+Qt4.8.2+QtCreator2.5.1+opencv2.4.2
PCA数学理论:
关于PCA的理论,资料很多,公式也一大把,本人功底有限,理论方面这里就不列出了。下面主要从应用的角度大概来讲讲具体怎么实现数据集的降维。
把原始数据中每个样本用一个向量表示,然后把所有样本组合起来构成一个矩阵。当然了,为了避免样本的单位的影响,样本集需要标准化。
求该矩阵的协防差矩阵(关于协方差的介绍可以参考我的博文:一些知识点的初步理解_4(协方差矩阵,ing...))。
求步骤2中得到的协方差矩阵的特征值和特征向量。
将求出的特征向量按照特征值的大小进行组合形成一个映射矩阵,并根据指定的PCA保留的特征个数取出映射矩阵的前n行或者前n列作为最终的映射矩阵。
用步骤4的映射矩阵对原始数据进行映射,达到数据降维的目的。
实验说明:
在本次实验实现的过程中,需要用到opencv的这些函数,下面简单介绍下这些函数。
Mat Mat::reshape(int cn, int rows=0) const
该函数是改变Mat的尺寸,即保持尺寸大小=行数*列数*通道数 不变。其中第一个参数为变换后Mat的通道数,如果为0,代表变换前后通道数不变。第二个参数为变换后Mat的行数,如果为0也是代表变换前后通道数不变。但是该函数本身不复制数据(这点不是很理解,调用一个Mat的reshape,如果我们不把调用后的Mat做为返回值去用,难道此时调用前的Mat一点变化都没有?)。
void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 ) const
该函数其实是对原Mat的每一个值做一个线性变换。参数1为目的矩阵,参数2为目d矩阵的类型,参数3和4变换的系数,看完下面的公式就明白了:

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个人脸为最具代表人脸特征的3个PCA人脸特征。
实验主要部分代码即注释(附录有实验工程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 = );
~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", );
//下面的代码为设置图片显示区域自适应图片的大小
ui->face1Browser->setFixedHeight(src_face1.rows+);
ui->face1Browser->setFixedWidth(src_face1.cols+);
ui->face2Browser->setFixedHeight(src_face1.rows+);
ui->face2Browser->setFixedWidth(src_face1.cols+);
ui->face3Browser->setFixedHeight(src_face1.rows+);
ui->face3Browser->setFixedWidth(src_face1.cols+); ui->face4Browser->setFixedHeight(src_face1.rows+);
ui->face4Browser->setFixedWidth(src_face1.cols+);
ui->face5Browser->setFixedHeight(src_face1.rows+);
ui->face5Browser->setFixedWidth(src_face1.cols+);
ui->face6Browser->setFixedHeight(src_face1.rows+);
ui->face6Browser->setFixedWidth(src_face1.cols+); ui->face7Browser->setFixedHeight(src_face1.rows+);
ui->face7Browser->setFixedWidth(src_face1.cols+);
ui->face8Browser->setFixedHeight(src_face1.rows+);
ui->face8Browser->setFixedWidth(src_face1.cols+);
ui->face9Browser->setFixedHeight(src_face1.rows+);
ui->face9Browser->setFixedWidth(src_face1.cols+); for(int i = ; i <= ; i++)
{
stringstream ss;
string num;
ss<<i;//将整数i读入字符串流
ss>>num;//将字符串流中的数据传入num,这2句代码即把数字转换成字符
string image_name = ("./images/" + num + ".pgm");//需要读取的图片全名
src.push_back(imread(image_name, ));
}
total= src[].rows*src[].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, , , 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 = ; i < src.size(); i++)
{
Mat col_tmp = mat.col(i);
src[i].reshape(, total).col().convertTo(col_tmp, CV_32FC1, /.);
}
int number_principal_compent = ;//保留最大的主成分数
//构造pca数据结构
PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, number_principal_compent);
//pca.eigenvectors中的每一行代表输入数据协方差矩阵一个特征向量,且是按照该协方差矩阵的特征值进行排序的
pca_face1 = normalize(pca.eigenvectors.row()).reshape(, src[].rows);//第一个主成分脸
imwrite("./result/pca_face1.jpg", pca_face1);//显示主成分特征脸1
ui->face7Browser->append("<img src=./result/pca_face1.jpg>"); pca_face2 = normalize(pca.eigenvectors.row()).reshape(, src[].rows);//第二个主成分脸
imwrite("./result/pca_face2.jpg", pca_face2);//显示主成分特征脸2
ui->face8Browser->append("<img src=./result/pca_face2.jpg>"); pca_face3 = normalize(pca.eigenvectors.row()).reshape(, src[].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()).reshape(, src[].rows);
imwrite("./result/project_face1.jpg", project_face1);
ui->face4Browser->append("<img src=./result/project_face1.jpg>"); project_face2 = normalize(pca.backProject(dst).col()).reshape(, src[].rows);
imwrite("./result/project_face2.jpg", project_face2);
ui->face5Browser->append("<img src=./result/project_face2.jpg>"); project_face3 = normalize(pca.backProject(dst).col()).reshape(, src[].rows);
imwrite("./result/project_face3.jpg", project_face3);
ui->face6Browser->append("<img src=./result/project_face3.jpg>");
} void PCAFace::on_closeButton_clicked()
{
close();
}
实验总结:
通过本次实验,对Opencv中的PCA这个类的使用有了一定的了解。
作者:tornadomeet 出处:http://www.cnblogs.com/tornadomeet 欢迎转载或分享,但请务必声明文章出处。 (新浪微博:tornadomeet,欢迎交流!)
OpenCV中PCA实现人脸降维的更多相关文章
- 【转】PCA算法学习_1(OpenCV中PCA实现人脸降维)
前言: PCA是大家经常用来减少数据集的维数,同时保留数据集中对方差贡献最大的特征来达到简化数据集的目的.本文通过使用PCA来提取人脸中的特征脸这个例子,来熟悉下在oepncv中怎样使用PCA这个类. ...
- opencv基于PCA降维算法的人脸识别
opencv基于PCA降维算法的人脸识别(att_faces) 一.数据提取与处理 # 导入所需模块 import matplotlib.pyplot as plt import numpy as n ...
- 基于PCA的人脸识别步骤
代码下载:基于PCA(主成分分析)的人脸识别 人脸识别是一个有监督学习过程,首先利用训练集构造一个人脸模型,然后将测试集与训练集进行匹配,找到与之对应的训练集头像.最容易的方式是直接利用欧式距离计算测 ...
- [OpenCV-Python] OpenCV 中图像特征提取与描述 部分 V (二)
部分 V图像特征提取与描述 OpenCV-Python 中文教程(搬运)目录 34 角点检测的 FAST 算法 目标 • 理解 FAST 算法的基础 • 使用 OpenCV 中的 FAST 算法相关函 ...
- OpenCV 学习笔记 05 人脸检测和识别
本节将介绍 Haar 级联分类器,通过对比分析相邻图像区域来判断给定图像或子图像与已知对象是否匹配. 本章将考虑如何将多个 Haar 级联分类器构成一个层次结构,即一个分类器能识别整体区域(如人脸) ...
- 在opencv3中进行图片人脸检测
在opencv中,人脸检测用的是harr或LBP特征,分类算法用的是adaboost算法.这种算法需要提前训练大量的图片,非常耗时,因此opencv已经训练好了,把训练结果存放在一些xml文件里面.在 ...
- 【从零学习openCV】IOS7根据人脸检测
前言: 人脸检測与识别一直是计算机视觉领域一大热门研究方向,并且也从安全监控等工业级的应用扩展到了手机移动端的app.总之随着人脸识别技术获得突破,其应用前景和市场价值都是不可估量的,眼下在学习ope ...
- OpenCV中的绘图函数-OpenCV步步精深
OpenCV 中的绘图函数 画线 首先要为画的线创造出环境,就要生成一个空的黑底图像 img=np.zeros((512,512,3), np.uint8) 这是黑色的底,我们的画布,我把窗口名叫做i ...
- OpenCV中cv2的用法
一.读入图像 使用函数cv2.imread(filepath,flags)读入一副图片 filepath:要读入图片的完整路径 flags:读入图片的标志 cv2.IMREAD_COLOR:默认参数 ...
随机推荐
- css笔记 - 张鑫旭css课程笔记之 border 篇
border地址 border特性: 能形成BFC但是不能清除浮动.但是bfc也是把子元素的margin包裹进来,但是拿自己的margin穿透没办法的. 边框宽度不支持百分比 透明border可以突破 ...
- MFC控件CTabCtrl关联变量
1.先建立一个对话框MFC应用程序,然后在工具箱里面把Tab Control控件放到对话框中的合适位置上. 再在对话框类中,将该控件绑定一个变量 用两种方法: 1 ) 自己定义成员变量 CTabCtr ...
- POP3命令与分析
http://www.cnblogs.com/crystalray/p/3302121.html POP3(Post Office Protocol 3)即邮局协议的第3个版本,它是规定个人计算机如何 ...
- Unity3D动作资源(AnimatinClip)优化
能做到去掉Scale曲线,降低浮点精度 using System; using UnityEngine; using System.Collections; using System.Collecti ...
- kettle中使用JavaScript的一个例子
最近在使用kettle的时候遇到一个问题,需要对输入的一个字段进行格式化,逻辑比较复杂(需要做替换掉指定的字符串,然后将数字部分不足四位的数前边补0等操作),kettle中没有提供直接的插件来支持复杂 ...
- Unity3D笔记二十 多媒体与网络
1 游戏音频 1.游戏音乐:如游戏背景音乐 2.游戏音效:如开枪或打怪物时“砰砰”的游戏音效 Unity 3D游戏引擎共支持4种音乐格式的文件,具体如下. aiff:适用于较短的音乐文件,可用作游戏音 ...
- backbone.js之Model篇 简单总结和深入(2)
一.模型属性的一些操作方法 1.mmodel.get() 获取属性的值 2.mmodel.set('age',5) 更新单个属性的值 mmodel.set({name:'aaa',age:6}) ...
- 生成式对抗网络GAN 的研究进展与展望
生成式对抗网络GAN的研究进展与展望.pdf 摘要: 生成式对抗网络GAN (Generative adversarial networks) 目前已经成为人工智能学界一个热门的研究方向. GAN的基 ...
- linux下配置Java_web环境
I安装JDK 1下载jdk*.bin/jdk*.rpm文件 2把安装包copy到安装目录 cp jdk*.bin /www/java 3安装JDK 如果是.bin文件,直接在复制的当前目录执行即可 . ...
- 牛客网多校赛第9场 E-Music Game【概率期望】【逆元】
链接:https://www.nowcoder.com/acm/contest/147/E 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524 ...