十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ubuntu下Qt+OpenCV人脸识别实战:从环境配置到部署全攻略

Ubuntu下Qt+OpenCV人脸识别实战:从环境配置到部署全攻略 简介在Ubuntu系统下基于Qt与OpenCV的人脸识别项目资料面向计算机相关专业学生及开发者。资源包含完整可运行的工程代码覆盖人脸检测、视频识别、图片输入等核心功能并配有界面设计文件、头文件及说明文档可作为毕业设计、课程设计或人脸识别入门的学习参考。压缩包内共23个文件包括7个C源文件、6个界面UI文件、6个头文件以及工程配置和README等整体大小约21KB结构紧凑便于阅读和二次修改。目前已有75人学习浏览项目经导师指导并获答辩高分认可代码通过测试功能稳定。对于希望快速掌握Qt与OpenCV集成开发、理解人脸识别流程的读者这份资料提供了从界面搭建到算法调用的完整范例能够有效节省前期资料搜集与调试时间。1. 基于Qt和OpenCV人脸识别程序在Ubuntu下最该先解决的不是算法拿到一个写着“详细文档全部资料高分项目.zip”的压缩包解压后往往是几个 .cpp、.ui、一堆 xml 和一段写得很满的 word 说明。但双击运行不了报错不是“cannot find -lopencv_face”就是“QV4L2Capture: ioctl failed”。这个标题对应的是非常典型的课程设计用 Qt 做界面OpenCV 调摄像头做人脸检测与识别最终交付文档。真正的难点不在人脸识别算法本身而在 Ubuntu 系统下把摄像头权限、OpenCV 的 contrib 模块、Qt 的界面刷新线程这三件事接起来。适合正在做课设、或者想拿一个能现场演示的 Qt 项目去求职的开发者。我会按环境、原理、代码再到发布的顺序把能跑通的那条路讲清楚。2. 摄像头到人脸框OpenCV调用相机的原理与Qt线程模型OpenCV 的 VideoCapture 不是直接操作硬件的库它在 Linux 上通过 V4L2 或 GStreamer 跟内核驱动打交道。理解这一点很多“摄像头打开失败”就能自己排查。2.1 OpenCV 调用相机原理V4L2 与 VideoCapture 参数执行cv::VideoCapture cap(0, cv::CAP_V4L2)时OpenCV 会去打开/dev/video0用 ioctl 查询设备能力请求设置像素格式再用 mmap 映射驱动里的缓存。之后每次cap.read(frame)都从驱动缓冲区把图像复制回用户态。因此权限、设备占用、格式不支持都会在打开或读帧阶段暴露。下面是一段最小可运行的打开代码。#include opencv2/opencv.hpp #include iostream int main() { // 显式指定 V4L2 后端避免默认后端选错 cv::VideoCapture cap(0, cv::CAP_V4L2); if (!cap.isOpened()) { std::cerr camera open failed std::endl; return -1; } cap.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc(M, J, P, G)); cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280); cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720); cap.set(cv::CAP_PROP_FPS, 30); cv::Mat frame; cap.read(frame); if (frame.empty()) { std::cerr empty frame std::endl; return -1; } return 0; }显式指定CAP_V4L2可以避免 OpenCV 自动后端选到错误的 GStreamer 管线fourcc要在宽高之前设置部分 UVC 摄像机会在改分辨率后重置格式。cap.set只是下发请求最终是否生效要看cap.get(cv::CAP_PROP_FRAME_WIDTH)返回的值。read是阻塞调用默认会等待下一帧所以直接把它放进 Qt 界面主线程会卡住拖拽、按钮点击这些交互。摄像头常用参数与优先级参数典型值说明CAP_PROP_FOURCCMJPG优先使用硬件编码可提高分辨率帧率CAP_PROP_FRAME_WIDTH1280请求宽度需同时检查实际值CAP_PROP_FRAME_HEIGHT720请求高度比例要匹配CAP_PROP_FPS30帧率请求不保证精确CAP_PROP_BUFFERSIZE1设置最小缓冲可降低延迟CAP_PROP_BUFFERSIZE不是所有驱动都支持设置失败时 OpenCV 不会报错只知道get返回 0。如果测到画面延迟明显建议先把它设为 1再做后续图像处理。2.2 人脸检测与人脸识别算法选型Haar、LBPH 还是深度学习模型先理清“检测”和“识别”是两步。检测是在画面中找到人脸框识别是把框内图像与注册的人脸比对。OpenCV 内置的 Haar 级联用 AdaBoost 训练得到模型文件小、CPU 推理快适合正脸和光线均匀的室内场景LBPH 识别器训练快几十个人的小库也能跑正好匹配课设的数据量。如果追求侧脸、遮挡下的效果用 OpenCV DNN 模块加载 YuNet 检测模型更合适但模型文件有十几 MB部署时容易丢。下面是常见方案的横向对比。方案检测/识别模型形态CPU 开销适用场景Haar cascade检测自带 xml低正脸、环境可控LBPH识别自训练 yml低小规模人脸库DNN YuNet检测onnx中侧脸、遮挡改善FaceNet/ArcFace识别大型模型高大规模高精度我一般会让项目先以 Haar LBPH 掉通haarcascade_frontalface_default.xml 直接在 OpenCV 安装目录里训练 LBPH 不需要 GPU写文档时公式也容易讲清楚。detectMultiScale的两个参数决定演示效果scaleFactor从 1.1 到 1.2 之间调越小越慢但越容易召回minNeighbors默认 5误检多就调到 8漏检多就降到 3。参数要结合演示环境光线再调整不能照抄默认值。2.3 Qt 的 QTimer 里读帧用 QImage 把 cv::Mat 送给 QLabel常见做法不是给摄像头单独开一个无限 while 循环而是用 QTimer 每 30ms 触发一次读帧和检测。这样不破坏 Qt 事件循环按钮依然响应。核心代码片段如下。QTimer timer; connect(timer, QTimer::timeout, this, [this]() { cv::Mat frame; if (!cap.read(frame)) return; std::vectorcv::Rect faces; cv::Mat gray; cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); cascade.detectMultiScale(gray, faces, 1.1, 5); for (const auto r : faces) { cv::rectangle(frame, r, cv::Scalar(0, 255, 0), 2); } cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB); QImage img(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888); videoLabel-setPixmap(QPixmap::fromImage(img.copy())); }); timer.start(33);这里必须调用img.copy()否则 QImage 不会共享 cv::Mat 的引用计数下一帧cap.read会重写同一块内存界面出现花屏或残影。frame.step用于处理 BGR 转 RGB 后每行字节数不等于 width*3 的情况漏了它会在宽高不是 4 的倍数时出现斜条纹。人脸框画在彩色图上而检测在灰度图上做两个图不要互相覆盖。理解完线程模型下一步就是环境安装。3. Ubuntu 上把 Qt和OpenCV装到能编译版本、CMake 和摄像头权限这一章如果不是全新机器也可以先看现有版本再决定是否卸载重装。重点是把 opencv_face 这个容易缺的模块提前识别出来。3.1 用系统包安装 Qt 和 OpenCV判断是否需要自编译Ubuntu 仓库的 libopencv-dev 通常不带 contrib 模块也就是没有 opencv2/face.hpp。LBPH 就用不了。先用一条命令判断ls /usr/include/opencv4/opencv2/face.hpp如果这个文件不存在说明系统包不完整。接下来安装基础环境sudo apt update sudo apt install -y build-essential cmake git pkg-config sudo apt install -y qtbase5-dev qttools5-dev-tools qtchooser sudo apt install -y libopencv-dev libopencv-videoio-dev安装后验证版本qmake -v pkg-config --modversion opencv4在 Ubuntu 20.04 上 OpenCV 一般是 4.2Ubuntu 22.04 是 4.5。对课程设计来说版本不是越新越好系统包和 Qt 5 配合最省事。缺少face.hpp时有两个方向源码编译 OpenCV with contrib时间大概二十分钟或者放弃 LBPH改用特征点匹配和 SVM 做识别。前者需要准备编译依赖工程量偏大但如果你要把“详细文档”写成带 opencv_face 源码编译记录反而更能体现工作量。3.2 CMakeLists.txt 模板AUTOMOC 与 Qt/OpenCV 链接顺序用 Qt OpenCV 的 C 工程CMake 是最好的构建方式。最关键的是把CMAKE_AUTOMOC打开否则包含Q_OBJECT的类会报虚表或信号槽未定义错误。一个最小模板如下。cmake_minimum_required(VERSION 3.16) project(FaceRecognitionQt LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) find_package(Qt5 COMPONENTS Widgets REQUIRED) find_package(OpenCV REQUIRED) add_executable(facerec main.cpp mainwindow.cpp mainwindow.h ) target_include_directories(facerec PRIVATE ${OpenCV_INCLUDE_DIRS}) target_link_libraries(facerec PRIVATE Qt5::Widgets ${OpenCV_LIBS} )find_package(OpenCV REQUIRED)在 Ubuntu 上默认找的是 libopencv-dev 安装的 cmake 配置它会给出 OpenCV_LIBS 变量。不写成OpenCV::opencv_core这种形式是为了兼容系统包版本源码编译的 OpenCV 也支持但变量形式更通用。如果在 Ubuntu 22.04 上找不到 Qt5先确认安装的是 qtbase5-dev 而不是 Qt6Qt5 的 CMake 配置在/usr/lib/x86_64-linux-gnu/cmake/Qt5。3.3 摄像头打不开权限、设备节点与 V4L2 格式排查开发机上摄像头不存在往往不是代码问题。先看设备节点ls -l /dev/video* sudo usermod -aG video $USER重新登录后权限生效。如果是 VMware 或 VirtualBox 虚拟机还要检查 USB 摄像头是否已经连接到客户机连接后v4l2-ctl --list-devices才有输出。常用排查表如下。现象原因处理isOpened() 返回 false无设备节点或节点权限不足usermod -aG video重登read() 一直返回 false摄像头被其他进程占用lsof /dev/video0画面窄且拉伸设置了不支持的宽高比例用 v4l2-ctl --list-formats-ext 查支持规格色彩发绿交错FOURCC 没有设置显式设置 MJPG再设宽高如果两步都排除接下来用v4l2-ctl --list-formats-ext -d /dev/video0查看摄像头实际支持的格式。很多普通摄像头在 1280x720 下只支持 MJPG如果 VideoCapture 默认走 YUYV就无法打开高分模式。解决办法是在cap.open后立刻设置CAP_PROP_FOURCC为MJPG然后设置分辨率。全部环境就绪后再进入界面与识别代码。4. 从空白窗口到可演示的人脸识别Qt界面、图像处理与考勤记录实现直接写纯代码创建的界面省去 .ui 文件的路径问题。识别流程放在 QTimer 回调里与第 2 章一致。这一章给出能直接编译的骨架。4.1 最小界面定义QLabel 显示画面按钮切换摄像头开关在MainWindow头文件里声明要用的成员class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); private slots: void openCamera(); void closeCamera(); void onTimeout(); private: cv::VideoCapture cap; cv::CascadeClassifier cascade; cv::Ptrcv::face::LBPHFaceRecognizer recognizer; QTimer timer; QLabel *videoLabel; QPushButton *openBtn; };构造函数里创建 central widget、一个 QLabel 和一个 QPushButton再把 clicked 连接到 openCamera/slot。用代码创建界面的好处是发布时少带一个 .ui 文件但布局代码会多一些。控件和信号的关系如下表。控件成员信号槽打开按钮openBtnclicked()openCamera()关闭按钮closeBtnclicked()closeCamera()视频标签videoLabel无由 timer 更新实际演示时最好把两个按钮并排放关闭后第一时间释放摄像头避免下次打开失败。4.2 detectMultiScale 的参数调优与画框技巧完整的人脸检测部分在灰度图上做 histogram equalization 能减少侧光影响。代码cv::Mat frame, gray, eq; cap.read(frame); cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); cv::equalizeHist(gray, eq); std::vectorcv::Rect faces; cascade.detectMultiScale(eq, faces, 1.1, 5, cv::CASCADE_SCALE_IMAGE, cv::Size(80, 80)); for (const auto r : faces) { cv::rectangle(frame, r, cv::Scalar(0, 255, 0), 2); }注意minSize设置为 80x80能过滤很多背景纹理误检如果你要检测 3 米外的小脸这个值要调低。scaleFactor从 1.1 改到 1.05 会显著增加计算量但小脸召回率会好一点。画框颜色用cv::Scalar(0, 255, 0)是 BGR 空间下的绿色如果想用 Qt 的红色习惯要写成(0, 0, 255)。4.3 LBPH 人脸识别训练数据目录、标签映射与置信度阈值先按“每个人的照片一个目录”整理训练集然后用下面的代码遍历目录、读取灰度图并 resize 到统一尺寸。这一步很关键因为 LBPH 对输入大小不敏感但不统一会让训练容易过拟合。#include filesystem std::vectorcv::Mat images; std::vectorint labels; int label 0; for (const auto entry : std::filesystem::directory_iterator(train)) { if (!entry.is_directory()) continue; for (const auto file : std::filesystem::directory_iterator(entry.path())) { cv::Mat img cv::imread(file.path().string(), cv::IMREAD_GRAYSCALE); cv::resize(img, img, cv::Size(100, 100)); images.push_back(img); labels.push_back(label); } label; } auto recognizer cv::face::LBPHFaceRecognizer::create(); recognizer-train(images, labels); recognizer-save(face_model.yml);代码中用std::filesystem需要 C17所以第 3 章的 CMake 里写了 C17。训练完成后predict会把 confidence 返回LBPH 的置信度是距离值越小越像。课程项目中通常设 80 为阈值小于 80 显示对应人员大于等于 80 显示 unknown。这个值要在实际摄像头上试拍几张再定室内光线差时 80 可能过严。4.4 识别结果记录到 CSV并同步到 QTableWidget在每次识别到已知人脸时把时间、标签、置信度追加到本地文件这是答辩时的直观展示点。写文件适合用 QFile 和 QTextStream。void MainWindow::saveRecord(int label, double confidence) { QFile f(records.csv); if (!f.open(QIODevice::Append | QIODevice::Text)) return; QTextStream out(f); out QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss) , label , QString::number(confidence, f, 2) \n; f.close(); }同步 QTableWidget 就是 insertRow 和 setItem不再展开。要注意的是识别成功与否需要做“去重”同一个 ID 在 5 秒内重复出现在画面里只记录第一次否则演示时记录表会被刷屏。可以保存一个QHashint, QDateTime lastSeen在 saveRecord 里判断时间差。至此项目骨架已经能展示“摄像头打开-画框-显示名字-记录表格”完整流程。5. 从能跑到高分QtOpenCV人脸识别程序的模型路径、发布包与自测技巧许多项目源码在 Qt Creator 里跑得好好的评审机器上却打不开摄像头或找不到模型文件。最后一章集中解决这三个最丢分的问题。5.1 模型文件路径不要依赖当前目录而是绑定可执行文件位置Haar 模型文件和 LBPH 训练结果通常放在项目根目录但双击运行时当前目录是启动目录不在项目目录。修复方式是用QCoreApplication::applicationDirPath()拼接。QString modelDir QCoreApplication::applicationDirPath() /models; cascade.load(modelDir.toStdString() /haarcascade_frontalface_default.xml); recognizer-read(modelDir.toStdString() /face_model.yml);发布时把models目录和可执行文件放在同一级。如果程序放在/usr/local/bin这个目录可能是只读的课程设计场景下建议直接放可写目录。5.2 用 ldd 和 patchelf 生成可拷贝的发布包Qt 和 OpenCV 都是动态库只拷可执行文件必然报错。先确认依赖ldd facerec | grep -E opencv|Qt5把列出的 so 文件复制到lib/目录后用 patchelf 写死运行路径。mkdir -p lib plugins/platforms cp /usr/lib/x86_64-linux-gnu/libopencv_core.so.4.5* lib/ cp /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5* lib/ patchelf --set-rpath $ORIGIN/lib facerec再创建qt.conf让 Qt 知道去哪里找插件。[Paths] Plugins plugins将编译出的plugins/platforms/libqxcb.so拷贝到对应目录。目标电脑上如果还提示 xcb 库缺失需要用ldd plugins/platforms/libqxcb.so补齐 xcb 相关的系统库。5.3 识别演示前的两分钟自测启动程序后先确认三件事摄像头指示灯是否亮起人脸框是否贴合人脸切换到已登记人脸时名字是否在 1 秒内出现。然后用命令观察内存与 CPU。while true; do ps -C facerec -o pid,rss,%cpu,etime --no-headers sleep 2 done如果 RSS 持续上涨优先检查 QImage 是否每次 copy 新的内存如果 CPU 一直 100%把timer.setInterval(33)调整为 50或将检测放到cv::UMat中。另一个常见问题是画面像素格式偏绿此时应回头确认摄像头是否真的进入了 MJPG 格式而不是靠cap.get(CAP_PROP_FOURCC)检查。最后可以在records.csv同目录输出 run.log把模型加载失败和摄像头打开失败两个分支写清楚这份日志会让项目的可信度明显提升。本文还有配套的精品资源点击获取
返回列表