基于 ArcFace SDK 的跨平台人脸特征值下发、同步以及存储方案

16 阅读12分钟

适用范围:基于虹软 ArcFace 人脸识别 SDK 进行服务器端特征库管理及设备端身份识别的集成开发。


1. 背景

虹软 ArcFace SDK针对不同的操作系统和设备,提供了众多版本,本文对不同版本SDK特征之间的兼容性关系做了梳理,并以Linux Pro(C++)版本为例,来说明特征值的管理流程。

2.不同版本特征值之间兼容性

本文这里讨论兼容性的SDK版本均为V5.0+版本,其余版本不适用,目前官网上各平台的SDK版本 均已经升级到V5.0版本,官网链接ai.arcsoft.com.cn/。

版本查看示例:

虹软 ArcFace 提供多套 SDK适配不同应用场景的需求。总的来说,产品线包含终端SDK与服务器SDK,分别适用于终端本地与服务器本地的SDK部署场景。终端SDK产品具体分为:APP人脸识别增值版、人脸识别RK356X、人脸识别RV1126B(P)、人脸识别增值版;服务器SDK产品具体分为:人脸识别Linux Pro和人脸识别Arm Pro。

以下为产品适用场景说明:

产品线产品名称适用场景
终端 SDKAPP 人脸识别增值版提供了安卓、鸿蒙、IOS 系统的 SDK 以及对应平台的 APP demo。另外,目前仅这个产品提供了交互式炫光活体功能。请注意该产品仅用于手机 app 场景,不可用于非手机 app 类应用。
终端 SDK人脸识别 RK356X限定 RK3566/3568 芯片并提供 NPU 驱动,其他芯片不支持使用。
终端 SDK人脸识别 RV1126B(P)限定 1126B(P) 芯片并提供 NPU 驱动,其他芯片不支持使用。
终端 SDK人脸识别增值版—Android(仅 Android 平台有 V5.0,本文仅对这一平台的特征值兼容性进行说明)全面兼容各类 Android 设备,涵盖嵌入式设备与智能手机,不受芯片型号与使用场景限制。
服务器 SDK人脸识别 Linux Pro广泛兼容 X86 架构,支持 Ubuntu、CentOS、EulerOS、麒麟等多种操作系统。
服务器 SDK人脸识别 Arm Pro广泛兼容 ARM 架构,支持 Ubuntu、CentOS、EulerOS、麒麟等多种操作系统。

各版本的人脸特征值(feature)长度存在差异,各版本特征长度及互兼容关系如下:

SDK 版本运行平台特征值长度互兼容性
APP 人脸识别增值版iOS / 安卓 / 鸿蒙2056 字节与人脸识别RK356X、人脸识别RV1126B(P)、人脸识别增值版、服务器SDK-中模型实现2056字节特征互通。APP人脸识别增值版部分老版本提取出来的特征是1032字节,但是同样支持下发到最新版本的SDK 中。
人脸识别RK356XLinux/安卓2056 字节与其余 2056 字节版本互通
人脸识别RV1126B(P)Linux2056 字节与其余 2056 字节版本互通
人脸识别增值版安卓2056 字节与其余 2056 字节版本互通
人脸识别Linux ProLinux2056/3080 字节中模型特征长度为2056字节;大模型特征长度3080字节
人脸识别Arm ProLinux2056/3080 字节中模型特征长度为2056字节;大模型特征长度3080字节

核心结论:所有V5.0版本的人脸SDK:APP 人脸识别增值版、人脸识别RK356X、人脸识别RV1126B(P)、人脸识别增值版、服务器SDK-中模型的特征是互相兼容的,能够跨端下发比对。服务器SDK-大模型(3080 字节)特征无法与其他版本互通,须单独管理。

特别说明:服务器版 SDK 内置 *中、大 *双模型:中模型推荐 1 万人脸库以内,大模型推荐 10 万人脸库以内,灵活适配不同业务场景。双模型配置示例(以 C++ 版本为例):特征模型由 ASFInitEngine 的 recModel 参数指定。需注意,若初始化时模型选择错误,将导致后续特征比对阶段出现异常

enum ASF_RecModel {
    ASF_REC_LARGE  = 0x1,   // 人脸识别大模型 → 特征 3080 字节
    ASF_REC_MIDDLE = 0x2    // 人脸识别中模型 → 特征 2056 字节
};

3. 特征值存储方案

3.1 特征值的数据形态

特征值由 ASFFaceFeatureExtractEx(..., ASF_REGISTER, ...) 从注册照/识别照中提取,本质为二进制字节序列,其字节中可能包含 0x00。因此特征值最好不要直接存储,这可能会导致截断导致异常。

另外,ASFFaceFeatureExtractEx(..., ASF_REGISTER, ...)接中分为注册照: *ASF_REGISTER *和 *识别照:ASF_RECOGNITION *两个模型,由接口ASFFaceFeatureExtractEx中参 数registerOrNot决定提取哪种特征。两种模型提取的特征值有差异,REGISTER用于注册场景,注册到人脸库里面的照片,用register接口提取特征值,RECOGNITION用于识别场景,识别照是在设备端实时的人脸数据,用recognition接口提取特征值,所有的场景都是设备端采集过来的识别照和人脸库里面的注册照进行比对。 *两个模型需严格区分,未严格区分会出现人脸比对相似度异常情况 *。

特别注意:ASFFaceFeatureExtractEx接口提取的特征是存储在SDK临时内存区域,需要及时读走,否则下一次调用ASFFaceFeatureExtractEx会覆盖之前的特征值。

处理规范:特征提取后建议先进行 Base64 编码再存储或传输;使用时再 Base64 解码还原为原始字节后传入 SDK。目前base64编码有多种方式,跨端下发解码的时候需要注意:使用同一种编码规范,否则会导致特征无法识别。

常见的 Base64 标准和变体

类型字符表差异典型用途
标准 Base64(RFC 4648)使用 +、/通用数据编码、文件和协议传输
URL-safe Base64用 - 替换 +,用 _ 替换 /URL、文件名、Web Token
MIME Base64通常每 76 个字符换一行邮件附件
无填充 Base64省略末尾的 =部分 URL、Token 和自定义协议

3.2 特征存储

不同的业务,在特征存储的时候会有一些差异,一般会采用数据库来存储。某些低性能设备没有建立数据库,会直接采用存文件的形式。但是无论是哪种存储形式,整合之后的结构化数据,一般会包含如下一些字段:

├── search_id     服务端自维护的唯一标识(对应 SDK searchId)
├── feature       TEXT   ← 特征值(二进制转 Base64 后的字符串)
├── feature_len   INT    ← 特征值原始字节长度(2056 / 3080)
├── feature_ver   版本号  ← 用于 SDK 版本
├── image_url     原始注册照的url,用来重建特征使用 ← 用于 SDK 版本
├── synced        表征当前的人脸是否被同步到SDK的handle对应内存中(设备端才有)

设计要点:

  • feature 字段存 Base64 字符串:原始 2056 字节经 Base64 编码后约 2741 字符,字段容量按此预留。
  • feature_len 字段存特征值原始字节长度。
  • feature_ver 字段须预留:SDK 升级算法后特征语义可能变化,记录版本号便于统一重注册与迁移,避免比对时触发 MERR_FSDK_FR_MISMATCHED_FEATURE_LEVEL(特征版本不一致)。
  • image_url:便于算法升级或特征丢失时以原图重新提取,避免重新采集。这里也可以是实际图片的路径

上述存储内容只是基础的内容,根据业务流程的不同,还会有一些额外业务字段。例如,在设备端,服务器下发人脸信息之后,设备端一般会需要额外的字段synced:来表征当前的人脸是否被同步到SDK的handle对应内存中。


4. 特征下发及同步流程

本章描述特征由服务器下发至设备端,经存储并更新至引擎内存的完整流程,以及流程各环节的异常处理策略。一个完整的人脸下发协议,一般至少包含:人脸下发、人脸更新、人脸删除、人脸列表查询等。

人脸下发:用于服务器下发人脸数据到设备端

人脸更新:用于服务器更新设备端人脸信息

人脸删除:用于服务器上操作删除设备端人脸信息

人脸列表查询:用于服务器查询当前设备上的人员列表,用于一些增量更新。

除此之外,可能还包括:设备端主动请求服务器更新当前人脸信息等协议,需要根据具体的业务来决定。本文这里以特征下发为例来说明。

4.1 特征下发

服务器与设备间通过业务协议(HTTP、MQTT 或私有 TCP 等)通信,下发内容采用结构化的消息,常见的协议消息结构示例如下(仅作为示例版本,不同业务有差异):

// 服务器 → 设备:全量下发某用户特征
{
  "cmd": "FACE_SYNC",
  "type": "UPSERT",              // UPSERT / DELETE / CLEAR
  "batchId": "20261019123045",
  "users": [
    {
      "user_id": "U10001",
      "name": "张三",
      "features": [
        {
          "feature_id": 90001,
          "search_id": 123456,       // 对应 SDK ASF_FaceFeatureInfo.searchId
          "feature_len": 2056,       // 特征原始字节长度,接收端须校验
          "feature_b64": "eJx...",   // 特征值 Base64(因含 0x00 字节须编码)
          "feature_ver": 3,          // 特征版本,用于增量同步与幂等
        }
      ]
    }
  ],
  "total": 100,                    // 本批应含特征条数(批次校验)
  "batch_sha256": "cd34..."        // 整批哈希(批次校验)
}

其中feature_len和feature_ver字段,设备端可以用来过滤服务器下发一些异常的特征,例如:其他版本的特征。

此外,一些人脸业务中,可能会有这样的需求:一个人支持添加多张人脸,比如不同时期的人脸,提高识别通过率,那么此时数组 “features” 下面就有多个人脸特征。对应到人脸SDK中,一个用户就对应多个 "search_id" ,均通过注册接口ASFRegisterFaceFeature注册下去,在应用层通过 “user_id” 来做顶层的人员维护。

4.2 内存中特征同步

平台下发的人脸信息一般遵循"先落盘、后注册"的操作,落盘之后,通过字段synced 来异步处理注册到handle对应的内存中。

synced 为设备端本地维护的同步状态标记(非 SDK 接口字段),用于记录特征是否已成功写入引擎内存,取值约定如下:

取值含义
0(未生效)已写入本地持久化库,但尚未成功注册进 SDK 引擎内存
1(已生效)已成功注册进引擎内存,可参与比对

该标记用于支撑本地库与引擎内存的一致性管理,其作用体现在:

  • 先落盘、后注册:落盘后置为 0,注册成功后置为 1;
  • 启动重建:设备重启重建内存引擎库时,仅对 synced = 0 的记录补注册,已生效的记录跳过,避免重复注册;
  • 异常恢复:注册失败的特征保持 synced = 0,配合重试逻辑直至成功或告警;
  • 全量校准:与服务器做人脸列表全量差集校准时,借助 synced 判断"本地有但未生效"的记录是否需要补处理。

特征同步到内存中,通过如下几个接口来实现:

设备端 SDK 接口说明
ASFRegisterFaceFeature新增人脸特征
ASFUpdateFaceFeature以新特征替换指定 searchId 的特征
ASFRemoveFaceFeature按 searchId 删除特征

以注册接口为例,读取synced = 0的人脸信息,通过 ASFRegisterFaceFeature 批量写入引擎内存库,注意:当人脸较多的时候,建议批量注册,这样效率高。另外,设备端收到的特征为 Base64 字符串,注册进 SDK 前须先 Base64 解码还原为原始 字节长度,再填充至 ASF_FaceFeature.feature;否则传入 SDK 的为 Base64 文本,无法正确比对。

ASF_FaceFeatureInfo faceList[N] = {0};
for (i...) {
    faceList[i].searchId = recv_feature[i].searchId;
    faceList[i].feature   = &localFeature[i];   // 深拷贝后的本地副本
    faceList[i].tag       = recv_feature[i].tag;
}
res = ASFRegisterFaceFeature(handle, faceList, N);

4.3 异常处理

人脸下发过程,可能会遇到一些异常情况,例如:设备突然断电断网、某次人脸下发失败、本地数据库损坏、服务器下发一些异常的特征值等情况。下面列出了一些常见异常的处理。

(1)设备断电断网。 当设备断电断网时,设备电源和网络恢复之后,应该主动向服务器同步当前的人脸列表,恢复因为断电断网未继续下载的人员信息

(2)存储异常。 本地持久化失败(如磁盘空间不足、权限受限)时,该特征不进入"待生效"状态,设备端将该 feature_id 计入失败回执,待存储恢复正常后重新执行下发流程。

(3)内存注册异常。 调用 ASFRegisterFaceFeature / ASFUpdateFaceFeature 返回非 MOK 时,特征未成功进入内存库,设备端应将对应 feature_id 标记为"未生效"并回执失败,触发重试或告警,避免内存库与本地库状态不一致。

(4)应用层回执。 设备端完成存储与内存注册后,向服务器回执处理结果,服务器据此推进同步状态:

// 设备 → 服务器
{
  "cmd": "FACE_SYNC_ACK",
  "batchId": "20261019123045",
  "result": "SUCCESS",              // SUCCESS / PARTIAL / FAILED
  "succeeded_ids": [90001, 90002],
  "failed_ids": [{"id":90003, "reason":"checksum_mismatch"}]
}

服务器仅将回执为 SUCCESS 的批次标记为已完成;PARTIAL / FAILED 对应的 feature_id 进入重发队列。

(5)启动兜底校准。 设备启动时向服务器请求全量特征清单(例如:GET /api/faces/full?since=xx)进行校准,以 searchId 集合差集,处理"服务器有、本地无"及"本地残留、服务器已删"两类不一致,并从本地持久化库重建内存引擎库。