本文介紹了對一組照片進行PCA處理的過程和結果。本文使用OpenCV的PCA處理函數,參考了夏天的味道的部落格opencv pca。本文使用的照片來源于YelaFaces(直接Baidu就能找到在CSDN上的下載下傳連結,就不上傳了)。
一、操作步驟
PCA處理的基本步驟為:
1、擷取m個樣本,每個樣本有n個特征。
2、每個樣本作為一行,構成m*n的舉證A。
3、将矩陣轉置再乘以自己得到C=A(t)*A。
4、求出矩陣C的特征值及特征向量,特征向量即為特征臉。
二、代碼及解釋
#include "stdafx.h"
#include <string>
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
//擷取了mean_face
int num_sample = 38;
int norm_row = 64, norm_col = 56;
int num;
Mat imgs = loadImages(num);
Mat mean_face = Mat(norm_row, norm_col, CV_8UC1);
vector<int> mean_face_total;
mean_face_total.resize(norm_row * norm_col);
for (int i = 0; i < num; i++)
{
for (int j = 0; j < norm_row * norm_col; j++)
{
mean_face_total.at(j) += imgs.at<uchar>(i * norm_row * norm_col + j);
//eigen_face_total[j] += imgs.at<uchar>(i * 192 * 168 + j);
}
}
for (int j = 0; j < norm_row * norm_col; j++)
{
mean_face.at<uchar>(j) = (uchar)(mean_face_total.at(j) / num);
}
imwrite("C:/Users/dhj555/Desktop/YelaFaces/eigen_face/0001.jpg", mean_face);
imshow("eigen_face", mean_face);
//1、初始化資料
CvMat* pData = cvCreateMat(num_sample, norm_row * norm_col, CV_32FC1);
CvMat* pMean = cvCreateMat(1, norm_row * norm_col, CV_32FC1);
//每個數标志一個特征值
CvMat* pEigVals = cvCreateMat(1, min(num_sample, norm_row * norm_col), CV_32FC1);
//每行表示一個特征向量
CvMat* pEigVecs = cvCreateMat(min(num_sample, norm_row * norm_col), norm_row * norm_col, CV_32FC1);
for (int i = 0; i < num_sample; i++)
{
for (int j = 0; j < norm_row * norm_col; j++)
cvmSet(pData, i, j, imgs.at<uchar>(i * norm_row * norm_col + j));
}
//2、PCA處理
cvCalcPCA(pData, pMean, pEigVals, pEigVecs, CV_PCA_DATA_AS_ROW);
//3、選出前P個特征向量(主成份),然後投影,結果儲存在pResult中,pResult中包含了P個系數
//CvMat* pResult = cvCreateMat(num_sample, 20, CV_32FC1);
//cvProjectPCA(pData, pMean, pEigVecs, pResult);
//4、重構, 結果儲存在pRecon中
//CvMat* pRecon = cvCreateMat(num_sample, norm_row*norm_col, CV_32FC1);
//cvBackProjectPCA(pResult, pMean, pEigVecs, pRecon);
//5、顯示重構的圖像
//Mat mRecon = Mat(pRecon);
//4、顯示與儲存特征向量
for (int i = 0; i < min(num_sample, norm_row * norm_col); i++)
{
float min = LLONG_MAX, max = LLONG_MIN, span = 0.0;
for (int index = 0; index < norm_row*norm_col; index++)
{
float d = cvmGet(pEigVecs, i, index);
if (d>max)
max = d;
if (d < min)
min = d;
}
span = max - min;
Mat eigen_face = Mat(norm_row, norm_col, CV_8UC1);
for (int index = 0; index < norm_row*norm_col; index++)
{
float d = cvmGet(pEigVecs, i, index);
eigen_face.at<uchar>(index) = (d - min) / span * 255.0;
}
char buffer[128];
sprintf_s(buffer, "C:/Users/dhj555/Desktop/YelaFaces/eigen_face/001/1-000%d.jpg", i);
string imgPath(buffer);
imshow(imgPath, eigen_face);
printf("%d st:\t%f\n", i, cvmGet(pEigVals, 0, i));
imwrite(imgPath, eigen_face);
}
}
1、第9至16行:聲明變量。 2、第18至32行:讀取檔案到imgs中,總共讀取了38張人臉,并将每張人臉縮放為64*56大小,是以imgs為38行,64*56列的矩陣。 3、第34至40行:聲明了PCA計算相關的矩陣,具體說明見代碼注釋。 4、第42至46行:将imgs中的資料寫入pData中,以用于PCA計算。 5、第49行:調用OpenCV的函數計算特征值及特征向量。 6、第64至88行:根據特征值導出特征臉并儲存顯示。
三、實驗結果
求出的特征值及相應的特征臉(至上傳了前11個,總共38個,可以在這裡 下載下傳檢視所有的):
特征值 | 特征臉 |
0 st: 1512882.000000 | |
1 st: 316433.875000 | |
2 st: 268737.531250 | |
3 st: 248890.359375 | |
4 st: 177919.671875 | |
5 st: 139744.718750 | |
6 st: 112001.937500 | |
7 st: 108768.914063 | |
8 st: 85501.773438 | |
9 st: 72161.312500 | |
10 st: 67518.437500 | |
11 st: 62258.648438 |