活体检测

2025.04.09 10:38:50

    根据提示做出相应动作,SDK 实时采集动态信息,判断用户是否为活体、真人

    兼容性

    条目 说明
    适配版本 HarmonyOS 12 及以上版本

    资源引入

    远程仓库依赖(推荐)

    活体检测共享包 HAR 发布在 OpenHarmony 三方库中心仓,需要从 OpenHarmony 三方库中心仓引用

    方式一 在 Terminal 窗口中,执行如下命令安装三方包,DevEco Studio 会自动在工程的 oh-package.json5 中自动添加三方包依赖

    ohpm install @yidun/alive
    
    

    方式二 在工程的 oh-package.json5 中设置三方包依赖

    "dependencies": {
      "@yidun/alive": "^1.0.3"
    }
    
    

    各种配置

    权限配置

    共享包依赖相机和文件存储权限,请在 module.json5 中配置

     "requestPermissions": [
          {
            "name": "ohos.permission.CAMERA",
            "reason": "$string:reason",
            "usedScene": {
              "abilities": [
                "EntryAbility"
              ],
              "when": "always"
            }
          },
          {
            "name": "ohos.permission.WRITE_MEDIA",
            "reason": "$string:reason",
            "usedScene": {
              "abilities": [
                "EntryAbility"
              ],
              "when": "always"
            }
          },
          {
            "name": "ohos.permission.READ_MEDIA",
            "reason": "$string:reason",
            "usedScene": {
              "abilities": [
                "EntryAbility"
              ],
              "when": "always"
            }
          }
        ],
    

    并且需要在代码中动态授权,具体可参考官方文档 https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/request-user-authorization-V5

    快速调用示例

    NisCameraPreview 是活体检测自定义组件,需要将它嵌入页面中

    注意点

    • 为了避免在某些中低端机型上检测卡顿,建议 NisCameraPreview 的宽与高不要设置为全屏,过大的预览控件会导致处理的数据过大,降低检测流畅度
    • 预览宽高不要随意设置,请遵守大部分相机支持的预览宽高比,3:4 或 9:16
    • 最好限制竖屏,横屏会影响效果

    示例

    import common from '@ohos.app.ability.common'
    import { ActionType, AliveDetect, Logger, NisCameraPreview } from '@yidun/alive'
    
    @Component
    export default struct Test {
      private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
      @State currentTip: string = "请正视前方"
      @State pic: string = "app.media.pic_front_2x"
      private logger: Logger = new Logger("AliveDetectDemo")
      
      initAlive() {
        AliveDetect.getInstance().init(this.context, "844f553a25034a3ab5f38fcd386305d2")
        AliveDetect.getInstance().setDetectedListener({
          onReady: (isInitSuccess: boolean) => {
            if (isInitSuccess) {
              this.logger.debug("引擎初始化成功")
            }
          },
          onActionCommands: (_: ActionType[] | null): void => {
            this.logger.debug("获取配置成功")
          },
          onStateTipChanged: (actionType: ActionType, stateTip: string, _: number): void => {
            switch (actionType) {
              case ActionType.ACTION_ERROR:
                // 更新错误提示语
                this.currentTip = stateTip
                break;
              case ActionType.ACTION_TURN_HEAD_TO_LEFT:
                this.currentTip = stateTip
                this.pic = "app.media.turn_left"
                // 左转头
                break;
              case ActionType.ACTION_TURN_HEAD_TO_RIGHT:
                this.currentTip = stateTip
                this.pic = "app.media.turn_right"
                // 右转头
                break;
              case ActionType.ACTION_OPEN_MOUTH:
                this.currentTip = stateTip
                this.pic = "app.media.open_mouth"
                // 张张嘴
                break;
              case ActionType.ACTION_BLINK_EYES:
                this.currentTip = stateTip
                this.pic = "app.media.open_eyes"
                // 眨眨眼
                break;
            }
          },
          onPassed: (isPassed: boolean, token: string): void => {
            AlertDialog.show({ title: '活体检测', message: `活体检测结果${isPassed} Token is:${token}` })
          },
          onCheck: (): void => {
    
          },
          onError: (_: number, msg: string): void => {
            // 检测异常
            this.logger.error(msg)
          },
          onOverTime: (): void => {
            // 检测超时
            AlertDialog.show({ title: '活体检测', message: '检测超时了' })
          }
        })
        AliveDetect.getInstance().startDetect()
      }
    
      onPageShow(): void {
         this.initAlive()
      }
      
      build() {
         RelativeContainer() {
          NisCameraPreview({ previewWidth: 1080, previewHeight: 1440 })
            .alignRules({
              top: { anchor: "statusBar", align: VerticalAlign.Bottom },
              middle: { anchor: "__container__", align: HorizontalAlign.Center }
            })
    
          Image($r('app.media.alive_bg'))
            .alt('背景图')
            .width('100%')
            .height('100%')
            .objectFit(ImageFit.Contain)
            .alignRules({
              left: { anchor: '__container__', align: HorizontalAlign.Start },
              top: { anchor: '__container__', align: VerticalAlign.Top }
            })
    
          Image($r(this.pic))
            .alt('动作提示图')
            .width('140vp')
            .height('140vp')
            .interpolation(ImageInterpolation.Medium)
            .objectFit(ImageFit.Contain)
            .margin({
              bottom: '50vp'
            })
            .alignRules({
              bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
              left: { anchor: '__container__', align: HorizontalAlign.Start },
              right: { anchor: '__container__', align: HorizontalAlign.End }
            })
            .onComplete(() => {
              this.logger.info("动作提示图加载完成")
            })
            .id('action')
    
          Text(this.currentTip)
            .fontSize('18fp')
            .fontColor('#ff000000')
            .textAlign(TextAlign.Center)
            .fontWeight(600)
            .margin({
              bottom: '16vp'
            })
            .alignRules({
              bottom: { anchor: 'action', align: VerticalAlign.Top },
              left: { anchor: '__container__', align: HorizontalAlign.Start },
              right: { anchor: '__container__', align: HorizontalAlign.End }
            })
        }
        .height('100%')
        .width('100%')
      }
    }
    

    共享包方法说明

    首先需要在页面中嵌入 NisCameraPreview 组件,NisCameraPreview 的 id 是 NTSCamera,可以根据需要设置它的位置

    build() {
        RelativeContainer() {
            NisCameraPreview({ previewWidth: 1080, previewHeight: 1440 })
            .alignRules({
              top: { anchor: "statusBar", align: VerticalAlign.Bottom },
              middle: { anchor: "__container__", align: HorizontalAlign.Center }
            })
        }
    }
    

    1.活体检测初始化

    代码说明

     AliveDetect.getInstance().init(this.context, "844f553a25034a3ab5f38fcd386305d2")
    
    

    参数说明

    参数 类型 是否必填 默认值 描述
    context common.UIAbilityContext 上下文
    businessId String 活体检测业务 id

    2. 设置回调监听

    代码说明

    代码添加在 init 之后 startDetect 之前调用

    AliveDetect.getInstance().setDetectedListener(listener: DetectedListener)
    
    

    参数说明

    参数 类型 是否必填 默认值 描述
    listener DetectedListener 监听接口

    DetectedListener 接口说明

     interface DetectedListener {
        /**
         * 活体检测引擎初始化时回调
         *
         * @param isInitSuccess 活体检测引擎是否初始化成功:
         *                      1)true,初始化完成可以开始检测
         *                      2)false,初始化失败,可尝试重新启动活体检测流程 {@link AliveDetector#startDetect()}
         */
        onReady: (isInitSuccess: boolean) => void;
    
        /**
         * 此次活体检测下发的待检测动作指令序列,{@link ActionType}
         *
         * @param actionTypes
         */
         onActionCommands: (actionTypes: ActionType[] | null) => void;
    
        /**
         * 活体检测状态是否改变,当引擎检测到状态改变时会回调该接口
         *
         * @param actionType 当前动作类型,枚举值,总共6种类型:
         *     ACTION_STRAIGHT_AHEAD("0", "正视前方"),
         *     ACTION_TURN_HEAD_TO_RIGHT("1", "向右转头"),
         *     ACTION_TURN_HEAD_TO_LEFT("2", "向左转头"),
         *     ACTION_OPEN_MOUTH("3", "张嘴动作"),
         *     ACTION_BLINK_EYES("4", "眨眼动作"),
         *     ACTION_ERROR("5", "动作错误"),
         *     ACTION_PASSED("6", "动作通过")
         *                   
         * @param stateTip   引擎检测到的实时状态
         * @param code 错误码,在ACTION_ERROR时用于国际化使用(1:请移动人脸到摄像头视野中间、2:环境光线暗、3:环境光线过亮、4:图像质量模糊)
         */
        onStateTipChanged: (actionType: ActionType, stateTip: string, code: number) => void;
    
        /**
         * 活体检测是否通过回调
         *
         * @param isPassed 活体检测是否通过,true:通过,false:不通过
         * @param token    此次活体检测返回的易盾token
         */
        onPassed: (isPassed: boolean, token: string) => void;
        /**
         * 活体检测本地检测通过
         * 启动远程检测
         */    
        onCheck: () => void
        /**
         * 活体检测过程中出现错误时回调
         *
         * @param code 错误码
         * -1001:获取配置失败 -1002:打开相机失败 -1003:上传图片失败 -1004:云端检测失败
         * @param msg  出错原因
         */
        onError: (code: number, msg: string) => void;
    
        /**
         * 活体检测过程超时回调
         */
        onOverTime: () => void
    }
    

    3. 开始活体检测

    代码添加在 init 之后调用

    代码说明

    AliveDetect.getInstance().startDetect()
    
    

    4. 停止活体检测

    代码说明

    AliveDetect.getInstance().stopDetect()
    
    
    在线咨询 电话咨询:95163223 免费试用