鸿蒙应用开发之场景化视觉服务人脸活体检测基础
一、工具
 

DevEco Studio
 
二、项目介绍
 
开发步骤
 将实现人脸活体检测相关的类添加至工程。
 import { interactiveLiveness } from '@kit.VisionKit';
 在module.json5文件中添加CAMERA权限,其中reason,abilities标签必填,配置方式参见requestPermissions标签说明。
 "requestPermissions":[
   {
     "name": "ohos.permission.CAMERA",
     "reason": "$string:camera_desc",
     "usedScene": {"abilities": []}
   }
 ]
 简单配置页面的布局,选择人脸活体检测验证完后的跳转模式。如果使用back跳转模式,表示的是在检测结束后使用router.back()返回。如果使用replace跳转模式,表示的是检测结束后使用router.replaceUrl()去跳转相应页面。默认选择的是replace跳转模式。
 Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
   Text("验证完的跳转模式:")
     .fontSize(18)
     .width("25%")
   Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
     Row() {
       Radio({ value: "replace", group: "routeMode" }).checked(true)
         .height(24)
         .width(24)
         .onChange((isChecked: boolean) => {
           this.routeMode = "replace"
         })
       Text("replace")
         .fontSize(16)
     }
     .margin({ right: 15 })
 
    Row() {
       Radio({ value: "back", group: "routeMode" }).checked(false)
         .height(24)
         .width(24)
         .onChange((isChecked: boolean) => {
           this.routeMode = "back";
         })
       Text("back")
         .fontSize(16)
     }
   }
   .width("75%")
 }
 如果选择动作活体模式,可填写验证的动作个数。
 Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
   Text("动作数量:")
     .fontSize(18)
     .width("25%")
   TextInput({
     placeholder: this.actionsNum != 0 ? this.actionsNum.toString() : "动作数量为3或4个"
   })
     .type(InputType.Number)
     .placeholderFont({
       size: 18,
       weight: FontWeight.Normal,
       family: "HarmonyHeiTi",
       style: FontStyle.Normal
     })
     .fontSize(18)
     .fontWeight(FontWeight.Bold)
     .fontFamily("HarmonyHeiTi")
     .fontStyle(FontStyle.Normal)
     .width("65%")
     .onChange((value: string) => {
       this.actionsNum = Number(value) as interactiveLiveness.ActionsNumber;
     })
 }
 点击“开始检测“按钮,触发点击事件。
 Button("开始检测", { type: ButtonType.Normal, stateEffect: true })
   .width(192)
   .height(40)
   .fontSize(16)
   .backgroundColor(0x317aff)
   .borderRadius(20)
   .margin({
     bottom: 56
   })
   .onClick(() => {
     this.privateStartDetection();
   })
 触发CAMERA权限校验。
 // 校验CAMERA权限
 private privateStartDetection() {
   abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.context, this.array).then((res) => {
     for (let i = 0; i < res.permissions.length; i++) {
       if (res.permissions[i] === "ohos.permission.CAMERA" && res.authResults[i] === 0) {
         this.privateRouterLibrary();
       }
     }
   }).catch((err: BusinessError) => {
     hilog.error(0x0001, "LivenessCollectionIndex", Failed to request permissions from user. Code is ${err.code}, message is ${err.message});
   })
 }
 配置人脸活体检测控件的配置项InteractiveLivenessConfig,用于跳转到人脸活体检测控件。
 配置中具体的参数可参考API文档。
 let routerOptions: interactiveLiveness.InteractiveLivenessConfig = {
   isSilentMode: this.isSilentMode as interactiveLiveness.DetectionMode,
   routeMode: this.routeMode as interactiveLiveness.RouteRedirectionMode,
   actionsNum: this.actionsNum
 };
 调用interactiveLiveness的startLivenessDetection接口,判断跳转到人脸活体检测控件是否成功。
 // 跳转到人脸活体检测控件
 private privateRouterLibrary() {
   if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {
     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});
     })
   } else {
     hilog.error(0x0001, "LivenessCollectionIndex", 'this api is not supported on this device');
   }
 }
 检测结束后回到当前界面,可调用interactiveLiveness的getInteractiveLivenessResult接口,验证人脸活体检测的结果。
 // 获取验证结果
 private getDetectionResultInfo() {
   // getInteractiveLivenessResult接口调用完会释放资源
   if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {
     let resultInfo = interactiveLiveness.getInteractiveLivenessResult();
     resultInfo.then(data => {
       this.resultInfo = data;
     }).catch((err: BusinessError) => {
       this.failResult = {
         "code": err.code,
         "message": err.message
       }
     })
   } else {
     hilog.error(0x0001, "LivenessCollectionIndex", 'this api is not supported on this device');
   }
 }
 开发实例
 import { common, abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
 import { interactiveLiveness } from '@kit.VisionKit';
 import { BusinessError } from '@kit.BasicServicesKit';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 
@Entry
 @Component
 struct LivenessIndex {
   private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
   private array: Array = ["ohos.permission.CAMERA"];
   @State actionsNum: number = 0;
   @State isSilentMode: string = "INTERACTIVE_MODE";
   @State routeMode: string = "replace";
   @State resultInfo: interactiveLiveness.InteractiveLivenessResult = {
     livenessType: 0
   };
   @State failResult: Record<string, number | string> = {
     "code": 1008302000,
     "message": ""
   };
 
  build() {
     Stack({
       alignContent: Alignment.Top
     }) {
       Column() {
         Row() {
           Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
             Text("验证完的跳转模式:")
               .fontSize(18)
               .width("25%")
             Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
               Row() {
                 Radio({ value: "replace", group: "routeMode" }).checked(true)
                   .height(24)
                   .width(24)
                   .onChange((isChecked: boolean) => {
                     this.routeMode = "replace"
                   })
                 Text("replace")
                   .fontSize(16)
               }
               .margin({ right: 15 })
 
              Row() {
                 Radio({ value: "back", group: "routeMode" }).checked(false)
                   .height(24)
                   .width(24)
                   .onChange((isChecked: boolean) => {
                     this.routeMode = "back";
                   })
                 Text("back")
                   .fontSize(16)
               }
             }
             .width("75%")
           }
         }
         .margin({ bottom: 30 })
 
          Row() {
             Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
               Text("动作数量:")
                 .fontSize(18)
                 .width("25%")
               TextInput({
                 placeholder: this.actionsNum != 0 ? this.actionsNum.toString() : "动作数量为3或4个"
               })
                 .type(InputType.Number)
                 .placeholderFont({
                   size: 18,
                   weight: FontWeight.Normal,
                   family: "HarmonyHeiTi",
                   style: FontStyle.Normal
                 })
                 .fontSize(18)
                 .fontWeight(FontWeight.Bold)
                 .fontFamily("HarmonyHeiTi")
                 .fontStyle(FontStyle.Normal)
                 .width("65%")
                 .onChange((value: string) => {
                   this.actionsNum = Number(value) as interactiveLiveness.ActionsNumber;
                 })
             }
           }
       }
       .margin({ left: 24, top: 80 })
       .zIndex(1)
 
      Stack({
         alignContent: Alignment.Bottom
       }) {
         if (this.resultInfo?.mPixelMap) {
           Image(this.resultInfo?.mPixelMap)
             .width(260)
             .height(260)
             .align(Alignment.Center)
             .margin({ bottom: 260 })
           Circle()
             .width(300)
             .height(300)
             .fillOpacity(0)
             .strokeWidth(60)
             .stroke(Color.White)
             .margin({ bottom: 250, left: 0 })
         }
 
        Text(this.resultInfo.mPixelMap ?
           "检测成功" :
           this.failResult.code != 1008302000 ?
             "检测失败" :
             "")
           .width("100%")
           .height(26)
           .fontSize(20)
           .fontColor("#000000")
           .fontFamily("HarmonyHeiTi")
           .margin({ top: 50 })
           .textAlign(TextAlign.Center)
           .fontWeight("Medium")
           .margin({ bottom: 240 })
 
        if(this.failResult.code != 1008302000) {
           Text(this.failResult.message as string)
             .width("100%")
             .height(26)
             .fontSize(16)
             .fontColor(Color.Gray)
             .textAlign(TextAlign.Center)
             .fontFamily("HarmonyHeiTi")
             .fontWeight("Medium")
             .margin({ bottom: 200 })
         }
 
        Button("开始检测", { type: ButtonType.Normal, stateEffect: true })
           .width(192)
           .height(40)
           .fontSize(16)
           .backgroundColor(0x317aff)
           .borderRadius(20)
           .margin({
             bottom: 56
           })
           .onClick(() => {
             this.privateStartDetection();
           })
       }
       .height("100%")
     }
   }
 
  onPageShow() {
     this.resultRelease();
     this.getDetectionResultInfo();
   }
 
  // 跳转到人脸活体检测控件
   private privateRouterLibrary() {
     let routerOptions: interactiveLiveness.InteractiveLivenessConfig = {
       isSilentMode: this.isSilentMode as interactiveLiveness.DetectionMode,
       routeMode: this.routeMode as interactiveLiveness.RouteRedirectionMode,
       actionsNum: this.actionsNum
     }
 
    if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {
       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});
       })
     } else {
       hilog.error(0x0001, "LivenessCollectionIndex", 'this api is not supported on this device');
     }
   }
 
  // 校验CAMERA权限
   private privateStartDetection() {
     abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.context, this.array).then((res) => {
       for (let i = 0; i < res.permissions.length; i++) {
         if (res.permissions[i] === "ohos.permission.CAMERA" && res.authResults[i] === 0) {
         this.privateRouterLibrary();
       }
      }
     }).catch((err: BusinessError) => {
       hilog.error(0x0001, "LivenessCollectionIndex", Failed to request permissions from user. Code is ${err.code}, message is ${err.message});
     })
   }
 
  // 获取验证结果
   private getDetectionResultInfo() {
     // getInteractiveLivenessResult接口调用完会释放资源
     if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {
       let resultInfo = interactiveLiveness.getInteractiveLivenessResult();
       resultInfo.then(data => {
         this.resultInfo = data;
       }).catch((err: BusinessError) => {
         this.failResult = {
           "code": err.code,
           "message": err.message
         }
       })
     } else {
       hilog.error(0x0001, "LivenessCollectionIndex", 'this api is not supported on this device');
     }
   }
 
  // result release
   private resultRelease() {
     this.resultInfo = {
       livenessType: 0
     }
     this.failResult = {
       "code": 1008302000,
       "message": ""
     }
   }
 }
 




















