使用 C++/OpenCV 构建员工在岗状态监控系统

⚠️ 重要声明与道德考量:
本文旨在提供一个基于计算机视觉技术的概念验证项目,仅供学习和技术研究之用。在真实环境中使用此类系统涉及严重的员工隐私、数据安全和道德问题。在任何情况下部署员工监控系统都必须严格遵守当地法律法规,并获得被监控人员的明确、知情同意。滥用此类技术可能会侵犯个人隐私,并对工作环境产生负面影响。开发者应始终将道德和隐私放在首位。

引言

在某些需要确保人员在特定位置的场景中(例如关键控制室、生产线或特定服务台),自动化在岗状态检测可以提高管理效率和安全性。传统的人工巡查或打卡系统存在延迟和人力成本。计算机视觉为此提供了一种非接触式、实时的解决方案。

本文将详细介绍如何使用 C++ 和强大的 OpenCV 库,构建一个基础的监控系统,该系统能够通过摄像头自动检测一个预设的工位(或任何“感兴趣区域”)是否有人,并实时显示“在岗”或“离岗”状态。

核心思路与技术选择

我们的目标是判断一个特定区域(工位)是否有人。最直接的实现思路是:

  1. 定义感兴趣区域 (Region of Interest, ROI): 在摄像头的视野中,手动划定一个或多个矩形区域,每个矩形代表一个工位。
  2. 行人检测: 在视频的每一帧中,运行一个人体检测算法,找出画面中所有人的位置。
  3. 关联判断: 检查检测到的人体边界框 (Bounding Box) 是否与我们预先定义的工位 ROI 有足够的重叠。
  4. 状态更新: 如果某个工位 ROI 与人体边界框发生重叠,我们将其标记为“在岗”;否则,标记为“离岗”。

在技术选择上,OpenCV 提供了多种行人检测的方法。对于这个项目,我们将采用 HOG (Histogram of Oriented Gradients, 面向梯度直方图) 特征描述子。选择 HOG 的原因是:

  • 内置与高效: OpenCV 内置了基于 HOG 的行人检测器,无需额外配置复杂的深度学习框架。它在CPU上的运行速度相对较快,足以满足实时处理的基本需求。
  • 经典与稳定: HOG 是一种在计算机视觉领域经过验证的、非常经典的特征提取方法,对于姿态相对正直的行人有较好的检测效果。

环境准备

开始编码前,请确保您的开发环境满足以下条件:

  • C++ 编译器: 一个现代的 C++ 编译器,如 g++, Clang, 或 MSVC。
  • OpenCV 库: 安装 OpenCV 库 (推荐 3.x 或 4.x版本)。您需要确保在编译时能够链接到 OpenCV 的核心模块 (core)、图像处理模块 (imgproc)、视频IO模块 (videoio)、高级GUI模块 (highgui) 以及对象检测模块 (objdetect)。

项目结构

这是一个非常基础的项目,所有代码都可以在一个文件中完成。

in-position-monitoring/
|-- main.cpp

C++/OpenCV 代码实现

以下是完整的 main.cpp 代码。它会从摄像头读取视频流,并在一个预设的工位区域进行在岗状态检测。

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

int main() {
    // 1. 初始化视频捕获 (0代表默认摄像头, 也可替换为视频文件路径)
    cv::VideoCapture cap(0);
    if (!cap.isOpened()) {
        std::cerr << "错误: 无法打开摄像头或视频文件。" << std::endl;
        return -1;
    }

    // 2. 初始化OpenCV内置的基于HOG的行人检测器
    cv::HOGDescriptor hog;
    hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector());

    // 3. 定义工位区域 (ROI - Region of Interest)
    // 根据您的摄像头画面和工位位置调整 x, y, width, height
    // 例如,一个在画面左侧的工位
    cv::Rect workstation_roi(50, 100, 250, 350); 

    std::cout << "在岗状态监测已启动... 按 'q' 键退出。" << std::endl;

    cv::Mat frame;
    while (true) {
        // 读取一帧
        cap >> frame;
        if (frame.empty()) {
            std::cerr << "错误: 无法读取视频帧。" << std::endl;
            break;
        }

        // 4. 行人检测
        std::vector<cv::Rect> found_people;
        std::vector<double> weights;
        // detectMultiScale 参数: 图像, 检测结果, 置信度, winStride, padding, scale, finalThreshold
        hog.detectMultiScale(frame, found_people, weights, 0, cv::Size(8,8), cv::Size(0,0), 1.05, 2.0);

        // 5. 状态判断
        bool is_occupied = false;
        // 遍历所有检测到的人
        for (const auto& person_rect : found_people) {
            // 计算检测到的人体框与工位ROI的交集
            cv::Rect intersection = person_rect & workstation_roi;
            
            // 如果交集面积大于0 (即有重叠), 则认为工位有人
            if (intersection.area() > 0) {
                is_occupied = true;
                // 为了可视化,用绿色绘制检测到的人
                 cv::rectangle(frame, person_rect, cv::Scalar(0, 255, 0), 2);
            }
        }

        // 6. 可视化绘制
        std::string status_text;
        cv::Scalar roi_color;
        
        if (is_occupied) {
            status_text = "Status: ZAI GANG (Occupied)";
            roi_color = cv::Scalar(0, 255, 0); // 绿色
        } else {
            status_text = "Status: LI GANG (Vacant)";
            roi_color = cv::Scalar(0, 0, 255); // 红色
        }

        // 绘制工位ROI区域
        cv::rectangle(frame, workstation_roi, roi_color, 2);
        // 在ROI上方显示工位标签
        cv::putText(frame, "Workstation 1", cv::Point(workstation_roi.x, workstation_roi.y - 10),
                    cv::FONT_HERSHEY_SIMPLEX, 0.5, roi_color, 2);
        
        // 在画面左上角显示整体状态
        cv::putText(frame, status_text, cv::Point(15, 30),
                    cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 255, 255), 2);

        // 显示结果
        cv::imshow("In-Position Monitoring System", frame);

        // 按 'q' 退出
        if (cv::waitKey(1) == 'q') {
            break;
        }
    }

    // 释放资源
    cap.release();
    cv::destroyAllWindows();

    return 0;
}

代码解析

  1. 初始化视频捕获: cv::VideoCapture cap(0); 打开默认的摄像头。您可以将其替换为视频文件路径,如 cv::VideoCapture("office.mp4");
  2. 初始化HOG检测器: hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); 加载OpenCV预训练好的行人检测模型。
  3. 定义工位ROI: cv::Rect workstation_roi(...) 创建一个矩形来代表工位。这是您唯一需要根据实际场景主要调整的参数。 您需要观察摄像头画面,确定工位所在的大致像素坐标 (x, y) 及其 宽度高度
  4. 行人检测: hog.detectMultiScale(...) 是核心检测函数。它在图像上以不同尺度滑动窗口,寻找与HOG行人模型匹配的区域。结果会返回一个包含所有检测到的人体位置的 cv::Rect 向量。
  5. 状态判断:
    • 我们设置一个布尔变量 is_occupied,默认为 false
    • 遍历检测到的所有人 found_people
    • 关键逻辑是 cv::Rect intersection = person_rect & workstation_roi;& 运算符被重载用于计算两个矩形的交集。
    • 如果 intersection.area() > 0,说明检测到的人体框与工位区域有重叠,我们将 is_occupied 设为 true
  6. 可视化:
    • 根据 is_occupied 的状态,我们改变工位ROI框的颜色(在岗为绿色,离岗为红色)和显示的文本。
    • 使用 cv::rectangle 绘制矩形框,使用 cv::putText 在画面上添加文本,提供直观的视觉反馈。

编译与运行

您可以使用 g++pkg-config 来编译代码,这是在Linux环境下最便捷的方式:

g++ main.cpp -o monitoring_system `pkg-config --cflags --libs opencv4`

(如果您的OpenCV版本是3.x或名称不同,请将 opencv4 替换为正确的名称,如 opencv)

编译成功后,运行程序:

./monitoring_system

程序将打开摄像头,并在窗口中实时显示检测结果。

局限性与改进方向

这个基础系统存在一些明显的局限性,但同时也为我们指明了改进的方向:

  • 检测精度: HOG检测器对遮挡、非标准姿态(如弯腰、侧身)和光照变化比较敏感,可能导致漏检或误检。
  • 身份不可知: 系统只知道“有人”,但不知道是“谁”。它无法区分员工与访客。
  • 单一视角: 单个摄像头的视角有限,如果员工被高大的物体(如显示器、隔板)遮挡,检测就会失败。
  • 简单的逻辑: 系统只判断“在”或“不在”的瞬时状态。无法判断“暂时离开”(如去喝水)和“长时间离岗”的区别。

改进方向:

  • 更换更强的检测模型: 使用基于深度学习的模型,如 YOLO (You Only Look Once)SSD (Single Shot MultiBox Detector)。这些模型精度更高,对遮挡和姿态变化的鲁棒性更好,但需要更强的计算能力(最好有GPU)。
  • 增加时间维度逻辑: 引入一个计时器。当一个工位从“在岗”变为“离岗”时,启动计时。只有当离岗时间超过一个预设阈值(如5分钟)时,才真正触发“离岗”警报。
  • 多目标与多工位: 修改代码以支持在一个画面中定义多个ROI,并分别跟踪每个工位的状态。
  • 结合人脸识别: 如果业务需求和隐私法规允许,可以集成人脸识别技术,实现身份绑定,做到“指定员工在指定工位”的检测。

结论

本文通过一个完整的C++/OpenCV项目,展示了如何从零开始构建一个基础的在岗状态监控系统。我们利用了经典的HOG行人检测算法和简单的几何逻辑,实现了对特定区域人员存在与否的判断。尽管这个原型很简单,但它清晰地揭示了计算机视觉技术在自动化监控领域的应用潜力,并为探索更复杂、更智能的系统奠定了坚实的基础。最重要的是,它也提醒我们,在探索技术可能性的同时,必须时刻保持对隐私和道德规范的敬畏。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐