石国付 發表於 2025-8-22 21:18:00

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

<p>&gt;大家好,我是 V 哥。<br>今天给大家介绍在 HarmonyOS 原生鸿蒙开发中,实现人脸识别功能,这个功能在常用的 APP 开发中上镜率还是很高的,在传统的 Android 或 iOS 开发中,通常我们要借助第三方库来实现,而在鸿蒙原生开发中,天然的集成了这个功能,使用起来也超级方便,接下来听 V 哥细细说来。</p>
<p>在鸿蒙 NEXT 中实现人脸识别功能,可通过 **CoreVision Kit** 的 **FaceDetector** 模块实现。以下是基于 API 12+ 的实现流程及核心代码示例:</p>
<p>---</p>
<p>### **一、开发准备**<br>1. **配置权限**<br>   在 `module.json5` 中添加以下权限:<br>```json<br>   "requestPermissions": [<br>   { "name": "ohos.permission.READ_MEDIA" },       // 读取图片权限<br>   { "name": "ohos.permission.CAMERA" },          // 相机权限<br>   { "name": "ohos.permission.ACCESS_BIOMETRIC" } // 生物认证权限<br>   ]<br>```</p>
<p>2. **导入依赖**<br>   使用鸿蒙 NEXT 的视觉服务接口:<br>```typescript<br>   import { faceDetector } from '@kit.CoreVisionKit';<br>   import image from '@kit.ImageKit'; // 图像处理模块<br>```</p>
<p>---</p>
<p>### **二、核心实现步骤**</p>
<p>****1. 初始化人脸检测器****<br>```typescript<br>// 初始化参数配置<br>let faceDetectConfig: faceDetector.FaceDetectOptions = {<br>maxFaceNum: 5,         // 最大检测人脸数<br>featureLevel: faceDetector.FeatureLevel.TYPE_FULL, // 检测全部特征<br>algorithmMode: faceDetector.AlgorithmMode.TYPE_MODE_ACCURATE // 高精度模式<br>};</p>
<p>// 创建检测器实例<br>let detector = faceDetector.createFaceDetector(faceDetectConfig);<br>```</p>
<p>****2. 获取图像数据****<br>通过相机或相册获取图像,转换为 `PixelMap` 格式:<br>```typescript<br>// 示例:从相册选择图片并转换为 PixelMap<br>async function getImagePixelMap() {<br>let imageSource = image.createImageSource(selectedImageUri);<br>let decodeOptions = {<br>    desiredSize: { width: 1024, height: 1024 } // 调整尺寸优化性能<br>};<br>let pixelMap = await imageSource.createPixelMap(decodeOptions);<br>return pixelMap;<br>}<br>```</p>
<p>****3. 执行人脸检测****<br>```typescript<br>async function detectFaces(pixelMap: image.PixelMap) {<br>try {<br>    // 执行检测<br>    let faces: faceDetector.Face[] = await detector.detect(pixelMap);<br>    // 处理检测结果<br>    faces.forEach((face: faceDetector.Face) =&gt; {<br>      console.log("人脸置信度: " + face.confidence);<br>      console.log("人脸坐标框: " + JSON.stringify(face.rect));<br>      console.log("五官坐标点: " + JSON.stringify(face.landmarks));<br>    });<br>} catch (error) {<br>    console.error("人脸检测失败: " + error.code + ", 信息: " + error.message);<br>}<br>}<br>```</p>
<p>****4. 身份验证集成****</p>
<p>结合生物认证接口验证机主身份:<br>```typescript<br>import userAuth from '@kit.BiometricAuthenticationKit';</p>
<p>// 检查是否支持人脸识别<br>let status = userAuth.getAvailableStatus(<br>userAuth.UserAuthType.FACE, <br>userAuth.AuthTrustLevel.ATL3<br>);<br>if (status.isAvailable) {<br>// 执行人脸认证<br>userAuth.executeAuth(<br>    userAuth.UserAuthType.FACE,<br>    userAuth.AuthTrustLevel.ATL3,<br>    (err, result) =&gt; {<br>      if (result?.result === userAuth.AuthResult.SUCCESS) {<br>      console.log("身份验证通过");<br>      }<br>    }<br>);<br>}<br>```</p>
<p>### **三、以下是一个完整示例代码**</p>
<p>1. Index.ets 主页面(示例代码是使用元服务项目实现)</p>
<p>``` typescript<br>import { authentication } from '@kit.AccountKit';<br>import { BusinessError } from '@kit.BasicServicesKit';<br>import { hilog } from '@kit.PerformanceAnalysisKit';</p>
<p>const DOMAIN = 0x0000;</p>
<p>export const pathStack: NavPathStack = new NavPathStack();</p>
<p>@Entry<br>@ComponentV2<br>struct Index {<br>build() {<br>    Navigation(pathStack) {<br>      Column({ space: 20 }) {<br>      Button("人脸活体检测-示例")<br>          .width("80%")<br>          .borderRadius(10)<br>          .onClick(() =&gt; {<br>            pathStack.pushPath({ name: "live" })<br>          })<br>      }<br>      .height('100%')<br>      .width('100%')<br>    }<br>    .title("元服务")<br>}</p>
<p>aboutToAppear() {<br>    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');<br>    this.loginWithHuaweiID();<br>}</p>
<p>/**<br>   * Sample code for using HUAWEI ID to log in to atomic service.<br>   * According to the Atomic Service Review Guide, when a atomic service has an account system,<br>   * the option to log in with a HUAWEI ID must be provided.<br>   * The following presets the atomic service to use the HUAWEI ID silent login function.<br>   * To enable the atomic service to log in successfully using the HUAWEI ID, please refer<br>   * to the HarmonyOS HUAWEI ID Access Guide to configure the client ID and fingerprint certificate.<br>   */<br>private loginWithHuaweiID() {<br>    // Create a login request and set parameters<br>    const loginRequest = new authentication.HuaweiIDProvider().createLoginWithHuaweiIDRequest();<br>    // Whether to forcibly launch the HUAWEI ID login page when the user is not logged in with the HUAWEI ID<br>    loginRequest.forceLogin = false;<br>    // Execute login request<br>    const controller = new authentication.AuthenticationController();<br>    controller.executeRequest(loginRequest).then((data) =&gt; {<br>      const loginWithHuaweiIDResponse = data as authentication.LoginWithHuaweiIDResponse;<br>      const authCode = loginWithHuaweiIDResponse.data?.authorizationCode;<br>      // Send authCode to the backend in exchange for unionID, session</p>
<p>    }).catch((error: BusinessError) =&gt; {<br>      hilog.error(DOMAIN, 'testTag', 'error: %{public}s', JSON.stringify(error));<br>      if (error.code === authentication.AuthenticationErrorCode.ACCOUNT_NOT_LOGGED_IN) {<br>      // HUAWEI ID is not logged in, it is recommended to jump to the login guide page</p>
<p>      }<br>    });<br>}<br>}<br>```</p>
<p>2. LiveDetectPage.ets 页面<br>```typescript<br>import { common, abilityAccessCtrl, Permissions } from '@kit.AbilityKit';<br>import { interactiveLiveness } from '@kit.VisionKit';<br>import { BusinessError } from '@kit.BasicServicesKit';<br>import { hilog } from '@kit.PerformanceAnalysisKit';<br>@Builder export function buildPage(){<br>    LiveDetectPage()<br>}<br>@Component<br>export struct LiveDetectPage {<br>    private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;<br>    // 权限<br>    private array: Array&lt;Permissions&gt; = ["ohos.permission.CAMERA"];<br>    // 动作个数<br>    @State actionsNum: number = 0;<br>    /**<br>   * 是否是静默模式<br>   * 静默模式(SILENT_MODE):表示静默活体检测模式,暂未支持。<br>   * 交互模式(INTERACTIVE_MODE):表示动作活体检测模式。<br>   */<br>    @State isSilentMode: string = "INTERACTIVE_MODE";<br>    // 验证完的跳转模式<br>    @State routeMode: string = "replace";<br>    // 验证结果<br>    @State resultInfo: interactiveLiveness.InteractiveLivenessResult = {<br>      livenessType: 0<br>    };<br>    // 验证失败结果<br>    @State failResult: Record&lt;string, number | string&gt; = {<br>      "code": 1008302000,<br>      "message": ""<br>    };</p>
<p>    build() {<br>      NavDestination() {<br>            // 层叠布局<br>            Stack({<br>                // 内容对齐方式:顶部对齐<br>                alignContent: Alignment.Top<br>            }) {<br>                // 列容器组件<br>                Column() {<br>                  // 行容器组件<br>                  Row() {<br>                        // 弹性布局:主轴方向为横向,内容对齐方式为起始对齐,垂直方向对齐方式为居中对齐<br>                        Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {<br>                            // 文本显示组件<br>                            Text("验证完的跳转模式:").fontSize(18).width("25%")<br>                            // 弹性布局:主轴方向为横向,内容对齐方式为起始对齐,垂直方向居中对齐<br>                            Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {<br>                              // 行容器组件<br>                              Row() {<br>                                    // 单选框组件<br>                                    Radio({ value: "replace", group: "routeMode" }).checked(true).height(24).width(24)<br>                                        .onChange((isChecked: boolean) =&gt; {<br>                                          this.routeMode = "replace"<br>                                        })<br>                                    Text("replace").fontSize(16)<br>                              }.margin({ right: 15 })</p>
<p>                              Row() {<br>                                    // 单选框组件<br>                                    Radio({ value: "back", group: "routeMode" }).checked(false).height(24).width(24)<br>                                        .onChange((isChecked: boolean) =&gt; {<br>                                          this.routeMode = "back";<br>                                        })<br>                                    Text("back").fontSize(16)<br>                              }<br>                            }.width("75%")<br>                        }<br>                  }.margin({ bottom: 30 })</p>
<p>                  // 行容器组件<br>                  Row() {<br>                        // 弹性布局:主轴方向为横向,内容对齐方式为起始对齐,垂直方向对齐方式为居中对齐<br>                        Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {<br>                            Text("动作数量:").fontSize(18).width("25%")<br>                            TextInput({<br>                              placeholder: this.actionsNum != 0 ? this.actionsNum.toString() : "动作数量为3或4个"<br>                            }).type(InputType.Number).placeholderFont({<br>                              size: 18,<br>                              weight: FontWeight.Normal,<br>                              family: "HarmonyHeiTi",<br>                              style: FontStyle.Normal<br>                            }).fontSize(18).fontWeight(FontWeight.Bold).fontFamily("HarmonyHeiTi")<br>                              .fontStyle(FontStyle.Normal).width("65%").onChange((value: string) =&gt; {<br>                              this.actionsNum = Number(value) as interactiveLiveness.ActionsNumber;<br>                            })<br>                        }<br>                  }<br>                }.margin({ left: 24, top: 80 }).zIndex(1)</p>
<p>                // 层叠布局组件:内容对齐方式为底部对齐<br>                Stack({ alignContent: Alignment.Bottom }) {<br>                  if (this.resultInfo?.mPixelMap) {<br>                        // 如果存在mPixelMap,则显示检测的图片<br>                        Image(this.resultInfo?.mPixelMap).width(260)<br>                            .height(260).align(Alignment.Center)<br>                            .margin({ bottom: 260 })<br>                        // 圆形遮罩<br>                        Circle().width(300).height(300).fillOpacity(0)<br>                            .strokeWidth(60).stroke(Color.White)<br>                            .margin({ bottom: 250, left: 0 })<br>                  }</p>
<p>                  // 判断检测成功还是失败<br>                  Text(this.resultInfo.mPixelMap ? "检测成功" : this.failResult.code != 1008302000 ? "检测失败" : "")<br>                        .width("100%").height(26).fontSize(20).fontColor("#000000").fontFamily("HarmonyHeiTi")<br>                        .margin({ top: 50 }).textAlign(TextAlign.Center).fontWeight("Medium").margin({ bottom: 240 })</p>
<p>                  // 如果检测失败,则显示检测失败的原因<br>                  if(this.failResult.code != 1008302000) {<br>                        Text(this.failResult.message as string)<br>                            .width("100%").height(26)<br>                            .textAlign(TextAlign.Center).fontFamily("HarmonyHeiTi").fontWeight("Medium")<br>                            .margin({ bottom: 200 })<br>                  }</p>
<p>                  // 开始检测的按钮<br>                  Button("开始检测").type(ButtonType.Normal).borderRadius(10)<br>                        .width('60%').height(40).fontSize(16)<br>                        .margin({ bottom: 56 })<br>                        .onClick(() =&gt; {<br>                            this.privateStartDetection();<br>                        })<br>                }.height("100%")<br>            }<br>      }.title('活体检测')<br>      .onWillShow(() =&gt; {<br>            // 释放结果<br>            this.resultRelease();<br>            // 获取检测的结果<br>            this.getDetectionResultInfo();<br>      })<br>    }</p>
<p>    // 跳转到人脸活体检测控件<br>    private privateRouterLibrary() {<br>      // 交互式活体检测配置实例<br>      let routerOptions: interactiveLiveness.InteractiveLivenessConfig = {<br>            // 是否是静默模式<br>            isSilentMode: this.isSilentMode as interactiveLiveness.DetectionMode,<br>            // 路由模式:返回还是替换<br>            routeMode: this.routeMode as interactiveLiveness.RouteRedirectionMode,<br>            // 动作个数<br>            actionsNum: this.actionsNum<br>      }</p>
<p>      // 如果可以使用该能力(活体检测能力)<br>      // syscap配置<br>      if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {<br>            // 开始活体检测<br>            interactiveLiveness.startLivenessDetection(routerOptions).then((DetectState: boolean) =&gt; {<br>                // 如果检测成功,则跳转到下一个页面<br>                hilog.info(0x0001, "LivenessCollectionIndex", `Succeeded in jumping.`);<br>            }).catch((err: BusinessError) =&gt; {<br>                // 如果检测失败,则显示检测失败的原因<br>                hilog.error(0x0001, "LivenessCollectionIndex", `Failed to jump. Code:${err.code},message:${err.message}`);<br>            })<br>      } else {<br>            // 如果不可以使用该能力(活体检测能力),则显示不支持该能力的提示<br>            hilog.error(0x0001, "LivenessCollectionIndex", '当前设备不支持活体检测API');<br>      }<br>    }</p>
<p>    /**<br>   * 返回从用户获取到的权限列表,遍历该列表,如果包含了ohos.permission.CAMERA权限,则调用privateRouterLibrary方法<br>   */<br>    private privateStartDetection() {<br>      abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.context, this.array).then((res) =&gt; {<br>            for (let i = 0; i &lt; res.permissions.length; i++) {<br>                if (res.permissions === "ohos.permission.CAMERA" &amp;&amp; res.authResults === 0) {<br>                  this.privateRouterLibrary();<br>                }<br>            }<br>      }).catch((err: BusinessError) =&gt; {<br>            hilog.error(0x0001, "LivenessCollectionIndex", `Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);<br>      })<br>    }</p>
<p>    /**<br>   * 获取检测的结果<br>   */<br>    private getDetectionResultInfo() {<br>      // getInteractiveLivenessResult接口调用完会释放资源<br>      // 如果可以使用活体检测能力(syscap配置)<br>      if (canIUse("SystemCapability.AI.Component.LivenessDetect")) {<br>            // 获取活体检测结果<br>            let resultInfo = interactiveLiveness.getInteractiveLivenessResult();<br>            resultInfo.then(data =&gt; {<br>                // 获取到结果,则赋值<br>                this.resultInfo = data;<br>            }).catch((err: BusinessError) =&gt; {<br>                // 如果发生了异常,则设置失败结果的字段值<br>                this.failResult = { "code": err.code, "message": err.message }<br>            })<br>      } else {<br>            // 当前设备不支持活体检测能力<br>            hilog.error(0x0001, "LivenessCollectionIndex", '该设备不支持活体检测API。');<br>      }<br>    }</p>
<p>    /**<br>   * 结果重置<br>   */<br>    private resultRelease() {<br>      this.resultInfo = {<br>            livenessType: 0<br>      }<br>      this.failResult = {<br>            "code": 1008302000,<br>            "message": ""<br>      }<br>    }<br>}<br>```</p>
<p>注意,需要在module.json5中配置权限。</p>
<p>### **四、注意事项**<br>1. **真机调试**<br>   需使用支持人脸识别的真机(如 P50 系列)测试,模拟器不支持)。<br>2. **性能优化**<br>    - 降低图像分辨率以提升检测速度。<br>    - 使用 `AlgorithmMode.TYPE_MODE_FAST` 快速模式平衡性能与精度。<br>3. **特征比对**<br>   进阶场景可通过 `FaceComparator` 比对两张人脸相似度。</p>
<p>---</p>
<p>### **五、扩展场景**<br>- **活体检测**:通过 `VisionKit` 监测眨眼、转头动作。<br>- **暗光增强**:结合 `ImageKit` 调整图像亮度/对比度强化识别效果。</p>
<p>通过上述步骤,可快速实现基础人脸识别功能,并根据需求扩展至复杂场景。</p>
<p>---<br>### **六、小结一下**</p>
<p>以上就是在鸿蒙 NEXT 开发中实现人脸识别功能,也称为活体检测,通过鸿蒙 NEXT 提供的能力,可以轻松实现,兄弟们,可以抓紧试试(请自备真机测试)。学习鸿蒙可以关注威哥写的《鸿蒙 HarmonyOS NEXT 开发之路》卷1,卷2已上市,卷3正在加紧印刷中。如果你还没有拿下鸿蒙认证,快到碗里来(https://developer.huawei.com/consumer/cn/training/classDetail/042cb1cc4d7d44ecbdbd902fd1275dcc)。</p>
<p>&nbsp;</p>
<p>![](https://files.mdnice.com/user/57732/2f80e74d-cd64-4f9c-b763-38ddf26899b5.png)</p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:威哥爱编程,转载请注明原文链接:https://www.cnblogs.com/finally-vince/p/19053652</p><br><br>
来源:https://www.cnblogs.com/finally-vince/p/19053652
頁: [1]
查看完整版本: 【鸿蒙开发实战篇】鸿蒙 NEXT开发中轻松实现人脸识别功能