人脸识别算法

Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.


在 Quectel Pi M2 Debian 系统上,基于人脸识别算法 SDK,从零搭建一个带桌面图形界面的人脸识别项目。运行后会在设备桌面弹出窗口,支持全屏。

界面采用三栏布局:

  • 左栏 —— 算法模块 + 能力清单(人脸检测 / 21 点关键点 / 头部姿态 / 1:N 识别);

  • 中栏 —— 标题 + 实时显示区 + 控制按钮;

  • 右栏 —— 设备状态面板(人脸底库数量 / 内存 RAM / 存储 ROM / CPU 占用)。

提供两种输入方式,并支持运行时注册人脸:

  • 打开摄像头 —— 打开 USB 摄像头,实时检测人脸并与底库 1:N 比对;

  • 本地图片 —— 选择一张本地图片,离线检测 + 识别;

  • 注册人脸 —— 输入姓名后,把当前画面中面积最大、code == FACE_OK 且特征有效的人脸保存到 facedb/registered/,并加入内存底库。下次启动自动恢复,无需重复注册;保存失败时不会提示注册成功。

../../../_images/image_NXx8bnN60oMEHlxKNcxcV8cWngn.webp

算法能力

项目

说明

算法类型

人脸检测 + 21 点关键点 + 头部姿态 + 特征提取识别

检测模型

facedet_512_q_3576_enc.rknn(512²,多尺度人脸框)

关键点模型

facelandmark_q_3576_enc.rknn(21 点动态关键点)

识别模型

facereg_l_q_3576_enc.rknn(512 维人脸特征)

输入

BGR 连续内存(cv::Mat.data,rows×cols×3)

输出

每张人脸一个 FaceResults_t(框 xywh + 置信度 + yaw/pitch/roll + 21 关键点 + 特征向量)

比对

cosine_similarity 余弦相似度 1:N 匹配,默认阈值 0.6,支持最高 10000 张底库

在 Demo 中的体现:

  • 人脸框(绿=底库命中 / 橙=未匹配或人脸不可用);当前界面不单独显示检测置信度 conf;

  • 21 点关键点(可用 [Landmarks] 按钮开关);

  • 头部姿态 Y:yaw P:pitch R:roll(框下方);

  • 框上方显示姓名与匹配分数;未命中显示 Unknown,code != FACE_OK 时显示 low-quality。low-quality 是 Demo 的统一提示,具体原因需查看 SDK 状态码。

部署前准备

资源获取

名称

说明

face_RK3576(算法 SDK 包)

人脸识别算法包,含公开头、动态库、第三方运行时、模型、样例图与人脸底库

face_project(Demo 工程)

本教程提供的示例工程,含源码、Makefile 与三个脚本(复制依赖 / 编译 / 运行)

算法 SDK 包(RK_FR_NPU_v1.1/rknn-2.3.2-linux,下文记作 face_RK3576)的关键目录:

rknn-2.3.2-linux/
├── include/
│   ├── faceEngine.h             # 算法公开头(集成时唯一需要包含的头)
│   ├── authorization.h          # 授权相关定义(模块表 / 错误码)
│   ├── opencv2/                # 随包 OpenCV 头文件
│   └── rknn_api.h              # RKNN 接口头文件(同目录还有其他 RKNN 头)
├── lib/                         # libfacerecognition.so 及依赖 + libopencv_world.so
├── model/rknn/RK3576/           # 检测 / 关键点 / 识别 .rknn 模型
├── facedb/                      # 人脸底库(文件名=姓名)
├── image/                       # 样例图
└── main.cpp                     # 官方最小调用示例

硬件/系统要求

项目

要求

主控

Quectel Pi M2

系统

Debian / Ubuntu aarch64(带桌面环境,Wayland 或 X11)

外设

USB 摄像头

连接

PC 可通过 adb shell 进入设备

软件要求

Demo 在 Quectel Pi M2 上本地编译,需确认以下软件已安装:

g++ --version          # C++17 编译器
make --version
pkg-config --exists Qt5Widgets Qt5Gui Qt5Core && echo "Qt5 OK"   # 显示用 Qt5(LGPL)
v4l2-ctl --version     # 用于枚举和排查 USB 摄像头节点

界面方案说明:显示层采用系统自带的 Qt5,直接在桌面显示、支持全屏;采集/推理/绘制使用 随 SDK 交付的 OpenCV world 单库(libopencv_world.so,与 libfacerecognition.so 一致),与系统 OpenCV 隔离,避免版本冲突。

工程结构

face_project/
├── src/
│   └── face_cam_demo.cpp        # Demo 主程序(Qt 界面 + 工作线程推理 + 1:N 识别)
├── include/                     # setup 后:SDK 公开头 faceEngine.h + 随包 opencv2/rknn 头
├── lib/                         # setup 后:libfacerecognition.so 及依赖 + libopencv_world.so
├── models/                      # setup 后:检测/关键点/识别 .rknn 模型
├── facedb/                      # setup 后:人脸底库(文件名=姓名)
├── images/                      # setup 后:样例图
├── Makefile                     # 编译规则
├── build.sh                     # 一键编译
├── setup_from_sdk.sh            # 从 SDK 包复制依赖到本工程
├── run.sh                       # 一键运行(配置环境变量 + 启动)
└── README.md                    # 说明文档

include/ lib/ models/ facedb/ images/ 由 setup_from_sdk.sh 从原始 SDK 包复制生成。

核心代码讲解

Demo 基于 SDK 公开头 faceEngine.h 的单类 FaceEngine,调用流程为:构造 → init(加载模型与授权)→ set_param(设阈值)→ inference(逐帧推理)→ cosine_similarity(1:N 比对)→ uinit(释放)。

源码位于 src/face_cam_demo.cpp,下面只讲解人脸识别算法相关调用。以下为函数体内的关键代码片段,需包含 faceEngine.h、<cstdio>、<cstdlib> 等头文件。

初始化

FaceEngine engine;
char det_model[]  = "models/facedet_512_q_3576_enc.rknn";   // 人脸检测
char land_model[] = "models/facelandmark_q_3576_enc.rknn";  // 21 点关键点
char reg_model[]  = "models/facereg_l_q_3576_enc.rknn";     // 512 维特征提取
char license[512] = "face_license.ini";
if (const char* path = std::getenv("FACE_LICENSE_FILE")) {
    if (path[0]) std::snprintf(license, sizeof(license), "%s", path);
}
int module = 3;
if (const char* value = std::getenv("FACE_MODULE")) {
    if (value[0]) module = std::atoi(value);
}
int app_id = 145;
if (const char* value = std::getenv("FACE_APP_ID")) {
    if (value[0]) app_id = std::atoi(value);
}
char auth_buf[512] = {0};
if (const char* auth = std::getenv("FACE_AUTH_CODE")) {
    if (auth[0]) std::snprintf(auth_buf, sizeof(auth_buf), "%s", auth);
}
char* p_auth = auth_buf;
FACE_RES_CODE rc = engine.init(det_model, land_model, reg_model, license, module, p_auth, app_id);
engine.set_param(0.35f, 0.45f);
int alg_status = engine.get_status(0);
int auth_status = engine.get_status(1);
std::printf("rc=%d alg_status=%d auth_status=%d\n", rc, alg_status, auth_status);

上述片段演示环境变量配置;Demo 二进制还支持第二个命令行参数传入授权码,其优先级高于 FACE_AUTH_CODE。

参数说明:

  • init(det_model, land_model, reg_model, license, module, p_auth, app_id) 一次加载三颗模型并完成授权:

    • det_model:人脸 检测 模型(512²,输出人脸框 + 置信度)。

    • land_model:关键点 模型(21 点动态关键点,兼得头部姿态)。

    • reg_model:特征 模型(输出 512 维人脸特征向量,用于比对)。

    • license:授权文件落点(首次联网授权后写入,之后可离线)。

    • module:平台标识,RK3576 固定传3(可用环境变量 FACE_MODULE 覆盖)。

    • p_auth / app_id:算法授权码与授权 ID。

  • set_param(det_conf, nms):det_conf 人脸检测置信度阈值(默认 0.35,调高减少误检)、nms 检测框 NMS IoU 阈值(默认 0.45)。

  • get_status(opt) 查询状态:get_status(0) 算法初始化状态(0 成功);get_status(1) 授权状态(0 正式授权,其他见 AuthErrCode_e)。

推理

每一帧(摄像头帧或本地图片)调用一次 inference,SDK 内部完成「检测 → 关键点 → 姿态 → 特征提取」,返回每张人脸的完整结果。

std::vector<FaceResults_t> results;
// 入参:BGR 数据指针、行(高)、列(宽)、通道数(必须 3)、输出结果向量
FACE_RES_CODE inference_rc = engine.inference(
    frame.data, frame.rows, frame.cols, frame.channels(), results);
if (inference_rc != FACE_OK) {
    std::printf("inference failed: %d\n", inference_rc);
    return;
}
for (const auto& r : results) {
    // r.code                     : 0(FACE_OK) 表示该人脸可用;负值=质量/角度/越界等问题
    // r.x, r.y, r.width, r.height : 人脸框(原图像素坐标)
    // r.conf                     : 人脸置信度
    // r.yaw / r.pitch / r.roll   : 头部姿态(偏航 / 俯仰 / 横滚)
    // r.key_pts                  : 21 个关键点坐标
    // r.feature                  : 512 维人脸特征向量(用于 1:N 比对)
}

输入约定:

  • 输入须非空、为 BGR 连续内存。裁剪得到的 ROI 等 cv::Mat 不一定连续,传入前检查 isContinuous(),必要时 clone()。

  • channels 必须为 3;灰度图需先 cv::cvtColor 转 BGR。

  • 模型预处理由 SDK 内部完成;调用方仍需保证图像格式、通道数、连续性和方向正确,不应自行重复执行模型归一化。

输出结构FaceResults_t(每张人脸一个):

字段

含义

code

0=人脸可用;负值表示质量/角度/越界等原因未提取特征

x / y / width / height

人脸框在原图的像素坐标

conf

人脸检测置信度

yaw / pitch / roll

头部姿态角(度)

key_pts

21 个人脸关键点坐标

feature

该模型的人脸特征为 512 维;比对前必须检查 code == FACE_OK 且 feature 非空

1:N 比对识别

用 cosine_similarity 把当前人脸特征与底库(每人一条特征)做余弦相似度匹配,返回命中索引与分数。

// 底库:facedb/ 下每张图跑一次 inference,取人脸特征 + 文件名(去扩展名)作为姓名
std::pair<int,float> m = engine.cosine_similarity(db_features, face.feature, 0.6f);
// m.first >= 0 : 命中底库中第 m.first 个人;m.first == -1 : 未知人脸(Unknown)
// m.second     : 余弦相似度分数(越大越相似)
  • 第三个参数为 判定阈值(默认 0.6):相似度高于阈值且为 top1 才判为命中,否则 Unknown。

  • 启动时逐张读取 facedb/ 图片,每张图仅取第一张 code == FACE_OK 且特征非空的人脸,以文件名去扩展名作为姓名。请使用仅包含一个目标人脸的底图,避免录入错误身份。

  • 运行时点 [Register Face] 会选择面积最大的可用人脸,将姓名和 512 维特征原子保存到 facedb/registered/*.json,保存成功后追加到内存;不保存摄像头原图。下次启动先加载底图,再恢复有效注册记录。同名注册保留为多条样本,不覆盖旧记录。界面姓名建议使用英文/ID,当前 OpenCV 画面文字不支持完整的中文姓名显示。

  • 注册 JSON 使用随机文件名且仅属主可读写。请以同一用户启动程序,备份并保护 facedb/registered/ 中的生物特征数据,不要提交到公开仓库;更换特征模型后应重新注册。

  • 阈值越高越严格(更少误识、更多拒识);跨设备/光照差异大时可适当下调。

释放

engine.uinit();   // 释放检测/关键点/识别模型与授权对象

部署与运行

传输文件

在 PC 的 Windows PowerShell 中,从包含 SDK 和工程的目录执行。先传到设备 /tmp。

adb 传输示例:

adb devices
adb push "RK_FR_NPU_v1.1/rknn-2.3.2-linux" /tmp/
adb push "face_project" /tmp/

随后在 Quectel Pi M2 桌面会话打开终端,以运行普通用户执行。以下步骤需要 sudo 权限。若家目录已有同名目录,先停止程序并备份授权文件与人脸底库,再安排更新,避免把新目录嵌入旧目录。

sudo mv /tmp/rknn-2.3.2-linux /tmp/face_project "$HOME"/
sudo chown -R "$USER:$(id -gn)" "$HOME/rknn-2.3.2-linux" "$HOME/face_project"
cd "$HOME/face_project" && chmod +x *.sh

安装编译环境和开发工具

设备联网后执行:

sudo apt update 
sudo apt install -y build-essential pkg-config qtbase5-dev

复制依赖

setup_from_sdk.sh 从 SDK 包把头文件、动态库、模型、样例图与人脸底库复制到工程对应目录:

cd "$HOME/face_project"
sh setup_from_sdk.sh "$HOME/rknn-2.3.2-linux"   # 也可不传参数,使用此默认路径

编译

sh build.sh
#产物:/home/$USER/face_project/face_cam_demo

build.sh 内部调用 make:用系统 Qt5 做界面,链接工程内 libfacerecognition.so 与随包 OpenCV world 生成可执行文件。

运行

在设备 桌面会话 中运行(Qt 窗口需要桌面环境):

cd "$HOME/face_project"
./run.sh

./run.sh 打开界面后,点击 Open Camera 才开始采集。也可根据 v4l2-ctl --list-devices 的实际输出指定节点,例如 ./run.sh /dev/video73;节点号仅为示例,指定节点打开失败时程序仍会尝试自动探测 USB 采集节点。

推荐从设备桌面终端启动。若使用 adb shell,需切换为桌面登录用户,并确保 XDG_RUNTIME_DIR、WAYLAND_DISPLAY 或 DISPLAY/XAUTHORITY 与实际桌面会话一致;脚本的显示平台探测不保证 root 或其他用户能连接桌面。

界面操作

窗口为三栏布局:左栏算法模块、中栏显示与控制、右栏设备状态。

操作

说明

[Open Camera]

打开/关闭 USB 摄像头,实时检测 + 1:N 识别;关闭时自动清屏

[Local Image]

弹出文件选择框,选一张图片离线识别

[Register Face]

定格当前画面并弹窗输入姓名,把最大的人脸录入底库,随后可被识别

[Landmarks]

开关 21 点人脸关键点显示

[Full Screen]

切换全屏 / 窗口显示

[Quit]

关闭程序

快捷键

Esc/Q 退出,F 全屏

设备状态面板(右栏) 每秒刷新一次:

指标

说明

Face DB

当前底库人脸数量(含运行时注册)

内存 RAM

已用百分比 + 已用/总量(MB)

存储 ROM

根文件系统已用百分比 + 已用/总量(GB)

处理器 CPU

CPU 占用百分比

授权说明

授权文件和应用配置必须与当前设备及许可范围匹配:

  • 授权码通过环境变量 FACE_AUTH_CODE 下发(run.sh 已 export,集成方替换为自己的码即可);

  • FACE_MODULE 默认为 3(RK3576,见 include/authorization.h)。

  • FACE_APP_ID 当前默认为 145,对应 CV_SET_APPID。

  • FACE_LICENSE_FILE 默认为工程内绝对路径 $ROOT/face_license.ini。首次无有效本地件时 SDK 会尝试联网申请,授权成功后保存设备绑定文件;后续启动优先校验本地件。保留文件并确保运行用户可写工程目录,不要随公开工程分发授权码、license 或人脸底库。

  • 有效本地件可能使程序在未使用当前新码在线申请的情况下授权成功。验证新授权码时应与本地件复用测试区分,不能只根据文件存在、非空或窗口打开判断成功。

查询状态:get_status(0) 算法初始化状态(0 成功),get_status(1) 授权状态(0 成功,其他值见 AuthErrCode_e)。

应用演示

face-demo

常见问题排查

问题

可能原因

解决方案

初始化失败 alg_status != 0

模型缺失或初始化失败

先复制依赖并核对三个模型路径,结合 SDK 返回码定位

授权失败 auth_status != 0

许可、应用 ID、设备绑定或资源范围不匹配

核对 FACE_AUTH_CODE、FACE_APP_ID、FACE_MODULE;联系授权方,不要盲目修改模块号

auth_status=100024

请求的 FaceRecognition 资源不在许可范围内

请授权方确认应用及资源许可;

注册保存失败或重启后未恢复

目录不可写、磁盘空间不足、记录损坏或更换了运行用户/模型

检查 facedb/registered/、文件属主与运行日志,确认从同一工程目录启动;更换特征模型后重新注册

窗口不显示

非桌面会话 / QT_QPA_PLATFORM 未匹配

在设备桌面会话运行;或由 run.sh 自动探测 Wayland/xcb

无法打开摄像头

USB 摄像头未插入 / 指定节点错误

默认 auto 自动探测 USB 采集节点;可用 v4l2-ctl --list-devices 确认后 ./run.sh /dev/videoN

链接/运行报找不到 .so

LD_LIBRARY_PATH 未设置

用 run.sh 启动(已配置),或手动 export 工程内 lib 路径

人脸标注为 low-quality

检到框但角度/大小/越界/质量不达标,未提特征

让人脸正对镜头、清晰、完整入镜;可调 set_param 的检测置信度

技术支持与贡献

如在使用过程中遇到问题,欢迎在 移远官方论坛 提交技术咨询,移远技术支持团队将及时为您解答。

项目开源地址: https://github.com/Quectel-Pi/demo-edge-algorithms

欢迎提交Issue反馈问题或Pull Request贡献代码改进!