
简介这是基于Qt与OpenCV实现的人脸识别C项目源码适合计算机、通信、人工智能等相关专业学生、老师用于毕业设计、期末课程大作业或项目实战学习。项目源自个人毕设答辩评分达98分代码经过调试测试可稳定运行提供的基础框架便于二次开发可帮助读者快速理解人脸检测、特征提取与识别的完整流程。资源为ZIP压缩包共496个文件大小5.2MB。主要包含核心C源文件cpp/h、Qt界面文件ui/pro、配置文件xml/user以及大量PBM/PGM格式的人脸样本图片同时附带说明文档与辅助Python脚本覆盖从界面设计到算法实现的各个环节。工程内实现了摄像头采集、人脸框选、特征比对与结果展示等主要功能目录结构清晰便于按模块查阅修改。已有425人浏览学习适合有一定C基础并希望结合OpenCV做实际项目的读者学习借鉴也可以此为模板扩展功能完成个性化的课程设计或毕业设计。1. 为什么毕业设计选 Qt OpenCV 做人脸识别对于计算机专业或软件工程的毕业设计Qt OpenCV 的组合一直是门槛适中、展示效果好的方案。PyQt 或 Python 的 OpenCV 绑定虽然写起来更快但 C 源码的形式更容易体现工程结构、内存管理和算法理解答辩时不至于被追问底层就当机。这套项目源码通常指的是一个完整的桌面应用用 Qt 负责窗口和界面交互OpenCV 处理摄像头采集、人脸检测、特征提取和比对数据库或文件目录管理样本。它解决的是“离线环境下的人脸识别门禁或考勤”这类需求适合用来演示完整的识别流程而不是简单地贴一个人脸检测框。如果你是独立完成这个题目难点在于两套库的集成和 C 的类型转换不在算法本身。接下来会从环境搭设到参数调优给出可以直接复现的路径和常见问题排查尽量让你少踩一些已经存在的坑。2. Qt 与 OpenCV 的版本选型和编译配置先要明确一个原则OpenCV 的预编译库与 Qt 之间没有直接依赖但都依赖编译器 ABI。你用 Qt 的 MSVC 套件就必须匹配 MSVC 编译的 OpenCV用 MinGW 套件就必须自己编译或找到 MinGW 版的 OpenCV 库。这是第一个也是很多项目源码跑不起来的根本原因。2.1 版本搭配的选择与分析常见做法是 Qt 5.15.2 搭配 OpenCV 4.5.x编译器选择 msvc2019_64。这主要因为 Qt 5.15 是越来越多的源码项目默认的基础版本OpenCV 4.5 之后接口趋于稳定cv::face::LBPHFaceRecognizer这类组件在 contrib 模块里也没有大的变动。若你的 Qt 是 6.x核心功能不影响但一些旧教程里的QT core gui配置和QImage转换可能需要微调OpenCV 4.8 以后官方不再提供编译好的 contrib 包需要从源码构建。提示不要为了追求新版本而选择 OpenCV 4.12 或 Qt 6.7除非你熟悉 CMake 交叉编译。毕业设计项目源码更看重稳定可编译而不是版本号最新。如果你用的是在线安装的 Qt 工具包安装目录下通常会有Tools\CMake_64也可以单独装 CMake。OpenCV 的源码包解压后用 CMake GUI 配置设置源码目录和构建目录勾选WITH_QT和OPENCV_PYTHON等选项。但如果你只是为了跑人脸识别建议直接下载官方 Compiled 版本的 win pack 包例如opencv-4.5.5-windows.exe解压后得到build目录里面包含include和x64\vc15\bin等。2.2 在 Qt Creator 的 .pro 文件中配置 OpenCV 路径拿到项目源码后通常需要按自己的目录结构调整.pro文件。下面是一个最小可用配置QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET FaceRecognition TEMPLATE app DEFINES QT_DEPRECATED_WARNINGS # OpenCV 目录按实际安装位置修改 OPENCV_DIR D:/opencv/opencv-4.5.5-win-windows/opencv INCLUDEPATH $${OPENCV_DIR}/include LIBS -L$${OPENCV_DIR}/x64/vc15/lib CONFIG(debug, debug|release) { LIBS -lopencv_world455d } else { LIBS -lopencv_world455 }之前的 ABI 匹配原则在这里直接体现opencv_world455d和opencv_world455是 OpenCV 4.5.5 的库名分别是 debug 和 release 版本。如果你的 OpenCV 版本是 4.8 以上库名会变成opencv_world48d。另外OpenCV 从 4.5 开始类似opencv_face已经并入opencv_world库所以只要链这一个就够了。运行程序前要把opencv_world455.dll放到 exe 同目录或者把D:/opencv/.../x64/vc15/bin加入系统环境变量PATH否则启动即报“找不到 opencv_world455.dll”。编写.pro时注意不要用LIBS -L${OPENCV_DIR}/x64/vc15/lib这种写法因为变量会被当成字面路径。建议先用OPENCV_DIR D:/...定义路径再用$${OPENCV_DIR}引用。另外Qt Creator 里如果切换了构建套件比如从 MSVC 切到 MinGW必须重新设置库路径和链接的库名。MinGW 版本的opencv_world455.dll依赖libgcc_s_seh-1.dll和libstdc-6.dll这些在 Qt 的 MinGW 目录里存在但不会自动加入 PATH所以二进制发布时要把 OpenCV 的 MinGW 版 dll 和 Qt 的对应 dll 一起带上。2.3 从源码构建带 contrib 的 OpenCV如果你需要 LBPH在官方编译包里其实已经包含opencv_face不需要格外构建。但如果想使用 FaceDetectorYNYuNet或更好的 DNN 模型官方包里的 DNN 模块可以用来加载 onnx 模型。若要自己编译可以用如下 CMake 命令cmake -B build -S opencv-4.8.0 -G Visual Studio 17 2022 -A x64 \ -DCMAKE_BUILD_TYPERelease -DWITH_QTON -DWITH_OPENGLON \ -DOPENCV_EXTRA_MODULES_PATHD:/opencv_contrib-4.8.0/modules cmake --build build --config Release --target installOPENCV_EXTRA_MODULES_PATH指定的 contrib 模块路径必须和 OpenCV 版本严格一致否则会有编译宏不匹配的问题。WITH_QTON可以让 OpenCV 的imshow函数在 Qt 窗口中显示但这和你的 Qt 主界面无关。构建时间较长建议只在需要额外模块时编译。如果你更习惯用 CMake 构建 Qt 项目也可以不用.pro。CMakeList.txt 中的关键是find_package(OpenCV REQUIRED)会自动设置OpenCV_INCLUDE_DIRS和OpenCV_LIBS然后target_link_libraries(FaceRecognition ${OpenCV_LIBS})。但find_package依赖OpenCVConfig.cmake的路径配置时要把CMAKE_PREFIX_PATH指向 OpenCV 的 build 目录。这比 qmake 少了一些自定义的东西更利于跨平台。对于毕业设计源码提供.pro文件是主流因为 Qt Creator 直接双击就能编译而 CMake 还需要先配置 CMake 编译器等。2.4 验证环境是否打通在写整个项目之前先用一个不到 50 行的最小程序确认 OpenCV 和 Qt 都正常。在main.cpp写入一个加载摄像头并做基础检测的测试创建一个cv::VideoCapture(0)打开失败则输出错误读取一帧并转成QImage显示在 QLabel 上。这步能提前暴露摄像头占用、dll 缺失、图像旋转等问题。代码是一段典型的“摄像头影像标签”测试。逻辑上分为三步VideoCapture以 640x480 初始化cap.read抓帧cv::cvtColor把 BGR 转 RGB 后再构造QImage最后通过setPixmap显示到界面上。QTimer定时器每隔 30ms 触发一次更新对应 30fps 左右的刷新率。#include QApplication #include QLabel #include QTimer #include opencv2/opencv.hpp int main(int argc, char *argv[]) { QApplication a(argc, argv); cv::VideoCapture cap(0); if (!cap.isOpened()) return -1; QLabel label; label.resize(640, 480); QTimer timer; QObject::connect(timer, QTimer::timeout, []() { cv::Mat frame, rgb; cap frame; if (!frame.empty()) { cv::cvtColor(frame, rgb, cv::COLOR_BGR2RGB); QImage img(rgb.data, rgb.cols, rgb.rows, rgb.step, QImage::Format_RGB888); label.setPixmap(QPixmap::fromImage(img)); } }); timer.start(30); label.show(); return a.exec(); }注意这里QImage构造时用了rgb.step因为 Mat 在内存中可能带字节对齐其他资料里很多直接写QImage(img.data, img.cols, img.rows, QImage::Format_RGB888)当cols * 3和step不相等时图像会发生错位只改颜色顺序不解决这个问题。转回灰度或进行检测时不要忘了从QImage再转回cv::Mat的路径。3. 人脸检测与识别算法的选型Haar 还是 DNNLBPH 还是深度学习很多毕业设计答辩时老师会问“你的系统怎么区分活体和照片”。如果只是用 Haar 检测 LBPH 比对照片是会直接通过的因为将照片打印出来仍然检测得到人脸。如果想让源码里的设计更有说服力可以增加一个简单的运动检测——在检测到人脸后比较连续几帧的人脸位置和面积变化是否明显照片静止时变化很小。这个扩展不复杂但能让项目从“检测识别”上升到“防伪”的层面也避免一眼被看出只是抄了一个 Demo。拿到源码后识别流程几乎都是四个步骤从摄像头或图片中检测出人脸框将人脸区域裁剪并标准化提取特征最后和样本库比对。检测和识别是两个独立模块很多人混为一谈。标题里只写了“人脸识别”实际上源码里至少包含“检测”和“识别”两层结构。3.1 人脸检测算法对比OpenCV 里最常见的检测器是 Haar 级联分类器它使用 AdaBoost 训练级联分类器用 Haarcascade 特征快速判断一个区域是否是人脸。优点是依赖少只需要一个haarcascade_frontalface_default.xml文件CPU 上每帧几十毫秒缺点是对側脸、遮挡和暗光比较敏感检测框抖动明显。LBP 级联速度更快但准确率更低一般用于视频流实时预览。注意haarcascade_frontalface_default.xml与haarcascade_frontalface_alt2.xml的差异。default是老的低精度模型漏检率较高alt2用更多特征和更深层数精度更好速度慢一些。项目源码里如果用的是default建议替换成alt2通常不需要改动任何参数只换 XML 文件路径即可。如果源码中把检测代码写成这样cv::CascadeClassifier detector; detector.load(haarcascade_frontalface_default.xml); std::vectorcv::Rect faces; detector.detectMultiScale(gray, faces, 1.1, 5, 0, cv::Size(80, 80));这里的1.1是scaleFactor表示每次缩放比例越小检测越细致但速度越慢5是minNeighbors表示每个检测框至少被周围几个框命中才算真脸值越大漏检越多cv::Size(80, 80)是minSize低于这个尺寸的候选框会被丢弃对毕业设计来说这个值应该根据摄像头距离调整。如果你在源码里看到阈值是 1.2 或 6也不要急着改需要结合你的测试样本来定。OpenCV 4.5 之后多了一个FaceDetectorYN能直接加载深度学习模型YuNet。它比 Haar 在精度上好一个数量级且自带关键点输出。但模型、代码都要扩展不少有些源码项目不会用因为环境里还需要 ONNX Runtime 或 OpenCV DNN 模块。我的建议是先保留 Haar 或 LBP 跑通完整流程如果最后有余力再换 DNN。3.2 识别算法Eigenfaces、Fisherfaces 与 LBPH识别部分OpenCV 的contrib模块提供了三种经典算法Eigenfaces把图像拉伸成高维向量PCA 降维后投影到特征脸空间用欧氏距离判断。FisherfacesLDA 降维注重类别可分性对小样本效果比 Eigenfaces 好。LBPH提取局部二值模式直方图不需要整幅图当作全局向量对光照变化更稳健且支持在线更新样本。大多数毕业设计项目源码选的是 LBPH它和摄像头场景的匹配度最高训练速度也快。它的三个关键参数cv::Ptrcv::face::LBPHFaceRecognizer model cv::face::LBPHFaceRecognizer::create(1, 8, 8, 8);构造参数依次是radius、neighbors、grid_x、grid_y。radius 1是像素半径表示中心像素比较的邻域半径neighbors 8是采样点数越多特征越细但计算量越大grid_x和grid_y把图片划分成网格每个网格分别统计直方图网格越多对局部细节更敏感但也更容易过拟合。默认值是(1, 8, 8, 8)实际项目中如果发现识别低我一般先调grid_x grid_y 6减少划分块数增强泛化能力。用 LBPH 的流程是准备每个样本的灰度图统一缩放或截取人脸区域到相同尺寸model-train(images, labels)训练预测时model-predict(face, label, confidence)。confidence的值表示距离或相似度不同的距离度量定义不同在 LBPH 中是归一化后的直方图距离值越低越相似。很多源码只判断label是否匹配忽略confidence这是导致“陌生人也被识别”的主要原因。阈值问题会在后面单独讨论。3.3 样本库与标签管理识别系统需要维护一个样本库。简单做法是文件夹结构一个文件夹对应一个 ID里面放若干张该 ID 的人脸灰度图命名随意。加载时遍历目录用文件名或子目录名作为 label。更正规的做一个 SQLite 表存图片路径、姓名、学号/工号、时间戳。毕业设计展示用文件系统就足够了。一个关键细节是样本尺寸。LBPH 虽然不需要固定输入尺寸但为了训练和预测一致建议把所有裁剪到的人脸 resize 到(200, 200)。如果源码里有 resize 到 100x100 的并不是错误只是训练快但细节少。注意灰度图LBPH 输入必须是单通道颜色信息在这个模型里没有意义不要用彩色图。3.4 为什么不用深度学习方案很多网上的教程已经在用face_recognition或InsightFace但在纯 C Qt 的开源项目里这些库的配置复杂度大幅上升。FaceNet 等模型通常需要 TensorFlow Lite 或 ONNX Runtime跨平台发布部署时 dll 体积保持在几百 MB部署到普通教室电脑上并不方便。所以作为毕业设计源码LBPH Haar 是从“能跑”到“能讲”的平衡点。如果答辩老师问为什么不用深度学习你可以回答数据集只有几十张样本用小规模的 PCA/LBP 统计方法更容易解释且对 CPU 要求低。4. 项目核心实现摄像头采集、人脸检测与 LBPH 识别这一章会按实际源码的常见组织方式给出关键代码目的是让拿到源码的人知道该在看哪儿、改哪儿。通常源码会分为main.cpp、mainwindow.h/cpp、traindata样本图片来源、modelsXML 和训练结果等目录。没有统一标准但以下结构最不容易丢东西。4.1 主窗口类接口设计主窗口继承QMainWindow界面上至少有这几个控件用于显示画面的QLabel、控制摄像头的“打开/关闭”按钮、触发单一识别或注册采集的按钮。信号槽声明如下class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); private slots: void on_openCamera_clicked(); void on_recognize_clicked(); void on_register_clicked(); void updateFrame(); private: cv::VideoCapture capture; QTimer timer; QLabel *videoLabel; QPushButton *openCameraBtn; cv::CascadeClassifier faceCascade; };updateFrame用定时器周期性从VideoCapture读取帧而按钮点击只是开关。注意人脸识别是一个耗时过程不要在updateFrame里直接调用predict否则界面卡顿。常见做法是把capture读取到的帧放在一个成员变量currentFrame里点击“识别”时再从currentFrame中截取人脸区域执行识别而不是重新读摄像头。4.2 打开摄像头与定时器void MainWindow::on_openCamera_clicked() { if (capture.isOpened()) { timer.stop(); capture.release(); openCameraBtn-setText(打开摄像头); return; } capture.open(0); if (!capture.isOpened()) return; capture.set(cv::CAP_PROP_FRAME_WIDTH, 640); capture.set(cv::CAP_PROP_FRAME_HEIGHT, 480); timer.start(30); openCameraBtn-setText(关闭摄像头); }capture.open(0)中的0是摄像头设备索引在笔记本上通常是 0如果插了 USB 摄像头可能变成 1 或 2。CAP_PROP_FRAME_WIDTH和HEIGHT不一定生效因为摄像头驱动会返回接近的分辨率所以实际帧大小要以capture.get(CAP_PROP_FRAME_WIDTH)为准。4.3 帧转换与绘制检测框updateFrame里要完成抓帧、灰度化、detectMultiScale、转 QImage、画矩形等。代码如下void MainWindow::updateFrame() { cv::Mat frame, gray; capture frame; if (frame.empty()) return; cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); std::vectorcv::Rect faces; faceCascade.detectMultiScale(gray, faces, 1.1, 5, 0, cv::Size(80, 80)); for (const auto rect : faces) { cv::rectangle(frame, rect, cv::Scalar(0, 0, 255), 2); cv::putText(frame, face, cv::Point(rect.x, rect.y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1); } cv::Mat rgb; cv::cvtColor(frame, rgb, cv::COLOR_BGR2RGB); QImage img(rgb.data, rgb.cols, rgb.rows, rgb.step, QImage::Format_RGB888); videoLabel-setPixmap(QPixmap::fromImage(img)); }这段代码的逻辑很直观先做灰度化Cascade 检测在灰度图上完成但画框在彩色frame上。cv::rectangle的参数Scalar(0, 0, 255)是 BGR 顺序的红色如果你转成 RGB 后才画就会变成蓝色很多项目踩了这个坑。另外detectMultiScale在gray上运行不要传frame彩色图否则报类型不匹配或检测结果异常。4.4 注册采集人脸样本“注册”和“识别”是配套流程。注册需要把当前帧里检测到的人脸保存到样本目录并关联一个 label。伪代码如下void MainWindow::on_register_clicked() { if (currentFrame.empty()) return; std::vectorcv::Rect faces; cv::cvtColor(currentFrame, currentGray, cv::COLOR_BGR2GRAY); faceCascade.detectMultiScale(currentGray, faces, 1.1, 5, 0, cv::Size(80, 80)); if (faces.empty()) return; cv::Mat face currentGray(faces[0]); cv::resize(face, face, cv::Size(200, 200)); std::string dir train/sample_ std::to_string(newLabel); cv::imwrite(dir / timestamp .jpg, face); }实际项目中常常把标签统一为整数比如label_1的用户名存在label_map.txt里。单次注册只保存一帧的效果并不好至少要连续采集 10 到 20 帧并且每帧之间稍微改变点头、偏头角度这样才能覆盖到 LBPH 需要的纹理变化。如果保存的是没有裁剪或没有缩放的图训练时会报维度不一致。4.5 训练模型与预测训练通常在程序启动时自动完成或点一个“训练”按钮。训练函数大概长这样void MainWindow::trainModel() { std::vectorcv::Mat images; std::vectorint labels; loadTrainingSet(train, images, labels); // 遍历目录 model cv::face::LBPHFaceRecognizer::create(1, 8, 8, 8); model-train(images, labels); model-save(models/lbph.yml); }loadTrainingSet需要自己写规则是train下面每个子目录名是 label子目录里的所有 jpg 灰度图全部 resize 到统一大小后加入imageslabel 加入labels。训练结束后保存模型。预测的代码void MainWindow::on_recognize_clicked() { // 假设 currentGray 已从当前帧转换 std::vectorcv::Rect faces; faceCascade.detectMultiScale(currentGray, faces, 1.1, 5, 0, cv::Size(80, 80)); if (faces.empty()) { statusLabel-setText(未检测到人脸); return; } cv::Mat face currentGray(faces[0]); cv::resize(face, face, cv::Size(200, 200)); int label -1; double confidence 0.0; model-predict(face, label, confidence); // 阈值判断 if (confidence 70) { statusLabel-setText(识别为 QString::number(label)); } else { statusLabel-setText(无法识别); } }注意predict返回的confidence含义LBPH 的距离阈值没有统一标准。训练样本少、光照变化大时阈值 70 可能太低导致一直“无法识别”训练样本充足且环境一致时阈值 80 也可能太高导致陌生人被接受。这个阈值应该放到配置文件里不要写死在代码中。在识别前最好也先对face做一次直方图均衡化能轻微提升 LBPH 的稳健性。4.6 多线程还是单线程上面代码是单线程实现优点是简单缺点是识别时画面卡顿。要改进可以让QTimer一直刷新画面识别逻辑放入QtConcurrent::run或QThread工作对象通过信号把识别结果发送回主线程。如果项目源码里用的是单线程也不要贸然改因为线程切换涉及到 OpenCV Mat 深拷贝否则工作线程读取的 frame 可能被主线程覆盖。5. 参数调优与模型验证让识别真正可用很多项目源码本身能跑但准确率感人。问题往往不出在算法选型而是训练样本和参数的不一致。这一章给出一个可操作的验证方法和常见参数表。5.1 人脸框尺寸与训练尺寸对齐训练时把人脸 resize 到了 200×200预测时也必须 resize 到 200×200。如果两者尺寸不一致LBPH 内部会先 resize但结果明显变差。另外检测框需要包含完整的面部包括额头和下巴。detectMultiScale默认给出的是矩形框如果检测框过紧会截断眉毛和下颌特征信息丢失。建议做法拿到faces[0]后把矩形向外扩展 10%再做 resize。cv::Rect r faces[0]; int dx r.width * 0.1, dy r.height * 0.1; r.x std::max(0, r.x - dx / 2); r.y std::max(0, r.y - dy / 2); r.width std::min(gray.cols - r.x, r.width dx); r.height std::min(gray.rows - r.y, r.height dy); cv::Mat face gray(r);这样做的好处是避免检测框边缘紧贴皮肤导致样本纹理信息减少。但也要注意不要扩展到头发和背景太多。5.2 LBPH 参数与识别效果的关系下表是 LBPH 四参数对识别的影响参数常见值增大效果减小效果适用场景radius1-3邻域更大特征更全局抗噪强但细节丢失特征更细致对噪声敏感样本光照稳定用 1光照变化明显时试 2neighbors8-16直方图特征更细计算增大易过拟合特征粗糙速度更快默认 8采集样本充足后升到 12grid_x4-8局部信息更细需要更多训练样本全局统计更均匀所需样本少样本总数低于 50 时设 6grid_y4-8同 grid_x同 grid_x同 grid_x这里没有绝对最优必须结合“训练集 测试集”一起看。很多人把同一个人的图片既训练又预测得到 100% 正确率但换一个光线位置就崩。合理做法是每个 ID 采集 20 张15 张训练5 张测试用最终的 confidence 分布来决定阈值。5.3 用跨验证找识别阈值你可以在 Qt 里加一个“自检”按钮用程序加载训练集遍历所有测试图记录每个样本的预测 label 和 confidence。对测试集 margin 进行调整是常规操作double minConfidence 9999, maxConfidence 0; for (auto img : testImages) { int label 0; double conf 0; model-predict(img, label, conf); if (img.label ! label) { qDebug() 错误: img.label - label conf conf; } minConfidence std::min(minConfidence, conf); maxConfidence std::max(maxConfidence, conf); }如果错误样本的 confidence 大多在 70-90而正确样本在 20-50那么阈值的合理区间就是 60 左右。你还可以用model-setThreshold或直接比较 confidence 来过滤。5.4 样本采集与数据增强样本质量比参数重要得多。采集人脸时不要只拍正脸LBPH 对旋转和表情不敏感但对尺度变化敏感。每个 ID 最好覆盖不同视角和表情。由于 C 项目里没有现成的数据增强库你可以自己用 OpenCV 做一点水平翻转、小幅旋转±10°、亮度调整。代码实现很简单cv::flip(face, flipped, 1); // 水平翻转 for (int angle : {-10, 0, 10}) { cv::Mat M cv::getRotationMatrix2D(cv::Point2f(face.cols/2, face.rows/2), angle, 1.0); cv::Mat rotated; cv::warpAffine(face, rotated, M, face.size()); // 保存 rotated }增强后训练集变为原来的 6 倍接口完整度会明显上升。但注意增强只用于训练不用于预测二者要保持本质分布。5.5 失败时看的日志与关键指标调参时不要只靠肉眼观察建议在状态栏输出当前帧率、检测框坐标、confidence 和识别结果。帧率过低时优先减小scaleFactor或把分辨率降到 320x240再考虑换检测算法。检测框频繁闪烁时增大minNeighbors到 6-8。识别结果稳定但错了检查标签映射可能是训练时 label 从 0 开始而界面显示把 label 0 当成了“未识别”。6. 验证整个识别流程的一个最小可行方案运行时常见的几个问题值得单独说COULD NOT READ OR STORE XML FILE、find_cascade失败以及摄像头被占用。前两个都是路径错了。不要在代码里写相对路径haarcascade_frontalface_default.xml。因为 Qt Creator 的工作目录不一定是 exe 目录你可以用QCoreApplication::applicationDirPath()拼接出模型路径。这样发布时把 XML 和 DNN 文件放同目录即可。对于随机性较强的识别效果我建议做一个离线验证脚本提取两段视频作为样本一段用来训练一段用来模拟测试。比对着网上的参数表改数值直观得多。具体步骤是先录制 30 秒正脸视频通过检测框截取人脸帧每 5 帧保存一张前 20 张用于训练后 10 张用于预测。然后看预测出来的 label 和 confidence 是否有明显抖动。如果 confidence 在正确样本上始终大于阈值说明阈值设得太低如果陌生人也能通过说明阈值太高。另一个实用技巧是把 LBPH 的grid_x和grid_y与图片尺寸关联。当图片是 200×200 时grid_x8, grid_y8意味着每个格子只有 25×25 像素脸部纹理局部信息粒度很粗糙。如果你发现模型对表情太敏感可以把 resize 尺寸拉到 160×160 并对网格为 4×4让统计区域更大。训练样本数量小于 30 时不要过度使用高维度特征否则过拟合。最后发布毕业设计时别忘了把models里的lbph.yml一起带上并在代码里用cv::FileStorage检查模型文件是否成功打开。如果程序崩溃在model-predict先确认face是 CV_8UC1 且非空。那些在 320 分辨率摄像头下能运行的参数换成 1280 后要重新调整 minNeighbors 和 minSize因为同样的距离下人脸像素尺寸会翻倍。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。