• OpenCV笔记--人脸识别算法Eigenfaces和Fisherfaces


    目录

    1--前言

    2--处理ORL数据集

    3--Eigenfaces复现过程

    4--Fisherfaces复现过程

    5--分析


    1--前言

    ①SYSU模式识别课程作业

    ②配置:基于Windows11、OpenCV4.5.5、VSCode、CMake参考OpenCV配置方式

    ③原理及源码介绍:Face Recognition with OpenCV

    ④数据集:ORL Database of Faces

    2--处理ORL数据集

    ①源码:

    1. import sys
    2. import os.path
    3. if __name__ == "__main__":
    4. BASE_PATH = './ORL/att_faces/orl_faces/'
    5. SEPARATOR = ";"
    6. dir_txt = open("./dir.txt", 'w')
    7. label = 0
    8. for dirname, dirnames, filenames in os.walk(BASE_PATH):
    9. # dirname当前路径; dirnames当前路径下所有目录名(不包含子目录);filenames当前路径下的所有文件名(不包含子目录)
    10. for subdirname in dirnames: # 遍历每一个目录
    11. subject_path = os.path.join(dirname, subdirname)
    12. for filename in os.listdir(subject_path):
    13. abs_path = "%s/%s" % (subject_path, filename)
    14. print("%s%s%d" % (abs_path, SEPARATOR, label))
    15. dir_txt.write(abs_path)
    16. dir_txt.write(SEPARATOR)
    17. dir_txt.write(str(label))
    18. dir_txt.write("\n")
    19. label = label + 1
    20. dir_txt.close()

    ②运行及结果:

    python create_csv.py

    3--Eigenfaces复现过程

    ①源码:

    1. // 引用依赖
    2. #include "opencv2/core.hpp"
    3. #include "opencv2/face.hpp"
    4. #include "opencv2/highgui.hpp"
    5. #include "opencv2/imgproc.hpp"
    6. #include
    7. #include
    8. #include
    9. // 使用相应的命名空间
    10. using namespace cv;
    11. using namespace cv::face;
    12. using namespace std;
    13. // 标准化函数
    14. static Mat norm_0_255(InputArray _src) {
    15. Mat src = _src.getMat();
    16. // Create and return normalized image:
    17. Mat dst;
    18. switch(src.channels()) {
    19. case 1:
    20. cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
    21. break;
    22. case 3:
    23. cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
    24. break;
    25. default:
    26. src.copyTo(dst);
    27. break;
    28. }
    29. return dst;
    30. }
    31. // 读取CSV文件函数
    32. static void read_csv(const string& filename, vector& images, vector<int>& labels, char separator = ';') {
    33. std::ifstream file(filename.c_str(), ifstream::in);
    34. if (!file) {
    35. string error_message = "No valid input file was given, please check the given filename.";
    36. CV_Error(Error::StsBadArg, error_message);
    37. }
    38. string line, path, classlabel;
    39. while (getline(file, line)) {
    40. stringstream liness(line);
    41. getline(liness, path, separator);
    42. getline(liness, classlabel);
    43. if(!path.empty() && !classlabel.empty()) {
    44. images.push_back(imread(path, 0));
    45. labels.push_back(atoi(classlabel.c_str()));
    46. }
    47. }
    48. }
    49. int main(int argc, const char *argv[]) {
    50. //检查argc是否符合要求
    51. if (argc < 2) {
    52. cout << "usage: " << argv[0] << " " << endl;
    53. exit(1);
    54. }
    55. string output_folder = ".";
    56. if (argc == 3) {
    57. output_folder = string(argv[2]);
    58. }
    59. // CSV文件的路径
    60. string fn_csv = string(argv[1]);
    61. // 初始化存储imgs和labels的向量
    62. vector images;
    63. vector<int> labels;
    64. // 读取CSV文件
    65. try {
    66. read_csv(fn_csv, images, labels);
    67. } catch (const cv::Exception& e) {
    68. cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
    69. exit(1);
    70. }
    71. // 判断img数目是否符合要求
    72. if(images.size() <= 1) {
    73. string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
    74. CV_Error(Error::StsError, error_message);
    75. }
    76. // images的高度
    77. int height = images[0].rows;
    78. // 从训练集中选择一张图片作为测试集
    79. Mat testSample = images[images.size() - 1];
    80. int testLabel = labels[labels.size() - 1];
    81. images.pop_back();
    82. labels.pop_back();
    83. // 创建模型,使用PCA特征脸算法
    84. Ptr model = EigenFaceRecognizer::create();
    85. model->train(images, labels); // 训练模型
    86. int predictedLabel = model->predict(testSample); // 使用测试集测试模型
    87. // 打印准确率
    88. string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
    89. cout << result_message << endl;
    90. // 获取模型的特征值
    91. Mat eigenvalues = model->getEigenValues();
    92. // 展示特征向量
    93. Mat W = model->getEigenVectors();
    94. // 从训练集中获取样本均值
    95. Mat mean = model->getMean();
    96. // 根据argc判断进行展示或保存操作
    97. if(argc == 2) {
    98. imshow("mean", norm_0_255(mean.reshape(1, images[0].rows)));
    99. } else {
    100. imwrite(format("%s/mean.png", output_folder.c_str()), norm_0_255(mean.reshape(1, images[0].rows)));
    101. }
    102. // 显示或保存特征脸
    103. for (int i = 0; i < min(10, W.cols); i++) {
    104. string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
    105. cout << msg << endl;
    106. // 获取特征向量
    107. Mat ev = W.col(i).clone();
    108. // resize成原始大小,并归一化到0-255
    109. Mat grayscale = norm_0_255(ev.reshape(1, height));
    110. // 显示图像并应用Jet颜色图以获得更好的观感。
    111. Mat cgrayscale;
    112. applyColorMap(grayscale, cgrayscale, COLORMAP_JET);
    113. // 根据argc判断进行展示或保存操作
    114. if(argc == 2) {
    115. imshow(format("eigenface_%d", i), cgrayscale);
    116. } else {
    117. imwrite(format("%s/eigenface_%d.png", output_folder.c_str(), i), norm_0_255(cgrayscale));
    118. }
    119. }
    120. // 在一些预定义的步骤中显示或保存图像重建的过程:
    121. for(int num_components = min(W.cols, 10); num_components < min(W.cols, 300); num_components+=15) {
    122. // 从模型中分割特征向量
    123. Mat evs = Mat(W, Range::all(), Range(0, num_components));
    124. Mat projection = LDA::subspaceProject(evs, mean, images[0].reshape(1,1));
    125. Mat reconstruction = LDA::subspaceReconstruct(evs, mean, projection);
    126. // 归一化
    127. reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
    128. // 根据argc判断进行展示或保存操作
    129. if(argc == 2) {
    130. imshow(format("eigenface_reconstruction_%d", num_components), reconstruction);
    131. } else {
    132. imwrite(format("%s/eigenface_reconstruction_%d.png", output_folder.c_str(), num_components), reconstruction);
    133. }
    134. }
    135. // 如果没有写入输出文件夹,则等待键盘输入
    136. if(argc == 2) {
    137. waitKey(0);
    138. }
    139. return 0;
    140. }

    ②编译过程:

    CMakeLists.txt如下:

    1. cmake_minimum_required(VERSION 3.24) # 指定 cmake的 最小版本
    2. project(test) # 设置项目名称
    3. find_package(Opencv REQUIRED)
    4. INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})
    5. add_executable(eigenfaces_demo eigenfaces.cpp) # 生成可执行文件
    6. target_link_libraries(eigenfaces_demo ${OpenCV_LIBS} ) # 设置target需要链接的库
    1. mkdir build
    2. cd build
    3. cmake ..
    4. cd ..
    5. mingw32-make

    ③运行及结果展示:

    ./eigenfaces_demo.exe ./dir.txt ./Engenfaces_Result

    特征图:(简单修改源程序生成的文件名,再按顺序进行拼接即可生成拼接图,拼接程序参考

    重建过程:

    均值图:

    4--Fisherfaces复现过程

    ①源码:

    1. // 引用依赖
    2. #include "opencv2/core.hpp"
    3. #include "opencv2/face.hpp"
    4. #include "opencv2/highgui.hpp"
    5. #include "opencv2/imgproc.hpp"
    6. #include
    7. #include
    8. #include
    9. // 使用相应的命名空间
    10. using namespace cv;
    11. using namespace cv::face;
    12. using namespace std;
    13. // 标准化函数
    14. static Mat norm_0_255(InputArray _src) {
    15. Mat src = _src.getMat();
    16. Mat dst;
    17. switch(src.channels()) {
    18. case 1:
    19. cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
    20. break;
    21. case 3:
    22. cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
    23. break;
    24. default:
    25. src.copyTo(dst);
    26. break;
    27. }
    28. return dst;
    29. }
    30. // 读取csv文件函数
    31. static void read_csv(const string& filename, vector& images, vector<int>& labels, char separator = ';') {
    32. std::ifstream file(filename.c_str(), ifstream::in);
    33. if (!file) {
    34. string error_message = "No valid input file was given, please check the given filename.";
    35. CV_Error(Error::StsBadArg, error_message);
    36. }
    37. string line, path, classlabel;
    38. while (getline(file, line)) {
    39. stringstream liness(line);
    40. getline(liness, path, separator);
    41. getline(liness, classlabel);
    42. if(!path.empty() && !classlabel.empty()) {
    43. images.push_back(imread(path, 0));
    44. labels.push_back(atoi(classlabel.c_str()));
    45. }
    46. }
    47. }
    48. int main(int argc, const char *argv[]) {
    49. //检查argc是否符合要求
    50. if (argc < 2) {
    51. cout << "usage: " << argv[0] << " " << endl;
    52. exit(1);
    53. }
    54. string output_folder = ".";
    55. if (argc == 3) {
    56. output_folder = string(argv[2]);
    57. }
    58. // CSV文件的路径
    59. string fn_csv = string(argv[1]);
    60. // 初始化存储imgs和labels的向量
    61. vector images;
    62. vector<int> labels;
    63. // 读取CSV文件
    64. try {
    65. read_csv(fn_csv, images, labels);
    66. } catch (const cv::Exception& e) {
    67. cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
    68. exit(1);
    69. }
    70. // 判断img数目是否符合要求
    71. if(images.size() <= 1) {
    72. string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
    73. CV_Error(Error::StsError, error_message);
    74. }
    75. // images的高度
    76. int height = images[0].rows;
    77. // 从训练集中选择一张图片作为测试集
    78. Mat testSample = images[images.size() - 1];
    79. int testLabel = labels[labels.size() - 1];
    80. images.pop_back();
    81. labels.pop_back();
    82. // 创建模型,使用LDA线性判别分析
    83. Ptr model = FisherFaceRecognizer::create();
    84. model->train(images, labels); // 训练模型
    85. int predictedLabel = model->predict(testSample); // 使用测试集测试模型
    86. // 打印准确率
    87. string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
    88. cout << result_message << endl;
    89. // 获取模型的特征值
    90. Mat eigenvalues = model->getEigenValues();
    91. // 展示特征向量
    92. Mat W = model->getEigenVectors();
    93. // 从训练集中获取样本均值
    94. Mat mean = model->getMean();
    95. // 根据argc判断进行展示或保存操作
    96. if(argc == 2) {
    97. imshow("mean", norm_0_255(mean.reshape(1, images[0].rows)));
    98. } else {
    99. imwrite(format("%s/mean.png", output_folder.c_str()), norm_0_255(mean.reshape(1, images[0].rows)));
    100. }
    101. // 显示或保存特征脸
    102. for (int i = 0; i < min(16, W.cols); i++) {
    103. string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
    104. cout << msg << endl;
    105. // 获取特征向量
    106. Mat ev = W.col(i).clone();
    107. // resize成原始大小,并归一化到0-255
    108. Mat grayscale = norm_0_255(ev.reshape(1, height));
    109. // 显示图像并应用Jet颜色图以获得更好的观感。
    110. Mat cgrayscale;
    111. applyColorMap(grayscale, cgrayscale, COLORMAP_BONE);
    112. // 根据argc判断进行展示或保存操作
    113. if(argc == 2) {
    114. imshow(format("fisherface_%d", i), cgrayscale);
    115. } else {
    116. imwrite(format("%s/fisherface_%d.png", output_folder.c_str(), i), norm_0_255(cgrayscale));
    117. }
    118. }
    119. // 在一些预定义的步骤中显示或保存图像重建的过程:
    120. for(int num_component = 0; num_component < min(16, W.cols); num_component++) {
    121. // 从模型中分割特征向量
    122. Mat ev = W.col(num_component);
    123. Mat projection = LDA::subspaceProject(ev, mean, images[0].reshape(1,1));
    124. Mat reconstruction = LDA::subspaceReconstruct(ev, mean, projection);
    125. // 归一化
    126. reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
    127. // 根据argc判断进行展示或保存操作
    128. if(argc == 2) {
    129. imshow(format("fisherface_reconstruction_%d", num_component), reconstruction);
    130. } else {
    131. imwrite(format("%s/fisherface_reconstruction_%d.png", output_folder.c_str(), num_component), reconstruction);
    132. }
    133. }
    134. // 如果没有写入输出文件夹,则等待键盘输入
    135. if(argc == 2) {
    136. waitKey(0);
    137. }
    138. return 0;
    139. }

    ②编译过程:

    CMakeLists.txt如下:

    1. cmake_minimum_required(VERSION 3.24) # 指定 cmake的 最小版本
    2. project(test) # 设置项目名称
    3. find_package(Opencv REQUIRED)
    4. INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})
    5. #add_executable(eigenfaces_demo eigenfaces.cpp) # 生成可执行文件
    6. #target_link_libraries(eigenfaces_demo ${OpenCV_LIBS} ) # 设置target需要链接的库
    7. add_executable(fisherfaces_demo fisherfaces.cpp) # 生成可执行文件
    8. target_link_libraries(fisherfaces_demo ${OpenCV_LIBS} ) # 设置target需要链接的库
    1. mkdir build
    2. cd build
    3. cmake ..
    4. cd ..
    5. mingw32-make

    ③运行及结果展示:

    ./fisherfaces_demo.exe ./dir.txt ./Fisherfaces_Result

    特征图:

    重建过程:

    均值图:

    5--分析

    未完待续!

  • 相关阅读:
    Node.js初步学习
    怎么用CSS画一个爱心?
    C++中(封闭)类的定义及使用特性---知识要点篇1
    Java进阶03 IO基础
    15. Redis 持久化
    【深度学习实验】前馈神经网络(final):自定义鸢尾花分类前馈神经网络模型并进行训练及评价
    一个工作薄中快速新建多个数据表
    Photoshop_00000
    三、nginx两种压缩配置[gzip]
    论文投稿指南——中文核心期刊推荐(电子、通信技术)
  • 原文地址:https://blog.csdn.net/weixin_43863869/article/details/127703544