1. 项目概述
在嵌入式视觉应用开发中,人脸检测是最基础也是最重要的功能模块之一。瑞芯微RV1126B作为一款面向AI视觉处理的低功耗SoC,配合EASY-EAI开发套件,为开发者提供了高效的人脸检测解决方案。本文将详细介绍如何在EASY-EAI-Nano-TB硬件平台上实现实时人脸检测功能。
提示:本文基于EASY-EAI-Toolkit-1126B开发套件,所有代码和模型均可从官方GitHub仓库获取。
2. 开发环境搭建
2.1 硬件准备
进行人脸检测开发前,需要准备以下硬件设备:
- EASY-EAI-Nano-TB开发板(搭载RV1126B芯片)
- 5V/2A电源适配器
- Type-C数据线(用于串口调试)
- 网线(可选,用于SSH连接)
- USB摄像头或MIPI摄像头
2.2 软件环境配置
开发主机建议使用Ubuntu 18.04/20.04 LTS系统,按以下步骤配置开发环境:
- 安装基础依赖包:
bash复制sudo apt update
sudo apt install -y git cmake make gcc g++ python3-dev \
libopencv-dev libssl-dev libusb-1.0-0-dev
- 设置交叉编译工具链:
bash复制cd ~
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
echo 'export PATH=$PATH:~/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin' >> ~/.bashrc
source ~/.bashrc
- 配置EASY-EAI开发环境:
bash复制cd ~/develop_environment
./run.sh
注意:首次运行run.sh脚本时会自动下载和配置SDK所需的各种组件,这可能需要较长时间,请保持网络畅通。
3. 人脸检测算法实现
3.1 算法原理
RV1126B采用基于RKNN(Rockchip Neural Network)的加速架构,其人脸检测算法核心流程如下:
-
图像预处理:
- 归一化(Normalization):将像素值从[0,255]归一化到[0,1]
- 减均值(Mean Subtraction):减去训练时的均值像素值
- 除方差(Variance Division):除以训练时的方差值
-
神经网络推理:
- 使用轻量级Backbone网络(如MobileNetV2)提取特征
- 采用SSD(Single Shot MultiBox Detector)检测框架
- 输出人脸框坐标和关键点位置
-
后处理:
- 非极大值抑制(NMS)去除冗余检测框
- 关键点坐标转换到原图空间
3.2 模型部署
从百度网盘下载人脸检测模型(face_detect.model)后,需要将其转换为RKNN格式:
bash复制cd /opt/EASY-EAI-Toolkit/EASY-EAI-Toolkit-1126B/Demos/algorithm-face_detect/
python3 convert_rknn.py --model_path face_detect.model --output face_detect.rknn
模型转换关键参数说明:
mean_values:训练时使用的均值,必须与训练时一致std_values:训练时使用的方差,必须与训练时一致quantize:是否进行量化,建议开启以提升推理速度target_platform:设为"rv1126"以启用芯片特定优化
4. 代码实现详解
4.1 主程序流程
人脸检测示例程序的主要执行流程如下:
- 初始化RKNN上下文
- 加载输入图像
- 执行人脸检测
- 绘制检测结果
- 释放资源
核心代码解析:
cpp复制int main(int argc, char **argv) {
// 1. 参数检查
if(argc != 2) {
printf("Usage: ./test-face-detect <image_path>\n");
return -1;
}
// 2. 初始化
rknn_context ctx;
std::vector<det> results;
Mat image = imread(argv[1], 1);
// 3. 人脸检测初始化
if(face_detect_init(&ctx, "face_detect.rknn") != 0) {
printf("Init failed!\n");
return -1;
}
// 4. 执行检测
struct timeval start, end;
gettimeofday(&start, NULL);
face_detect_run(ctx, image, results);
gettimeofday(&end, NULL);
// 5. 输出耗时
float time_use = (end.tv_sec-start.tv_sec)*1000 +
(end.tv_usec-start.tv_usec)/1000.0;
printf("Detection time: %.2f ms\n", time_use);
// 6. 绘制结果
for(auto &det : results) {
rectangle(image, Rect(det.box.x, det.box.y,
det.box.width, det.box.height),
Scalar(0,255,0), 2);
for(auto &pt : det.landmarks) {
circle(image, Point(pt.x, pt.y), 2, Scalar(255,0,255), 2);
}
}
// 7. 保存结果
imwrite("result.jpg", image);
// 8. 释放资源
face_detect_release(ctx);
return 0;
}
4.2 API详解
4.2.1 初始化函数
cpp复制int face_detect_init(rknn_context *ctx, const char *model_path);
参数说明:
ctx:输出参数,返回初始化后的RKNN上下文model_path:RKNN模型文件路径
返回值:
- 0表示成功,非0表示失败
4.2.2 检测函数
cpp复制int face_detect_run(rknn_context ctx, cv::Mat &input_image,
std::vector<det> &results);
参数说明:
ctx:RKNN上下文input_image:输入图像(BGR格式)results:输出检测结果
返回值:
- 检测到的人脸数量
det结构体定义:
cpp复制struct det {
struct {
int x, y; // 左上角坐标
int width; // 框宽度
int height; // 框高度
float score; // 置信度
} box;
std::vector<cv::Point2f> landmarks; // 关键点坐标
};
4.2.3 资源释放函数
cpp复制int face_detect_release(rknn_context ctx);
参数说明:
ctx:需要释放的RKNN上下文
返回值:
- 0表示成功,非0表示失败
5. 性能优化技巧
5.1 模型量化
通过将FP32模型量化为INT8,可以显著提升推理速度:
python复制# 在convert_rknn.py中添加量化配置
rknn.config(quantize_input_node=True,
quantized_dtype='asymmetric_quantized-8',
quantized_algorithm='normal')
5.2 多线程处理
利用RV1126B的多核CPU特性,可以实现多线程流水线处理:
cpp复制#include <thread>
void detection_thread(rknn_context ctx, Mat img, vector<det>* results) {
face_detect_run(ctx, img, *results);
}
int main() {
// ...
thread t(detection_thread, ctx, image, &results);
t.join();
// ...
}
5.3 图像预处理优化
使用OpenCV的UMat代替Mat可以启用硬件加速:
cpp复制UMat uimage;
image.copyTo(uimage); // 转换为UMat
face_detect_run(ctx, uimage, results);
6. 常见问题排查
6.1 模型加载失败
现象:face_detect_init返回非0值
可能原因:
- 模型文件路径错误
- 模型格式不兼容
- 内存不足
解决方案:
- 检查模型文件是否存在
- 确认模型是为RV1126B转换的RKNN格式
- 使用free -m检查内存使用情况
6.2 检测结果不准确
现象:漏检或误检率高
可能原因:
- 输入图像分辨率与训练时不一致
- 预处理参数(均值/方差)设置错误
- 光照条件差异大
解决方案:
- 保持输入图像与训练时相同的长宽比
- 确认convert_rknn.py中的mean_values和std_values正确
- 增加图像直方图均衡化预处理
6.3 性能不达标
现象:推理时间远高于预期
可能原因:
- 未启用NPU加速
- 图像分辨率过高
- 系统负载过高
解决方案:
- 确认RKNN模型是为RV1126B优化过的版本
- 适当降低输入图像分辨率(建议640x480)
- 使用top命令检查CPU负载
7. 实际应用扩展
7.1 视频流人脸检测
基于OpenCV实现实时视频检测:
cpp复制VideoCapture cap(0); // 打开摄像头
while(true) {
Mat frame;
cap >> frame; // 获取帧
vector<det> results;
face_detect_run(ctx, frame, results);
// 绘制结果
for(auto &det : results) {
rectangle(frame, Rect(det.box.x, det.box.y,
det.box.width, det.box.height),
Scalar(0,255,0), 2);
}
imshow("Face Detection", frame);
if(waitKey(1) == 27) break; // ESC退出
}
7.2 多算法协同工作
结合人脸识别和人脸属性分析:
cpp复制// 人脸检测
vector<det> faces;
face_detect_run(detect_ctx, image, faces);
// 对每个检测到的人脸进行识别和属性分析
for(auto &face : faces) {
Mat face_roi = image(Rect(face.box.x, face.box.y,
face.box.width, face.box.height));
// 人脸识别
int person_id = face_recognize(recognize_ctx, face_roi);
// 属性分析
FaceAttributes attr = face_attribute(attribute_ctx, face_roi);
// 显示结果
printf("Person %d: age=%d, gender=%s\n",
person_id, attr.age, attr.gender?"Male":"Female");
}
7.3 模型自定义训练
使用自定义数据集训练人脸检测模型:
- 数据准备:
bash复制# 使用labelImg标注工具生成VOC格式标注
pip install labelImg
labelImg
- 模型训练(以TensorFlow为例):
python复制import tensorflow as tf
from object_detection import model_lib_v2
pipeline_config = 'ssd_mobilenet_v2_face.config'
model_dir = 'training/'
# 启动训练
model_lib_v2.train_loop(
pipeline_config_path=pipeline_config,
model_dir=model_dir,
train_steps=100000)
- 模型转换:
python复制from tensorflow.lite.python import convert
converter = tf.lite.TFLiteConverter.from_saved_model('saved_model')
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()
with open('face_detect.tflite', 'wb') as f:
f.write(tflite_model)