当前位置: 首页 > news >正文

使用 C/C++、OpenCV 和 Libevent 构建联网人脸识别考勤系统 [特殊字符]‍[特殊字符]

使用 C/C++、OpenCV 和 Libevent 构建联网人脸识别考勤系统 👨‍💻

本文将详细介绍如何利用 C/C++、强大的计算机视觉库 OpenCV 和高性能的网络库 libevent,从零开始构建一个完整的人脸识别考勤系统。该系统能够通过摄像头实时识别人脸,并将考勤数据通过网络发送到服务器进行记录和管理。


核心技术栈 🛠️

  • C/C++: 主要的编程语言,以其高性能和对底层系统强大的控制力而著称。
  • OpenCV (Open Source Computer Vision Library): 用于处理所有与计算机视觉相关的任务,包括摄像头视频流的读取、人脸检测和人脸识别。
  • Libevent: 一个轻量级、事件驱动的高性能网络库,用于构建我们的考勤服务器,处理客户端(即人脸识别终端)的网络请求。
  • SQLite (可选): 一个轻量级的嵌入式数据库,用于在服务器端存储员工信息和考勤记录。

系统架构 🏛️

整个系统分为两个主要部分:

  1. 客户端 (人脸识别终端):

    • 使用 OpenCV 从摄像头捕获实时视频。
    • 进行人脸检测,识别人脸在视频帧中的位置。
    • 提取人脸特征,并与数据库中的已知人脸进行比对。
    • 识别成功后,将员工 ID 和时间戳等考勤信息发送到服务器。
  2. 服务器端:

    • 使用 libevent 构建一个稳定高效的 TCP 服务器。
    • 监听来自客户端的连接和数据请求。
    • 接收客户端发送的考勤数据。
    • 将考勤数据存入数据库(例如 SQLite)或文件中。
    • (可选) 提供 API 接口用于查询考勤记录或管理员工信息。

<center>一个简单的系统架构示意图。</center>


步骤一:环境配置 ⚙️

在开始编码之前,你需要确保开发环境中已经安装了必要的库。

安装 OpenCV

在基于 Debian/Ubuntu 的系统上,可以使用 apt 进行安装:

sudo apt-get update
sudo apt-get install build-essential cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install libopencv-dev

安装 Libevent

同样,在 Ubuntu/Debian 上:

sudo apt-get install libevent-dev

安装 SQLite3 (如果使用)

sudo apt-get install libsqlite3-dev

步骤二:人脸数据采集与训练

在进行识别之前,你需要一个包含员工人脸数据的数据库。

1. 人脸数据采集

你需要编写一个简单的脚本来采集每个员工的多张人脸图像。

  • 启动摄像头。
  • 使用 OpenCV 的 Haar 级联分类器DNN 人脸检测器 来检测人脸。
  • 当检测到人脸时,将其裁剪、转换为灰度图并保存为图像文件。文件名可以包含员工的 ID(例如 user.1.1.jpg, user.1.2.jpg…)。

关键代码片段 (data_collector.cpp):

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {VideoCapture cap(0);if (!cap.isOpened()) {cerr << "Error: Camera not found!" << endl;return -1;}CascadeClassifier face_cascade;face_cascade.load("haarcascade_frontalface_default.xml"); // 需要下载此文件int user_id = 1; // 示例用户IDint count = 0;while (count < 50) { // 采集50张图片Mat frame;cap >> frame;if (frame.empty()) break;Mat gray;cvtColor(frame, gray, COLOR_BGR2GRAY);vector<Rect> faces;face_cascade.detectMultiScale(gray, faces, 1.1, 4);for (const auto& rect : faces) {rectangle(frame, rect, Scalar(255, 0, 0), 2);Mat face_roi = gray(rect);// 调整大小以统一尺寸resize(face_roi, face_roi, Size(200, 200), 1.0, 1.0, INTER_CUBIC);string file_name = "data/user." + to_string(user_id) + "." + to_string(count) + ".jpg";imwrite(file_name, face_roi);count++;}imshow("Face Collector", frame);if (waitKey(30) >= 0) break;}return 0;
}

2. 模型训练

采集到数据后,你需要训练一个人脸识别模型。OpenCV 提供了 cv::face::LBPHFaceRecognizer (局部二值模式直方图) 算法,它对于光照变化有很好的鲁棒性。

关键代码片段 (trainer.cpp):

#include <opencv2/opencv.hpp>
#include <opencv2/face.hpp>
#include <iostream>
#include <vector>using namespace cv;
using namespace cv::face;
using namespace std;int main() {vector<Mat> images;vector<int> labels;// ... 此处添加代码以从 "data/" 目录读取所有图像和标签 ...// 示例: labels.push_back(1); images.push_back(imread("data/user.1.0.jpg", IMREAD_GRAYSCALE));Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();model->train(images, labels);model->save("attendance_model.yml"); // 保存训练好的模型cout << "Model trained successfully!" << endl;return 0;
}

步骤三:客户端开发 - 人脸识别与数据发送 👨‍💻

客户端是系统的核心交互部分。

1. 加载模型并识别人脸

程序启动时加载训练好的 .yml 模型文件。然后,实时捕获视频流,检测人脸,并使用模型进行预测。

// ... 在主循环中 ...
Ptr<LBPHFaceRecognizer> model = LBPHFaceRecognizer::create();
model->read("attendance_model.yml"); // 加载模型// ...
cvtColor(frame, gray, COLOR_BGR2GRAY);
vector<Rect> faces;
face_cascade.detectMultiScale(gray, faces, 1.1, 4);for (const auto& rect : faces) {Mat face_roi = gray(rect);resize(face_roi, face_roi, Size(200, 200));int predicted_label = -1;double confidence = 0.0;model->predict(face_roi, predicted_label, confidence);if (confidence < 70) { // 设置一个置信度阈值string name = "User " + to_string(predicted_label);putText(frame, name, rect.tl(), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);// 此处触发发送考勤数据到服务器的逻辑} else {putText(frame, "Unknown", rect.tl(), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);}rectangle(frame, rect, Scalar(255, 0, 0), 2);
}
// ...

2. 连接服务器并发送数据

当识别成功后,我们需要创建一个简单的 TCP 客户端来连接 libevent 服务器并发送数据。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>void send_attendance_data(int user_id) {int sock = 0;struct sockaddr_in serv_addr;char buffer[1024] = {0};if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {printf("\n Socket creation error \n");return;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(8080); // 服务器端口// 将IPv4地址从文本转换为二进制形式if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {printf("\nInvalid address/ Address not supported \n");return;}if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {printf("\nConnection Failed \n");return;}string message = "ATTENDANCE:ID=" + to_string(user_id);send(sock, message.c_str(), message.length(), 0);close(sock);
}

步骤四:服务器端开发 - 使用 Libevent 接收数据 🌐

服务器使用 libevent 来高效地处理来自多个客户端的并发连接。

1. 设置 Libevent 服务器

我们需要设置一个 event_base,一个 evconnlistener 来监听新的连接,并为每个连接设置读写回调函数。

关键代码片段 (server.cpp):

#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>// 读回调函数:当客户端发送数据时被调用
void read_cb(struct bufferevent *bev, void *ctx) {struct evbuffer *input = bufferevent_get_input(bev);size_t len = evbuffer_get_length(input);char *data = (char*)malloc(len + 1);evbuffer_remove(input, data, len);data[len] = '\0';printf("Received data: %s\n", data);// 在这里解析数据 "ATTENDANCE:ID=1"// 并将考勤信息写入数据库或文件// ...free(data);
}// 事件回调函数:处理连接错误或 EOF
void event_cb(struct bufferevent *bev, short events, void *ctx) {if (events & BEV_EVENT_ERROR)perror("Error from bufferevent");if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {bufferevent_free(bev);}
}// 连接监听器回调:当有新连接时被调用
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,struct sockaddr *addr, int socklen, void *ctx) {struct event_base *base = evconnlistener_get_base(listener);struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);bufferevent_enable(bev, EV_READ | EV_WRITE);
}int main() {struct event_base *base = event_base_new();if (!base) {fprintf(stderr, "Could not initialize libevent!\n");return 1;}struct sockaddr_in sin;memset(&sin, 0, sizeof(sin));sin.sin_family = AF_INET;sin.sin_addr.s_addr = htonl(0); // 监听所有接口sin.sin_port = htons(8080);     // 端口struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, NULL,LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,(struct sockaddr*)&sin, sizeof(sin));if (!listener) {perror("Couldn't create listener");return 1;}printf("Server listening on port 8080...\n");event_base_dispatch(base); // 启动事件循环evconnlistener_free(listener);event_base_free(base);return 0;
}

2. 编译与运行

你需要将所有源文件链接上对应的库来编译。

# 编译客户端
g++ -o attendance_client client_main.cpp -I/usr/include/opencv4 -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_videoio -lopencv_objdetect -lopencv_face# 编译服务器
g++ -o attendance_server server.cpp -levent

总结与展望 🚀

通过结合 OpenCV 的强大视觉处理能力和 libevent 的高性能网络功能,我们成功构建了一个功能完善且可扩展的人脸识别考勤系统。

未来可扩展的功能:

  • 活体检测: 防止使用照片或视频进行欺骗。
  • 更高级的人脸识别模型: 使用基于深度学习的模型(如 FaceNet, ArcFace)以获得更高的准确率。
  • Web 管理后台: 开发一个 Web 界面,用于管理员查看考勤报表、管理员工信息和人脸数据。
  • 客户端 GUI: 为客户端添加更友好的图形用户界面。

希望这篇教程能为你提供一个清晰的起点,祝你编码愉快!

http://www.xdnf.cn/news/975727.html

相关文章:

  • 电机控制基础,小白入门篇
  • 第三章支线六 ·数据幻域 · 状态管理与数据流
  • Android 默认第三方app运行权限(android11-13)
  • 小程序 UI 设计,怎样在方寸间实现高效交互
  • Fastapi + vue3 自动化测试平台(6):AI + Web UI的完美结合
  • 把下载的ippicv.tgz放入<opencv_build_dir>/3rdparty/ippicv/download/中cmake依然无法识别
  • 快速了解JVM的GC历史
  • 【Lua热更新知识】学习三 XLua学习
  • 【AI 时代,食品科技远未触及天花板,新一轮颠覆性突破正在酝酿】
  • 神舟笔记本Control Center无法打开风扇设置
  • Web 架构之服务网格(Service Mesh)实战解析
  • 机器视觉开发-边缘提取
  • Python爬虫(54)Python数据治理全攻略:从爬虫清洗到NLP情感分析的实战演进
  • 2025-6-9Vue3快速上手
  • ubuntu22 arm 编译安装input leap
  • 数据的聚合
  • 审计效率升级!Word一键批量给数字添加千位分隔符
  • 传统机器学习与大模型 + Prompt 的对比示例
  • eureka如何绕过 LVS 的虚拟 IP(VIP),直接注册服务实例的本机真实 IP
  • SpringMVC异步处理Servlet
  • Wyn 商业智能与 3D 大屏的深度融合应用
  • 在ARM 架构的 Mac 上 更新Navicat到17后连接Oracle时报错:未加载 Oracle 库。
  • 高频面试之6Hive
  • 机器学习算法——集成学习
  • 电路图识图基础知识-变频器控制电动机系统解析(二十四)
  • 渗透测试PortSwigger Labs:遭遇html编码和转义符的反射型XSS
  • uniapp 云打包 iOS 应用上传到 app store 商店的过程
  • ZZU-ARM汇编语言实验 34
  • 【Rust UDP编程】rust udp编程方法解析与应用实战
  • uni-app bitmap.load() 返回 code=-100