更新记录
1.0.0(2026-06-04)
UTS 人脸识别与录入组件 (y-tech-faceai)
本插件是基于 Android CameraX 与 FaceAI SDK 原生封装的 uni-app UTS 插件。包含三个核心模块:
faceai-search-view:视图组件,用于人脸 1:N 检索、活体检测、TTS语音播报。
faceai-register-view:视图组件,用于采集人脸图像、提取人脸特征码并存入本地数据库进行人脸录入。
- 人脸数据管理 API:JS 函数集,提供人脸数据的初始化、录入(图片/相机)、删除、清空、数量查询等操作。
模块一:人脸比对检索组件 (faceai-search-view)
用于在已录入的本地人脸库中匹配当前画面中的人脸。
#
平台兼容性
uni-app(4.81)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-vue插件版本 |
app-nvue |
app-nvue插件版本 |
Android |
Android插件版本 |
iOS |
鸿蒙 |
| √ |
√ |
- |
- |
√ |
1.0.0 |
√ |
1.0.0 |
7.0 |
1.0.0 |
- |
- |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| - |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
uni-app x(4.81)
| Chrome |
Safari |
Android |
iOS |
鸿蒙 |
微信小程序 |
| - |
- |
7.0 |
- |
- |
- |
UTS 人脸识别与录入组件 (y-tech-faceai)
本插件是基于 Android CameraX 与 FaceAI SDK 原生封装的 uni-app UTS 插件。包含三个核心模块:
faceai-search-view:视图组件,用于人脸 1:N 检索、活体检测、TTS语音播报。
faceai-register-view:视图组件,用于采集人脸图像、提取人脸特征码并存入本地数据库进行人脸录入。
- 人脸数据管理 API:JS 函数集,提供人脸数据的初始化、录入(图片/相机)、删除、清空、数量查询等操作。
模块一:人脸比对检索组件 (faceai-search-view)
用于在已录入的本地人脸库中匹配当前画面中的人脸。
1. 组件属性 (Props)
| 属性名 |
类型 |
默认值 |
描述说明 |
searchThreshold |
Number |
0.80 |
人脸相似度比对成功阈值(0~1)。低于此分值判定为未知人员。 |
needFaceLive |
Boolean |
false |
是否开启活体检测(过滤静态照片/视频作弊)。 |
isCameraSizeHigh |
Boolean |
false |
是否开启 720p 高清分辨率采集。默认 false(使用 4:3 预览以保帧率)。 |
searchIntervalTime |
Number |
1800 |
匹配成功后的冷却延迟时间(毫秒),防止同人多次触发。 |
livenessThreshold |
Number |
0.3 |
活体判定分数阈值。 |
searchTimeOut |
Number |
4000 |
人脸检测超时时间。 |
previewShape |
String |
'circle' |
相机遮罩形状:'circle' (圆形), 'square' (四角方形), 'none' (不加遮罩遮蔽)。 |
maskColor |
String |
'#ffffff' |
相机框外的遮罩背景色(支持透明度配置,格式如 '#FF1a1a1a'、'#66000000')。 |
frameBorderColor |
String |
'#4CD964' |
指示框边缘线颜色(避免使用 borderColor 导致被 CSS 样式拦截)。 |
borderWidth |
Number |
4 |
边缘线粗细(px)。 |
2. 组件事件 (Events)
| 事件名 |
回调数据格式 (e.detail) |
触发时机 |
@faceMatched |
{ faceID: String, score: Number, livenessValue: Number } |
比对成功(匹配分高于阈值)时触发。 |
@faceMatchedAll |
{ results: String } |
返回本次比对的所有相似候选人(JSON字符串)。 |
@livenessFailed |
{ faceID: String, score: Number, livenessValue: Number } |
人脸相似但活体判定分值过低(照片/视频作弊)。 |
@processTips |
{ code: Number, message: String } |
原生提示通知(如人脸太近、太远、请正对相机等)。 |
3. 主动控制方法 (Methods via Ref)
startSearch(): void:开启相机预览并启动检索引擎。
stopSearch(): void:停止检索并关闭相机,清除人脸框。
setCameraLensFacing(facing: Number): void:切换摄像头,0 后置,1 前置。
模块二:人脸录入注册组件 (faceai-register-view)
用于给新用户录入人脸特征码和图像。
1. 组件属性 (Props)
| 属性名 |
类型 |
默认值 |
描述说明 |
performanceMode |
Number |
1 |
采集算法模式:0 代表普通精细图,1 代表性能高速采集(推荐)。 |
isCameraSizeHigh |
Boolean |
false |
是否开启 720p 分辨率采集。 |
previewShape |
String |
'circle' |
遮罩形状:'circle', 'square', 'none'。 |
maskColor |
String |
'#ffffff' |
遮罩背景色(支持带透明度的颜色编码如 '#AARRGGBB')。 |
frameBorderColor |
String |
'#4CD964' |
指示框边缘颜色(改用 frameBorderColor 命名,避免被 CSS 默认的 borderColor 属性拦截)。 |
borderWidth |
Number |
4 |
指示框边缘宽度。 |
2. 组件事件 (Events)
| 事件名 |
回调数据格式 (e.detail) |
触发时机 |
@registerSuccess |
{ faceID: String, faceFeature: String, imagePath: String } |
人脸图像采集成功、特征码提取成功并存入本地库时触发。返回特征码及图像在本地缓存的绝对路径。 |
@registerFailed |
{ message: String } |
注册失败(如环境光线太暗、未检测到人脸、特征提取失败等)。 |
@processTips |
{ code: Number, message: String } |
人脸采集时的状态建议(如太近、太远、头抬高一点等)。 |
3. 主动控制方法 (Methods via Ref)
startRegister(faceID: String): void:传入唯一的 faceID,开启相机预览并开始录入人脸。
stopRegister(): void:主动终止录入并释放相机。
模块三:人脸数据管理 API
通过 JS 函数直接调用,无需使用组件。导入方式:
import {
initFaceSDK,
setCameraID,
openAddFaceCamera,
addFaceByImage,
clearAllFaces,
deleteFace,
getFaceCount
} from '@/uni_modules/y-tech-faceai'
API 列表
| 函数名 |
参数 |
返回值 |
描述 |
initFaceSDK() |
无 |
void |
初始化 SDK(MMKV、TTS、缓存目录等),建议在 App.vue 的 onLaunch 中调用。 |
setCameraID(cameraID) |
cameraID: Number(0:前置, 1:后置) |
void |
设置默认摄像头朝向。 |
openAddFaceCamera(faceID, callback) |
faceID: String, callback: Function |
void |
打开 SDK 内置的全屏相机录入界面。回调 { code: 1, message: '...' }(1 代表录入成功,其它为取消或错误)。 |
addFaceByImage(filePath, faceID, callback) |
filePath: String, faceID: String, callback: Function |
void |
通过图片路径注册人脸(支持 file://、content://、绝对路径、assets 路径)。 |
clearAllFaces() |
无 |
Object |
清空所有已注册的人脸数据及关联人脸图片。返回 { code: 0, message: '...' }。 |
deleteFace(faceID) |
faceID: String |
Object |
删除指定 ID 的人脸数据及关联人脸图片。返回 { code: 0, message: '...' }。 |
getFaceCount() |
无 |
Object |
查询已注册人脸数量。返回 { code: 0, count: Number }。 |
getAllFaces() |
无 |
Object |
查询所有已注册的人脸数据列表。返回 { code: 0, faces: String },其中 faces 为 JSON 数组字符串(结构包含 id 和 image 属性)。 |
四、 页面代码示例
1. 人脸检索 (1:N) 示例
<template>
<view class="face-container">
<view class="camera-wrapper">
<faceai-search-view
ref="faceView"
class="camera-preview"
:searchThreshold="0.82"
:needFaceLive="true"
previewShape="circle"
maskColor="#1a1a1a"
frameBorderColor="#00FF00"
:borderWidth="6"
@faceMatched="onFaceMatched"
@livenessFailed="onLivenessFailed"
@processTips="onProcessTips"
/>
</view>
<view class="control-panel">
<text class="tips-text">状态:{{ tipsMsg }}</text>
<button class="btn" type="primary" @click="startRecognize">开始人脸比对</button>
<button class="btn" type="warn" @click="stopRecognize">停止</button>
</view>
</view>
</template>
<script>
export default {
data() {
return { tipsMsg: "待比对" }
},
methods: {
startRecognize() {
this.$refs.faceView.startSearch();
this.tipsMsg = "识别模块启动中...";
},
stopRecognize() {
this.$refs.faceView.stopSearch();
this.tipsMsg = "识别已停止";
},
onFaceMatched(e) {
const { faceID, score, livenessValue } = e.detail;
this.tipsMsg = `匹配成功!ID: ${faceID}, 评分: ${(score * 100).toFixed(1)}%`;
uni.showToast({ title: `欢迎,${faceID}`, icon: 'success' });
},
onLivenessFailed(e) {
this.tipsMsg = `发现照片作弊,拒绝通行!`;
},
onProcessTips(e) {
this.tipsMsg = e.detail.message;
}
}
}
</script>
2. 人脸注册录入示例
<template>
<view class="face-container">
<view class="input-wrapper">
<input class="face-input" v-model="targetUserId" placeholder="请输入要录入的工号/姓名" />
</view>
<view class="camera-wrapper">
<faceai-register-view
ref="registerView"
class="camera-preview"
previewShape="circle"
maskColor="#1a1a1a"
frameBorderColor="#00aaff"
:borderWidth="6"
@registerSuccess="onRegisterSuccess"
@registerFailed="onRegisterFailed"
@processTips="onProcessTips"
/>
</view>
<view class="control-panel">
<text class="tips-text">录入建议:{{ tipsMsg }}</text>
<button class="btn" type="primary" @click="startEnroll">开始人脸录入</button>
<button class="btn" type="warn" @click="stopEnroll">停止录入</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
targetUserId: "",
tipsMsg: "准备录入"
}
},
methods: {
startEnroll() {
if (!this.targetUserId.trim()) {
uni.showToast({ title: '请输入用户ID', icon: 'none' });
return;
}
this.$refs.registerView.startRegister(this.targetUserId);
this.tipsMsg = "请将面部对准圆框...";
},
stopEnroll() {
this.$refs.registerView.stopRegister();
this.tipsMsg = "录入已停止";
},
onRegisterSuccess(e) {
const { faceID, faceFeature, imagePath } = e.detail;
this.tipsMsg = `录入成功!ID: ${faceID}`;
uni.showModal({
title: '录入成功',
content: `用户: ${faceID}\n特征长度: ${faceFeature.length}\n图片保存在: ${imagePath}`,
showCancel: false
});
},
onRegisterFailed(e) {
this.tipsMsg = `录入失败: ${e.detail.message}`;
uni.showToast({ title: '采集失败,请重试', icon: 'none' });
},
onProcessTips(e) {
this.tipsMsg = e.detail.message;
}
}
}
</script>
<style>
.face-container {
display: flex;
flex-direction: column;
align-items: center;
background-color: #1a1a1a;
height: 100vh;
padding-top: 30px;
}
.input-wrapper {
width: 80%;
margin-bottom: 25px;
}
.face-input {
background: #fff;
padding: 12px 15px;
border-radius: 8px;
font-size: 16px;
}
.camera-wrapper {
width: 320px;
height: 320px;
overflow: hidden;
border-radius: 160px;
background: #000;
}
.camera-preview {
width: 100%;
height: 100%;
}
.control-panel {
margin-top: 30px;
width: 80%;
display: flex;
flex-direction: column;
}
.tips-text {
color: #fff;
font-size: 15px;
margin-bottom: 15px;
text-align: center;
}
.btn {
width: 100%;
margin-bottom: 12px;
}
</style>
3. 人脸数据管理 API 示例
<template>
<view class="container">
<text class="title">人脸数据管理</text>
<view class="info-row">
<text>已注册人脸数:{{ faceCount }}</text>
<button size="mini" @click="refreshCount">刷新</button>
</view>
<view class="input-row">
<input class="input" v-model="inputFaceID" placeholder="输入人脸 ID(工号/姓名)" />
</view>
<button type="primary" @click="openCamera">SDK相机录入</button>
<button @click="doAddByImage">通过图片注册</button>
<button @click="doDelete">删除指定人脸</button>
<button type="warn" @click="doClear">清空全部人脸</button>
</view>
</template>
<script>
import {
initFaceSDK,
setCameraID,
openAddFaceCamera,
addFaceByImage,
clearAllFaces,
deleteFace,
getFaceCount,
getAllFaces
} from '@/uni_modules/y-tech-faceai'
export default {
data() {
return {
inputFaceID: '',
faceCount: 0
}
},
onLoad() {
// 初始化 SDK
initFaceSDK();
// 设置前置摄像头(0:前置,1:后置)
setCameraID(0);
// 查询数量
this.refreshCount();
this.loadAllFaces();
},
methods: {
refreshCount() {
const res = getFaceCount();
if (res['code'] === 0) {
this.faceCount = res['count'];
}
},
openCamera() {
openAddFaceCamera(this.inputFaceID, (res) => {
console.log('openAddFaceCamera 回调:', JSON.stringify(res));
});
},
doAddByImage() {
uni.chooseImage({
count: 1,
success: (chooseRes) => {
const filePath = chooseRes.tempFilePaths[0];
addFaceByImage(filePath, this.inputFaceID, (res) => {
const code = res['code'];
if (code === 0) {
uni.showToast({ title: '注册成功', icon: 'success' });
this.refreshCount();
} else {
uni.showToast({ title: res['message'], icon: 'none' });
}
});
}
});
},
doDelete() {
if (!this.inputFaceID) {
uni.showToast({ title: '请输入要删除的 ID', icon: 'none' });
return;
}
const res = deleteFace(this.inputFaceID);
uni.showToast({ title: res['message'], icon: res['code'] === 0 ? 'success' : 'none' });
this.refreshCount();
},
doClear() {
uni.showModal({
title: '警告',
content: '确定要清空所有已录入的人脸数据吗?此操作不可恢复!',
success: (modalRes) => {
if (modalRes.confirm) {
const res = clearAllFaces();
uni.showToast({ title: res['message'], icon: 'success' });
this.refreshCount();
}
}
});
}
}
}
</script>
<style>
.container { padding: 20px; }
.title { font-size: 20px; font-weight: bold; margin-bottom: 20px; }
.info-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.input-row { margin-bottom: 15px; }
.input { background: #f5f5f5; padding: 10px 12px; border-radius: 6px; }
button { margin-bottom: 10px; width: 100%; }
</style>