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