更新记录
1.0.5(2026-07-08)
问题修复
- 修复 Android 自动录像编译失败:
ImaLivenessAnalysisVideoRecorder.kt 在 synchronized(muxerLock) {} lambda 内直接 break,Kotlin 编译报 'break' or 'continue' jumps across a function or a class boundary;改为锁内返回 shouldBreak 状态,锁外再跳出编码输出循环
- 同步处理视频轨 / 音频轨
INFO_OUTPUT_FORMAT_CHANGED 分支,避免云打包 / 自定义基座在自动录像相关代码处中断
1.0.4(2026-06-17)
问题修复
- 修复云打包 / 自定义基座编译失败:
index.vue 中 readRawValue 对 any 使用 plain[key] 动态属性访问,UTS 编译为 Kotlin 时误解析为 String.get,报 index.kt:118 Unresolved reference;改为 JSON.stringify / JSON.parse 转为 UTSJSONObject 后再 .get(key),plain Object 仍可正常解析
1.0.3(2026-06-16)
新增功能
- 新增属性
actionThresholds(Object):可配置 Native 动作检测阈值(眨眼、张嘴、微笑、摇头/点头、回正等 14 项可选字段);未传或传 {} 时使用 Native 默认值
- 新增类型
ActionThresholds(utssdk/interface.uts)
- Native 新增
ImaLivenessActionThresholdApplier、ImaLivenessManager.applyActionThresholds();组件 applyFaceConfig / watch 自动下发
- 宿主示例:
pages/camera/livenessThresholdTune.nvue、pagesx/camera/livenessThresholdTune.uvue(步进器调参 + 预设 + remount 重测)
readme.md 补充 actionThresholds 字段说明、requiredFrames 与各动作关系及灵敏度调参方向(↑/↓)
问题修复
- 修复 nvue 下
actionThresholds 普通 Object 桥接至 Kotlin 失败、被静默当作 {} 重置为默认值的问题:index.vue 规范化为 UTSJSONObject 后再调用 Native;解析失败时打 log 且不再误 reset
- 修复快速眨眼难识别:活体眨眼边沿 + 帧间骤降 + 放宽闭眼阈值;
blink 固定 requiredFrames=1;分析帧改为保留最新帧队列,ML Kit 使用 PERFORMANCE_MODE_FAST
- 修复左转头回正后误触发右转:定向摇头增量 Yaw、baseline 仅在回正时锁定,
ensureHeadYawReady 加强
- 修复
head_shake 只摇一侧即通过:双向摇头三阶段(回正 → 左+右 → 再回正),左/右各需 requiredFrames 帧
- 修复 nvue 示例「重新开始」调用
$refs.startDetection() 无效:改为 v-if + :key remount(与拍照示例一致)
功能优化
- 定向摇头
head_shake_left / head_shake_right:须「转 → 回正 → 通过」三阶段状态机(DirectionalHeadShakePhase)
pages/camera/livenessOnly.nvue、pagesx/camera/livenessOnly.uvue:动作序列与 remount 重新开始对齐;首页增加阈值调参入口
- 调参示例页增加快捷动作(仅眨眼/仅张嘴/仅摇头)、拉大预设差异、展示下发摘要;
debug=true 时 logcat 输出已应用阈值
- Native 阈值变更需重编自定义基座后生效
查看更多
平台兼容性
uni-app(4.87)
| 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 |
× |
5.0以下(不包括5.0) |
1.0.0 |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| × |
× |
× |
× |
× |
× |
× |
× |
× |
× |
× |
× |
uni-app x(4.87)
| Chrome |
Safari |
Android |
Android插件版本 |
iOS |
鸿蒙 |
鸿蒙插件版本 |
微信小程序 |
| × |
× |
5.0 |
1.0.0 |
× |
5.0以下(不包括5.0) |
1.0.0 |
× |
其他
离线人脸活体检测,全程离线,无需联网,可自定义页面(ima-liveness)
离线人脸活体检测(ima-liveness) 原生组件,支持 动作活体、炫彩活体、混合活体,可选 检测过程自动录像、预览圆角、屏幕最大亮度 等能力。全程离线,无需联网。
⚠️注意️
- 本插件为 UTS 原生组件,支持 uni-app(nvue) 与 uni-app x(uvue);Android 端需制作自定义基座 后真机调试
- ⚠️注意️:只能在
.nvue、.uvue 页面中使用,不支持 .vue
- 组件标签为
<ima-liveness>
- 本插件实现的是 活体检测(Liveness),不是人脸识别、人脸比对、人脸搜索
ios端 暂未实现(保留模板)
harmony端 兼容式组件不支持
- 宿主示例页:
pages/camera/(uni-app + nvue)、pagesx/camera/(uni-app x + uvue);入口分别为 App.vue/main.js 与 App.uvue/main.uts
支持功能
open:打开摄像头预览
close:关闭摄像头预览
reset:重置活体检测状态
isOpened:查询摄像头预览是否已打开
startDetection:手动开始活体检测(可传入动作列表)
restartDetection:停止后重新开始检测
- 动作活体:眨眼、张嘴、微笑、点头、摇头等(见下方动作列表)
- 炫彩活体:屏幕白/红/绿/蓝阶段采样,防照片/屏幕攻击
- 混合活体:
livenessMode="hybrid",先炫彩后动作(原生协调器顺序)
- 自动录像:
autoRecordVideo,采用分析帧录制(Preview + Analysis),避免绑定 VideoCapture 导致预览变暗
- 正脸拍照:
returnPhoto + pictureTakenStage(before / during / after),分析帧 JPEG,拍照前校验正脸
- 预览圆角:
previewCornerRadius / previewCornerRadiusRate,支持圆形预览
- 炫彩展示:
previewColorFlash="true" 在预览框内 overlay;false 时由页面整页变色
- 自动最大亮度:
autoMaxScreenBrightness,检测期间窗口亮度调至最大并保持常亮
活体模式(livenessMode)
| 值 |
说明 |
action |
仅动作活体(默认) |
color |
仅炫彩活体 |
hybrid |
炫彩通过后继续动作活体 |
支持的动作(actions)
| 值 |
说明 |
blink |
眨眼 |
mouth_open |
张嘴 |
smile |
微笑 |
head_nod |
点头 |
head_shake |
左右摇头(须先回正 → 左+右均摇 → 再回正后通过) |
head_shake_left |
向左摇头(须先回正 → 左转 → 再回正后通过) |
head_shake_right |
向右摇头(须先回正 → 右转 → 再回正后通过) |
自定义调整
- 自定义调整页面地址:
uni_modules/ima-liveness/utssdk/app-android/index.vue
- 炫彩阈值、AWB、采样逻辑等可在
utssdk/app-android/ 下 Kotlin 源文件中调整(修改后需重编自定义基座)
- 类型定义:
uni_modules/ima-liveness/utssdk/interface.uts
需要权限
- 摄像头(必须)
- 麦克风(
autoRecordVideo + recordAudio 时)
- 存储 / 相册(
saveVideoToGallery 时)
"android.permission.CAMERA"
"android.permission.RECORD_AUDIO"
"android.permission.READ_MEDIA_VIDEO"
"android.permission.READ_EXTERNAL_STORAGE"
"android.permission.WRITE_EXTERNAL_STORAGE"
- 插件已在
AndroidManifest.xml 中声明上述权限;宿主 manifest.json 的 distribute.android.permissions 需同步配置
// 活体检测必须
<uses-permission android:name="android.permission.CAMERA" />
// 自动录像且含音频时需要
<uses-permission android:name="android.permission.RECORD_AUDIO" />
// 保存录像到相册时需要
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
使用示例【此示例仅实现基础动作活体,更多示例请导入宿主工程】
- 新建一个
liveness.nvue 文件
- ⚠️注意️:只能在
.nvue、.uvue 的文件后缀下才生效,不支持 .vue
<template>
<view class="page">
<text class="title">请将正脸置于框内</text>
<text class="tip">{{ actionTip }}</text>
<view class="camera-wrap">
<ima-liveness
ref="livenessRef"
class="camera-view"
facing="front"
:enabled="true"
:actions="livenessActions"
:requiredFrames="5"
:autoStart="true"
:previewCornerRadiusRate="0.5"
@onCameraOpened="onCameraOpened"
@onActionChange="onActionChange"
@onLivenessPass="onLivenessPass"
@onFaceError="onFaceError"
/>
</view>
<text class="status">{{ statusText }}</text>
</view>
</template>
<script>
export default {
data() {
return {
livenessActions: ['blink', 'mouth_open'],
actionTip: '请保持正脸',
statusText: '准备开始检测',
}
},
methods: {
onCameraOpened() {
this.statusText = '相机已打开,请完成活体动作'
},
onActionChange(e) {
const detail = e.detail || {}
this.actionTip = detail.tip || '请完成动作'
this.statusText = '动作 ' + ((detail.index || 0) + 1) + '/' + (detail.total || 0)
},
onLivenessPass(e) {
console.log('活体通过', e.detail)
this.statusText = '活体检测通过'
uni.showToast({ title: '验证通过', icon: 'success' })
// 可将结果回传上一页
const pages = getCurrentPages()
if (pages.length >= 2) {
const prev = pages[pages.length - 2]
if (prev.$vm && prev.$vm.onLivenessResult) {
prev.$vm.onLivenessResult({ passed: true, ...(e.detail || {}) })
}
}
uni.navigateBack()
},
onFaceError(e) {
console.warn('检测错误', e.detail)
},
},
}
</script>
<style>
.page {
flex: 1;
align-items: center;
padding: 40rpx;
}
.title {
font-size: 32rpx;
font-weight: 600;
}
.tip {
margin-top: 24rpx;
color: #1677ff;
}
.camera-wrap {
margin-top: 40rpx;
width: 620rpx;
height: 620rpx;
}
.camera-view {
width: 500rpx;
height: 500rpx;
}
.status {
margin-top: 40rpx;
font-size: 28rpx;
}
</style>
炫彩活体示例(片段)
<ima-liveness
facing="front"
livenessMode="color"
:colorPhaseDuration="600"
:previewColorFlash="false"
:actions="['blink']"
:autoStart="true"
@onColorPhaseChange="onColorPhaseChange"
@onColorLivenessPass="onColorLivenessPass"
@onColorLivenessFail="onColorLivenessFail"
@onLivenessPass="onLivenessPass"
/>
自动录像示例(片段)
<ima-liveness
facing="front"
:actions="['blink', 'mouth_open']"
:autoRecordVideo="true"
:recordAudio="true"
:saveVideoToGallery="true"
@onVideoRecordStart="onVideoRecordStart"
@onVideoRecordEnd="onVideoRecordEnd"
@onVideoRecordError="onVideoRecordError"
@onLivenessPass="onLivenessPass"
/>
正脸拍照示例(片段)
<ima-liveness
facing="front"
:actions="['blink', 'mouth_open']"
:returnPhoto="true"
pictureTakenStage="after"
:photoQuality="90"
@onPictureTaken="onPictureTaken"
@onPictureTakenError="onPictureTakenError"
@onPictureTakenReady="onPictureTakenReady"
@onLivenessPass="onLivenessPass"
/>
onPictureTaken(e) {
this.photoPath = e.detail?.path || ''
this.photoStage = e.detail?.stage || ''
}
pictureTakenStage 取值:
| 值 |
说明 |
before |
虚拟序列 [Photo] → actions,拍完自动进入动作 |
during |
随机 N∈[1, actions.length−1],完成前 N 个动作后插入拍照步 |
after |
虚拟序列 actions → Photo,含拍照完成后才 onLivenessPass |
Api
| 属性 |
类型 |
默认值 |
说明 |
平台 |
| facing |
String |
"front" |
摄像头:front(前置)、back(后置) |
android |
| previewCornerRadius |
Number |
0 |
预览圆角半径(像素) |
android |
| previewCornerRadiusRate |
Number |
0.5 |
预览圆角比例(0~1),0.5 为圆形 |
android |
| enabled |
Boolean |
true |
是否启用检测 |
android |
| actions |
Array |
[] |
动作列表,空则使用内置默认序列 |
android |
| requiredFrames |
Number |
5 |
单动作判定所需连续帧数(见下方说明) |
android |
| actionThresholds |
Object |
{} |
动作检测阈值,字段均为可选;传 {} 时使用 Native 默认值 |
android |
| autoStart |
Boolean |
true |
相机打开后是否自动开始检测 |
android |
| debug |
Boolean |
false |
调试模式 |
android |
| autoRecordVideo |
Boolean |
false |
检测过程是否自动录像 |
android |
| recordAudio |
Boolean |
true |
录像是否包含麦克风(需 autoRecordVideo) |
android |
| saveVideoToGallery |
Boolean |
false |
录像是否保存到系统相册 |
android |
| livenessMode |
String |
"action" |
活体模式:action / color / hybrid |
android |
| colorPhaseDuration |
Number |
450 |
炫彩单阶段时长(ms) |
android |
| colorRandomOrder |
Boolean |
false |
炫彩颜色顺序是否随机(白基准固定最前) |
android |
| previewColorFlash |
Boolean |
false |
是否在预览框内显示炫彩 overlay |
android |
| autoMaxScreenBrightness |
Boolean |
false |
检测期间是否自动将屏幕亮度调至最大 |
android |
| returnPhoto |
Boolean |
false |
是否返回正脸照片 |
android |
| pictureTakenStage |
String |
"after" |
拍照阶段:before / during / after |
android |
| savePhotoToGallery |
Boolean |
false |
照片是否保存到系统相册 |
android |
| photoQuality |
Number |
90 |
JPEG 质量(60~100) |
android |
requiredFrames 与各动作关系
| 动作 |
实际判定帧数 |
blink |
固定 1(与 prop 无关) |
smile |
max(requiredFrames + 3, 8) |
head_shake_left / head_shake_right |
转头阶段:requiredFrames;回正阶段:headYawCenterFramesRequired(默认 3,可通过 actionThresholds 配置) |
head_shake |
左、右各需 requiredFrames;回正同上 |
其他(mouth_open、head_nod 等) |
requiredFrames |
actionThresholds 字段
调参提示:「灵敏度」列中 [最小, 最大] 为推荐调参范围(非 Native 强制限制);↑ 数值越大越灵敏;↓ 数值越小越灵敏。未列出的字段保持 Native 默认即可。
宿主传入普通 Object 即可(nvue / vue);组件内部会通过 buildActionThresholdsJson 规范化为 UTSJSONObject 再下发 Native。设置 debug=true 时,可在 logcat 过滤 ImaLivenessManager 查看 Applied actionThresholds 日志确认已生效。
| 字段 |
类型 |
默认 |
说明 |
灵敏度 |
eyeCloseThreshold |
Number |
0.42 |
活体眨眼闭眼判定(睁眼概率低于此值视为闭眼) |
[0, 1] · ↑ 越大越易判闭眼,眨眼越灵敏 |
eyeOpenThreshold |
Number |
0.52 |
睁眼边沿检测(概率高于此值视为睁眼,用于 open→closed 边沿) |
[0, 1] · ↓ 越小越易判睁眼,利于捕捉快速眨眼 |
eyeCloseStrictThreshold |
Number |
0.30 |
拍照/正脸严格闭眼(低于此值视为闭眼,影响正脸抓拍) |
[0, 1] · ↑ 越大越易判闭眼,正脸拍照越严格 |
marThreshold |
Number |
0.35 |
张嘴 MAR(嘴部纵横比高于此值视为张嘴) |
[0.1, 0.8] · ↓ 越小越易判张嘴 |
smileThreshold |
Number |
0.62 |
微笑概率(ML Kit 微笑概率需达到此值) |
[0, 1] · ↓ 越小越易判微笑 |
smileCurveThreshold |
Number |
0.10 |
嘴角上扬( landmark 嘴角曲线阈值) |
[0.05, 0.3] · ↓ 越小对嘴角上扬要求越低 |
smileMaxMarRatio |
Number |
0.92 |
微笑时 MAR 上限比(相对 marThreshold,超过则不算微笑) |
[0.5, 1.2] · ↑ 越大允许嘴张得稍大仍算微笑 |
headYawRatioThreshold |
Number |
0.06 |
鼻尖横向偏移比(相对眼距,辅助摇头判定) |
[0.02, 0.15] · ↓ 越小越容易判摇头 |
headPitchRatioThreshold |
Number |
0.06 |
鼻尖纵向偏移比(相对眼距,辅助点头判定) |
[0.02, 0.15] · ↓ 越小越容易判点头 |
headYawDegreeThreshold |
Number |
8 |
摇头 Yaw 角阈值(度,相对 baseline 转角) |
[3, 20] · ↓ 越小转头幅度要求越低 |
headPitchDegreeThreshold |
Number |
8 |
点头 Pitch 角阈值(度) |
[3, 20] · ↓ 越小点头幅度要求越低 |
headCenterRatioThreshold |
Number |
0.03 |
正脸比例阈值(鼻尖偏移相对 baseline 小于此值视为回正) |
[0.01, 0.1] · ↑ 越大越容易判回正/正脸 |
headYawCenterDegreeThreshold |
Number |
5 |
回正 Yaw 上限(度,相对 baseline 小于此值视为回正) |
[2, 15] · ↑ 越大越容易判回正 |
headYawCenterFramesRequired |
Number |
3 |
连续回正帧数(定向/双向摇头回正阶段) |
[1, 10] · ↓ 越小回正所需连续帧越少 |
修改 Native 阈值或新增 actionThresholds 支持后,需重编自定义基座后才能在真机生效。
宿主示例:pages/camera/livenessThresholdTune.nvue、pagesx/camera/livenessThresholdTune.uvue(步进器调参 + 预设 + remount 重测)。
方法
| 方法名称 |
说明 |
方法参数 |
平台 |
| open |
打开摄像头预览 |
无 |
android |
| close |
关闭摄像头预览 |
无 |
android |
| reset |
重置检测状态 |
无 |
android |
| isOpened |
查询预览是否已打开 |
无,返回 Boolean |
android |
| startDetection |
开始活体检测 |
actionList:动作数组,可选 |
android |
| restartDetection |
重新开始检测 |
actionList:动作数组,可选 |
android |
事件
| 事件名称 |
说明 |
回调参数(detail) |
平台 |
| onCameraOpened |
相机打开 |
{} |
android |
| onCameraClosed |
相机关闭 |
{} |
android |
| onCameraError |
相机错误 |
{ action, message } |
android |
| onActionChange |
切换动作提示 |
{ action, index, total, tip, hasFace?, faceCount?, rePrompt? } |
android |
| onActionCompleted |
单项动作完成 |
{ action, index, total } |
android |
| onLivenessPass |
活体检测通过 |
{ actions, colorPassed?, mode? } |
android |
| onFaceStatus |
人脸状态 |
{ hasFace, faceCount } |
android |
| onFaceError |
检测错误 |
{ code, message } |
android |
| onVideoRecordStart |
自动录像开始 |
{ path } |
android |
| onVideoRecordEnd |
自动录像结束 |
{ path, uri, duration, size, suffix } |
android |
| onVideoRecordError |
自动录像失败 |
{ action, message } |
android |
| onColorPhaseChange |
炫彩阶段变化 |
{ phase, index, total, tip, color } |
android |
| onColorLivenessPass |
炫彩阶段通过 |
{ passed } |
android |
| onColorLivenessFail |
炫彩阶段失败 |
{ reason, retryable } |
android |
| onPictureTaken |
正脸照片抓拍成功(returnPhoto=true) |
{ stage, path, uri, width, height } |
android |
| onPictureTakenError |
正脸照片抓拍失败 |
{ stage, code, message } |
android |
| onPictureTakenReady |
during 模式计划就绪 |
{ duringInsertAfterActionCount } |
android |