更新记录
1.0.0(2026-11-09)
- 首个版本,仅支持 Android(minSdk 24)
- 组件
<zy-face-liveness>:相机预览、人脸框绘制、实时检测、相机录入、抓拍、前后摄像头切换、缩放模式
- 能力:多人检测、1:N 人脸库检索、1:1 比对、RGB 活体、动作活体(眨眼/张嘴/摇头/抬头)、年龄区间/性别/人种、口罩、睁闭眼、表情、人脸质量
- 人脸库持久化到应用目录(特征库 + 名单表分离,名单表由插件自建,支持分页与重命名)
平台兼容性
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 |
7.0 |
1.0.0 |
× |
× |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| × |
× |
× |
× |
× |
× |
× |
× |
× |
- |
× |
× |
uni-app x(4.72)
| Chrome |
Safari |
Android |
Android插件版本 |
iOS |
鸿蒙 |
微信小程序 |
| - |
- |
7.0 |
1.0.0 |
- |
- |
- |
其他
| 多语言 |
暗黑模式 |
宽屏模式 |
蒸汽模式 |
| × |
× |
√ |
× |
zy-face-liveness 离线人脸识别 / 活体检测
Android 平台 UTS 插件(uni-app / uni-app x),基于开源引擎方案,全程离线,不依赖任何云服务。
| 能力 |
说明 |
| 人脸检测 |
单图 / 相机实时,支持同时识别多人,返回位置、角度、置信度、跟踪 ID |
| 人脸识别 |
1:N 本地人脸库检索、1:1 两图比对(判断是否同一人) |
| 活体检测 |
RGB 活体(静默)+ 动作活体(眨眼 / 张嘴 / 摇头 / 抬头) |
| 人脸属性 |
年龄段、性别、人种、口罩、睁闭眼、表情、人脸质量 |
| 人脸库 |
注册(单/批量)、检索、删除、分页名单、重命名、总数,持久化到本机 |
| 相机组件 |
预览 + 人脸框 + 实时检测回调 + 相机录入 + 抓拍 + 前后摄切换 |
- 支持平台:App-Android(uni-app Vue2/Vue3、uni-app x)
- 最低系统:Android 7.0(API 24)
示例页面
| 示例 |
文件 |
内容 |
| 离线人脸识别(API) |
pages/face-liveness/face-liveness.vue |
初始化/模型包切换、单图多人检测与全部属性、两图比对、人脸库注册/检索/名单/删除/改名、运行日志 |
| 人脸相机(nvue 组件) |
pages/face-liveness/face-liveness-camera.nvue |
<zy-face-liveness> 预览、人脸框、实时属性、开关热切换、相机录入、抓拍 |
组件用法要点:uts 组件不要 import、也不要写 components 注册,直接用插件 id 作为标签名(<zy-face-liveness>),
由编译器自动解析到 uni_modules/zy-face-liveness/utssdk/app-android/index.vue。
若用 import Xxx from '@/uni_modules/.../index.vue',该文件会被 vue-loader 当普通 vue 文件解析,
报 Module parse failed: Unexpected token(<script lang="uts"> 的类型标注无法识别)。
组件依赖 NVLoad / NVLoaded 生命周期,只能用在 nvue 页面(参考 zy-uvccamera 的用法)。
一、快速开始
import { init, detect, compare, register, search, count, listUsers, remove, release } from '@/uni_modules/zy-face-liveness'
// 1) 初始化(首次会解压模型,耗时数秒,必须在页面加载完成之后调用)
init({
packName: 'Pikachu',
thresholds: 0.48, // 1:N 阈值,越大越严格
maxDetectFaceNum: 5, // 单帧最多人脸数
success: (res) => {
console.log('引擎就绪', res.version)
},
fail: (err) => {
console.error('初始化失败', err.errCode, err.errMsg)
}
})
1. 识别一张图片的人脸信息
detect({
image: '/storage/emulated/0/DCIM/a.jpg', // 本地路径 / file:// / content:// / http(s) / base64
flags: {
rgbLiveness: true, // 默认 true
quality: true,
mask: true,
eyeState: true,
actions: true,
attribute: true,
emotion: true
},
success: (res) => {
console.log('人脸数', res.faceNum)
const face = res.faces[0]
console.log('位置', face.rect, '角度', face.angle)
console.log('活体', face.liveness, '质量', face.quality, '口罩', face.mask)
console.log('年龄区间', face.ageBracket, '性别', face.gender, '人种', face.race)
console.log('左眼睁', face.leftEyeOpen, '表情', face.emotion)
console.log('动作', face.actions) // { blink, jawOpen, shake, headRaise, normal }
}
})
2. 判断两张图片是否同一人
compare({
image1: '/sdcard/a.jpg',
image2: '/sdcard/b.jpg',
thresholds: 0.48,
success: (res) => {
console.log('相似度', res.similarity, res.percentage + '%', '是否同一人', res.same)
},
fail: (err) => {
// 9280005 表示某张图没检测到人脸
}
})
3. 录入人脸库 + 检索
// 录入(可批量,多张一起提交)
register({
images: ['/sdcard/a.jpg', '/sdcard/b.jpg'],
names: ['张三', '李四'],
success: (res) => {
res.items.forEach(item => {
console.log(item.success, item.id, item.name, item.message)
})
}
})
// 从人脸库找人
search({
image: '/sdcard/c.jpg',
topK: 3,
success: (res) => {
if (res.matched) {
console.log('命中', res.items[0].id, res.items[0].name, res.items[0].similarity)
}
}
})
// 名单管理
count({ success: (res) => console.log('人脸库总数', res.count) })
listUsers({ page: 1, pageSize: 20, success: (res) => console.log(res.total, res.users) })
remove({ ids: [3, 4], success: (res) => console.log(res.removed) })
4. 页面卸载时释放
onUnload(() => {
release({})
})
二、相机组件
<template>
<view style="flex:1">
<zy-face-liveness
ref="faceRef"
style="width:100%;height:500px;"
:front="true"
:auto-start="true"
:detect-flags="flags"
@onEvent="onFaceEvent"></zy-face-liveness>
<button @tap="startRegister">录入当前人脸</button>
</view>
</template>
<script>
export default {
data() {
return {
// 初始开关(JSON 字符串),也可运行时用 setDetectFlags 修改
flags: '{"rgbLiveness":true,"recognize":true,"attribute":true}'
}
},
methods: {
onFaceEvent(e) {
const detail = e.detail
// type: loaded / started / stopped / detect / recognize / register / capture / error / flags / scaleType / switchCamera
if (detail.type === 'detect') {
const data = JSON.parse(detail.dataJson)
// data.faceNum / data.faces[]
}
if (detail.type === 'recognize') {
const data = JSON.parse(detail.dataJson)
console.log('命中', data.faces[0].name)
}
if (detail.type === 'register') {
const data = JSON.parse(detail.dataJson)
console.log('录入', data.success, data.id, data.name, data.path)
}
if (detail.type === 'error') {
console.warn('相机/引擎错误', detail.code, detail.msg)
}
},
startRegister() {
this.$refs.faceRef.registerFace({ name: '张三', needBase64: false })
}
}
}
</script>
组件 props
| 属性 |
类型 |
默认 |
说明 |
front |
Boolean |
true |
是否前置摄像头 |
auto-start |
Boolean |
true |
视图就绪并拿到相机权限后自动开始预览 |
detect-flags |
String |
"" |
初始检测开关(JSON 字符串) |
组件方法
| 方法 |
参数 |
说明 |
start |
{ front?, cameraId?, physicalCameraId? } |
开始预览(cameraId / physicalCameraId 当前版本预留未生效) |
stop |
- |
停止预览 |
switchCamera |
- |
前后摄切换 |
setDetectFlags |
{ rgbLiveness?, quality?, mask?, eyeState?, actions?, attribute?, emotion?, recognize? } |
运行时更新开关 |
registerFace |
{ name?, savePath?, needBase64? } |
请求录入:下一帧若恰好 1 张人脸则写入人脸库,结果经 onEvent(type=register) |
capture |
{ savePath?, needBase64? } |
抓拍当前帧,结果经 onEvent(type=capture) |
setScaleType |
'FILL_CENTER' \| 'FIT_CENTER' |
画面缩放模式 |
release |
- |
释放相机 |
组件事件 onEvent(e)
e.detail = { type, code, msg, dataJson },dataJson 需自行 JSON.parse。
| type |
dataJson |
loaded |
{} |
started / switchCamera |
{ front } |
stopped |
{} |
detect |
{ faceNum, width, height, faces:[FaceInfo] }(每帧回调) |
recognize |
{ faceNum, width, height, faces:[FaceInfo] }(仅命中人脸库的人脸) |
register |
{ success, id, name, code, message, path?, base64? } |
capture |
{ path, base64, width, height } |
error |
{}(看 detail.code / detail.msg) |
三、module API
| 函数 |
说明 |
init(options) |
初始化引擎(首次解压模型,耗时数秒) |
detect(options) |
单图人脸信息 |
compare(options) |
两图比对 |
register(options) |
批量注册人脸 |
search(options) |
1:N 检索 |
remove(options) |
按 ID 删除 |
count(options) |
人脸库特征总数 |
listUsers(options) |
分页名单(id + name) |
rename(options) |
修改姓名 |
setThreshold(options) |
修改 1:N 阈值(0~1) |
version(options) |
引擎版本(同步回调) |
release(options) |
释放引擎 |
所有函数统一 { success, fail, complete } 回调,失败为 UniError(errSubject = 'zy-face-liveness')。
init 参数
| 字段 |
默认 |
说明 |
packName |
'Pikachu' |
模型包:Pikachu(17MB,快)/ Megatron(61MB,准) |
thresholds |
0.48 |
1:N 检索阈值 |
maxDetectFaceNum |
5 |
单帧最多人脸数 |
searchMode |
0 |
0 EAGER(快)/ 1 EXHAUSTIVE(准) |
videoDetectMode |
2 |
视频:0 每帧检测 / 1 轻量跟踪 / 2 检测+跟踪 |
imageDetectMode |
0 |
图片:0 每帧检测 |
detectPixelLevel |
-1 |
检测像素级别,-1 自动 |
FaceInfo 字段
| 字段 |
说明 |
index |
本帧序号 |
trackId |
跟踪 ID(视频稳定,图片为 -1) |
confidence |
检测置信度 |
rect |
{ x, y, width, height } 像素坐标 |
angle |
{ pitch, roll, yaw } 欧拉角(度) |
faceId |
命中的人脸库 ID(>0 命中;-1 未命中/未开启识别) |
liveness |
RGB 活体 0~1,>0.5 判为活体 |
quality |
人脸质量 0~1,>0.5 判为清晰 |
mask |
口罩置信度 0~1,>0.5 判为戴口罩 |
leftEyeOpen / rightEyeOpen |
1 睁 / 0 闭 |
emotion |
0 无表情 1 开心 2 伤心 3 吃惊 4 恐惧 5 厌恶 6 愤怒 |
ageBracket |
0:0~2 1:3~9 2:10~19 3:20~29 4:30~39 5:40~49 6:50~59 7:60~67 8:>70 |
gender |
0 女 / 1 男 |
race |
0 黑人 1 亚洲人 2 拉丁裔 3 中东 4 白人 |
actions |
{ blink, jawOpen, shake, headRaise, normal },0 未发生 / 1 发生 |
四、错误码
| 码 |
含义 |
| 9280001 |
参数错误 |
| 9280002 |
引擎未初始化,请先调用 init |
| 9280003 |
引擎初始化失败(模型包缺失 / 设备不支持) |
| 9280004 |
图片读取失败(路径不存在 / 下载失败 / base64 非法) |
| 9280005 |
未检测到人脸 |
| 9280006 |
检测到多张人脸(注册要求单张) |
| 9280007 |
特征提取失败 |
| 9280008 |
人脸库操作失败 |
| 9280009 |
相机不可用 |
| 9280010 |
未获得相机权限 |
| 9280011 |
无法获取应用上下文(请在页面 onLoad 之后调用) |
| 9280012 |
引擎已初始化(重复 init 会按成功返回,此码保留) |
| 9280013 |
文件保存失败 |
| 9280014 |
操作失败(含异常信息) |
五、性能与最佳实践
- 只开需要的开关:
quality / mask / eyeState / actions / attribute / emotion 每开一项都会增加单帧耗时;
相机实时识别默认只开 rgbLiveness,需要属性时再开。recognize: true 会额外做特征提取 + 检索,最耗性能。
- 动作活体:不要只看单帧
actions,建议统计 2~3 秒内连续帧(如 blink 命中 ≥1 且 liveness > 0.5 的帧数占比)。
- RGB 活体:建议连续采集多帧,活体帧数 > 非活体帧数时判定为真人(单帧容易误判)。
- 注册质量:录入图不要戴口罩 / 墨镜,人脸尽量正对、光线均匀;
detect 先看 quality > 0.5 再注册。
- init 时机:首次 init 会解压模型(Pikachu 约 17MB),务必在页面可交互后再调用并给 loading。
六、存储与已知限制
- 特征库:
Android/data/<包名>/files/zy_face_liveness/face_feature.db(由引擎持久化)
- 名单表:
Android/data/<包名>/files/zy_face_liveness/face_names.db(插件自建,id → name)
- 卸载应用或清除数据会丢失人脸库,请按需导出备份
- 若通过其他途径(直接操作引擎)增删特征,名单表不会同步;以
count() 返回的特征数为准
cameraId / physicalCameraId(多摄指定)当前版本预留未生效,仅支持前后摄像头切换
- 仅支持 Android