更新记录
1.0.0(2026-11-12)
- 首个版本:基于虹软 ArcFace 5.0 增值版 SDK 的离线人脸识别插件(注册 / 检索 / 比对均在本机完成)
- 激活与引擎:
requestPermissions / getDeviceFinger / activeOnline / activeOffline / isActivated /
customFaceDataPath / init(options)(path 指定人脸库目录)
- 图片能力:
getImageFace / imageFaceRegister / batchRegister / faceCompare(1:N)
- 1:1 比对:
faceCompare11(两张图片)/ faceCompareWithUser(图片 vs 库内人员)/ compareFeature(特征),
统一返回 similar / pass / threshold
- 人脸库:增删查清 + 本地持久化(特征与注册头像落盘,
init 自动回灌)
- nvue 相机组件
<zy-ArcFacePro>:CameraX 预览、人脸框、活体、1:N 识别、相机内注册、前后摄像头切换
- demo:
pages/arcfacepro/arcfacepro.vue(API)、pages/arcfacepro/arcfacepro-camera.nvue(组件)
平台兼容性
uni-app(4.72)
| Vue2 |
Vue2插件版本 |
Vue3 |
Vue3插件版本 |
Chrome |
Safari |
app-vue |
app-vue插件版本 |
app-nvue |
app-nvue插件版本 |
Android |
Android插件版本 |
iOS |
鸿蒙 |
| √ |
1.0.0 |
√ |
1.0.0 |
× |
× |
√ |
1.0.0 |
√ |
1.0.0 |
5.0 |
1.0.0 |
× |
× |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| × |
× |
× |
× |
× |
× |
× |
× |
× |
- |
× |
× |
uni-app x(4.72)
| Chrome |
Safari |
Android |
Android插件版本 |
iOS |
鸿蒙 |
微信小程序 |
| - |
- |
5.0 |
1.0.0 |
- |
- |
- |
其他
| 多语言 |
暗黑模式 |
宽屏模式 |
蒸汽模式 |
| × |
× |
√ |
× |
zy-ArcFacePro
安卓虹软 ArcFace 5.0(增值版)离线人脸识别 UTS 插件:本地注册、本地检索、本地比对,全程无需联网推理。
特性
- 激活:在线激活(activeKey + appId + sdkKey)、离线激活(.dat 授权文件)、设备指纹、激活状态查询
- 人脸库(本地持久化):图片注册、批量注册、删除、清空、按 id 查询、全部查询、数量统计
- 图片能力:单图人脸信息(年龄 / 性别 / 口罩 / 活体 / 3D 角度 / 人脸框)、单图注册、1:N 比对
- 1:1 比对:两张图片比对、图片与库内指定人员比对、两个特征直接比对(均返回相似度 + 是否达标)
- 相机组件:CameraX 预览 + 实时人脸框 + 活体检测 + 1:N 识别 + 相机内注册 + 自定义提示文案 + 前后摄像头切换
- 无需联网识别:推理全部在本机完成,特征与人脸头像只保存在应用私有目录
平台支持
| 平台 |
支持 |
| Android(uni-app vue2 / vue3) |
✅ |
| Android(uni-app x) |
✅ |
| iOS / Web / 小程序 |
❌ |
- minSdkVersion:21
- 架构:
armeabi-v7a、arm64-v8a
- 依赖:
config.json 已声明全部三方依赖与仓库,无需手动配置
安装
- 把
zy-ArcFacePro 整个目录放入项目的 uni_modules/;
- 由于插件带原生 so 与三方 SDK,标准基座无法使用,必须打自定义基座或云打包;
config.json 已声明三方依赖(CameraX 等)与所需权限(相机 / 网络 / 手机状态 / 媒体读取),
无需额外配置。
体积提示:人脸 SDK 约 173MB(每个 ABI 的 libarcsoft_face.so 约 105MB,模型已编译进 so),
插件目录本身不含二进制,构建时自动拉取;打出的 apk 会显著变大,
只需单架构时可在打包时只勾选 arm64-v8a。
权限说明:targetSdkVersion >= 33 时不再申请 READ_EXTERNAL_STORAGE(系统会直接抛
IllegalArgumentException),requestPermissions 会按 targetSdkVersion 自动改用 READ_MEDIA_*。
激活流程
- 调用
requestPermissions 拿到所需运行时权限;
- 调用
getDeviceFinger 拿到设备指纹;
- 到 虹软开放平台 创建应用,用设备指纹 + 包名申请
appId / sdkKey / activeKey;
- 调用
activeOnline({ appId, sdkKey, activeKey }) 激活(同一 activeKey 有激活次数限制,成功后可不再调用);
- 调用
init({ path }) 初始化引擎(组件内也会自动初始化;path 可选,用于指定人脸库目录)。
Module API
所有方法都是 (options?, callback) 风格,回调 res = { code, message, data },code === 0 表示成功。
| 方法 |
参数 |
返回 data |
说明 |
requestPermissions(callback) |
- |
grantedList / deniedList / doNotAskAgain |
申请相机、手机状态、存储权限 |
getDeviceFinger(callback) |
- |
info |
设备指纹(申请激活码用) |
activeOnline(options, callback) |
activeKey appId sdkKey |
- |
在线激活 |
activeOffline(options, callback) |
path |
- |
离线激活(.dat 文件路径) |
isActivated(callback) |
- |
isActivated |
是否已激活 |
customFaceDataPath(options, callback) |
path |
- |
自定义人脸库目录(须在 init 之前;也可直接用 init 的 path) |
init(options, callback) |
path? |
version count |
初始化引擎并加载人脸库(幂等);path 可在首次初始化时指定人脸库目录 |
getImageFace(options, callback) |
url |
list: ArcFaceImageFaceInfo[] |
图片人脸信息(年龄/性别/口罩/活体/3D 角度/人脸框) |
imageFaceRegister(options, callback) |
url id? name? registerMultiple? |
user: ArcFaceRegisterFaceInfo |
图片注册人脸 |
batchRegister(options, callback) |
list[{url,id?,name?}] registerMultiple? |
status success failed count progress user |
批量注册(回调多次触发) |
deleteFace(options, callback) |
id |
- |
删除单个人脸(不存在返回 -106) |
getFace(options, callback) |
id |
user(含 imageBase64) |
查询单个人脸 |
getAllFace(callback) |
- |
list: ArcFaceRegisterFaceInfo[] |
全部人脸(不含 base64,用 imagePath 自行读取) |
getFaceCount(callback) |
- |
count |
已注册人脸数量 |
clearFace(callback) |
- |
- |
清空人脸库(特征 + 头像文件) |
faceCompare(options, callback) |
url similar? |
user + similar |
图片 1:N 比对(默认阈值 0.85) |
faceCompare11(options, callback) |
url url2 similar? |
similar pass threshold |
两张图片 1:1 比对 |
faceCompareWithUser(options, callback) |
url id similar? |
similar pass threshold + user |
图片与库内指定人员 1:1 比对(id 未注册返回 -106) |
compareFeature(options, callback) |
feature1 feature2 similar? |
similar pass threshold |
两个特征 Base64 的 1:1 比对(无需图片) |
url 支持:本地绝对路径、file://、content://(相册选择)、http(s)://、base64(可带 data:image/jpeg;base64, 前缀)。
数据结构
type ArcFaceImageFaceInfo = {
age: number // -1 未知
gender: number // 1 男 / 0 女 / -1 未知(虹软原始值)
mask: number // 1 佩戴口罩 / 0 未佩戴 / -1 未知
liveness: number // 1 真人 / 0 非真人 / -1 未知
face3D: { yaw: number, roll: number, pitch: number }
face: { top: number, bottom: number, left: number, right: number, width: number, height: number }
}
type ArcFaceRegisterFaceInfo = {
userId: string
userName: string
faceId: number // 特征在引擎内的 id
imagePath: string // 注册头像本地路径
registerTime: number
featureData: string // 特征 Base64
imageBase64?: string
}
示例
import {
requestPermissions, getDeviceFinger, activeOnline, init,
getImageFace, imageFaceRegister, faceCompare, getAllFace, getFaceCount
} from '@/uni_modules/zy-ArcFacePro'
requestPermissions((res) => console.log('权限', res.code, res.data.grantedList))
getDeviceFinger((res) => {
if (res.code === 0) console.log('设备指纹', res.data.info)
})
activeOnline({ appId: 'xxx', sdkKey: 'yyy', activeKey: 'zzz' }, (res) => {
if (res.code !== 0) return uni.showToast({ title: res.message, icon: 'none' })
init({}, (r) => console.log('引擎版本', r.data.version, '人脸库', r.data.count))
// 需要自定义人脸库目录时:init({ path: '/sdcard/xxx/faceDB' }, (r) => {})
})
// 图片注册
imageFaceRegister({ url: '/sdcard/face/zhangsan.jpg', id: '1001', name: '张三' }, (res) => {
console.log(res.code, res.data.user)
})
// 1:N 比对
faceCompare({ url: '/sdcard/face/test.jpg', similar: 0.85 }, (res) => {
if (res.code === 0) console.log('命中', res.data.user.userName, res.data.similar)
})
1:1 比对
三种用法共用一套返回语义:比对流程本身成功就返回 code === 0,是否同一人由 data.pass 决定
(等价于 data.similar >= data.threshold)。只有取图失败、未检出人脸、提特征失败才返回错误码
(-102 未发现人脸、-10002 文件不存在、-10003 url 有误、-106 id 未注册)。
import { faceCompare11, faceCompareWithUser, compareFeature } from '@/uni_modules/zy-ArcFacePro'
// 两张图片各取面积最大的人脸比对
faceCompare11({ url: '/sdcard/a.jpg', url2: '/sdcard/b.jpg', similar: 0.85 }, (res) => {
if (res.code !== 0) return console.log('比对失败', res.message)
console.log('相似度', res.data.similar, res.data.pass ? '同一人' : '不是同一人')
})
// 现场照片 vs 工号对应的人(只比这一条记录,不走 1:N 检索,结果与人脸库规模无关)
faceCompareWithUser({ url: '/sdcard/live.jpg', id: '1001', similar: 0.85 }, (res) => {
if (res.code === 0 && res.data.pass) console.log('核验通过', res.data.user.userName)
})
// 特征对特征(特征来自注册记录 / onFaceResult / 服务端下发)
compareFeature({ feature1: featureA, feature2: featureB, similar: 0.85 }, (res) => {
console.log(res.data.similar, res.data.pass)
})
相机组件
只在 nvue 页面 中可用(page.nvue),标签即插件 id(SFC 模板中大小写敏感,须与插件 id 完全一致):
<zy-ArcFacePro
ref="camera"
class="camera"
:config="config"
:auto-start="true"
@onInitResult="onInitResult"
@onFaceResult="onFaceResult"
@onError="onError"></zy-ArcFacePro>
属性
| 属性 |
类型 |
默认值 |
说明 |
config |
Object |
{} |
相机 + 视频检测配置,见下表 |
auto-start |
Boolean |
true |
原生视图就绪且拿到相机权限后自动开始预览 |
config.camera:
| 字段 |
默认值 |
说明 |
rotation |
0 |
在帧旋转基础上额外旋转角度 |
facing |
0 |
1 前置 / 0 后置 |
radius |
0 |
预览圆角(px) |
size |
[1280,720] |
预览分辨率 |
screenLocked |
true |
仅记录,不强制旋转屏幕 |
isHorizontalMirror |
false |
是否水平镜像(前置摄像头始终按镜像显示) |
config.video:
| 字段 |
默认值 |
说明 |
orient |
360 |
检测角度(引擎按全角度 ALL_OUT 处理) |
isContraryX / isContraryY |
false |
人脸框与画面方向相反时翻转坐标 |
similar |
0.8 |
识别阈值 |
liveness |
true |
是否活体检测 |
showFaceInfo |
true |
是否解析年龄 / 性别 / 口罩 |
livenessParams |
{rgb:0.5, ir:0.5} |
活体阈值 |
showFaceResultNotice |
true |
是否显示人脸上方提示 |
faceSize |
0 |
人脸框宽度小于该值不识别(0 不限制) |
isKeepMaxFace |
true |
单人脸识别 |
maxDetectFaces |
1 |
最大识别人脸数 |
drawFaceBorder |
true |
是否绘制人脸框 |
compareImage |
true |
识别成功是否返回当前人脸 base64 |
failImage |
false |
识别失败是否返回图片 base64 |
isOutRecognize |
true |
识别后需人脸移出画面才能再次识别 |
continueTime |
3 |
isOutRecognize=false 时的识别间隔(秒) |
isRegisterImageBase64 |
true |
是否返回库内注册头像 base64 |
方法
所有方法最后一个参数为 callback(res),res = { code, message, data }。
| 方法 |
参数 |
说明 |
start(callback) |
- |
开始预览 |
stop(callback) |
- |
停止预览(触发 onCameraClosed) |
switchCamera(callback) |
- |
切换前后摄像头(触发 onCameraConfigurationChanged) |
closeFace(callback) |
- |
关闭人脸检测(预览保留) |
openFace(callback) |
- |
开启人脸检测 |
getCameraData(callback) |
- |
data.cameraId、data.supportedPreviewSizesJson([{width,height}]) |
register(options, callback) |
id? name? registerMultiple? |
注册当前画面的人脸 |
setFaceResult(options, callback) |
trackId message |
自定义某个人脸的识别成功文案 |
release() |
- |
释放相机资源(组件卸载会自动调用) |
事件
| 事件 |
detail |
onInitResult |
{ status, code, message } |
onCameraOpened |
{} |
onCameraClosed |
{} |
onCameraConfigurationChanged |
{ cameraID, displayOrientation } |
onFaceResult |
ArcFaceFaceResultDetail(见下) |
onError |
{ code, message, data } |
onFaceResult.detail:trackId status code message userId userName faceId imagePath
registerTime similar faceSize featureData imageBase64 compareImageBase64
registerImageBase64 failFaceImageBase64 failImageBase64。
组件示例
<template>
<view style="flex:1;">
<zy-ArcFacePro ref="camera" style="width:750rpx;height:800rpx;" :config="config"
@onInitResult="onInitResult" @onFaceResult="onFaceResult"></zy-ArcFacePro>
<button @click="registerFace">注册当前人脸</button>
</view>
</template>
<script>
export default {
data() {
return {
config: {
camera: { facing: 1, size: [1280, 720] },
video: { similar: 0.8, liveness: true, isKeepMaxFace: true, drawFaceBorder: true }
}
}
},
methods: {
onInitResult(e) { console.log('初始化', e.detail.status) },
onFaceResult(e) {
const d = e.detail
console.log(d.status ? '识别成功 ' + d.userName : '未匹配 code=' + d.code)
},
registerFace() {
this.$refs.camera.register({ id: '1001', name: '张三' }, (res) => {
console.log('注册', res.code, res.message)
})
}
}
}
</script>
状态码
| code |
说明 |
0 |
成功 |
-1 |
通用错误码 |
-100 |
注册时带口罩 |
-101 |
裁剪图片错误 |
-102 |
未发现人脸 |
-103 |
注册失败 |
-104 |
已注册(registerMultiple=false 且同 id 重复注册) |
-105 |
存在相同人脸(registerMultiple=false 且同一张脸换 id 注册) |
-106 |
人脸未注册 |
-107 |
未初始化 |
-10001 |
缺少必要的权限 |
-10002 |
文件不存在 |
-10003 |
url 地址有误 |
-10004 |
相机不可用 |
-10005 |
SDK 未激活 |
五位数(如 90126) |
虹软 SDK 原始错误码,可查虹软官方错误码表 |
注意事项
- 人脸库持久化:虹软 SDK 的特征库只在内存,插件把特征(Base64)与注册头像持久化到
Android/data/<包名>/files/Pictures/faceDB(faces.json + registerFaces/),每次 init 自动回灌;
需要换目录用 init({ path })(等价于 init 之前调 customFaceDataPath),
引擎初始化之后再传不同目录会返回 -1(特征已回灌内存,换目录须释放引擎或重启应用)。
- 激活与 init 的关系:未激活时
init 可能返回虹软错误码,识别也会失败;请先激活。
- 引擎单例:整个进程只有一个引擎实例(module API 与组件共用),调用已在内部串行化;
组件未初始化时会自动
init。
- 性能:默认以 1280×720 作为分析分辨率,图片解码最大边 1280;偏远/低端机可把
config.camera.size
降到 [640,480]。
- 识别频次:默认
isOutRecognize=true(同一张脸只回调一次,移出画面后才再次识别);
需要定频刷新时置为 false 并调整 continueTime。
- 性别取值是虹软原始值(1 男 / 0 女),与部分文档描述的 0/1 相反,业务侧按此判断。