Linux RV1106-SDK
人脸离线识别 Linux RV1106 SDK 用户接入文档
百度人脸离线识别 SDK(RV1106 定制版)
文档名称:人脸离线识别 Linux RV1106 SDK 用户接入文档
所属平台:Linux C++(Rockchip RV1106)
文档版本:V1.1
文档提交日期:2026.09.22
百度在线网络技术(北京)有限公司(版权所有,翻版必究)
修改记录
| No | 版本号 | 修改内容简介 | 修改日期 |
|---|---|---|---|
| 1 | V1.0 | 适配 RV1106,首个版本 | 2026-09-15 |
| 2 | V1.1 | 底层算法 SDK 升级至 3.2.0.204:RGB 单目活体模型换为 int8 量化版(liveness_rgb_int8_mmse.rknn.enc);接口并发加固;补充 device_id_tool 取设备指纹独立工具说明;补充 RKNN 内存/CMA 不足导致模型加载失败的排查与板型(256+256)建议;补充 models/ 需可写以持久化联网激活的 license.ini 说明;sdk_init 在未放授权文件时改返回 -1015(授权文件不存在),与 -13(鉴权未过)区分 |
2026-09-22 |
目录
- 1 设计背景
- 2 名词解释
-
3 SDK 简介
- 3.1 功能组成 3.2 运行环境与版本 3.3 交付物
-
4 SDK 工程结构
- 4.1 目录结构 4.2 models / lib / 体积说明
-
5 授权激活
- 5.1 自动激活(设备联网) 5.2 官网离线激活(推荐)
- 5.3 与设备、系统时间绑定 5.4 状态与排查
-
6 运行前置条件(依赖库与授权)
- 6.1 librga.so 6.2 libatomic.so.1 6.3 自检命令
-
7 SDK 集成及 DEMO
- 7.1 编译(交叉编译 / 板载编译) 7.2 部署与运行
- 7.3 最小接入代码 7.4 线程安全约束 7.5 示例工程
-
8 模型能力加载及模型说明
- 8.1 config.toml 8.2 模型清单 8.3 内存与特征模型 8.4 按需加载与内存优化
-
9 功能接口
- 9.1 信息与初始化 9.2 检测与跟踪 9.3 属性检测 9.4 特征与比对 9.5 活体
- 9.6 人脸库管理 9.7 识别 9.8 JSON 返回格式 9.9 配置文件
- 10 结构体描述
- 11 错误码及错误信息
- 12 常见问题
1 设计背景
本 SDK 面向 Rockchip RV1106 平台的摄像头类设备(门禁、考勤、闸机等),提供完全离线的 人脸识别能力:人脸检测、跟踪、关键点、属性检测(姿态 / 光照 / 模糊 / 遮挡 / 口罩)、质量评估、 特征提取、1:1 比对、1:N 识别、RGB / NIR 单目活体、活体策略整链判定(含手部 / 反光 / 2D 面具 三类攻击检测),以及基于 SQLite 的本地人脸库管理。全部算法在设备本地完成,不联网 (仅首次激活 license 需要网络)。
SDK 提供统一的 C++ 接口层(唯一入口类 BaiduFaceApi),将算法状态码收口为
稳定的错误码表,并补充了本地人脸库、日志、路径定位等工程化能力。
2 名词解释
| 名词 | 说明 |
|---|---|
| 1:1 比对 | 两张人脸图直接比较,返回相似度分值,用于「是不是同一个人」的判定。 |
| 1:N 识别 | 一张人脸与本地人脸库(多组、多用户)逐一比较,返回超过识别阈值的结果。 |
| 人脸检测 Detect | 从图像中找出人脸的位置(人脸框)及其置信度。 |
| 人脸跟踪 Track | 在检测基础上为每张脸分配稳定的 face_id,用于多帧关联。 |
| 关键点 Landmark | 人脸的 72 个特征点(两两一组,共 144 个 float),用于对齐、属性计算。 |
| 特征值 Feature | 人脸经识别模型提取的 256 维浮点向量,同一模型下可比对,但只与本套模型互认。 |
| 活体 Liveness | 判断图像是否来自真人,而非照片、视频翻拍、2D 面具等攻击。 |
| RGB / NIR | 可见光图像 / 近红外图像。NIR 图像对光照不敏感,活体更稳,但需要红外摄像头。 |
| 人脸库 | 以 SQLite 文件持久化的已注册人脸集合,含用户、组、特征三张表。 |
| 识别阈值 | 1:N 识别时判定「命中」的最小相似度,由 face.ini 的 IdentifyScore 配置。 |
3 SDK 简介
3.1 功能组成
SDK 由一个公共头文件 baidu_face_api.h(唯一 API 类 BaiduFaceApi)、
一个出参结构体头文件 struct_info.h、运行库 libbaidu_face_api.so
及全部 models/ 模型构成。提供的能力分五类:
| 类别 | 接口 |
|---|---|
| 检测 / 跟踪 / 属性 | detect track face_landmark face_head_pose face_illumination face_blur face_mouth_mask face_occlusion face_quality |
| 图像处理 | face_crop(人脸抠图) |
| 特征 / 比对 | face_feature pipline_feature(单 / 双目) compare_feature match |
| 活体 | rgb_liveness nir_liveness liveness_pipeline(活体策略整链,含攻击检测) |
| 人脸库 / 识别 | group_add group_delete user_add(图 / 特征) user_update user_delete get_user_info get_user_list get_group_list get_user_image db_face_count load_db_face identify(图 / 特征) identify_with_all(图 / 特征) |
另提供 sdk_init / sdk_version / get_device_id /
is_auth / is_log_open 五个信息与生命周期接口。
接口统一约定:出参一律放第一个参数,返回值是错误码(或人脸个数、分值); 底层算法库的状态码不会透传,只会有错误码表中列出的值。
3.2 运行环境与版本
| 项 | 值 |
|---|---|
| SoC | Rockchip RV1106(Cortex-A7 单核,armv7-a,硬浮点,NEON) |
| 板载内存 | 建议 256 MB DDR + 256 MB(俗称「256+256」)及以上配置的开发板;识别特征模型单个约 27 MB,由 NPU 从 CMA / DMA 堆分配,CMA 预留过小会导致模型加载失败、SDK 跑不起来(报 E RKNN: failed to allocate model memory,见 12 章 Q3) |
| C 库 | uClibc(libc.so.0 + ld-uClibc.so.1),不是 glibc |
| 工具链 | arm-rockchip830-linux-uclibcgnueabihf(gcc 8.3 及以上) |
| C++ 标准 | C++14 |
| CMake | 3.5.1 及以上 |
| SDK 版本 | 以 sdk_version() 的返回值为准(形如 Vx.x.x),见 9.1 |
注意:用 arm-linux-gnueabihf(glibc)那套工具链编出来的目标文件
和 lib/ 下的库链不到一起,也跑不了,这是最容易踩的一步。
3.3 交付物
交付物即本目录 face_offline_sdk/ 整包(src/ + include/ +
lib/ + models/ 完整工程,见第 4 章)。需要编译产物时,在本目录下建
build/ 文件夹编译(mkdir build && cd build && cmake .. && make),
产出 build/face_offline_sdk 一个可执行文件;lib/、models/
等运行时资源直接复用 face_offline_sdk/ 目录下的,不另行拷贝。
完整流程见 7.1 / 7.2。
4 SDK 工程结构
4.1 目录结构
1face_offline_sdk/
2├── include/ 公开头文件,只有两个
3│ ├── baidu_face_api.h 唯一的 API 类 BaiduFaceApi
4│ └── struct_info.h 出参结构体(FaceBox / Feature / Quality …)
5├── lib/ 运行时库,见 lib/README.md
6├── third_party/opencv2/ OpenCV 3.2 头文件(公开头 include 了 opencv.hpp)
7├── src/ 示例代码,face.cpp 是 main(按能力分子目录)
8├── models/ 算法模型 + config.toml + 授权文件(勿改名、勿删,见第 5 节)
9├── images/ 示例图片
10├── face.ini 日志开关、识别阈值、人脸库路径
11├── pipline.ini 活体 / 姿态 / 光照 / 遮挡 / 模糊阈值
12├── run.sh 一键运行脚本(见 7.2)
13├── build/ 编译产物目录(build/face_offline_sdk,见 7.1)
14├── cmake/toolchain-rv1106.cmake 交叉编译工具链配置
15├── device_id_tool/ 取设备指纹的独立小工具(预编译 armhf 可执行 get_device_id,见 5.2)
16├── CMakeLists.txt 内部模式 / 客户模式自动切换
17└── doc/ 本用户接入文档
4.2 models / lib / 体积说明
| 项 | 大小 | 说明 |
|---|---|---|
models/ |
约 59 MB | 14 个 .enc 模型 + 2 个 .bin 先验框文件 + config.toml(照旧,勿改名、勿删) |
lib/ |
约 25 MB | libbaidu_face_api.so、libmot_sort.so、libopencv_world.so、librknnmrt.so、libatomic.so.1 等(含算法库的链接依赖;不含 librga.so,见 6.1) |
| 整棵树 | 约 89 MB | —— |
models/ 的体积大头是两个特征模型(各约 26.6 MB),详见第 8 节。
5 授权激活
SDK 需要授权激活后才能正常使用。授权分按设备授权和按应用授权两种方式,本 SDK
采用按设备授权(一个授权绑定一台设备,设备指纹 = get_device_id() 的返回值);
按应用授权适用于批量大规模客户,需要时请通过工单或联系百度商务另行获取支持。
授权文件位于 models/ 目录下(不是单独的 license/ 文件夹),共两个:
1models/license.key 授权 key;可填 16 位授权序列号(见 5.1),激活后会被正式授权文件覆盖
2models/license.ini 正式授权文件;由官网下载后放入(见 5.2),或联网自动激活时由底层库生成
未通过授权时,sdk_init 或所有算法接口返回 -13
(NOT_AUTHORIZED)。特例:models/ 下 license.key 与 license.ini
两个授权文件都不存在(压根没放授权文件)时,sdk_init 直接返回
-1015(LICENSE_FILE_NOT_EXIST,授权文件不存在),以便和"文件放了但无效 /
过期 / 设备指纹不匹配"的 -13 区分开。按设备能否联网,有两种激活方式:
5.1 设备能联网:序列号自动激活
在百度官网申请到 16 位授权序列号后,把它按 models/license.key 的原格式
填入(覆盖)该文件。在设备能联网的情况下运行 SDK,由底层算法库自动完成激活,
并拉取新的授权文件 license.ini 覆盖旧的 license.key 内容,之后即可离线运行。
适合先在一台能联网的设备上完成激活,再把 models/ 整目录拷到其它目标机的场景。
注意:激活时需要访问百度授权服务器(ai.baidu.com),请确认设备网络可达;
face.ini 里没有授权相关的配置项,激活过程由底层库完成,无需也无法在应用层干预。
models/ 目录必须可写:联网自动激活时,底层库会把拉取到的 license.ini
写回 models/(config.toml 的 license_ini_path 指向该目录);sdk_init 也会
在此重写 log.conf。所以运行 SDK 的账户对 models/ 要有写权限:
- 若
models/位于只读分区 / 只读挂载(部分固件把/oem、/rootfs设为只读), 激活结果无法落盘——表现为激活失败,或每次启动都要重新联网激活、拿不到可离线复用的凭据。 - 建议把整个
face_offline_sdk/(含models/)放在可写分区(如 RV1106 的/userdata)。 - 只读部署场景的变通:先在一台可写且能联网的设备上激活好,再把
models/整目录(含生成的license.ini)拷到只读目标机——离线运行只读不写,不再需要写权限 (log.conf只读时仅告警,不影响识别,见 5.4 / 8.4)。
5.2 设备不能联网:官网离线激活(推荐)
-
在目标板上获取设备指纹,任选其一:
- 最省事:直接运行随包的独立小工具
device_id_tool/get_device_id,它把设备指纹打到标准输出, 跑完即退。该工具只链授权库、不依赖相机 / 模型 / OpenCV / rknn,也无需sdk_init, 可在裸板上直接执行; - 或运行示例
src/sdk_info/,或直接跑build/face_offline_sdk(启动时会打印); - 或在自己的程序里调
get_device_id()——该接口不需要授权、不需要sdk_init,也不依赖相机和模型;
- 最省事:直接运行随包的独立小工具
- 在能上网的电脑上打开百度官网,填入设备指纹和申请到的授权序列号,
完成激活后下载
license.ini和license.key两个文件; - 把这两个文件拷贝到目标机的
models/目录,重新运行 SDK 即通过授权。
5.3 授权与设备、系统时间绑定
授权与设备的系统时间绑定:激活和运行期校验都基于当前时间。板子时钟与真实时间偏差过大
(未同步、断电回拨、RTC 电池没电)会导致激活失败,或表现为运行期间退回未授权。上电后先确认
date 输出正确;没有 NTP 的话,开机脚本里先 hwclock -s 从 RTC 同步一次。
一个授权绑定一台设备:重刷固件导致设备指纹变化后,需要重新申请;同一台设备上,授权文件随
models/ 走,任意目录放一份 SDK 都可复用,无需重新申请。
5.4 授权状态与排查
is_auth() 返回当前是否已通过授权(sdk_init 成功之后才有意义)。
先看返回码区分两种情况:sdk_init 返回 -1015(LICENSE_FILE_NOT_EXIST)说明
根本没放授权文件(license.key / license.ini 都不在 models/ 下),照 5.1 / 5.2
放入授权文件即可;返回 -13(NOT_AUTHORIZED)说明文件在但鉴权没过,按顺序排查:
models/下license.key/license.ini是否完整、未损坏(缺失会先报-1015);- 设备指纹与授权是否匹配(与申请时填的
get_device_id()返回值是否一致, 重刷固件后必然变化,需重新申请); - 系统时间是否与真实时间一致(
date看偏差,见 5.3,时间错乱同样表现为-13); - 走 5.1 自动激活时,确认设备能访问外网且激活已完成。
6 运行前置条件(依赖库与授权)
下面四项在第一次上板前逐条确认。任何一项不满足,程序都在 main 之前就退出,
看不到 SDK 的任何日志,很难从现象反推原因。
| # | 检查项 | 缺失后的现象 |
|---|---|---|
| 1 | lib/librga.so 存在,或板子默认库路径下有 |
ld-uClibc 报 librga.so 找不到,进程立刻退出 |
| 2 | lib/libatomic.so.1 存在(本包自带,别删) |
同上,报 libatomic.so.1 找不到 |
| 3 | 板子 rootfs 有 libstdc++.so.6 / libgcc_s.so.1 / libc.so.0 |
同上;正常的 Rockchip rootfs 都有 |
| 4 | models/license.key(或 license.ini)已放入 |
两个都没放时 sdk_init 返回 -1015(LICENSE_FILE_NOT_EXIST);放了但无效 / 过期 / 设备不匹配时返回 -13(NOT_AUTHORIZED) |
6.1 librga.so(不随包,从板子上取)
原因:SDK 算法库(lib/ 下)的 DT_NEEDED 里有 librga.so
(Rockchip RGA 2D 图形加速库),但 SDK 不附带它,
因为版本必须与板子固件配对(见下)。编译期缺它只打一条 warning、链接不会失败(见 7.1.1),
但程序在板子上会因动态库加载失败直接退出:
1/userdata/face_offline_sdk/build/face_offline_sdk: can't load library 'librga.so'
解决步骤(全部在板子上执行,PC 上找不到这个库);例中
/userdata/face_offline_sdk 是 SDK 的部署目录(整个 face_offline_sdk/
放过去的位置),换成你实际的路径即可:
1# ① 查找:板子固件自带的 librga.so 一般在下面两个位置,哪个有取哪个
2ls -l /oem/usr/lib/librga.so /usr/lib/librga.so
3# → 有输出(能看到文件、大小几百 KB ~ 几 MB):说明找到了,走 ②
4# → 报 No such file or directory:两个位置都没有,走 ①'
5
6# ①' 两个位置都没有(个别精简固件)→ 全盘找一遍
7find / -name 'librga.so*' 2>/dev/null
8# 找遍全盘还是没有:固件把 RGA 裁掉了,联系固件提供方确认
9
10# ② 拷贝到 SDK 的 lib/ 目录(源 = ① 找到的路径,目标 = 部署目录/lib/)
11cp /oem/usr/lib/librga.so /userdata/face_offline_sdk/lib/
12
13# ③ 确认拷成功了(大小与源文件一致,不能是 0 字节)
14ls -l /userdata/face_offline_sdk/lib/librga.so
15
16# ④ 重新运行(run.sh 会自动切到自身所在目录并设好 LD_LIBRARY_PATH)
17cd /userdata/face_offline_sdk
18./run.sh
- 必须用当前这套固件自带的版本:RGA 库与固件内核驱动、MPP 有版本配对关系,
从网上或别的开发板拷一个
librga.so,轻则sdk_init返回-10, 重则图像加速异常这类说不清的问题。 - 这个库只在板子 rootfs 里有,PC 上找不到(连交叉编译工具链里都没有)——所以
上面的
ls/find/cp都必须到板子上执行, 不要试图在 PC 上找一份拷过去。 - 拷进的是 SDK 根目录下的
lib/。编译只写build/,不会动lib/,所以之后重新编译、重新打包都不需要再拷;只有重新解压 / 覆盖整个目录时 才需要再放一次。 - 同样要注意:不要在 PC 交叉编译时把
librga.so加进链接参数来消除那条 warning——它必须来自板子,PC 上找不到。
6.2 libatomic.so.1(本包已自带)
它是 lib/libbaidu_face_api.so 和 demo 可执行文件的 DT_NEEDED
(armv7 上 64 位原子操作要靠它),来自交叉工具链的 sysroot。板子 rootfs 通常没有这个库,
所以它随包下发,位于 lib/,不要删。自己改 CMakeLists.txt 或换工具链
重编时,-latomic 从工具链 sysroot 解析(链接期),运行期仍然用 lib/ 下
这一份;换工具链时记得把 lib/libatomic.so.1 一起换成新 sysroot 里的那份。
6.3 自检命令
在板子上、SDK 根目录下执行(第 1~3 项一次全查):
1ldd ./build/face_offline_sdk | grep -i 'not found'
没有输出就是第 1~3 项都过了。板子上没有 ldd 的话,改用
LD_TRACE_LOADED_OBJECTS=1 ./build/face_offline_sdk。
若运行时(还没进 main)报 can't load library 'xxx.so',报错里的库名
就是缺的那个,先看它属于哪一类:libmot_sort.so 是本包自带的
(lib/ 下约 5.7 MB,随包下发),它被「找不到」说明传输 / 解压
不完整或 RPATH 未被识别,排查见 12 的 Q5 情况一;librga.so 不随包,
必须按 6.1 从板子上取。
7 SDK 集成及 DEMO
7.1 编译
本工程是自包含的(src/ + include/ + third_party/ +
lib/ + models/ 等一整套),编译有两种方式:在 PC 上交叉编译,
以及在板子上直接编译(板子装有 gcc / g++ / cmake 时)。
两种方式的产物一致:build/ 下只有一个可执行文件(见 7.2)。
7.1.1 方式一:交叉编译(PC 上)
CMakeLists.txt 会自动识别构建环境:头文件用本目录的 include/ 与
third_party/,库链 lib/ 下的预编译库:
1cd face_offline_sdk
2mkdir build && cd build
3cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-rv1106.cmake \
4 -DTOOLCHAIN_ROOT=/opt/toolchain/arm-rockchip830-linux-uclibcgnueabihf ..
5make -j8
TOOLCHAIN_ROOT指向工具链含bin/的那一层。工具链已在PATH上时可以省掉; 也可以用环境变量RV1106_TOOLCHAIN_ROOT/RV1106_TOOLCHAIN_PREFIX(注意:CMake 只认-DTOOLCHAIN_ROOT=传参或工具链文件内解析,环境变量名是 build.sh 的约定)。- 默认
Release(-O2 -DNDEBUG),调试用-DCMAKE_BUILD_TYPE=Debug(-O0 -g)。 lib/下没有librga.so时 configure 会打一条 warning,不影响编译; 它只在板子运行时需要,见 6.1。- 产物是
build/下的可执行文件;运行时资源(lib/、models/、images/、face.ini、pipline.ini)不在build/里, 直接用工程根目录(face_offline_sdk/)下的那一份:
1build/
2└── face_offline_sdk ← 编译产出的可执行文件
7.1.2 方式二:板子上直接编译
前提:板子 rootfs 装有 gcc / g++ / cmake
(官方 buildroot 镜像一般自带),且必须是 uClibc 工具链——与 lib/ 下库的
C 库一致(见 3.2),否则链不到一起,只能用方式一。把整个 face_offline_sdk
目录拷到板子后:
1# PC 上,把工程整目录拷到板子(含 src/ include/ third_party/ lib/ models/ 等):
2scp -r face_offline_sdk root@<板子IP>:/userdata/
3
4# 板子上:
5cd /userdata/face_offline_sdk
6mkdir -p build && cd build
7cmake .. && make
- 不要传
-DCMAKE_TOOLCHAIN_FILE——那是 PC 交叉编译用的;板载编译直接用板子 自带的编译器,CMake 自动走客户模式:头文件用本目录include/与third_party/,链接lib/下的预编译库。 - RV1106 是单核(Cortex-A7),
make即可(-j无意义),编译耗时 几分钟属正常;64 MB 内存型号板载编译偏紧(个别大文件可能卡顿),建议 128 MB 型号或改回方式一。 - 板载存储需要留出余量:
models/约 59 MB、lib/约 25 MB, 再加上编译出的可执行文件。 lib/下没有librga.so时同上,configure 打 warning 不影响编译, 运行时从板子库路径加载(rootfs 一般自带,见 6.1)。- 产物与方式一完全一致:
build/face_offline_sdk;lib/models/images/+run.sh在工程根目录,直接照 7.2 运行即可。
7.2 部署与运行
交付物是本目录(face_offline_sdk/)整包:编译产物只有 build/
下的可执行文件,lib/、models/、images/、face.ini、
pipline.ini、run.sh 都在工程根目录,部署时整个目录(含
build/)一起下发:
1face_offline_sdk/
2├── build/ ← 编译产物目录
3│ └── face_offline_sdk ← 可执行文件(方式一在 PC 上编好后随目录拷到板子;方式二就地生成)
4├── lib/ ← 运行时库(直接复用本目录这份,不另拷贝)
5├── models/ images/
6├── run.sh ← 一键运行脚本(见下)
7└── face.ini pipline.ini
方式一:在 PC 上按 7.1.1 编好后,把整个 face_offline_sdk/(含
build/)scp -r 或串口拷到板子;方式二:按 7.1.2 在板子上编译,
产物就地生成,无需传输。两种方式都提供 run.sh,一条命令启动(脚本会先切到
自身所在目录 <root>,把 lib/ 加进 LD_LIBRARY_PATH,
再执行 ./build/face_offline_sdk):
1# 板子上,在 SDK 根目录下
2cd /userdata/face_offline_sdk # 换成你实际存放 face_offline_sdk/ 的路径
3./run.sh
在任意目录用绝对路径调 /path/to/run.sh 也一样;直接调可执行文件则必须
cd 到 <root>(图片路径是相对的,见下面的硬约束)。
run.sh 里显式设置的 LD_LIBRARY_PATH 与可执行文件本身已带的
RPATH($ORIGIN/../lib,即 face_offline_sdk/lib/)是双保险——
单靠 RPATH 本来就不需要 LD_LIBRARY_PATH。
启动前确认第 6 章的几项前置条件(librga / libatomic / license)。
正常启动后的输出(示例 src/face.cpp 的执行流程):
- 先打印
[崩溃处理器已启用]、in main、device id:…(设备指纹,用于申请授权,见第 5 章;这一步不需要sdk_init)、before/after sdk_init; sdk_init失败:打印sdk init result is:<错误码>后退出 (例如-1023是face.ini没找到、-4是models/不存在、-13是未授权),排查见第 11、12 章;- 成功:随后依次运行
face_demo()里的 13 个示例(SDK 信息、姿态角、模糊、质量、 抠图、光照、口罩、遮挡、特征值、比对与识别、人脸库管理、关键点、单目活体),每个示例打印 各自的结果(示例图片在images/下);检测与跟踪两个示例默认注释 (跟踪示例走 USB 摄像头),需要时在src/face_demo.cpp的face_demo()里取消注释即可; - 最后打印
time cost is :<ms>,然后停在getchar()等一个回车, 按回车后打印end main正常退出。脚本化 / 后台运行请重定向 stdin,例如./run.sh </dev/null。
运行时会在 <root>/(即 face_offline_sdk/ 根目录)下产生:
| 路径 | 内容 |
|---|---|
db/face.db |
人脸库(sqlite3)。face.ini 配了 DbDir 则为 <DbDir>/db/face.db |
face_sdk.log |
SDK 业务日志,LogType = 1 时才有内容 |
/tmp/logs/SLog-*.log |
底层算法库日志,同样由 LogType 控制(单文件上限 10 MB,会滚动) |
目录布局是硬约束,不能随意改,两条原因:
- SDK 用
/proc/self/exe定位资源,从可执行文件所在目录退一级去找face.ini、pipline.ini、models/。所以可执行文件必须在<root>/build/,资源(face.ini、pipline.ini、models/、lib/)必须在<root>/(face_offline_sdk根目录)。把可执行文件挪到别处会让sdk_init返回-1023(face.ini不存在)。 - 示例代码里的图片路径是相对当前工作目录的(
images/2.jpg等),所以要cd到<root>再跑。
不想用这个布局,就给 sdk_init 传绝对路径(见 7.3),此时
face.ini、pipline.ini、models/ 都必须在你传的那个目录下。
7.3 最小接入代码
只需要 #include "baidu_face_api.h",所有能力都在 BaiduFaceApi 这一个类上:
1#include "baidu_face_api.h"
2#include "opencv2/opencv.hpp"
3
4int main() {
5 BaiduFaceApi api; // 不可拷贝、不可移动
6
7 // 传 nullptr = 用默认路径(可执行文件上一级)
8 // 也可以传绝对路径,此时 face.ini / pipline.ini / models/ 都要在该目录下:
9 // api.sdk_init("/userdata/face"); → 模型在 /userdata/face/models
10 int ret = api.sdk_init(nullptr);
11 if (ret != 0) {
12 // 常见:-1023 face.ini 不存在、-4 models 目录不存在、
13 // -13 未授权、-1003 建库失败
14 return ret;
15 }
16
17 cv::Mat img = cv::imread("images/2.jpg"); // BGR,CV_8UC3
18
19 // 人脸检测
20 std::vector<FaceBox> boxes;
21 int n = api.detect(boxes, &img); // 返回人脸个数,<0 是错误码
22
23 // 1:1 比对,返回 0~100 的相似度(已四舍五入),<0 是错误码
24 cv::Mat img2 = cv::imread("images/1.jpg");
25 int score = api.match(img, img2);
26
27 // 注册 / 识别这类底库接口出参是 JSON 字符串
28 std::string res;
29 api.user_add(res, &img, "user_001", "group_1", "张三");
30 api.identify(res, &img, "group_1", nullptr);
31
32 return 0; // 析构里会 bd_sdk_uninit
33}
编译时只要指到 include/ 和 third_party/,链接 lib/ 下的库即可,
照抄本目录 CMakeLists.txt 的客户模式分支最省事。
sdk_init只需要调一次,重复调用返回-12(ABILITY_ALREADY_LOADED)。 野指针检查:model_path传nullptr才走默认路径,传空字符串会当普通路径处理(大概率-4)。- 只调
get_device_id、sdk_version不需要先sdk_init(取指纹用于申请 license)。
7.4 线程安全约束
请把整个 BaiduFaceApi 当成单线程对象,所有接口自行串行化。
内部确实有一把 _db_mutex,底库相关接口(user_add / user_update /
user_delete / group_* / identify* / load_db_face /
db_face_count 等)都持它,但它只保护 SQLite 连接和内存里那份特征表,不保护底层算法引擎。
而这些底库接口本身就要先做检测、对齐、提特征——也就是说 user_add(持锁)与
detect(不持锁)并发时,两边同时在动同一份算法上下文,_db_mutex 对此毫无作用。
检测、特征、活体、质量等纯算法接口之间也一样没有锁。
所以不要「底库接口可以并发、算法接口自己串行」地区别对待,任意两个接口都不要并发调用。
要在多线程里用,就在应用层加一把自己的锁包住全部调用。BaiduFaceApi 全进程只建一个实例。
析构不能与在途调用并发。~BaiduFaceApi 会销毁内部算法对象、销毁
_db_mutex(此时若还有线程持锁即为未定义行为),并调用底层的反初始化。必须确认所有
工作线程都已退出、没有任何接口还在执行,才能销毁这个对象。
7.5 示例工程
src/ 下按能力分了子目录,face.cpp 是 main。打开日志、跑通全流程后,
再照 src/ 改成自己的业务。注意:
- OpenCV 只能用
lib/下这一份。face_crop和get_user_image是库内分配、调用方释放,同一进程里出现两份 OpenCV (各自一套内存池和全局状态)会在释放时崩。不要自己另外编一份 OpenCV 链进来,也不要用别的版本的opencv2头。 - OpenCV 3.2 没有 GUI 后端(无 GTK / Qt):
cv::imshow/cv::waitKey一调用就抛cv::Exception(示例src/face_track/track.cpp因此改成跑固定帧数、 把结果写成usb_track.jpg);没有 FFMPEG / GStreamer,打不开视频文件;cv::VideoCapture(0)读 USB 摄像头可以(V4L2 在)。 - 输入图像格式:
CV_8UC3、BGR 排列。灰度图和 4 通道图会被拒(-15)。 宽高不需要自己做 4 对齐——底层检测接口要求 4 的整数倍,SDK 内部已统一补边,且只在右边和下边补, 所以返回的人脸框和关键点就是原图坐标,不用做偏移换算。
8 模型能力加载及模型说明
8.1 config.toml
模型清单在 models/config.toml 的 [[models]] 段,enable = true
才加载。设为 false 或文件缺失只影响对应能力,不影响 sdk_init 本身
(需要该能力的接口会返回对应错误码,各模型关闭后的影响见 8.4)。模型路径支持在 config.toml 里通过相对路径定制,
一般不需要动。
8.2 模型清单
| 模型文件 | 大小 | type(能力) | 说明 |
|---|---|---|---|
align_v7.0.rknn.enc |
772 KB | align rgb |
对齐 / 关键点 |
detect_H192x108_v7.6_fp16.rknn.enc + ..._mbox_priorbox_v7.4.bin |
766 KB + 38 KB | detect horizontal rgb |
人脸检测(水平) |
detect_V108x192_v7.6.rknn.enc + ..._mbox_priorbox_v7.4.bin |
766 KB + 38 KB | detect vertical rgb |
人脸检测(垂直) |
liveness_rgb_int8_mmse.rknn.enc |
613 KB | liveness rgb |
RGB 单目活体(int8 量化,MMSE 校准) |
liveness_nir_dy16_v8.0.rknn.enc |
613 KB | liveness nir |
NIR 单目活体 |
handcheck_dy16_v7.6.rknn.enc |
175 KB | handcheck |
手部攻击检测(仅 liveness_pipeline) |
reflection_dy16_v7.6.rknn.enc |
175 KB | reflection |
反光攻击检测(仅 liveness_pipeline) |
mask2d_dy16_v7.6.rknn.enc |
175 KB | mask2d |
2D 面具攻击检测(仅 liveness_pipeline) |
quality_v7.0.rknn.enc |
448 KB | quality rgb |
质量评估(姿态 / 光照 / 遮挡 / 模糊 / 总分) |
feature_rgb_v8.0.rknn.enc |
26.6 MB | recognition rgb |
通用识别特征,face_feature / identify 用的就是它 |
feature_id_v8.0.rknn.enc |
26.6 MB | recognition rgb id |
证件照专用特征,本 SDK 未使用(见 8.3) |
occlusion_v7.0.rknn.enc |
169 KB | occlusion |
遮挡检测 |
mouthmask_v8.0.rknn.enc |
119 KB | mouthmask |
口罩检测 |
blur_v7.0.rknn.enc |
137 KB | blur |
模糊检测 |
8.3 内存与特征模型
两个特征模型各 26.6 MB,是 models/ 的体积大头。recognition rgb 与
recognition rgb id 是两个不同的模型,同一张人脸用它们提的特征互不兼容、不能互相比对;
本 SDK 所有接口都只走通用版 recognition rgb;recognition rgb id 不会被用到,
交付的 config.toml 已把它预置为 enable = false 不加载(省 26.6 MB,
sdk_init 不受影响,见 8.4)——确需启用再改回 true。
特征值是与模型版本绑定的:feature_rgb_v8.0 提的特征只和同一套模型的比对结果
有意义,升级 SDK / 换模型后建议重新注册,旧特征值不能保证可比。内存方面:底库特征常驻内存,
约 1 KB/人(1 万人约 10 MB),models/ 全量约 59 MB,大库 + 全模型场景先实测内存水位。
8.4 按需加载与内存优化
config.toml 的 [[models]] 段就是模型加载开关:enable = true
加载、false 不加载。这是厂商底层行为——sdk_init 只把 models/
目录交给算法库,加载哪些模型完全由 config.toml 决定,本 SDK 封装层不做任何干预。
改完重启程序即生效,不需要重新编译。某一项 enable = false(或删掉模型文件)
不影响 sdk_init 本身,只会让对应能力的接口调用失败(返回 -11「能力未加载」一类错误码)。
这正是 64 MB 内存型号内存不够(sdk_init 返回 -10,或直接打印
E RKNN: failed to allocate model memory,见 12 章 Q3)的解决办法——按需裁剪加载的模型。
8.4.1 各模型关闭后的影响
| 模型(type) | 用到的接口 | 关掉后的影响 | 建议 |
|---|---|---|---|
align rgb |
对齐、关键点(face_landmark,提特征 / 识别的内部前置) |
提特征、识别不可用 | 保留 |
detect horizontal / vertical rgb(含两个 priorbox) |
人脸检测(detect / track 及一切依赖检测的接口) |
全部功能不可用 | 保留(两个方向都要) |
recognition rgb(feature_rgb) |
识别特征(face_feature / identify* / user_add 提特征) |
识别、注册、比对不可用 | 保留 |
liveness rgb |
RGB 活体(rgb_liveness、pipline_feature、liveness_pipeline) |
活体判定不可用 | 需要活体就保留 |
quality rgb |
质量评估(face_quality、pipline_feature / liveness_pipeline 的质量门限) |
质量分、质量过滤不可用 | 用 pipline_* 就保留 |
liveness nir |
NIR 活体(nir_liveness、双目 pipline_feature) |
NIR 活体不可用 | 没有红外相机可关 |
handcheck / reflection / mask2d |
攻击检测(仅 liveness_pipeline) |
活体策略链退化为无攻击检测(各仅 175 KB) | 按需 |
occlusion |
遮挡检测(face_occlusion) |
该属性不可用(169 KB) | 按需 |
mouthmask |
口罩检测(face_mouth_mask) |
该属性不可用(119 KB) | 按需 |
blur |
模糊检测(face_blur) |
该属性不可用(137 KB) | 按需 |
recognition rgb id(feature_id) |
本 SDK 未使用(封装层零引用,见 8.3) | 无任何影响 | 维持关闭(交付配置已默认 false,省 26.6 MB) |
8.4.2 64 MB 型号推荐裁剪
在板子(或工程源目录)的 models/config.toml 上,确认下面这些条目是
false。交付配置已预置好(① 就是 false,无需手动改);
只有从旧版本升级、config.toml 还是旧内容时,才需要把
recognition rgb id 那条从 true 改成 false:
1# ① 已默认关闭(省 26.6 MB,本 SDK 未使用,无任何影响)——确认是 false 即可:
2[[models]]
3enable = false # ← 确认这一行是 false
4file_name = "feature_id_v8.0.rknn.enc"
5type = "recognition rgb id"
6
7# ② 按业务可选(每个省 100~200 KB):
8# occlusion_v7.0 / mouthmask_v8.0 / blur_v7.0 → 不用对应属性检测就关
9# handcheck_dy16 / reflection_dy16 / mask2d_dy16 → 不用 liveness_pipeline 就关
10# liveness_nir_dy16 → 没有红外相机就关
11# quality_v7.0 → 不用 pipline_feature / face_quality 就关
改完重启程序即生效。裁剪后 sdk_init 正常、识别 / 检测 / 活体等核心能力不受影响;
调用到未加载能力的接口会返回错误(-11 一类),业务侧要自行保证按需裁剪后不调用对应接口。
- 改哪份文件:板子上直接改
<root>/models/config.toml立即生效; 但重新编译、重新打包会从工程models/重新拷贝覆盖(见 7.1 产物布局), 所以要持久化的话,改工程源目录face_offline_sdk/models/config.toml后重新打包。 config.toml的[license_info]/[log_info]/[liveness_param]段和log.conf(由代码生成)都不要动。- 内存账:一条
feature_id约省 26.6 MB(.enc 尺寸;运行时解密后实际占用更大一点), 其余小模型合计约 1 MB。64 MB 型号关闭feature_id后,free应能看到 约 27 MB 的余量回增。
9 功能接口
出参一律放在第一个参数上,返回值一律是错误码(或个数、分值)。
9.1 信息与初始化
9.1.1 SDK 初始化接口
| 项 | 内容 |
|---|---|
| 方法名 | sdk_init |
| 说明 | 加载模型、初始化算法引擎并打开本地人脸库;全进程只需调用一次,重复调用返回 -12 |
| 函数 | int sdk_init(const char* model_path) |
| 返回信息 | 0 = 成功,其它为错误码(-1023 无 face.ini、-4 无 models、-13 未授权,见第 11 节) |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
model_path |
资源根目录 | 否 | const char* |
传 nullptr = 用默认路径(可执行文件上一级);传绝对路径时 face.ini / pipline.ini / models/ 都要在该目录下 |
9.1.2 获取 SDK 版本号接口
| 项 | 内容 |
|---|---|
| 方法名 | sdk_version |
| 说明 | 返回底层算法库的完整版本串,免初始化可调 |
| 函数 | void sdk_version(std::string& version) |
| 返回信息 | 无返回值(结果在 version) |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
version |
通过引用返回的版本串 | 是 | std::string& |
出参 |
9.1.3 获取设备指纹接口
| 项 | 内容 |
|---|---|
| 方法名 | get_device_id |
| 说明 | 返回设备指纹,用于申请 license;sdk_init 之前也能调用 |
| 函数 | void get_device_id(std::string& device_id) |
| 返回信息 | 无返回值(结果在 device_id) |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
device_id |
通过引用返回的设备指纹 | 是 | std::string& |
出参,一机一码 |
9.1.4 授权 / 日志状态查询接口
| 项 | 内容 |
|---|---|
| 方法名 | is_auth / is_log_open |
| 说明 | 查询当前是否已授权 / 是否已打开日志 |
| 函数 | bool is_auth() / bool is_log_open() |
| 返回信息 | bool:true = 已授权 / 日志已打开 |
请求参数:无
9.2 检测与跟踪
入参都是 const cv::Mat*,BGR / CV_8UC3;type:
0 = 可见光(RGB),1 = 近红外(NIR)——仅本组三个接口带 type。
9.2.1 人脸检测接口
| 项 | 内容 |
|---|---|
| 方法名 | detect |
| 说明 | 人脸检测,返回图中所有人脸框 |
| 函数 | int detect(std::vector<FaceBox>& out, const cv::Mat* mat, int type = 0) |
| 返回信息 | int:>=0 为检测到的人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的人脸框数组 | 是 | std::vector<FaceBox>& |
人脸框结构体,见 10.1 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
type |
检测类型 | 否 | int |
0=RGB 可见光(默认),1=NIR 近红外 |
9.2.2 人脸跟踪接口
| 项 | 内容 |
|---|---|
| 方法名 | track |
| 说明 | 人脸跟踪,返回带稳定 face_id 的人脸信息(多帧关联) |
| 函数 | int track(std::vector<TrackFaceInfo>& out, const cv::Mat* mat, int type = 0) |
| 返回信息 | int:>=0 为人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的跟踪结果数组 | 是 | std::vector<TrackFaceInfo>& |
跟踪结构体,见 10.3 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
type |
检测类型 | 否 | int |
0=RGB(默认),1=NIR |
9.2.3 人脸关键点接口
| 项 | 内容 |
|---|---|
| 方法名 | face_landmark |
| 说明 | 人脸关键点检测,返回 72 个关键点坐标 |
| 函数 | int face_landmark(std::vector<Landmarks>& out, const cv::Mat* mat, int type = 0) |
| 返回信息 | int:>=0 为人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的关键点数组 | 是 | std::vector<Landmarks>& |
关键点结构体,见 10.2(144 个 float,两两一组 x、y) |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
type |
检测类型 | 否 | int |
0=RGB(默认),1=NIR |
9.3 属性检测
本组接口入参均为 const cv::Mat*(BGR / CV_8UC3),返回值为检测到的人脸个数
(<0 为错误码)。属性的分数 / 角度不是「分数上限」:分数代表清晰、无遮挡、光照正常的
程度,越高越好;姿态角是绝对值,越小越好。推荐阈值见 9.9 的 pipline.ini 说明。
9.3.1 人脸姿态角接口
| 项 | 内容 |
|---|---|
| 方法名 | face_head_pose |
| 说明 | 人脸姿态角检测,返回 yaw / roll / pitch(度) |
| 函数 | int face_head_pose(std::vector<HeadPose>& out, const cv::Mat* mat) |
| 返回信息 | int:>=0 为人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的姿态角数组 | 是 | std::vector<HeadPose>& |
姿态角结构体,见 10.5 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.3.2 人脸光照检测接口
| 项 | 内容 |
|---|---|
| 方法名 | face_illumination |
| 说明 | 人脸光照检测,score[0] 亮度、score[1] 暗度 |
| 函数 | int face_illumination(std::vector<Illumination>& out, const cv::Mat* mat) |
| 返回信息 | int:>=0 为人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的光照数组 | 是 | std::vector<Illumination>& |
光照结构体,见 10.5 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.3.3 人脸模糊度检测接口
| 项 | 内容 |
|---|---|
| 方法名 | face_blur |
| 说明 | 人脸清晰度检测(分数越高越清晰) |
| 函数 | int face_blur(std::vector<Blur>& out, const cv::Mat* mat) |
| 返回信息 | int:>=0 为人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的清晰度数组 | 是 | std::vector<Blur>& |
模糊度结构体,见 10.5 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.3.4 人脸口罩佩戴检测接口
| 项 | 内容 |
|---|---|
| 方法名 | face_mouth_mask |
| 说明 | 人脸口罩佩戴检测,返回佩戴口罩分值 |
| 函数 | int face_mouth_mask(std::vector<MouthMask>& out, const cv::Mat* mat) |
| 返回信息 | int:>=0 为人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的口罩数组 | 是 | std::vector<MouthMask>& |
口罩结构体,见 10.5 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.3.5 人脸遮挡检测接口
| 项 | 内容 |
|---|---|
| 方法名 | face_occlusion |
| 说明 | 人脸遮挡检测,返回 7 个部位(左/右眼、鼻、嘴、左/右脸、下巴)的遮挡置信度 |
| 函数 | int face_occlusion(std::vector<Occlusion>& out, const cv::Mat* mat) |
| 返回信息 | int:>=0 为人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的遮挡数组 | 是 | std::vector<Occlusion>& |
遮挡结构体,见 10.5 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.3.6 人脸质量接口
| 项 | 内容 |
|---|---|
| 方法名 | face_quality |
| 说明 | 人脸质量综合评估,返回姿态 + 遮挡 + 光照 + 模糊 + 总分 |
| 函数 | int face_quality(std::vector<Quality>& out, const cv::Mat* mat) |
| 返回信息 | int:>=0 为人脸数量,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的质量数组 | 是 | std::vector<Quality>& |
质量结构体,见 10.6 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.3.7 人脸抠图接口
| 项 | 内容 |
|---|---|
| 方法名 | face_crop |
| 说明 | 人脸抠图,抠出检测到的第一张脸(库内分配,调用方负责释放 out) |
| 函数 | int face_crop(cv::Mat& out, const cv::Mat* mat) |
| 返回信息 | int:0 成功,<0 为错误码(未检测到人脸返回 -1012) |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
抠出的人脸图 | 是 | cv::Mat& |
出参;只能用 lib/ 下这份 OpenCV,见第 13 节 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.4 特征与比对
Feature.data 是 float[256],Feature.size 是有效长度。特征值可以自己落盘,
之后用 user_add(res, Feature*, ...) / identify(res, Feature*, ...) 走免解码的快路径
(不再走检测 → 对齐 → 提特征)。
9.4.1 人脸特征值接口
| 项 | 内容 |
|---|---|
| 方法名 | face_feature |
| 说明 | 提取人脸特征值。图里有多张脸时只取检测结果的第一张,成功返回 1,两个出参各一个元素、下标一一对应 |
| 函数 | int face_feature(std::vector<Feature>& out_fea, std::vector<FaceBox>& out_box, const cv::Mat* mat) |
| 返回信息 | int:成功返回 1,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out_fea |
通过引用返回的特征值数组 | 是 | std::vector<Feature>& |
特征值结构体,见 10.4 |
out_box |
通过引用返回的人脸框数组 | 是 | std::vector<FaceBox>& |
与 out_fea 下标一一对应 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.4.2 Pipline 全流程特征值提取接口(单目)
采用该接口提取的特征值先经质量门限 + 活体过滤,识别时更准确;门限来自 pipline.ini,
逐项开关由 conf 控制。
| 项 | 内容 |
|---|---|
| 方法名 | pipline_feature |
| 说明 | 质量判断 + 静默活体过滤 + 提特征一体化,任一项不过关返回负值 |
| 函数 | int pipline_feature(Feature& out_fea, PiplineRes& out_res, const PiplineConf& conf, const cv::Mat* mat) |
| 返回信息 | int:>0 为人脸数量,<=0 为未检测到人脸或错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out_fea |
通过引用返回的特征值 | 是 | Feature& |
特征值结构体,见 10.4 |
out_res |
通过引用返回的全流程结果 | 是 | PiplineRes& |
全流程结构体,见 10.7 |
conf |
全流程配置 | 是 | const PiplineConf& |
设定是否做质量 / 活体过滤,见 10.7 |
mat |
传入的 OpenCV 图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.4.3 Pipline 全流程特征值提取接口(双目)
| 项 | 内容 |
|---|---|
| 方法名 | pipline_feature(重载) |
| 说明 | 在单目版基础上额外做 NIR 活体(PiplineRes.rgb_score / nir_score 分项出分) |
| 函数 | int pipline_feature(Feature& out_fea, PiplineRes& out_res, const PiplineConf& conf, const cv::Mat* rgb_mat, const cv::Mat* nir_mat) |
| 返回信息 | int:>0 为人脸数量,<=0 为未检测到人脸或错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out_fea / out_res / conf |
同 9.4.2 | 是 | — | 见 9.4.2 |
rgb_mat |
传入的可见光图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
nir_mat |
传入的近红外图像帧 | 是 | const cv::Mat* |
用于 NIR 活体 |
9.4.4 特征值比对接口
| 项 | 内容 |
|---|---|
| 方法名 | compare_feature |
| 说明 | 两个特征值的相似度比对(仅支持单个结构体比对) |
| 函数 | float compare_feature(Feature* f1, Feature* f2) |
| 返回信息 | float:特征值比对相似度分值 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
f1 |
特征值 1 | 是 | Feature* |
特征值结构体,见 10.4 |
f2 |
特征值 2 | 是 | Feature* |
特征值结构体,见 10.4 |
9.5 活体
9.5.1 RGB 单目静默活体接口
| 项 | 内容 |
|---|---|
| 方法名 | rgb_liveness |
| 说明 | RGB 可见光单目静默活体(推荐配合 NIR 做双目校验),只取检测到的第一张脸 |
| 函数 | int rgb_liveness(std::vector<TrackFaceInfo>& out, float& score, const cv::Mat* mat) |
| 返回信息 | int:>=0 为人脸个数,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的人脸框 / 跟踪信息 | 是 | std::vector<TrackFaceInfo>& |
多人则返回最大人脸,见 10.3 |
score |
通过引用返回的活体分值 | 是 | float& |
[0,1],推荐通过分值 0.8 |
mat |
传入的可见光图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
9.5.2 NIR 单目静默活体接口
| 项 | 内容 |
|---|---|
| 方法名 | nir_liveness |
| 说明 | NIR 近红外单目静默活体(推荐配合 RGB 做双目校验),只取检测到的第一张脸 |
| 函数 | int nir_liveness(std::vector<TrackFaceInfo>& out, float& score, const cv::Mat* mat) |
| 返回信息 | int:>=0 为人脸个数,<0 为错误码 |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的人脸框 / 跟踪信息 | 是 | std::vector<TrackFaceInfo>& |
多人则返回最大人脸,见 10.3 |
score |
通过引用返回的活体分值 | 是 | float& |
[0,1] |
mat |
传入的近红外图像帧 | 是 | const cv::Mat* |
NIR 图 |
9.5.3 活体策略(pipeline)接口
| 项 | 内容 |
|---|---|
| 方法名 | liveness_pipeline |
| 说明 | 检测跟踪 + 最优帧 + 对齐 + 质量过滤 + 活体(含手部 / 反光 / 2D 面具攻击检测)整条策略链,内部固定单帧窗口 |
| 函数 | int liveness_pipeline(PipelineLivenessRes& out, const cv::Mat* rgb_mat, const cv::Mat* nir_mat = nullptr) |
| 返回信息 | int:1 = 本帧已处理(无脸或被过滤时出参全 0),0 或负数 = 错误码(超时返回 -14) |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
out |
通过引用返回的单帧策略结果 | 是 | PipelineLivenessRes& |
见 10.8 |
rgb_mat |
传入的可见光图像帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
nir_mat |
传入的近红外图像帧 | 否 | const cv::Mat* |
传 nullptr = 纯 RGB |
liveness_pipeline 详细说明:
- 与
rgb_liveness/nir_liveness(单模型活体分值)不同,它是整条策略链的 判定结果:内部固定单帧窗口(一次调用只处理一帧),返回1表示本帧已处理,每帧都会出结果。 - 输出
PipelineLivenessRes各字段:has_face本帧是否检出人脸(无脸时box/quality全 0,但rgb_status/nir_status仍有意义);rgb_score/nir_score是 RGB / NIR 活体分值(未给 NIR 输入时nir_score为 0);rgb_status/nir_status是活体过滤状态,与厂商一致:0= 未走到该能力 / 无结果,1= 没通过阈值,2= 通过阈值;frame_status:0= UNKNOWN,1= RUNNING,2= FINISHED(单帧调用下通常一次就到2)。 - 阈值全部来自
pipline.ini,与pipline_feature同一份。 - 单帧质量不过关不报错,只体现在各
*_status上,由调用方按业务需要决定是否采信 (视频流场景下偶发单帧失败是正常的)。 - 被过滤的帧同样返回
1:厂商流水线对「本帧未通过质量 / 活体 / 攻击检测」(其内部码-20)及「上一帧未处理完」(-21)不回调、不视为错误(官方示例就是打印 continue 后接着喂下一帧)。此时出参保持全 0(has_face = false、box/quality/ 各*_status均为 0),按「本帧无有效结果」继续下一帧即可; 只有返回负数才是真正的错误(-14表示超时或厂商异常,见错误码表)。 - 与其它接口一样不要并发调用(回调在厂商内部线程,本封装已同步好,但同一时刻仍只允许一个调用在途)。
9.6 人脸库管理
本组接口出参 std::string& res 均为 JSON 字符串,统一形状为
{ "errno": 0, "msg": "success", "data": { ... } },各接口 data 字段详见 9.8。
errno 为 0 才表示成功。
9.6.1 人脸注册接口(传入图片帧)
| 项 | 内容 |
|---|---|
| 方法名 | user_add |
| 说明 | 用户注册,传入图片帧,SDK 内部完成检测 → 对齐 → 提特征后入库 |
| 函数 | void user_add(std::string& res, const cv::Mat* img, const char* user_id, const char* group_id, const char* user_info = nullptr) |
| 返回信息 | 无返回值(结果在 res) |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
res |
返回的 JSON 结果 | 是 | std::string& |
data.face_token |
img |
人脸图片帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
user_id |
用户 id | 是 | const char* |
字母 / 数字 / 下划线,< 128B |
group_id |
用户组 id | 是 | const char* |
字母 / 数字 / 下划线,< 128B |
user_info |
用户资料 | 否 | const char* |
可含中文,≤ 256B |
9.6.2 人脸注册接口(传入特征值)
| 项 | 内容 |
|---|---|
| 方法名 | user_add(重载) |
| 说明 | 用户注册,直接传入特征值入库(免解码快路径,不再走检测 / 对齐 / 提特征) |
| 函数 | void user_add(std::string& res, Feature* f1, const char* user_id, const char* group_id, const char* user_info = nullptr) |
| 返回信息 | 无返回值(结果在 res) |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
res |
返回的 JSON 结果 | 是 | std::string& |
data.face_token |
f1 |
人脸特征值 | 是 | Feature* |
特征值结构体,见 10.4 |
user_id |
用户 id | 是 | const char* |
字母 / 数字 / 下划线,< 128B |
group_id |
用户组 id | 是 | const char* |
字母 / 数字 / 下划线,< 128B |
user_info |
用户资料 | 否 | const char* |
可含中文,≤ 256B |
9.6.3 人脸更新接口
| 项 | 内容 |
|---|---|
| 方法名 | user_update |
| 说明 | 更新已注册用户的人脸(重新提特征覆盖) |
| 函数 | void user_update(std::string& res, const cv::Mat* img, const char* user_id, const char* group_id, const char* user_info = nullptr) |
| 返回信息 | 无返回值(结果在 res) |
| 请求参数 | 说明 | 必须 | 类型 | 示例描述 |
|---|---|---|---|---|
res |
返回的 JSON 结果 | 是 | std::string& |
data.face_token |
img |
人脸图片帧 | 是 | const cv::Mat* |
BGR / CV_8UC3 |
user_id |
用户 id | 是 | const char* |
字母 / 数字 / 下划线,< 128B |
group_id |
用户组 id | 是 | const char* |
字母 / 数字 / 下划线,< 128B |
user_info |
用户资料 | 否 | const char* |
可含中文,≤ 256B |
底库规范(易踩的点和量级):
- 底库存 SQLite3 文件
<root>/db/face.db(face.ini配了DbDir则在<DbDir>/db/face.db),内部feature/user/user_group三张表。 db/目录可以直接删——下次启动自动重建空底库;数据库文件损坏(打不开、报-1003) 时删掉重启即可,代价是已注册数据全部丢失。删库前先备份face.db文件。- 单库量级建议 5 万人以下(厂商设计上限);识别时特征全量在内存比对,约 1 KB/人, 5 万人约 50 MB,结合 64/128 MB 的 DDR 自行评估。
user_id/group_id:只允许字母、数字、下划线,长度 < 128 字节 (identify的group_id_list另允许用逗号分隔多个组);user_info允许中文,≤ 256 字节(UTF-8 下约 85 个汉字)。DbDir、传给sdk_init的路径都不要用中文(模型加载和 SQLite 打开对非 ASCII 路径支持不佳)。
9.7 识别
| 接口 | 说明 |
|---|---|
match(const cv::Mat&, const cv::Mat&) |
1:1,返回 0~100(已四舍五入),<0 是错误码 |
identify(res, img 或 Feature*, group_id_list, user_id) |
1:N,group_id_list 多组用逗号分隔;user_id 传 nullptr 表示不限用户 |
identify_with_all(res, img 或 Feature*) |
1:N,与整个库比对(用内存中的 _db_vec,先调 load_db_face()) |
识别命中的判定阈值是 face.ini 的 IdentifyScore(1~100)。
result 只包含分值 ≥ 阈值的记录,顺序是底库顺序(未排序,要「最像的一个」请自行按
score 取最大);一条都没过阈值时 errno = -1005。
注意 identify 的 result[].score 是 0~100 口径,与
pipline.ini 的 0~1 分值不要混用;另外没有「101 表示放行」这种
特殊约定:101 越界后会被回落到 80,想宽松只能自己把阈值调小。
9.8 JSON 返回格式
底库和识别类接口的 res 统一是这个形状:
1{ "errno": 0, "msg": "success", "data": { ... } }
errno 为 0 才表示成功,非 0 时 data 里通常只有 log_id。各接口的 data:
1// user_add / user_update
2{ "log_id": "1725254400123", "face_token": "e3b0c44298fc1c14…" }
3
4// identify / identify_with_all
5// result 只包含分值 >= IdentifyScore 的记录,顺序是底库顺序(未排序,
6// 要"最像的一个"请自行按 score 取最大);一条都没过阈值时 errno = -1005
7{ "log_id": "…", "face_token": "…", "result_num": 1,
8 "result": [ { "group_id": "group_1", "user_id": "user_001", "score": 93.7 } ] }
9
10// get_user_info → data.result
11// get_user_list → data.user_id_list
12// get_group_list → data.group_id_list
13// 元素的 key 是小写的数据库列名(user_id / group_id / face_token / create_time …)
14{ "log_id": "…", "user_id_list": [ { "user_id": "user_001", "group_id": "group_1" } ] }
face_token 是特征值的 MD5,用于唯一标识一张已注册的人脸;log_id 是毫秒时间戳,
排查问题时和日志对齐用。业务侧判定「识别成功」建议用「errno == 0 且 result_num > 0」,
不要用「res 非空」去判——-1005(一条都没过识别阈值)的 res 也是一条完整 JSON,非空。
9.9 配置文件
face.ini(改完重启生效,无需重编):
| 节 / 键 | 默认 | 说明 |
|---|---|---|
[LogType] LogType |
0 |
0 关闭日志,1 输出日志文件。同时控制 SDK 业务日志和底层算法库日志 |
[IdentifyScore] IdentifyScore |
80 |
1:N 识别阈值,有效范围 1~100,越界自动回落到 80 |
[DbDir] DbDir |
空(注释掉) | 人脸库落盘目录,Linux 绝对路径;留空 = 可执行文件上一级 |
[MultiFactor] MultiFactor |
0(注释掉) |
活体 + 识别联合判定开关 |
[SaveDbFaceImage] SaveDbFaceImage |
1 |
注册/更新时是否把裁剪后的人脸图存进人脸库(USER.IMAGE)。1 存(默认,保持原行为);0 不存,省存储。见下方说明 |
SaveDbFaceImage 的实现细节:user_add / user_update 的图片重载会把整帧做
中心正方形裁剪并缩放到 100×100、base64 后存进 USER.IMAGE 列。配 0 时注册/更新
都跳过这一步——user_add 不写 IMAGE(列取建表默认空值),user_update 只刷
USER_INFO 而不动 IMAGE(原有底库图保持原样、不会被清空)。关掉不影响识别
(识别只用特征);此时 get_user_image 因底库无图返回 NO_DATAS(-1004)——记录仍在、
只是没存底库图,需与图片损坏的 IMAGE_TRANSFORM_FAILED(-16) 区分。特征入库的另一条
「特征重载」注册路径本就不写底库图,不受本开关影响。RV1106 存储吃紧时建议配 0。
LogType 的实现细节:sdk_init 时会按它的值生成 models/log.conf
(底层算法库的日志配置)。这个文件由代码生成,不要手工编辑,改了下次 sdk_init 会被覆盖。
若 models/ 挂在只读分区,会打印一条 warning 并沿用磁盘上原有的 log.conf,不影响识别功能。
pipline.ini 是 pipline_feature() / liveness_pipeline 的质量门限,
全部为 0~1 的分值或角度(度):
| 键 | 默认 | 说明 |
|---|---|---|
LivenessThresh |
0.8 |
活体分数下限,低于则判失败 |
YawThresh / RollThresh / PitchThresh |
20 |
三个姿态角上限(度),绝对值超过则判失败 |
IllumThresh |
0.8 |
光照分数下限,低于则判失败 |
OccluThresh |
0.8 |
遮挡分数下限(逐部位判断),低于则判失败 |
BlurThresh |
0.8 |
清晰度分数下限(分数越高越清晰),低于则判失败 |
阈值推荐取值(与厂商《接口推荐阈值参考》一致,不要凭感觉调):
| 项目 | 推荐 | 严格 | 宽松 |
|---|---|---|---|
| 检测 / 关键点 / 遮挡 / 模糊 / 质量 / 活体 / 亮度 / 暗度分数 | > 0.8 | > 0.85 | > 0.75 |
| 姿态角(yaw / roll / pitch) | < 20° | < 15° | < 30° |
10 结构体描述
全部出参结构体定义在 struct_info.h,均为 POD 结构,按值传递 / vector 装载。
10.1 FaceBox(人脸框)
| 成员 | 类型 | 说明 |
|---|---|---|
index |
int | 框序号 |
center_x / center_y |
float | 中心点坐标(不是左上角) |
width / height |
float | 宽、高 |
score |
float | 置信度 [0,1] |
转成 cv::Rect 的写法:
1cv::Rect rect(box.center_x - box.width / 2, box.center_y - box.height / 2,
2 box.width, box.height);
10.2 Landmarks(关键点)
| 成员 | 类型 | 说明 |
|---|---|---|
index |
int | 序号 |
size |
int | 关键点个数(72) |
data[144] |
float | 72 个关键点两两一组:data[2i]=x、data[2i+1]=y |
score |
float | 关键点置信度 |
10.3 TrackFaceInfo(跟踪结果)
| 成员 | 类型 | 说明 |
|---|---|---|
face_id |
unsigned long | 跟踪分配的稳定 ID(多帧关联) |
box |
FaceBox | 人脸框 |
facelandmarks |
Landmarks | 关键点 |
10.4 Feature(特征值)
| 成员 | 类型 | 说明 |
|---|---|---|
size |
int | 有效长度(256) |
data[256] |
float | 特征向量;与模型版本绑定,只与本套模型可比 |
10.5 属性类
| 结构体 | 成员 | 说明 |
|---|---|---|
Illumination |
float score[2] |
score[0] 亮度、score[1] 暗度(厂商口径「{明亮度,黑暗度}」) |
Blur |
float score |
清晰度,越高越清晰 |
MouthMask |
float mask_score |
佩戴口罩分值 |
HeadPose |
float yaw / roll / pitch |
左右偏转角 / 平行面内旋转角 / 上下偏转角(度) |
Occlusion |
7 个 float | 左眼、右眼、鼻、嘴、左脸、右脸、下巴的遮挡置信度 |
10.6 Quality(质量信息)
| 成员 | 类型 | 说明 |
|---|---|---|
head_pose |
HeadPose | 姿态角 |
occlusion |
Occlusion | 遮挡 |
illum |
Illumination | 光照 |
blur |
Blur | 模糊 |
score |
float | 质量总分 |
10.7 PiplineRes / PiplineConf(全流程特征)
| 结构体 | 成员 | 说明 |
|---|---|---|
PiplineRes |
FaceBox box |
选中的人脸框 |
Quality quality |
质量 | |
float rgb_score / nir_score |
RGB / NIR 活体分值(单图版 nir_score 为 0) |
|
PiplineConf |
5 个 bool | check_headpose / check_blur / check_illum / check_occlu / check_liveness,逐项开关对应质量门限与活体检查 |
10.8 PipelineLivenessRes(活体策略单帧结果)
| 成员 | 类型 | 说明 |
|---|---|---|
box |
FaceBox | 本帧选中的人脸框;无脸时全 0(看 has_face) |
quality |
Quality | 本帧质量(模糊 / 亮度 / 遮挡 / 姿态);未启用或未通过时对应分值为 0 |
rgb_score / nir_score |
float | RGB / NIR 活体分值 [0,1];未给 NIR 输入时 nir_score 为 0 |
rgb_status / nir_status |
int | 活体过滤状态(与厂商 FilterStatus 一致):0 = 未走到该能力 / 无结果,1 = 没通过阈值,2 = 通过阈值 |
frame_status |
int | 本帧处理状态(FrameStatus):0 = UNKNOWN,1 = RUNNING,2 = FINISHED |
has_face |
bool | 本帧是否检出人脸 |
另外 struct_info.h 中还定义了 LivenessScore 与 SafetyBelt
(安全带检测)两个结构体,其中 SafetyBelt 对应的接口未在公开 API 开放,仅供内部使用,
客户请勿依赖。
11 错误码及错误信息
include/ 里没有错误码头文件,完整列表如下。返回值只会是下面这些码,
底层算法库自己的状态码不会透传出来(它的码空间和这里重叠,透传会造成误判);
底层原始码在 sdk_init 失败时会打到 stderr,LogType=1 时也写进日志。
11.1 通用(算法层)
| 错误码 | 名称 | 错误描述 |
|---|---|---|
0 |
SUCCESS |
成功 |
-1 |
ILLEGAL_PARAMS |
参数非法 |
-2 |
MEMORY_ALLOCATION_FAILED |
内存分配失败 |
-3 |
INSTANCE_IS_EMPTY |
实例为空 |
-4 |
MODEL_IS_EMPTY |
模型数据为空 / models 目录不存在 |
-5 |
UNSUPPORT_ABILITY_TYPE |
不支持的能力类型 |
-6 |
UNSUPPORT_INFER_TYPE |
不支持的预测器类型 |
-7 |
NN_CREATE_FAILED |
预测器创建失败 |
-8 |
NN_INIT_FAILED |
预测器初始化失败 |
-9 |
IMAGE_IS_EMPTY |
图像数据为空 |
-10 |
ABILITY_INIT_FAILED |
能力初始化失败 |
-11 |
ABILITY_UNLOAD |
能力未加载 |
-12 |
ABILITY_ALREADY_LOADED |
能力已加载(重复 sdk_init) |
-13 |
NOT_AUTHORIZED |
未授权 |
-14 |
ABILITY_RUN_EXCEPTION |
能力运行异常(liveness_pipeline 超时也返回它) |
-15 |
UNSUPPORT_IMAGE_TYPE |
不支持的图像类型 |
-16 |
IMAGE_TRANSFORM_FAILED |
图像转换失败 |
-17 |
SDK_INIT_FAIL |
SDK 初始化失败 |
-18 |
FACE_INIT_FAIL |
人脸模块初始化失败 |
11.2 业务层
| 错误码 | 名称 | 错误描述 |
|---|---|---|
-1001 |
SYSTEM_ERROR |
系统错误 |
-1002 |
PARARM_ERROR |
参数错误 |
-1003 |
DB_OP_ERROR |
数据库操作错误 |
-1004 |
NO_DATAS |
没有数据 |
-1005 |
RECORD_UNEXIST |
记录不存在(含底库为空;identify 一条都没过阈值) |
-1006 |
RECORD_ALREADY_EXIST |
记录已存在 |
-1007 |
FILE_NOT_EXIST |
文件不存在 |
-1008 |
GET_FEATURE_FAIL |
提取特征值失败 |
-1009 |
FILE_TOO_BIG |
文件超过 10 MB |
-1010 |
FACE_RESOURCE_NOT_EXIST |
模型资源文件不存在(sdk_init 资源定位失败) |
-1011 |
FEATURE_LEN_ERROR |
特征值长度错误 |
-1012 |
DETECT_NO_FACE |
未检测到人脸 |
-1013 |
CAMERA_ERROR |
摄像头错误或不存在 |
-1014 |
FACE_INSTANCE_ERROR |
人脸实例初始化错误 |
-1015 |
LICENSE_FILE_NOT_EXIST |
授权文件不存在 |
-1016 |
LICENSE_KEY_EMPTY |
授权序列号为空 |
-1017 |
LICENSE_KEY_INVALID |
授权序列号无效 |
-1018 |
LICENSE_KEY_EXPIRE |
授权序列号过期 |
-1019 |
LICENSE_ALREADY_USED |
授权序列号已被其它设备使用 |
-1020 |
DEVICE_ID_EMPTY |
设备指纹为空 |
-1021 |
NETWORK_TIMEOUT |
网络超时 |
-1022 |
NETWORK_ERROR |
网络错误 |
-1023 |
CONF_INI_UNEXIST |
face.ini 不存在 |
说明:-1002 的枚举名 PARARM_ERROR 是底层头文件中的实际拼写(PARAM 误拼为
PARARM),此处照原样列出以便与源码对齐,不是文档笔误。
12 常见问题
Q1:sdk_init 返回 -1023
face.ini 没找到。检查目录布局:可执行文件在 <root>/build/,face.ini 在
<root>/。或者给 sdk_init 传绝对路径。
Q2:sdk_init 返回 -4
models/ 目录不存在或模型数据为空。同上,它应该在 <root>/models/;
sdk_init 传了绝对路径时,models/ 必须在该路径下。若是资源定位失败,返回的是 -1010。
Q3:sdk_init 返回 -10(能力初始化失败),或打印
E RKNN: failed to allocate model memory
模型加载失败。最典型的是下面这种,日志形如:
1E RKNN: failed to allocate fd, ret: -1, errno: 12, errstr: Cannot allocate memory
2E RKNN: failed to allocate model memory!, size: 28460288, flags: #2
errno: 12 = ENOMEM(内存不足),size 约 27 MB 正是识别特征模型
feature_rgb。RKNN 模型内存不走普通堆,而是从 CMA / DMA 堆分配连续内存——
所以哪怕 free 看着 MemFree 还有余量,只要 CMA 预留太小,这一步照样失败,
SDK 直接跑不起来(模型没加载,后续所有推理都返回「未检测到人脸」-1012、
提特征失败 -1008 一类,识别 / 注册 / 比对全线不可用)。按顺序处理:
- 确认板型内存:建议用 256+256(256 MB DDR + 256 MB)及以上配置的开发板; 64 MB 型号装不下 26.6 MB 识别模型 + 运行时,先按 8.4 裁剪模型再重试;
-
查 CMA 余量(这是本问题的关键,不是看
MemFree而是看 CMA):Plain Text1cat /proc/meminfo | grep -i -E 'MemFree|Cma' # 重点看 CmaTotal / CmaFree 2cat /proc/rk_dma_heap/* 2>/dev/null # rockchip dma heap 占用(有则看) 3dmesg | grep -i -E 'rknpu|cma|dma' | tail -30 # NPU / CMA 相关报错 4cat /proc/cmdline # 看内核 cma= 预留大小CmaFree远小于 27 MB 时,需在内核 dtb /cmdline里把 CMA 预留调大 (固件 / 内核侧改动,不在本 SDK 范围);RV1106 是 IPC SoC,出厂固件的 ISP / 编码 / RTSP 栈通常已占掉大半 DDR,必要时先停掉这些服务再跑; recognition rgb id交付配置已默认false;日志里仍在加载它说明config.toml被改过或不是交付版本,改回false后应能省 26.6 MB;- 其它情况:
models/是否完整(对照第 8 节的模型清单,.enc别被截断)、librga.so是否为板子系统配套的版本(ldd确认加载的是lib/下那份)。
Q4:sdk_init 返回 -13(NOT_AUTHORIZED)或 -1015(LICENSE_FILE_NOT_EXIST)
先看是哪个码:-1015 表示 models/ 下 license.key、license.ini 两个授权文件都不存在,
照 5.1 / 5.2 放入授权文件即可;-13 表示授权文件在但鉴权没过——检查设备指纹是否匹配、
license 是否过期,另外确认板子系统时间与真实时间一致(date 看偏差,见 5.3),
时间错乱同样表现为 -13。
Q5:程序起不来,报 can't load library 'xxx.so'(找不到共享库)
报错形如 /userdata/face_offline_sdk/build/face_offline_sdk: can't load library 'libmot_sort.so',
ld-uClibc 在 main 之前就终止了进程——这是缺运行库,不是程序 bug。
先按第 6 节的清单逐项过一遍(ldd ./build/face_offline_sdk | grep 'not found' 一条命令
就能定位);实际交付中遇到最多的是下面两个库,分别处理:
情况一:libmot_sort.so(本包自带,报错 = 「文件没到位」或「路径没生效」)
libmot_sort.so(约 5.7 MB,多目标跟踪库,见 4.2)在 lib/ 里随包
下发,demo 可执行文件和 SDK 算法库都直接依赖它。它被「找不到」只有两种可能:
① scp / tar 传输或解压不完整(文件缺失、0 字节或损坏);
② 板子的 ld-uClibc 没有按 RPATH 的 $ORIGIN/../lib 去找
(个别固件 / 老版本 ld 的行为,与代码无关)。按顺序排查:
1# ① 板子上确认文件在不在、大小对不对(应约 5.7 MB,不能是 0 字节;顺带把整个 lib/ 看一遍)
2ls -l /userdata/face_offline_sdk/lib/
3# 正常应有:libmot_sort.so、libbaidu_face_api.so、
4# libopencv_world.so*、librknnmrt.so、libatomic.so.1(注意:没有 librga.so 是正常的)
5
6# ② 文件缺失 / 大小为 0 / 解压报错 → 在 PC 上先校验包完整性(md5sum 对照),
7# 再重新 scp 整个包或重新 tar 解压,不要只补拷单个文件
8
9# ③ 文件正常仍报错 → 用 LD_LIBRARY_PATH 显式指定库目录,绕开 RPATH 未生效的问题:
10cd /userdata/face_offline_sdk
11LD_LIBRARY_PATH=$PWD/lib ./build/face_offline_sdk
12# (等价于直接跑 SDK 根目录下的 run.sh——它就是这么干的:
13# export LD_LIBRARY_PATH=$PWD/lib && ./build/face_offline_sdk)
若 ③ 能正常走到 device id:… 和 sdk_init,说明就是 RPATH 未生效而
不是文件问题:确认可执行文件在 <root>/build/、库在 <root>/lib/ 的
相对布局没有被改动(见 7.2)。LD_LIBRARY_PATH 只影响本次运行,正式部署不要依赖它——
布局正确时不需要(run.sh 里的那份只是双保险);确需常设,就固定写进启动脚本的
export 里。
情况二:librga.so(不随包,必须从板子上取,不需要重新编译)
librga.so 是 SDK 算法库的 DT_NEEDED(Rockchip RGA 2D
图形加速库),SDK 不附带——所以上面 ① 的 ls 里没有它,不要以为少传了文件。
它由板子固件提供,按 6.1 从板上取一份拷进 lib/ 即可:
1# ① 找到板子固件自带的(两个位置哪个有取哪个)
2ls -l /oem/usr/lib/librga.so /usr/lib/librga.so
3
4# ② 拷到 SDK 的 lib/ 目录(按实际部署路径改)
5cp /oem/usr/lib/librga.so /userdata/face_offline_sdk/lib/
6
7# ③ 重新运行
8cd /userdata/face_offline_sdk
9./build/face_offline_sdk
注意:必须用当前这套固件自带的版本(与内核 RGA 驱动、MPP 配对),不要从网上或其它
开发板随便拷;也不要想「编译只打 warning、反正不影响编译」就放过——它只影响运行时,
问题一定在板子上才暴露。两个位置都没有时先
find / -name 'librga.so*' 2>/dev/null,再没有就联系固件提供方确认 rootfs 是否裁掉了 RGA。
两个库都修好、程序能进 sdk_init 之后,若还报 libstdc++.so.6 /
libgcc_s.so.1 / libc.so.0 等找不到,是 rootfs 缺基础运行库(正常 Rockchip
rootfs 都有),对照第 6 节清单处理。最后确认一遍是用 uClibc 工具链编的:
readelf -d build/face_offline_sdk | grep NEEDED 里应该是 libc.so.0,
出现 libc.so.6 就是用错工具链了(见 3.2),得重新编译。
Q6:检测不到人脸(-1012)
确认输入是 BGR CV_8UC3;人脸像素别太小;NIR 图要把 type 传 1。
Q7:identify_with_all 返回 -1005
内存里的底库是空的。先调 load_db_face(),或者改用 identify() 按组比对。
Q8:识别结果不满足预期 / 想调整灵敏度和阈值
识别阈值在 face.ini 的 IdentifyScore(1~100,越小越宽松,越界回落 80);
质量 / 活体门限在 pipline.ini(0~1 或角度,推荐值见 9.9)。
两者口径不同,不要混用。想「最像的一个」请自行对 result 按 score 排序取最大,
SDK 不保证 result 有序。
Q9:内存吃紧
RV1106 典型配置是 64 MB / 128 MB DDR。底库特征常驻内存,约 1 KB/人;models/ 里两个特征模型
各约 26.6 MB(其中 recognition rgb id 本 SDK 未使用,交付配置已默认
enable = false,省 26.6 MB;完整裁剪方案见 8.4)。大库 + 全模型场景先实测内存水位(详见 8.3)。
Q10:打开日志
把 face.ini 的 LogType 改成 1 重启。业务日志在
<root>/face_sdk.log,底层算法库日志在 /tmp/logs/SLog-*.log(单文件上限 10 MB,会滚动)。
交付物没有调试符号、板子上也没有 gdb 环境,遇到没法复现的疑难杂症,第一件事就是把日志打开。
Q11:要给多台设备开发 / 验证
license 绑定设备指纹,换设备要重新申请;同设备任意目录可复用。取指纹只需
get_device_id()(免 sdk_init),SDK demo 启动时也会打印。
评价此篇文章
