天天看點

PCA算法:從一組照片中擷取特征臉(特征向量)

本文介紹了對一組照片進行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
PCA算法:從一組照片中擷取特征臉(特征向量)
1 st:   316433.875000
PCA算法:從一組照片中擷取特征臉(特征向量)
2 st:   268737.531250
PCA算法:從一組照片中擷取特征臉(特征向量)
3 st:   248890.359375
PCA算法:從一組照片中擷取特征臉(特征向量)
4 st:   177919.671875
PCA算法:從一組照片中擷取特征臉(特征向量)
5 st:   139744.718750
PCA算法:從一組照片中擷取特征臉(特征向量)
6 st:   112001.937500
PCA算法:從一組照片中擷取特征臉(特征向量)
7 st:   108768.914063
PCA算法:從一組照片中擷取特征臉(特征向量)
8 st:   85501.773438
PCA算法:從一組照片中擷取特征臉(特征向量)
9 st:   72161.312500
PCA算法:從一組照片中擷取特征臉(特征向量)
10 st:  67518.437500
PCA算法:從一組照片中擷取特征臉(特征向量)
11 st:  62258.648438
PCA算法:從一組照片中擷取特征臉(特征向量)