1. 代码整体结构
1.1 检测逻辑
2. 智能识别模块
2.1 Haar模型
摄像头 → 读取 → 转换 → 缩放 → 🎯识别人脸/手掌 → 如有目标:保存图像
↓
合并 → 显示
// detect_and_save.cpp
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
#include <ctime>
#include <unistd.h>
using namespace cv;
using namespace std;
static CascadeClassifier face_cascade;
static CascadeClassifier palm_cascade;
static bool initialized = false;
// 将像素数据(RGB)转为 OpenCV 图像,并检测人脸或手掌
extern "C" int DetectAndSaveIfNeeded(unsigned char *rgb_data, int width, int height)
{
if (!initialized)
{
// 注意:路径需根据你系统中模型文件位置修改
if (!face_cascade.load("/usr/share/opencv4/haarcascades/haarcascade_frontalface_default.xml"))
{
fprintf(stderr, "Failed to load face cascade\n");
return -1;
}
if (!palm_cascade.load("/usr/share/opencv4/haarcascades/palm.xml")) // 自定义路径或手动下载
{
fprintf(stderr, "Failed to load palm cascade\n");
return -1;
}
initialized = true;
}
// 构建 Mat 图像,RGB 格式
Mat frame(height, width, CV_8UC3, rgb_data);
// 转灰度图
Mat gray;
cvtColor(frame, gray, COLOR_BGR2GRAY);
equalizeHist(gray, gray);
// 检测人脸与手掌
vector<Rect> faces, palms;
face_cascade.detectMultiScale(gray, faces, 1.1, 3, 0, Size(30, 30));
palm_cascade.detectMultiScale(gray, palms, 1.1, 3, 0, Size(50, 50));
if (!faces.empty() || !palms.empty())
{
// 获取当前时间作为文件名
time_t now = time(NULL);
struct tm *t = localtime(&now);
char filename[128];
snprintf(filename, sizeof(filename), "/mnt/data/capture_%04d%02d%02d_%02d%02d%02d.jpg",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
imwrite(filename, frame);
printf("Saved: %s\n", filename);
return 1; // 表示检测成功
}
return 0; // 没检测到目标
}
2.2 多线程+DNN模型
主线程负责采集+显示,检测放到后台线程
- 主线程保持快速循环采集图像并显示;
- 将图像帧拷贝后传给一个“识别线程”;
- 识别线程中执行人脸/手掌检测并保存图像;
- 避免阻塞主线程,从而解决卡顿问题。
主线程:
摄像头采集帧 → 图像转换 → LCD 显示
↓
复制一帧图像 → 放入识别队列
识别线程:
从队列取图像 → OpenCV DNN 识别人脸 → 如果检测到,保存图片
2.2.1 模型文件
这两个文件不是 OpenCV 自带的安装文件,但它们是OpenCV 官方示例推荐的人脸检测模型,需要你手动下载。
OpenCV 的 cv::dnn 模块并不自带训练模型,而是提供了 “加载+执行模型” 的能力,支持多种格式:
- Caffe(
.prototxt+.caffemodel) - TensorFlow(
.pb+.pbtxt) - ONNX(
.onnx) - Torch、Darknet、OpenVINO 等等
deploy.prototxt
- 是模型的“结构图纸”;
- 用文本描述了每一层的类型、输入输出尺寸、激活函数等;
- 是 Caffe 框架的标准结构描述格式。
res10_300x300_ssd_iter_140000_fp16.caffemodel
- 是“模型大脑”,存储训练好的权重;
.caffemodel是 Caffe 框架保存的二进制文件;- 包含了从 140000 次迭代训练出的最佳参数。
这个模型的训练目标是:输入 300x300 图像,输出其中是否有人脸 + 位置框。
2.2.2 修改makefire
** 在 Haar模型的Makefile 的基础上添加:**
LDFLAGS += -lpthread -lopencv_dnn
2.2.3 指令下载模型:
下载人脸检测模型(网络不好下载失败)
# 第一步:
wget https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt
#第二步:
# 下载模型权重文件
wget https://huggingface.co/Durraiya/res10_300x300_ssd_iter_140000_fp16.caffemodel -O /mnt/haarcascade/res10_300x300_ssd_iter_140000_fp16.caffemodel
下载手掌检测模型 palm.xml
wget https://raw.githubusercontent.com/Aravindlivewire/Opencv/master/haarcascade/haarcascade_palm.xml -O ~/opencv/data/haarcascades/palm.xml
拷贝到目标板
cp /home/book/opencv/data/haarcascades/palm.xml ~/nfs_rootfs/haarcascade/
2.2.4. 手动下载模型
用 Caffe 框架训练的 SSD(Single Shot Detector)人脸检测模型。
下载地址:
2.2.5 拷贝到开发板
如果下载到电脑端本地了,也可以直接复制粘贴到Ubuntu的nf_rootfs。
scp deploy.prototxt root@192.168.x.x:/mnt/haarcascade/
scp res10_300x300_ssd_iter_140000_fp16.caffemodel root@192.168.x.x:/mnt/haarcascade/
2.3 模型对比
2.4 嵌入式专用模型
OpenCV DNN 模型检测手掌(精度更高),不过模型体积大,嵌入式使用需要裁剪。
*.xml是 Haar 分类器模型(级联)
- 使用 OpenCV 自定义的 XML 格式;
- 保存了若干个简单矩形特征 + 级联分类器结构;
- 可直接用 OpenCV 自带的
CascadeClassifier加载。
CascadeClassifier face_cascade;
face_cascade.load("haarcascade_frontalface_default.xml");
2. deploy.prototxt + .caffemodel 是深度神经网络模型(DNN)
.prototxt定义网络结构(类似工程蓝图);.caffemodel保存了训练后的权重参数;- 需使用 OpenCV DNN 模块加载(支持 Caffe、ONNX、TF):
Net net = readNetFromCaffe("deploy.prototxt", "model.caffemodel");
4. 编译:
在# V4L2摄像头数据采集(读取、转换、缩放、合并、显示)的基础上
4.1 修改Makefire(Haar版)
- 添加对 C++ 编译器支持:
CXX := $(CROSS_COMPILE)g++
export CXX
- 添加 OpenCV 头文件和链接库
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
CFLAGS += -I/home/book/opencv-arm-install/include
LDFLAGS := -lm -ljpeg
LDFLAGS += -L/home/book/opencv-arm-install/lib \
-Wl,-rpath=/home/book/opencv-arm-install/lib \
-lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_objdetect -lopencv_imgcodecs
- 添加 detect_and_save.o 到编译对象中(在 obj-y 区)
obj-y += main.o
obj-y += detect_and_save.o # ✅ 添加这一行
obj-y += convert/
obj-y += display/
obj-y += render/
obj-y += video/
4.2 修改Makefire.build
- 添加
.cpp文件的编译规则(添加在其后)
%.o : %.cpp
$(CXX) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<
4.3 编译测试命令
make clean
make
4.4 编译错误
5. 运行
1. 出现错误:./video2lcd: error while loading shared libraries: libopencv_core.so.412: cannot open shared object file: No such file or directory
2. 原因: 在Ubuntu上交叉编译opencv得到的库没有拷贝到开发板
3. 拷贝到开发板:
确保目标目录在开发板存在且有权限:mkdir -p /usr/lib/opencv
使用scp拷贝
# 方法 1:NFS 挂载方式
cp /home/book/opencv-arm-install/lib/libopencv_*.so* /home/book/nfs_rootfs/opencv-lib/
# 方法 2:通过 scp 拷贝
scp /home/book/opencv-arm-install/lib/libopencv_*.so* root@192.168.x.x:/usr/lib/opencv/
设置库文件路径: export LD_LIBRARY_PATH=/usr/lib/opencv:$LD_LIBRARY_PATH
运行错误:
/dev/video1 supports streaming i/o Convert yuv2rgb, ret = 0 terminate called after throwing an instance of 'cv::Exception'
what(): OpenCV(4.12.0-dev) /home/book/opencv/modules/core/src/persistence.cpp:742:
error: (-215:Assertion failed) buf in function 'open'
原因:模型下载失败,需重新下载
wget https://raw.githubusercontent.com/Aravindlivewire/Opencv/master/haarcascade/palm.xml -O ~/opencv/data/haarcascades/palm.xml
复制到开发板
cp /home/book/opencv/data/haarcascades/haarcascade_frontalface_default.xml ~/nfs_rootfs/haarcascade/
移除GUI
mv /etc/init.d/S99myirhmi2 /root
mv /etc/init.d/S05lvgl /root
实验过程中LCD过几分钟就黑屏,这是正常的。 可以关闭黑屏的功能,执行以下命令即可: echo -e "\033[9;0]" > /dev/tty0
6. LCD显示卡顿,画面延迟
根本原因:
识别逻辑耗时太长,阻塞主循环。
main.c 是一个“单线程”循环结构:
while (1) {
GetFrame(); // 获取图像帧
Convert(); // 图像格式转换
Detect(); // 检测人脸 / 手掌(很慢!)
Merge(); // 合并图像
Display(); // 刷屏到 LCD
}
加入检测逻辑后,每一帧都执行:
face_cascade.detectMultiScale(...); // 可能耗时 200~500ms
解决方案:
方法一:隔帧检测(最简单实用)。不要每一帧都识别人脸,只每 N 帧执行一次检测(比如每 10 帧)
方法二:缩小检测图像尺寸(ROI/下采样)。只在缩小版图像上做人脸/手掌识别,大幅提升速度:
方法三:使用多线程(高阶)
将检测逻辑放入后台线程,让主循环专注采集和显示。
方法四:更快的检测模型(如 DNN、YOLO)
若你使用的是 Haar 模型(detectMultiScale),其效率较低,建议尝试:
- OpenCV DNN 模块(可使用轻量人脸检测模型)
- 嵌入式专用模型:Ncnn / MNN(速度更快)
但这些需要 OpenCV 编译时启用 DNN 模块,且模型体积更大。