OpenCV | PCA人脸识别

主成分分析(PCA)——Eigenfaces(特征脸)——函数:createEigenFaceRecognizer()
PCA:低维子空间是使用主元分析找到的,找具有最大方差的哪个轴。
缺点:若变化基于外部(光照),最大方差轴不一定包括鉴别信息,不能实行分类。

程序实现了PCA的人脸识别,显示平均脸,特征脸以及重建。步骤以及解析都写在注释里面了。

#include <opencv2/opencv.hpp>
#include <opencv2/face.hpp>
#include <iostream>
using namespace cv;
using namespace std;
using namespace cv::face;

vector<Mat> g_mImages;
vector<int> g_nLabels;

void getSrcImagesData()
{
	string filename = "D:/source/images/orl_faces/images.csv";
	ifstream file(filename.c_str(), ifstream::in);
	if (!file)
		puts("could not load file.");

	string line, path, classlabel;

	char separator = ';';
	while (getline(file, line))
	{
		stringstream lines(line);
		getline(lines, path, separator);
		getline(lines, classlabel);
		if (!path.empty() && !classlabel.empty())
		{
			//printf("path = %s, label = %d\n", path.c_str(), atoi(classlabel.c_str()));
			g_mImages.push_back(imread(path, 0));
			g_nLabels.push_back(atoi(classlabel.c_str()));
		}
	}
}

int main()
{
	getSrcImagesData();
	if (g_mImages.size() < 1 || g_nLabels.size() < 1)
	{
		puts("load data error.");
		return -1;
	}
	int height = g_mImages[0].rows;
	int width = g_mImages[0].cols;
	//printf("height = %d, width = %d\n", height, width);

	// 2. 用最后一图像作为测试对象
	Mat testSample = g_mImages[g_mImages.size() - 1];
	int testLabel = g_nLabels[g_nLabels.size() - 1];
	g_mImages.pop_back();
	g_nLabels.pop_back();

	// 3. 训练样本
	Ptr<BasicFaceRecognizer> model = createEigenFaceRecognizer();
	model->train(g_mImages, g_nLabels);
	// 4. 识别测试样本
	int predictLabel = model->predict(testSample);
	printf("actual label = %d, predict label = %d\n", testLabel, predictLabel);

	// 5. 显示平均脸
	Mat eigenValues = model->getEigenValues();	// 特征值
	Mat eigenVector = model->getEigenVectors(); // 特征向量
	Mat mean = model->getMean(); // 一行的平均脸
	Mat meanFace = mean.reshape(0, height);
	printf("mean.height = %d, mean.width = %d, channels = %d\n", meanFace.rows, meanFace.cols, meanFace.channels());
	normalize(meanFace, meanFace, 0, 255, NORM_MINMAX, CV_8UC1);
	imshow("meanFace", meanFace);

	// 6. 显示特征脸
	for (int i = 0; i < min(10, eigenVector.cols); i++)
	{
		Mat ev = eigenVector.col(i).clone();
		Mat grayScale;
		Mat eigenFace = ev.reshape(1, height);
		normalize(eigenFace, grayScale, 0, 255, NORM_MINMAX,CV_8UC1);
		Mat colorFace;
		applyColorMap(grayScale, colorFace, COLORMAP_JET);
		char* winTitle = new char[128 + 1];
		sprintf(winTitle, "eigenFace_%d", i);
		imshow(winTitle, colorFace);
	}

	// 7. 重建人脸
	//	A. 把所有的训练数据投影到PCA子空间
	//	B. 把待识别图像投影到PCA子空间
	//	C. 找到训练数据投影后的向量和待识别图像投影后的向量最近的那个。
	for (int i = min(10, eigenVector.cols); i < min(eigenVector.cols, 60); i += 15)
	{
		Mat evs = Mat(eigenVector, Range::all(), Range(0, i)); // 提取特征向量的所有行 和 第0到i列,不包括i
		// 投影, 可以改变g_mImages[50]的下标,这样就是重建对应的人脸
		Mat projection = LDA::subspaceProject(evs, mean, g_mImages[50].reshape(1, 1)); 
		Mat reconstruction = LDA::subspaceReconstruct(evs, mean, projection); // 重建
		Mat result = reconstruction.reshape(1, height);
		normalize(result, result, 0, 255, NORM_MINMAX, CV_8UC1);
		char* winTitle = new char[128 + 1];
		sprintf(winTitle, "reconFace_%d", i);
		imshow(winTitle, result);
	}

	waitKey(0);
	return 0;
}

识别出来对应的人脸是哪个分类的:
在这里插入图片描述
平均脸:
在这里插入图片描述
特征脸:
在这里插入图片描述
重建脸:
在这里插入图片描述
投影人脸不同时候的重建:
在这里插入图片描述
在这里插入图片描述