C语言项目实战:基于MogFace-large的简易门禁系统原型

张开发
2026/4/5 6:03:06 15 分钟阅读

分享文章

C语言项目实战:基于MogFace-large的简易门禁系统原型
C语言项目实战基于MogFace-large的简易门禁系统原型最近在捣鼓一些嵌入式设备上的视觉应用发现人脸识别是个挺有意思的方向。手头正好有块开发板和摄像头就想着能不能用C语言自己搭一个简易的门禁系统原型。这个想法听起来有点复杂但其实拆解开来核心就是三件事用摄像头抓到画面、在画面里找到人脸、然后根据结果去控制一把锁。我选择了MogFace-large这个人脸检测模型它精度不错而且有现成的C语言接口很适合我们这种追求性能和可控性的场景。整个项目会用到OpenCV处理图像调用模型进行推理最后通过串口去控制一个模拟的电磁锁。下面我就把这个从零搭建的过程一步步分享出来希望能给想用C语言做类似项目的朋友一些参考。1. 项目整体思路与准备工作做任何项目动手之前先想清楚要做什么、需要什么能省去后面很多麻烦。这个简易门禁系统的核心逻辑非常直观持续捕捉视频流 - 检测每一帧中是否有人脸 - 如果有人脸则触发开锁信号。1.1 核心组件与工作流程我们可以把系统想象成一个流水线眼睛摄像头负责“看”使用OpenCV捕获实时视频。大脑MogFace-large模型负责“认”分析图像判断里面有没有人脸并标出位置。手串口/电磁锁负责“动”收到大脑的指令后执行开锁或关锁动作。整个流程的代码逻辑大致会像下面这样形成一个循环初始化摄像头 初始化人脸检测模型 初始化串口连接电磁锁 while(1) { 从摄像头读取一帧图像 如果读取成功 { 将图像送入人脸检测模型 分析检测结果 if (检测到人脸) { 通过串口发送“开锁”指令 等待一段时间模拟开门持续时间 通过串口发送“关锁”指令 } } 等待一小段时间控制循环频率 } 释放所有资源1.2 开发环境与工具准备工欲善其事必先利其器。我们需要准备好以下“工具”编译与开发环境Linux系统如Ubuntu这是最方便的选择包管理工具能轻松安装依赖。我使用的是Ubuntu 20.04。GCC编译器Linux自带的C语言编译器。代码编辑器VS Code、Vim等按自己习惯来。核心库安装OpenCV用于图像捕获、处理和显示。可以通过apt安装sudo apt update sudo apt install libopencv-devMogFace-large C SDK这是关键。你需要从模型的提供方获取预编译的C语言库文件通常是.a或.so文件以及对应的头文件.h。请将其放置在项目目录下例如libs/和include/文件夹内。硬件与模拟USB摄像头一个普通的电脑摄像头即可。电磁锁与控制器实物开发需要单片机如STM32、Arduino和电磁锁模块。为了简化原型验证我们将用串口助手软件模拟电磁锁。在电脑上运行一个串口助手监听某个虚拟串口如/dev/ttyS0或COM1我们的程序向这个串口发送特定字符串如“OPEN”和“CLOSE”串口助手收到后打印出来就相当于执行了开关锁动作。准备好这些我们的“车间”就算布置完毕了可以开始动手组装了。2. 分步实现核心功能模块接下来我们把整个系统拆成几个独立的模块来逐个实现这样代码结构清晰调试起来也方便。2.1 摄像头图像捕获模块这个模块的任务是打开摄像头并源源不断地获取图像帧。我们用OpenCV来做这件事非常简单。#include opencv2/opencv.hpp #include stdio.h // 初始化并打开摄像头 cv::VideoCapture init_camera(int camera_index 0) { cv::VideoCapture cap(camera_index); // 0通常代表默认摄像头 if (!cap.isOpened()) { fprintf(stderr, “错误无法打开摄像头 %d\n” camera_index); exit(-1); } printf(“摄像头初始化成功\n”); return cap; } // 从摄像头捕获一帧 cv::Mat capture_frame(cv::VideoCapture cap) { cv::Mat frame; cap frame; // 读取一帧到frame对象 if (frame.empty()) { printf(“警告捕获到空帧跳过处理。\n”); } return frame; }这段代码里cv::VideoCapture是OpenCV里负责视频捕获的类cv::Mat是存储图像数据的矩阵。cap frame这个操作就像从水龙头接一杯水一样把一帧图像数据“接”到frame变量里。2.2 人脸检测模型集成模块这是项目的核心。我们需要加载MogFace-large模型并编写函数将图像喂给模型然后解析输出结果。假设我们拿到的SDK提供了一个头文件mogface.h和动态库libmogface.so。// face_detector.h #ifndef FACE_DETECTOR_H #define FACE_DETECTOR_H #include stdint.h // 定义人脸框结构 typedef struct { float x1, y1, x2, y2; // 左上角和右下角坐标 float score; // 置信度 } FaceBox; // 初始化检测器 void* init_face_detector(const char* model_path); // 执行人脸检测 int detect_faces(void* detector, const unsigned char* image_data, int width, int height, int channels, FaceBox** boxes); // 释放检测器资源 void release_face_detector(void* detector); #endif// face_detector.c #include “face_detector.h” #include “mogface.h” // 来自SDK的头文件 #include stdlib.h void* init_face_detector(const char* model_path) { // 调用SDK接口初始化模型 void* handle mogface_init(model_path); if (!handle) { fprintf(stderr, “错误加载MogFace模型失败 [%s]\n” model_path); } return handle; } int detect_faces(void* detector, const unsigned char* image_data, int width, int height, int channels, FaceBox** boxes) { // 调用SDK接口进行检测返回人脸框数组 MogFaceResult* results NULL; int num_faces mogface_detect(detector, image_data, width, height, channels, results); if (num_faces 0) { // 分配内存并转换结果到我们的结构体 *boxes (FaceBox*)malloc(num_faces * sizeof(FaceBox)); for (int i 0; i num_faces; i) { (*boxes)[i].x1 results[i].x1; (*boxes)[i].y1 results[i].y1; (*boxes)[i].x2 results[i].x2; (*boxes)[i].y2 results[i].y2; (*boxes)[i].score results[i].score; } free(results); // 释放SDK返回的内存 } return num_faces; } void release_face_detector(void* detector) { if (detector) { mogface_release(detector); } }注意mogface_init,mogface_detect,mogface_release这些函数名和参数需要根据你实际获取的MogFace SDK文档进行调整。核心思想是封装一层让我们的主程序调用起来更清晰。2.3 串口控制模拟模块在原型阶段我们用串口通信来模拟控制电磁锁。在Linux下串口设备像文件一样操作。// serial_controller.h #ifndef SERIAL_CONTROLLER_H #define SERIAL_CONTROLLER_H // 初始化串口 int serial_init(const char* port, int baudrate); // 通过串口发送指令 int send_lock_command(int fd, const char* command); // 关闭串口 void serial_close(int fd); #endif// serial_controller.c #include “serial_controller.h” #include stdio.h #include string.h #include unistd.h #include fcntl.h #include termios.h int serial_init(const char* port, int baudrate) { int fd open(port, O_RDWR | O_NOCTTY | O_NDELAY); if (fd -1) { perror(“打开串口失败”); return -1; } struct termios options; tcgetattr(fd, options); cfsetispeed(options, baudrate); cfsetospeed(options, baudrate); options.c_cflag | (CLOCAL | CREAD); // 本地连接启用接收 options.c_cflag ~PARENB; // 无奇偶校验 options.c_cflag ~CSTOPB; // 1位停止位 options.c_cflag ~CSIZE; options.c_cflag | CS8; // 8位数据位 options.c_lflag ~(ICANON | ECHO | ECHOE | ISIG); // 原始模式 options.c_iflag ~(IXON | IXOFF | IXANY); // 关闭流控 options.c_oflag ~OPOST; // 原始输出 tcsetattr(fd, TCSANOW, options); printf(“串口 %s 初始化成功 (波特率: %d)\n” port, baudrate); return fd; } int send_lock_command(int fd, const char* command) { int len strlen(command); int n write(fd, command, len); if (n ! len) { perror(“发送串口指令失败”); return -1; } printf(“[串口指令] 已发送: %s\n” command); // 通常需要一个小延迟确保数据发送完毕 tcdrain(fd); return 0; } void serial_close(int fd) { if (fd 0) { close(fd); } }在主程序中我们会调用send_lock_command(fd, “OPEN”)来模拟开锁调用send_lock_command(fd, “CLOSE”)来模拟关锁。3. 系统整合与主程序逻辑各个模块准备好之后就像拼乐高一样把它们按照最初设计的流程组装起来。主程序main.c负责协调所有模块。// main.c #include opencv2/opencv.hpp #include “face_detector.h” #include “serial_controller.h” #include unistd.h // for sleep int main() { printf(“ 简易门禁系统原型启动 \n”); // 1. 初始化摄像头 cv::VideoCapture cap init_camera(0); // 设置一个合适的分辨率太高会影响检测速度 cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); // 2. 初始化人脸检测器 const char* model_path “./models/mogface_large.bin”; // 模型路径 void* face_detector init_face_detector(model_path); if (!face_detector) { serial_close(fd); cap.release(); return -1; } // 3. 初始化串口模拟电磁锁 const char* serial_port “/dev/ttyS0”; // 根据你的虚拟串口修改 int baudrate 9600; int serial_fd serial_init(serial_port, baudrate); if (serial_fd 0) { release_face_detector(face_detector); cap.release(); return -1; } // 4. 主循环 cv::Mat frame; FaceBox* boxes NULL; int lock_open 0; // 标记锁的状态0为关闭1为打开 while (1) { frame capture_frame(cap); if (frame.empty()) { continue; } // 将OpenCV的BGR图像转换为RGB如果模型需要 cv::Mat rgb_frame; cv::cvtColor(frame, rgb_frame, cv::COLOR_BGR2RGB); // 执行人脸检测 int num_faces detect_faces(face_detector, rgb_frame.data, rgb_frame.cols, rgb_frame.rows, rgb_frame.channels(), boxes); // 处理检测结果 if (num_faces 0) { printf(“检测到 %d 张人脸\n” num_faces); // 在图像上画出人脸框可视化可选 for (int i 0; i num_faces; i) { cv::rectangle(frame, cv::Point(boxes[i].x1, boxes[i].y1), cv::Point(boxes[i].x2, boxes[i].y2), cv::Scalar(0, 255, 0), 2); // 绿色框 } // 触发开锁逻辑这里简化检测到人就开锁 if (!lock_open) { if (send_lock_command(serial_fd, “OPEN”) 0) { lock_open 1; printf(“锁已打开5秒后关闭...\n”); // 模拟开门保持时间 sleep(5); if (send_lock_command(serial_fd, “CLOSE”) 0) { lock_open 0; printf(“锁已关闭。\n”); } } } free(boxes); // 释放检测结果内存 boxes NULL; } else { // 无人脸时的逻辑例如可以重置锁状态标记 // lock_open 0; } // 显示视频流可选会消耗性能 cv::imshow(“Access Control Demo” frame); if (cv::waitKey(1) ‘q’) { // 按‘q’键退出 break; } } // 5. 清理资源 printf(“\n正在关闭系统...\n”); cv::destroyAllWindows(); serial_close(serial_fd); release_face_detector(face_detector); cap.release(); printf(“系统已安全退出。\n”); return 0; }3.1 编译与运行最后一步把所有的C文件编译成一个可执行程序。我们需要链接OpenCV和MogFace的库。# 编译命令示例 gcc -o access_control_system main.c face_detector.c serial_controller.c \ pkg-config --cflags --libs opencv4 \ -I./include -L./libs -lmogface -lpthread -lm # 运行程序 ./access_control_system注意pkg-config --cflags --libs opencv4用于自动获取OpenCV的编译和链接参数。-I./include指定MogFace头文件路径。-L./libs -lmogface指定MogFace库文件路径和库名。运行前请确保虚拟串口助手已经打开并监听对应的端口如/dev/ttyS0。4. 效果演示与项目思考当你运行程序后会弹出一个窗口显示摄像头画面。当有人脸出现在画面中时程序会在画面中用绿色方框标出人脸。在终端打印“检测到X张人脸”。向串口发送“OPEN”指令你会在串口助手软件上看到这条消息。等待5秒后发送“CLOSE”指令。一个最简单的门禁系统原型就跑起来了。当然这只是一个起点。在实际应用中我们还需要考虑很多问题比如人脸识别1:1比对 vs 人脸检测1:N查找我们目前只做了检测有没有脸真正的门禁需要识别这是谁的脸。这需要增加人脸特征提取和数据库比对模块。活体检测如何防止用照片或视频欺骗系统需要增加眨眼、张嘴、摇头等动作验证。性能优化在嵌入式设备上可能需要使用更轻量的模型或者对图像进行缩放、降低检测频率来保证实时性。网络与数据库如何将识别记录上传到服务器如何管理用户人脸库异常处理摄像头断开、模型加载失败、串口通信错误等都需要完善的异常处理机制。不过通过这个项目我们已经把C语言、计算机视觉和硬件控制这几个关键环节串起来了。它就像是一个坚实的骨架后续的肌肉和皮肤都可以在此基础上慢慢添加。你可以尝试替换不同的模型增加更复杂的业务逻辑或者把它移植到树莓派、Jetson Nano等真正的嵌入式平台上会更有成就感。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章