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;
}
识别出来对应的人脸是哪个分类的:
平均脸:
特征脸:
重建脸:
投影人脸不同时候的重建: