鸿蒙 NEXT开发中轻松实现人脸识别功能

大家好,我是 V 哥。

今天给大家介绍在 HarmonyOS 原生鸿蒙开发中,实现人脸识别功能,这个功能在常用的 APP 开发中上镜率还是很高的,在传统的 Android 或 iOS 开发中,通常我们要借助第三方库来实现,而在鸿蒙原生开发中,天然的集成了这个功能,使用起来也超级方便,接下来听 V 哥细细说来。

在鸿蒙 NEXT 中实现人脸识别功能,可通过 CoreVision KitFaceDetector 模块实现。以下是基于 API 12+ 的实现流程及核心代码示例:


一、开发准备

  1. 配置权限
    module.json5 中添加以下权限:
json 复制代码
   "requestPermissions": [
     { "name": "ohos.permission.READ_MEDIA" },       // 读取图片权限
     { "name": "ohos.permission.CAMERA" },          // 相机权限
     { "name": "ohos.permission.ACCESS_BIOMETRIC" } // 生物认证权限
   ]
  1. 导入依赖
    使用鸿蒙 NEXT 的视觉服务接口:
typescript 复制代码
   import { faceDetector } from '@kit.CoreVisionKit';
   import image from '@kit.ImageKit'; // 图像处理模块

二、核心实现步骤

1. 初始化人脸检测器

typescript 复制代码
// 初始化参数配置
let faceDetectConfig: faceDetector.FaceDetectOptions = {
  maxFaceNum: 5,         // 最大检测人脸数
  featureLevel: faceDetector.FeatureLevel.TYPE_FULL, // 检测全部特征
  algorithmMode: faceDetector.AlgorithmMode.TYPE_MODE_ACCURATE // 高精度模式
};

// 创建检测器实例
let detector = faceDetector.createFaceDetector(faceDetectConfig);

2. 获取图像数据

通过相机或相册获取图像,转换为 PixelMap 格式:

typescript 复制代码
// 示例:从相册选择图片并转换为 PixelMap
async function getImagePixelMap() {
  let imageSource = image.createImageSource(selectedImageUri);
  let decodeOptions = {
    desiredSize: { width: 1024, height: 1024 } // 调整尺寸优化性能
  };
  let pixelMap = await imageSource.createPixelMap(decodeOptions);
  return pixelMap;
}

3. 执行人脸检测

typescript 复制代码
async function detectFaces(pixelMap: image.PixelMap) {
  try {
    // 执行检测
    let faces: faceDetector.Face[] = await detector.detect(pixelMap);
    // 处理检测结果
    faces.forEach((face: faceDetector.Face) => {
      console.log("人脸置信度: " + face.confidence);
      console.log("人脸坐标框: " + JSON.stringify(face.rect));
      console.log("五官坐标点: " + JSON.stringify(face.landmarks));
    });
  } catch (error) {
    console.error("人脸检测失败: " + error.code + ", 信息: " + error.message);
  }
}

4. 身份验证集成

结合生物认证接口验证机主身份:

typescript 复制代码
import userAuth from '@kit.BiometricAuthenticationKit';

// 检查是否支持人脸识别
let status = userAuth.getAvailableStatus(
  userAuth.UserAuthType.FACE, 
  userAuth.AuthTrustLevel.ATL3
);
if (status.isAvailable) {
  // 执行人脸认证
  userAuth.executeAuth(
    userAuth.UserAuthType.FACE,
    userAuth.AuthTrustLevel.ATL3,
    (err, result) => {
      if (result?.result === userAuth.AuthResult.SUCCESS) {
        console.log("身份验证通过");
      }
    }
  );
}

三、以下是一个完整示例代码

  1. Index.ets 主页面(示例代码是使用元服务项目实现)
typescript 复制代码
import { authentication } from '@kit.AccountKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

const DOMAIN = 0x0000;

export const pathStack: NavPathStack = new NavPathStack();

@Entry
@ComponentV2
struct Index {
  build() {
    Navigation(pathStack) {
      Column({ space: 20 }) {
        Button("人脸活体检测-示例")
          .width("80%")
          .borderRadius(10)
          .onClick(() => {
            pathStack.pushPath({ name: "live" })
          })
      }
      .height('100%')
      .width('100%')
    }
    .title("元服务")
  }

  aboutToAppear() {
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
    this.loginWithHuaweiID();
  }

  /**
   * Sample code for using HUAWEI ID to log in to atomic service.
   * According to the Atomic Service Review Guide, when a atomic service has an account system,
   * the option to log in with a HUAWEI ID must be provided.
   * The following presets the atomic service to use the HUAWEI ID silent login function.
   * To enable the atomic service to log in successfully using the HUAWEI ID, please refer
   * to the HarmonyOS HUAWEI ID Access Guide to configure the client ID and fingerprint certificate.
   */
  private loginWithHuaweiID() {
    // Create a login request and set parameters
    const loginRequest = new authentication.HuaweiIDProvider().createLoginWithHuaweiIDRequest();
    // Whether to forcibly launch the HUAWEI ID login page when the user is not logged in with the HUAWEI ID
    loginRequest.forceLogin = false;
    // Execute login request
    const controller = new authentication.AuthenticationController();
    controller.executeRequest(loginRequest).then((data) => {
      const loginWithHuaweiIDResponse = data as authentication.LoginWithHuaweiIDResponse;
      const authCode = loginWithHuaweiIDResponse.data?.authorizationCode;
      // Send authCode to the backend in exchange for unionID, session

    }).catch((error: BusinessError) => {
      hilog.error(DOMAIN, 'testTag', 'error: %{public}s', JSON.stringify(error));
      if (error.code === authentication.AuthenticationErrorCode.ACCOUNT_NOT_LOGGED_IN) {
        // HUAWEI ID is not logged in, it is recommended to jump to the login guide page

      }
    });
  }
}
  1. LiveDetectPage.ets 页面
typescript 复制代码
import { common, abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
import { interactiveLiveness } from '@kit.VisionKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
@Builder export function buildPage(){
    LiveDetectPage()
}
@Component
export struct LiveDetectPage {
    private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
    // 权限
    private array: Array<Permissions> = ["ohos.permission.CAMERA"];
    // 动作个数
    @State actionsNum: number = 0;
    /**
     * 是否是静默模式
     * 静默模式(SILENT_MODE):表示静默活体检测模式,暂未支持。
     * 交互模式(INTERACTIVE_MODE):表示动作活体检测模式。
     */
    @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() {
        NavDestination() {
            // 层叠布局
            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) {
                        // 如果存在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)
                            .textAlign(TextAlign.Center).fontFamily("HarmonyHeiTi").fontWeight("Medium")
                            .margin({ bottom: 200 })
                    }

                    // 开始检测的按钮
                    Button("开始检测").type(ButtonType.Normal).borderRadius(10)
                        .width('60%').height(40).fontSize(16)
                        .margin({ bottom: 56 })
                        .onClick(() => {
                            this.privateStartDetection();
                        })
                }.height("100%")
            }
        }.title('活体检测')
        .onWillShow(() => {
            // 释放结果
            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
        }

        // 如果可以使用该能力(活体检测能力)
        // syscap配置
        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", '当前设备不支持活体检测API');
        }
    }

    /**
     * 返回从用户获取到的权限列表,遍历该列表,如果包含了ohos.permission.CAMERA权限,则调用privateRouterLibrary方法
     */
    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接口调用完会释放资源
        // 如果可以使用活体检测能力(syscap配置)
        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", '该设备不支持活体检测API。');
        }
    }

    /**
     * 结果重置
     */
    private resultRelease() {
        this.resultInfo = {
            livenessType: 0
        }
        this.failResult = {
            "code": 1008302000,
            "message": ""
        }
    }
}

注意,需要在module.json5中配置权限。

四、注意事项

  1. 真机调试
    需使用支持人脸识别的真机(如 P50 系列)测试,模拟器不支持)。
  2. 性能优化
    • 降低图像分辨率以提升检测速度。
    • 使用 AlgorithmMode.TYPE_MODE_FAST 快速模式平衡性能与精度。
  3. 特征比对
    进阶场景可通过 FaceComparator 比对两张人脸相似度。

五、扩展场景

  • 活体检测 :通过 VisionKit 监测眨眼、转头动作。
  • 暗光增强 :结合 ImageKit 调整图像亮度/对比度强化识别效果。

通过上述步骤,可快速实现基础人脸识别功能,并根据需求扩展至复杂场景。


六、小结一下

以上就是在鸿蒙 NEXT 开发中实现人脸识别功能,也称为活体检测,通过鸿蒙 NEXT 提供的能力,可以轻松实现,兄弟们,可以抓紧试试(请自备真机测试)。学习鸿蒙可以关注威哥写的《鸿蒙 HarmonyOS NEXT 开发之路》卷1,卷2已上市,卷3正在加紧印刷中。如果你还没有拿下鸿蒙认证,快到碗里来(https://developer.huawei.com/consumer/cn/training/classDetail/042cb1cc4d7d44ecbdbd902fd1275dcc)。

相关推荐
大雷神3 小时前
鸿蒙中应用框架和应用模型
华为·harmonyos
张风捷特烈8 小时前
鸿蒙纪·Flutter卷#03 | 从配置证书到打包发布
android·flutter·harmonyos
大雷神17 小时前
鸿蒙安卓前端中加载丢帧:ArkWeb分析
harmonyos
小小小小小星1 天前
鸿蒙开发状态管理与工程化关键信息通俗解释及案例汇总
harmonyos
奶糖不太甜1 天前
鸿蒙开发问题之鸿蒙弹窗:方法论与技术探索
harmonyos
鸿蒙先行者1 天前
鸿蒙ArkUI布局与性能优化技术探索
harmonyos·arkui
威哥爱编程1 天前
鸿蒙 NEXT开发中轻松实现人脸识别功能
harmonyos
用户8054707368121 天前
【鸿蒙开发教程】HarmonyOS 实现List 列表
harmonyos