章
目
录
HarmonyOS 5的生态体系里,VisionKit是一个非常实用的场景化视觉服务工具包。它将一些原本需要三方应用借助SDK进行集成的常见解决方案,以Kit的形式直接集成到了HarmonyOS系统中,极大地方便了三方应用的快速开发,为人脸活体检测功能的实现提供了便利途径。今天,我们就深入了解一下VisionKit中的人脸活体检测技术。
一、认识VisionKit人脸活体检测
(一)功能与应用场景
人脸活体检测,顾名思义,其主要作用是判断当前进行检测的对象是不是真实的人,而不是照片、硅胶面具或者AI合成的视频等伪装物。虽然该算法接口已经通过了中金金融(CECA)认证,但出于安全考虑,官方不建议在高风险的支付和金融场景中直接使用,更推荐在登录、考勤、实名认证这类低风险场景中应用。另外,需要特别注意的是,人脸活体检测功能不支持在模拟器和预览器上使用。
(二)基本概念与相关配置
- 模块导入:在代码中,通过
import {interactiveLiveness } from @kit.VisionKit
来导入人脸活体检测功能接口。 - 检测模式:检测模式分为两种,
SILENT_MODE
表示静默活体检测,不过目前暂未支持;INTERACTIVE_MODE
是动作活体检测,这是当前可用的模式。 - 动作数量:可设置的动作数量有
ONE_ACTION
(1个,暂未支持)、TWO_ACTION
(2个,暂未支持)、THREE_ACTION
(3个,随机选择且有规则限制)、FOUR_ACTION
(4个,同样随机选择并遵循规则)。 - 跳转模式:
RouteRedirectionMode
定义了两种跳转模式,BACK_MODE
是检测完成后返回上一页,REPLACE_MODE
则是检测完成后替换跳转页面,默认使用REPLACE_MODE
。 - 配置项:
InteractiveLivenessConfig
用于配置检测的各项参数。其中,isSilentMode
(检测模式,默认INTERACTIVE_MODE
)是必填项,其他如actionsNum
(动作数量,默认3)、successfulRouteUrl
和failedRouteUrl
(成功/失败跳转路径,可选)、routeMode
(跳转模式,默认REPLACE_MODE
)、challenge
(安全摄像头场景挑战值,16 – 128位,可选)、speechSwitch
(语音播报开关,默认开启)、isPrivacyMode
(隐私模式,需申请权限,默认关闭)均为可选参数。
二、人脸活体检测的使用方法
(一)核心接口调用
- 启动检测接口:
interactiveLiveness.startLivenessDetection
是启动人脸活体检测的核心接口,使用时需要传入InteractiveLivenessConfig
配置对象来设置检测模式、动作数量等。它支持两种调用方式:- Promise方式:仅返回跳转结果(
boolean
类型),示例代码如下:
- Promise方式:仅返回跳转结果(
interactiveLiveness.startLivenessDetection(routerOptions).then((DetectState: boolean) => {
hilog.info(0x0001, "LivenessCollectionIndex", `Succeeded in jumping.`);
}).catch((err: BusinessError) => {
hilog.error(0x0001, "LivenessCollectionIndex", `Failed to jump. Code:${err.code},message:${err.message}`);
})
- **Promise + 回调方式**:这种方式同时返回跳转结果和检测结果,但仅适用于`BACK_MODE`。示例代码如下:
interactiveLiveness.startLivenessDetection(routerOptions, (err: BusinessError, result: interactiveLiveness.InteractiveLivenessResult | undefined) => {
if(err.code!== 0 &&!result) {
hilog.error(0x0001, "LivenessCollectionIndex", `Failed to detect. Code:${err.code},message:${err.message}`);
return;
}
hilog.info(0x0001, 'LivenessCollectionIndex', `Succeeded in detecting result:${result}`);
})
- 获取检测结果接口:在检测成功跳转后,可通过
getInteractiveLivenessResult
接口获取检测结果,返回值是InteractiveLivenessResult
对象,包含检测模式(livenessType
,如INTERACTIVE_LIVENESS
表示动作活体检测成功、SILENT_LIVENESS
暂未支持、NOT_LIVENESS
表示非活体)、活体特征图片(mPixelMap
,检测成功才有)、安全摄像头相关数据(securedImageBuffer
和certificate
,安全场景才有)等信息。示例代码如下:
let successResult = interactiveLiveness.getInteractiveLivenessResult();
successResult.then(data => {
hilog.info(0x0001, "LivenessCollectionIndex", `Succeeded in detecting.`);
}).catch((err: BusinessError) => {
hilog.error(0x0001, "LivenessCollectionIndex", `Failed to detect. Code:${err.code},message:${err.message}`);
})
(二)配置接口详解
调用人脸活体检测时,必须正确配置InteractiveLivenessConfig
对象。以下是各项配置参数的详细说明:
- 检测模式(DetectionMode):
SILENT_MODE
:静默活体检测,当前版本暂未支持。INTERACTIVE_MODE
:动作活体检测,为默认模式,用户需要完成指定动作来配合检测。
- 动作数量(ActionsNumber):
ONE_ACTION
:随机1个动作,暂未支持。TWO_ACTION
:随机2个动作,暂未支持。THREE_ACTION
:随机3个动作,其中“眨眼”和“注视”不同时存在且不相邻,相邻动作不重复。FOUR_ACTION
:随机4个动作,眨眼仅1次,注视最多1次,“眨眼”和“注视”不相邻,相邻动作不重复。
- 跳转模式(RouteRedirectionMode):
BACK_MODE
:检测完成后调用router.back
返回上一页。REPLACE_MODE
:检测完成后调用router.replaceUrl
跳转,默认采用此模式。
- 其他配置项:
isSilentMode
:必填,用于指定检测模式,默认值为INTERACTIVE_MODE
。actionsNum
:可选,设置动作数量,默认值为3。successfulRouteUrl
:可选,检测成功后的跳转路径,若未填写则使用系统默认页面。failedRouteUrl
:可选,检测失败后的跳转路径,未填写时也使用系统默认页面。routeMode
:可选,设置跳转模式,默认值为REPLACE_MODE
。challenge
:可选,安全摄像头场景挑战值,长度在16 – 128位之间,空值表示不使用安全摄像头。speechSwitch
:可选,语音播报开关,默认开启。isPrivacyMode
:可选,隐私模式,开启需申请ohos.permission.PRIVACY_WINDOW
权限,默认关闭。
示例代码展示如何配置:
import { interactiveLiveness } from '@kit.VisionKit';
let isSilentMode = "INTERACTIVE_MODE" as interactiveLiveness.DetectionMode;
let routeMode = "replace" as interactiveLiveness.RouteRedirectionMode;
let actionsNum = 3 as interactiveLiveness.ActionsNumber;
let routerOptions: interactiveLiveness.InteractiveLivenessConfig= {
isSilentMode: isSilentMode,
routeMode: routeMode,
actionsNum: actionsNum,
failedRouteUrl: "pages/FailPage",
successfulRouteUrl: "pages/SuccessPage"
}
三、DEMO源码示例解析
下面通过一个完整的DEMO示例,来帮助大家更好地理解如何在实际应用中使用人脸活体检测功能:
import { interactiveLiveness } from '@kit.VisionKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { abilityAccessCtrl, common } from '@kit.AbilityKit';
@Entry
@Component
struct FaceLivenessDemo {
// 权限状态,默认未授权
@State userGrant: boolean = false
// 检测结果展示
@State detectionResult: string = ""
// 动作数量,默认3个
@State actionCount: interactiveLiveness.ActionsNumber = interactiveLiveness.ActionsNumber.THREE_ACTION;
// 语音播报开关,默认开启
@State speechEnabled: boolean = true
// 跳转模式,默认检测完成后返回上一页
@State routeMode: interactiveLiveness.RouteRedirectionMode = interactiveLiveness.RouteRedirectionMode.BACK_MODE;
// 权限申请逻辑
private async requestPermissions() {
const context = getContext() as common.UIAbilityContext;
const atManager = abilityAccessCtrl.createAtManager();
const results = await atManager.requestPermissionsFromUser(context, ["ohos.permission.CAMERA"]);
// 判断权限是否全部申请成功
this.userGrant = results.authResults.every(status => status === 0);
}
// 检测配置生成
private generateDetectionConfig(): interactiveLiveness.InteractiveLivenessConfig {
return {
// 设置为动作活体检测模式
isSilentMode: interactiveLiveness.DetectionMode.INTERACTIVE_MODE,
// 设置动作数量
actionsNum: this.actionCount,
// 检测成功后的跳转路径,可自定义,未填则用系统默认
// successfulRouteUrl: "pages/result/success",
// 检测失败后的跳转路径,可自定义,未填则用系统默认
// failedRouteUrl: "pages/result/fail",
// 设置跳转模式
routeMode: this.routeMode,
// 语音播报开关
speechSwitch: this.speechEnabled,
// 安全摄像头场景挑战值,可自定义,未填则不使用安全摄像头
// challenge: "自定义挑战值1234567890abcdef",
// 是否开启隐私模式,开启需申请权限,默认关闭
// isPrivacyMode: true
};
}
// 启动检测
private async startDetection() {
if (!this.userGrant) {
this.detectionResult = "请先申请相机权限";
return;
}
const config = this.generateDetectionConfig();
try {
const jumpSuccess = await interactiveLiveness.startLivenessDetection(config);
if (jumpSuccess) {
hilog.info(0x0001, "Detection", "跳转检测页面成功");
// 检测完成后获取结果(需在返回页面时调用)
const result = await interactiveLiveness.getInteractiveLivenessResult();
this.processResult(result);
}
} catch (err) {
const error = err as BusinessError;
hilog.error(0x0001, "Detection", `检测失败: 错误码${error.code}, 信息${error.message}`);
this.detectionResult = `检测异常:错误码${error.code}`;
}
}
// 结果处理
private processResult(result: interactiveLiveness.InteractiveLivenessResult) {
let status = "";
let livenessType = result.livenessType;
switch (livenessType) {
case 0: // 动作活体检测成功
status = "活体检测通过";
// 可在此处处理特征图片或安全数据
break;
case 2: // 非活体
status = "检测到非活体(照片/视频GJ)";
break;
default:
status = "检测结果异常";
}
this.detectionResult = status;
}
build() {
Column({ space: 40 })
{
// 权限申请按钮
Button(this.userGrant? "权限已授权" : "申请相机权限")
.fontSize(18)
.margin(10)
.padding(12)
.backgroundColor(this.userGrant? Color.Green : Color.Blue)
.onClick(() => this.requestPermissions())
// 动作数量选择
Row({ space: 20 }) {
Text("动作数量:")
.fontSize(16)
Button("3个动作")
.backgroundColor(this.actionCount === 3? Color.Blue : Color.White)
.border({ width: 1, color: Color.Gray })
.onClick(() => this.actionCount = 3)
Button("4个动作")
.backgroundColor(this.actionCount === 4? Color.Blue : Color.White)
.border({ width: 1, color: Color.Gray })
.onClick(() => this.actionCount = 4)
}
// 语音播报开关
Toggle({ type: ToggleType.Checkbox, isOn: this.speechEnabled })
.onChange((isOn: boolean)=>{
this.speechEnabled = isOn;
})
// 跳转模式选择
Row({ space: 20 }) {
Text("跳转模式:")
.fontSize(16)
Button("替换页面")
.backgroundColor(this.routeMode === "replace"? Color.Blue : Color.White)
.border({ width: 1, color: Color.Gray })
.onClick(() => {
this.routeMode = interactiveLiveness.RouteRedirectionMode.REPLACE_MODE;
})
Button("返回上页")
.backgroundColor(this.routeMode === "back"? Color.Blue : Color.White)
.border({ width: 1, color: Color.Gray })
.onClick(() => {
this.routeMode = interactiveLiveness.RouteRedirectionMode.BACK_MODE;
})
}
// 启动检测按钮
Button("开始人脸活体检测")
.fontSize(20)
.padding(16)
.backgroundColor(Color.Orange)
.onClick(() => this.startDetection())
// 结果显示
Text(this.detectionResult)
.fontSize(16)
.margin({
top: 30
})
.foregroundColor(this.detectionResult.includes("通过")? Color.Green : Color.Red)
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
在这个示例中,FaceLivenessDemo
组件实现了权限申请、检测配置生成、启动检测以及结果处理等功能。用户可以在界面上选择动作数量、跳转模式,控制语音播报开关,点击按钮启动检测,检测结果会在界面上展示出来。
四、注意事项
(一)检测模式
目前仅INTERACTIVE_MODE
(动作活体检测)可用,用户需完成3或4个随机动作(如眨眼、点头、张嘴、左摇头、右摇头、注视)来完成检测,系统会通过动作组合验证活体,同时遵循特定规则避免相邻动作重复或出现特定组合。SILENT_MODE
(静默活体检测)虽然暂未支持,但未来可能会带来更便捷的检测体验,无需用户做动作,通过其他技术(如微表情、光线反射等)就能检测活体。
(二)配置要点
通过InteractiveLivenessConfig
配置对象,开发者可以灵活设置动作数量和跳转逻辑。actionsNum
可选3或4,默认3个动作,不同数量下动作组合有不同规则。routeMode
可选择BACK_MODE
返回上一页或REPLACE_MODE
替换跳转,默认是REPLACE_MODE
。successfulRouteUrl
和failedRouteUrl
可设置成功或失败后的自定义跳转路径,若不设置则使用系统默认页面。
(三)错误处理
在使用过程中,可能会遇到一些错误:
201(Permission denied)
:表示未申请ohos.permission.CAMERA
权限,需要在代码中添加权限申请逻辑。1008301002(Route switching failed)
:说明路由配置错误,此时需要检查successfulRouteUrl
和failedRouteUrl
路径是否正确,以及routeMode
是否与页面路由匹配。1008302000 - 1008302004(检测相关错误)
:这类错误通常是检测过程中算法初始化失败、超时或动作不符合规则导致的。可以通过回调或Promise
的catch
捕获错误码,提示用户重新检测并检查动作是否合规。
通过以上内容,相信大家对HarmonyOS 5中VisionKit的人脸活体检测功能有了全面的了解,希望能帮助大家在实际开发中更好地运用这一技术。