本教程将基于带您完成从环境配置到最终运行程序的全部流程。
第 0 步:准备您的系统
首先,确保您的 Arch Linux 系统是最新的。这是一个好习惯,可以避免很多潜在的软件版本冲突问题。
打开您的终端 (Konsole),输入以下命令并按回车。
Bash
sudo pacman -Syu
sudo: 以管理员权限运行命令,因为安装软件需要这个权限。pacman: Arch Linux 的包管理器,相当于您系统的“应用商店”。-Syu:-S代表同步 (Sync),-y代表刷新软件包列表,-u代表升级 (Update) 所有已安装的包。
系统会提示您输入密码,输入后按回车即可。等待系统更新完成。
第 1 步:安装必要的开发工具和库
我们需要 C++ 编译器、项目构建工具 (CMake) 和核心的图像处理库 (OpenCV)。
在终端中输入以下命令:
Bash
sudo pacman -S base-devel cmake opencv
base-devel: 这是一个软件包组,包含了gcc(C++ 编译器),make(编译工具) 等一系列最基础的开发工具。cmake: 一个跨平台的项目构建工具,它能读取我们后续编写的配置文件 (CMakeLists.txt) 并生成make所需的Makefile。opencv: 我们项目最核心的依赖库,提供了所有图像处理和人脸识别的功能。
第 2 步:创建项目文件夹结构
现在,我们需要为项目创建一个干净整洁的工作空间。
-
创建一个主文件夹。我们把它放在您的主目录 (
~) 下,并命名为FaceRecProject。Bash
# cd ~ 会确保您当前在主目录下 cd ~ mkdir FaceRecProject -
进入这个主文件夹。之后的所有操作都在这里进行。
Bash
cd FaceRecProject -
创建项目所需的子文件夹。
Bash
mkdir build mkdir data mkdir cascadesbuild: 用于存放所有编译过程中产生的临时文件和最终生成的可执行程序,让主目录保持干净。data: 用于存放我们提供给程序学习的人脸照片。cascades: 用于存放 OpenCV 官方提供的人脸检测模型文件。
第 3 步:下载人脸检测模型
我们需要一个预训练好的模型来告诉程序“人脸长什么样”。
在终端中(确保您仍在 FaceRecProject 文件夹下),运行以下命令:
Bash
wget -P ./cascades https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_alt.xml
wget: 一个从网络上下载文件的命令行工具。-P ./cascades:-P参数告诉wget把下载的文件放到指定的文件夹里,也就是我们刚创建的cascades文件夹。https://...: 这是模型文件的下载地址。
第 4 步:编写项目代码
现在我们要创建三个核心的文本文件:构建脚本和两个 C++ 源代码文件。
-
创建构建脚本
CMakeLists.txtBash
# 使用 nano 文本编辑器创建一个新文件 nano CMakeLists.txt将下面的代码完整地复制并粘贴到
nano编辑器窗口中:CMake
cmake_minimum_required(VERSION 3.10) project(FaceRecognition) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(OpenCV REQUIRED COMPONENTS core highgui imgproc objdetect videoio face) include_directories(${OpenCV_INCLUDE_DIRS}) add_executable(train_model train_model.cpp) target_link_libraries(train_model ${OpenCV_LIBS} stdc++fs) add_executable(recognize_face recognize_face.cpp) target_link_libraries(recognize_face ${OpenCV_LIBS})粘贴完成后,按
Ctrl+X,然后按Y,最后按回车来保存并退出。 -
创建训练程序
train_model.cppBash
nano train_model.cpp将下面的代码完整地复制并粘贴进去:
C++
#include <iostream> #include <fstream> #include <sstream> #include <vector> #include <string> #include <filesystem> #include <opencv2/core.hpp> #include <opencv2/imgcodecs.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/face.hpp> using namespace cv; using namespace cv::face; using namespace std; namespace fs = std::filesystem; void create_dataset_csv(const string& data_path, const string& output_file) { ofstream csv_file(output_file); int label = 0; for (const auto& entry : fs::directory_iterator(data_path)) { if (entry.is_directory()) { for (const auto& img_entry : fs::directory_iterator(entry.path())) { if (img_entry.is_regular_file()) { csv_file << img_entry.path().string() << ";" << label << endl; } } label++; } } csv_file.close(); cout << "Dataset CSV file created at " << output_file << endl; } static void read_csv(const string& filename, vector <Mat>& images, vector<int>& labels, char separator = ';') { ifstream file(filename.c_str(), ifstream::in); string line, path, classlabel; while (getline(file, line)) { stringstream liness(line); getline(liness, path, separator); getline(liness, classlabel); if(!path.empty() && !classlabel.empty()) { images.push_back(imread(path, IMREAD_GRAYSCALE)); labels.push_back(atoi(classlabel.c_str())); } } } int main() { string data_dir = "./data"; string csv_file_path = "./data.csv"; create_dataset_csv(data_dir, csv_file_path); vector <Mat> images; vector <int> labels; try { read_csv(csv_file_path, images, labels); } catch (const cv::Exception& e) { cerr << "Error opening file \"" << csv_file_path << "\". Reason: " << e.msg << endl; exit(1); } if(images.size() <= 1) { cerr << "Error: This program needs at least 2 images to train. Please add more images to your data directory." << endl; exit(1); } Ptr <LBPHFaceRecognizer> model = LBPHFaceRecognizer::create(); cout << "Training the model..." << endl; model->train(images, labels); string model_filename = "face_model.yml"; model->save(model_filename); cout << "Model trained and saved to " << model_filename << endl; return 0; }同样按
Ctrl+X,Y,回车保存退出。 -
创建识别程序
recognize_face.cppBash
nano recognize_face.cpp将下面的代码完整地复制并粘贴进去:
C++
#include <iostream> #include <map> #include <string> #include <filesystem> #include <chrono> #include <iomanip> #include <sstream> #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/objdetect.hpp> #include <opencv2/face.hpp> using namespace std; using namespace cv; using namespace cv::face; namespace fs = std::filesystem; int main() { string cascade_path = "./cascades/haarcascade_frontalface_alt.xml"; string model_path = "face_model.yml"; CascadeClassifier face_cascade; if (!face_cascade.load(cascade_path)) { cerr << "Error: Could not load face cascade." << endl; return -1; } Ptr <FaceRecognizer> model = LBPHFaceRecognizer::create(); try { model->read(model_path); } catch (const cv::Exception& e) { cerr << "Error: Could not load trained model. Have you trained the model yet?" << endl; return -1; } map<int, string> label_to_name; label_to_name[0] = "Person 1"; // 可在此处修改识别出的名字 label_to_name[1] = "Person 2"; string strangers_dir = "strangers"; if (!fs::exists(strangers_dir)) { fs::create_directory(strangers_dir); } auto last_stranger_save_time = chrono::steady_clock::now(); const auto save_cooldown = chrono::seconds(5); VideoCapture cap(0); if (!cap.isOpened()) { cerr << "Error: Could not open camera." << endl; return -1; } Mat frame; while (true) { cap >> frame; if (frame.empty()) break; Mat gray; cvtColor(frame, gray, COLOR_BGR2GRAY); equalizeHist(gray, gray); vector <Rect> faces; face_cascade.detectMultiScale(gray, faces, 1.1, 4, 0|CASCADE_SCALE_IMAGE, Size(30, 30)); for (const auto& face_rect : faces) { Mat face_roi = gray(face_rect); Mat resized_face; resize(face_roi, resized_face, Size(92, 112), 1.0, 1.0, INTER_CUBIC); int predicted_label = -1; double confidence = 0.0; model->predict(resized_face, predicted_label, confidence); rectangle(frame, face_rect, Scalar(0, 255, 0), 2); string text; if (predicted_label != -1 && confidence < 85.0) { string name = label_to_name.count(predicted_label) ? label_to_name[predicted_label] : "Known Person"; text = format("%s (Conf: %.2f)", name.c_str(), confidence); } else { text = "Stranger"; auto now = chrono::steady_clock::now(); if (chrono::duration_cast<chrono::seconds>(now - last_stranger_save_time) > save_cooldown) { auto time_now_t = chrono::system_clock::to_time_t(chrono::system_clock::now()); stringstream ss; ss << put_time(localtime(&time_now_t), "%Y%m%d_%H%M%S"); string filename = strangers_dir + "/stranger_" + ss.str() + ".jpg"; imwrite(filename, frame(face_rect)); cout << "Stranger detected! Image saved to " << filename << endl; last_stranger_save_time = now; } } Point text_pos(face_rect.x, face_rect.y - 10); putText(frame, text, text_pos, FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 255, 0), 2); } imshow("Face Recognition", frame); if (waitKey(10) == 'q') break; } return 0; }按
Ctrl+X,Y,回车保存退出。
第 5 步:准备训练数据
这是教程序认识人脸的关键。
-
在
data文件夹里,为每一个人创建一个单独的子文件夹。文件夹的名字最好是英文,比如zhangsan,lisi。Bash
# 例如,创建两个人的文件夹 mkdir data/zhangsan mkdir data/lisi -
将每个人的照片(JPG 或 PNG 格式)放入对应的文件夹。每个人的照片建议至少10张,越多越好,最好包含不同角度、不同表情、不同光线下的照片。
您可以用文件管理器来复制粘贴,或者用 cp 命令。
第 6 步:编译项目
现在,所有代码和数据都准备好了,让我们来编译它。
-
进入
build文件夹。Bash
cd build -
运行 CMake。它会读取上一级目录的
CMakeLists.txt文件并生成配置。Bash
cmake ....代表上一级目录。
-
运行 make。它会根据 CMake 生成的配置,调用 C++ 编译器来编译源代码。
Bash
make等待编译完成。如果一切顺利,您不会看到任何红色的错误信息。
第 7 步:运行程序
编译成功后,build 文件夹里会生成两个可执行文件:train_model 和 recognize_face。
-
首先,训练模型。我们需要回到项目主目录来运行,这样程序才能找到
data文件夹。Bash
# 回到主目录 FaceRecProject cd .. # 运行训练程序 ./build/train_model程序会读取
data文件夹里的照片,训练模型,然后在主目录生成一个face_model.yml文件。 -
最后,运行实时识别程序!
Bash
# 确保仍在主目录 ./build/recognize_face程序会请求打开您的摄像头。授权后,一个名为 “Face Recognition” 的窗口就会弹出。它会实时地检测和识别人脸,并将陌生人的头像保存在
strangers文件夹。按
q键可以关闭窗口并退出程序。
恭喜您!您已经从零开始,成功地在 Arch Linux 上构建并运行了一个属于您自己的 C++ 人脸识别应用。
Comments 2 条评论
帅啊
