在模式辨認領域中,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化到0~255,歸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