zl程序教程

您现在的位置是:首页 >  其他

当前栏目

如何使用HarmonyOS的面部识别能力

2023-04-18 14:35:31 时间

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

1. 介绍

本篇Codelab涉及两个HarmonyOS子系统(生物特征识别和相机),详情请参考生物特征识别和相机。本篇要介绍的是在人脸识别认证成功后,跳转到模拟相机的页面的实现方案。在这个应用中,我们通过调用相关接口,检查设备是否具有人脸识别的能力、进行人脸识别、打开相机,从而实现相关的功能。

  • 🕮 说明
  • 由于人脸录入不开放给三方应用调用,因此进行人脸识别前需要在手机设置中录入人脸信息。

2. 搭建HarmonyOS环境

安装DevEco Studio,详情请参考DevEco Studio下载。

设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

1.如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。

2.如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。

  • 🕮 说明
  • 人脸识别需要在真机上运行,因此需要提前申请证书和profile文件,详情请参考申请证书和profile。

3. 代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在参考中提供下载方式,接下来我们会用一小节来讲解整个工程的代码结构。

● slice:应用页面

◊ MainAbilitySlice:人脸识别的操作界面,包含校验设备是否支持人脸识别功能,人脸识别,人脸识别结果回显以及人脸识别成功后打开相机的功能。

◊ OpenCameraSlice:模拟相机的操作页面,包含打卡相机,拍照,存储相片以及切换摄像头的功能。

● util:工具类

◊ FaceAuthResult:人脸认证结果的返回码对应的常量。

◊ LogUtils:日志记录工具类。

◊ PermissionBridge:权限申请回调。

● resources:存放工程使用到的资源文件。

◊ resourcesaselayout下存放xml布局文件;

◊ resourcesasemedia下存放图片资源。

● config.json:工程相关配置文件。

如何使用HarmonyOS面部识别能力-鸿蒙HarmonyOS技术社区

4. 页面布局

人脸识别页面

本页面主要由DirectionalLayout布局和Button、Text组件共同来构成。其中两个Button组件,作用分别为开始人脸识别和取消人脸识别;两个Text组件,作用分别为显示标题和显示返回的人脸识别结果。在resourceslayoutability_main.xml下有如下代码:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <DirectionalLayout  
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"  
  4.     ohos:height="match_parent"  
  5.     ohos:width="match_parent"  
  6.     ohos:orientation="vertical">  
  7.   
  8.     <Text  
  9.         ohos:id="$+id:text_helloworld1"  
  10.         ohos:height="match_content"  
  11.         ohos:width="match_content"  
  12.         ohos:background_element="$graphic:background_ability_main"  
  13.         ohos:layout_alignment="horizontal_center"  
  14.         ohos:left_padding="80vp"  
  15.         ohos:right_padding="80vp"  
  16.         ohos:text="生物特征识别"  
  17.         ohos:text_size="30fp"  
  18.         ohos:top_padding="100vp"  
  19.         />  
  20.   
  21.     <Text  
  22.         ohos:id="$+id:text_status"  
  23.         ohos:height="100vp"  
  24.         ohos:width="match_parent"  
  25.         ohos:background_element="$graphic:background_ability_main"  
  26.         ohos:layout_alignment="center"  
  27.         ohos:text_alignment="center"  
  28.         ohos:max_text_lines="3"  
  29.         ohos:multiple_lines="true"  
  30.         ohos:margin="5vp"  
  31.         ohos:text=""  
  32.         ohos:text_font="serif"  
  33.         ohos:text_size="30fp"  
  34.         ohos:top_padding="5vp"  
  35.         ohos:visibility="invisible"  
  36.         />  
  37.   
  38.     <Button  
  39.         ohos:id="$+id:button_start"  
  40.         ohos:height="60vp"  
  41.         ohos:width="match_parent"  
  42.         ohos:align_parent_bottom="true"  
  43.         ohos:background_element="$graphic:button_element"  
  44.         ohos:layout_alignment="horizontal_center"  
  45.         ohos:left_padding="40vp"  
  46.         ohos:right_padding="40vp"  
  47.         ohos:text="开始人脸识别"  
  48.         ohos:text_color="#000000"  
  49.         ohos:text_size="30fp"  
  50.         ohos:left_margin="15vp"  
  51.         ohos:right_margin="15vp"  
  52.         ohos:top_margin="200vp"  
  53.        />  
  54.   
  55.     <Button  
  56.         ohos:id="$+id:button_cancel"  
  57.         ohos:height="60vp"  
  58.         ohos:width="match_parent"  
  59.         ohos:align_parent_bottom="true"  
  60.         ohos:background_element="$graphic:button_element"  
  61.         ohos:layout_alignment="horizontal_center"  
  62.         ohos:left_padding="40vp"  
  63.         ohos:right_padding="40vp"  
  64.         ohos:text="取消人脸识别"  
  65.         ohos:margin="15vp"  
  66.         ohos:text_color="#000000"  
  67.         ohos:text_size="30fp"  
  68.         />  
  69. </DirectionalLayout> 

模拟相机页面

此页面主要由DirectionalLayout、DependentLayout布局和Image组件组成,其中三个Image组件作为图标,左右分别为返回、开始拍照和切换摄像头。在resourceslayoutability_open_camera.xml下有如下代码:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"  
  3.                    ohos:height="match_parent"  
  4.                    ohos:width="match_parent">  
  5.   
  6.     <DependentLayout  
  7.             ohos:id="$+id:root_container"  
  8.             ohos:height="match_parent"  
  9.             ohos:width="match_parent">  
  10.   
  11.         <DirectionalLayout  
  12.                 ohos:id="$+id:surface_container"  
  13.                 ohos:height="match_parent"  
  14.                 ohos:width="match_parent" />  
  15.         <DirectionalLayout  
  16.                 ohos:width="match_parent"  
  17.                 ohos:height="match_content"  
  18.                 ohos:align_parent_bottom="$+id:root_container"  
  19.                 ohos:bottom_margin="30vp"  
  20.                 ohos:orientation="horizontal">  
  21.   
  22.             <Image  
  23.                     ohos:id="$+id:exit"  
  24.                     ohos:height="match_content"  
  25.                     ohos:width="match_parent"  
  26.                     ohos:weight="1"  
  27.                     ohos:enabled="false"  
  28.                     ohos:layout_alignment="vertical_center"  
  29.                     ohos:scale_mode="center"  
  30.                     ohos:image_src="$media:ic_camera_back" />  
  31.   
  32.             <Image  
  33.                     ohos:id="$+id:tack_picture_btn"  
  34.                     ohos:height="match_content"  
  35.                     ohos:width="match_parent"  
  36.                     ohos:weight="1"  
  37.                     ohos:enabled="false"  
  38.                     ohos:layout_alignment="vertical_center"  
  39.                     ohos:scale_mode="center"  
  40.                     ohos:image_src="$media:ic_camera_photo" />  
  41.   
  42.             <Image  
  43.                     ohos:id="$+id:switch_camera_btn"  
  44.                     ohos:height="match_content"  
  45.                     ohos:width="match_parent"  
  46.                     ohos:weight="1"  
  47.                     ohos:enabled="false"  
  48.                     ohos:layout_alignment="vertical_center"  
  49.                     ohos:scale_mode="center"  
  50.                     ohos:image_src="$media:ic_camera_switch" />  
  51.         </DirectionalLayout>  
  52.     </DependentLayout>  
  53. </DirectionalLayout> 
  •  🕮 说明
  • 布局文件中使用到的background_element样式,在entrysrcmain esourcesasegraphic下有做定义,详情可以参考完整代码。

5. 相关权限

为了保证应用的成功运行,需要在config.json中声明需要如下权限:

  1. "reqPermissions": [  
  2.       {  
  3.         "name""ohos.permission.ACCESS_BIOMETRIC"  
  4.       },  
  5.       {  
  6.         "name""ohos.permission.CAMERA"  
  7.       },  
  8.       {  
  9.         "name""ohos.permission.WRITE_USER_STORAGE"  
  10.       }  
  11.     ] 

此外还需要在OpenCamera的onStart()方法中向用户申请权限,代码示例如下:

  1. private void requestPermission() {  
  2.     String[] permissions = {  
  3.         // 存储权限  
  4.         SystemPermission.WRITE_USER_STORAGE,  
  5.         // 相机权限  
  6.         SystemPermission.CAMERA  
  7.     };  
  8.     List<String> permissionFiltereds = Arrays.stream(permissions)  
  9.             .filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED)  
  10.             .collect(Collectors.toList());  
  11.     if (permissionFiltereds.isEmpty()) {  
  12.         PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED);  
  13.         return;  
  14.     }  
  15.     requestPermissionsFromUser(permissionFiltereds.toArray(new String[permissionFiltereds.size()]),  
  16.             PERMISSION_REQUEST_CODE);  

6. 人脸识别业务逻辑

在人脸识别页面(ability_main.xml)中,我们添加了开始人脸识别和取消人脸识别的Button,通过监听不同Button的点击事件,从而实现不同的业务逻辑。下面我们将分别介绍开始人脸识别和取消人脸识别的业务逻辑。

开始人脸识别业务逻辑

在开始人脸识别之前,我们需要校验当前设备(手机)是否具备人脸识别能力,代码示例如下:

  1. private void createStartListener() {  
  2.     // 提示用户人脸识别时将人脸对准摄像头  
  3.     getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET, true);  
  4.     try {  
  5.         // 创建生物识别对象  
  6.         mBiometricAuthentication =   
  7.             BiometricAuthentication.getInstance(MainAbility.getMainAbility());  
  8.         // 检验设备是否有人脸识别功能  
  9.         int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability(  
  10.             BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,  
  11.             BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true);  
  12.         if (hasAuth == BiometricAuthentication.BA_CHECK_SUPPORTED) {  
  13.             // 如果支持人脸识别,则开启线程进行人脸识别               
  14.             ThreadPoolExecutor pool = new ThreadPoolExecutor(  
  15.                 POOL_CORE_SIZE, POOL_MAX_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,  
  16.                 new LinkedBlockingQueue<>(QUEUE_SIZE), new   
  17.                 ThreadPoolExecutor.DiscardOldestPolicy());  
  18.             pool.submit(runnable);  
  19.         } else {  
  20.             // 人脸识别不支持或存在其他问题 ,直接在页面显示结果,  
  21.             // 在主线程不需要通过EventHandler发送回显任务  
  22.             int retExcAuth = getRetExcAuth(hasAuth);  
  23.             getAndSetText(ResourceTable.Id_text_status, retExcAuth, true);  
  24.         }  
  25.     } catch (IllegalAccessException e) {  
  26.         LogUtils.error("createStartBtn""IllegalAccessException when start auth");  
  27.     }  
  •  🕮 说明
  • ● checkAuthenticationAvailability方法参数说明:
  • 1.BiometricAuthentication.AuthType中有三个类别,分别为
  • AUTH_TYPE_BIOMETRIC_FINGERPRINT_ONLY指纹识别,AUTH_TYPE_BIOMETRIC_FACE_ONLY脸部识别以及AUTH_TYPE_BIOMETRIC_ALL指纹和面部。
  • ● BiometricAuthentication.SecureLevel验证级别,3D人脸识别支持S3及以下级别的验证;2D人脸识别支持S2及以下级别的验证

由于人脸识别是耗时操作,所以这里新起了线程去做认证,代码示例如下:

  1. /**  
  2.  * 新建线程进行认证,避免阻塞其他任务  
  3.  */  
  4. private Runnable runnable = new Runnable() {  
  5.     private void initHandler() {  
  6.         runner = EventRunner.getMainEventRunner();  
  7.         if (runner == null) {  
  8.             return;  
  9.         }  
  10.         myEventHandle = new MyEventHandle(runner);  
  11.     }  
  12.   
  13.     @Override  
  14.     public void run() {  
  15.         // 初始化myEventHandle  
  16.         initHandler();  
  17.         // 开始认证  
  18.         startAuth();  
  19.     }  
  20. }; 

开始人脸识别,代码示例如下:

  1. private void startAuth() {  
  2.     // retExcAuth 0认证成功 1:比对失败 2:取消认证 3:认证超时 4:打开相机失败  
  3.     // 5:busy,可能上一个认证没有结束 6:入参错误 7:人脸认证锁定(达到错误认证次数了)  
  4.     // 8:没有录入人脸 100:其他错误。  
  5.     int retExcAuth = mBiometricAuthentication.execAuthenticationAction(  
  6.             BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,  
  7.             BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2,  
  8.             truefalsenull);  
  9.     // 将认证结果发给主线程处理  
  10.     myEventHandler.sendEvent(retExcAuth);  

由于我们在线程中执行的人脸识别操作,需要通过EventHandler将识别结果发送到主线程中,并将识别结果显示在页面中,代码示例如下:

  1. /**  
  2.  * 事件分发器  
  3.   */  
  4. private class MyEventHandle extends EventHandler {  
  5.     MyEventHandle(EventRunner runner) throws IllegalArgumentException {  
  6.         super(runner);  
  7.     }  
  8.   
  9.     @Override  
  10.     protected void processEvent(InnerEvent event) {  
  11.         super.processEvent(event);  
  12.         int eventId = event.eventId;  
  13.         getAndSetText(ResourceTable.Id_text_status, eventId, true);  
  14.     }  

取消人脸识别

点击取消人脸识别Button,触发取消人脸识别操作,代码示例如下:

  1. private void createCancelBtn() {  
  2.     // 创建点击事件  
  3.     Component component = findComponentById(ResourceTable.Id_button_cancel);  
  4.     // 创建按钮  
  5.     Button cancelBtn = null;  
  6.     if (component != null && component instanceof Button) {  
  7.         cancelBtn = (Button) component;  
  8.         cancelBtn.setClickedListener(view -> {  
  9.             if (mBiometricAuthentication != null) {  
  10.                 // 调用取消接口  
  11.                 int result = mBiometricAuthentication.cancelAuthenticationAction();  
  12.                 LogUtils.info("createCancelBtn:", result + "");  
  13.             }  
  14.         });  
  15.     }  

页面跳转

人脸识别成功后,跳转到模拟相机页面,代码示例如下:

  1. private void toAuthAfterPage() {  
  2.     Intent secondIntent = new Intent();  
  3.     // 指定待启动FA的bundleName和abilityName  
  4.     Operation operation = new Intent.OperationBuilder()  
  5.             .withDeviceId("")  
  6.             .withBundleName(getBundleName())  
  7.             .withAbilityName(OpenCamera.class.getName())  
  8.             .build();  
  9.     secondIntent.setOperation(operation);  
  10.     // startAbility接口实现启动另一个页面  
  11.     startAbility(secondIntent);  

7. 相机相关业务逻辑

在模拟相机页面(ability_open_camera.xml)中,包含打开相机和切换前后置摄像头的功能,我们下面将逐一介绍。

初始化SurfaceProvider

用户授权后,开始初始化SurfaceProvider,代码示例如下:

  1. private void initSurface() {  
  2.     surfaceProvider = new SurfaceProvider(this);  
  3.     DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig(  
  4.             ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT);  
  5.     surfaceProvider.setLayoutConfig(params);  
  6.     surfaceProvider.pinToZTop(false);  
  7.     // 添加SurfaceCallBack回调  
  8.     surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack());  
  9.     // 将SurfaceProvider加入到布局中  
  10.     Component component = findComponentById(ResourceTable.Id_surface_container);  
  11.     if (component instanceof ComponentContainer) {  
  12.         ((ComponentContainer) component).addComponent(surfaceProvider);  
  13.     }  
  14. }    

实现SurfaceOps.Callback回调,当Surface创建时,执行打开相机的操作,代码示例如下:

  1. /**  
  2.  * SurfaceCallBack,Surface回调 
  3.  */  
  4. class SurfaceCallBack implements SurfaceOps.Callback {  
  5.     @Override  
  6.     public void surfaceCreated(SurfaceOps callbackSurfaceOps) {  
  7.         if (callbackSurfaceOps != null) {  
  8.             callbackSurfaceOps.setFixedSize(SCREEN_HEIGHT, SCREEN_WIDTH);  
  9.         }  
  10.         openCamera();  
  11.     }  
  12.   
  13.     @Override  
  14.     public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {  
  15.     }  
  16.   
  17.     @Override  
  18.     public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {  
  19.     }  
  20. }    

打开相机

创建surface后触发surfaceCreated回调,执行打开相机的操作。打开相机并添加相片接收的监听,代码示例如下:

  1. private void openCamera() {  
  2.     CameraKit cameraKit = CameraKit.getInstance(getApplicationContext());  
  3.     String[] cameraLists = cameraKit.getCameraIds();  
  4.     String cameraId = cameraLists.length > 1 && isCameraRear ? cameraLists[1] : cameraLists[0];  
  5.     CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl();  
  6.     cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler);  
  7. }  
  8. /**  
  9.  * CameraStateCallbackImpl 相机状态回调  
  10.  */  
  11. class CameraStateCallbackImpl extends CameraStateCallback {  
  12.     CameraStateCallbackImpl() {  
  13.     }  
  14.   
  15.     @Override  
  16.     public void onCreated(Camera camera) {  
  17.         // 获取预览  
  18.         previewSurface = surfaceProvider.getSurfaceOps().get().getSurface();  
  19.         if (previewSurface == null) {  
  20.             LogUtils.error(TAG, "create camera filed, preview surface is null");  
  21.             return;  
  22.         }  
  23.         // Wait until the preview surface is created.  
  24.         try {  
  25.             Thread.sleep(SLEEP_TIME);  
  26.         } catch (InterruptedException exception) {  
  27.             LogUtils.warn(TAG, "Waiting to be interrupted");  
  28.         }  
  29.         CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder();  
  30.         // 配置预览  
  31.         cameraConfigBuilder.addSurface(previewSurface);     
  32.         camera.configure(cameraConfigBuilder.build());  
  33.         cameraDevice = camera;  
  34.         enableImageGroup();  
  35.     }  
  36.   
  37.     @Override  
  38.     public void onConfigured(Camera camera) {  
  39.         FrameConfig.Builder framePreviewConfigBuilder  
  40.                 = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);  
  41.         framePreviewConfigBuilder.addSurface(previewSurface);  
  42.         // 开启循环捕捉  
  43.         camera.triggerLoopingCapture(framePreviewConfigBuilder.build());  
  44.     }  
  45.   
  46.     private void enableImageGroup() {  
  47.         if (!exitImage.isEnabled()) {  
  48.             exitImage.setEnabled(true);  
  49.             switchCameraImage.setEnabled(true);  
  50.         }  
  51.     }  
  52. }    

切换前后置摄像头

点击切换摄像头图标后,执行切换前后置摄像头操作,代码示例如下:

  1. private void switchClicked() {  
  2.     isCameraRear = !isCameraRear;  
  3.     openCamera();  
  4. }    

8. 效果展示

人脸识别FA(MainAbilitySlice)完成了检验设备是否支持人脸识别,人脸识别,人脸识别结果显示,成功后跳转到打开相机的FA(OpenCameraSlice);相机FA实现了相机的打开,拍照,相片存储,摄像头切换的功能。具体效果图如下:

人脸识别初始页面:

如何使用HarmonyOS面部识别能力-鸿蒙HarmonyOS技术社区

人脸识别结果显示:

如何使用HarmonyOS面部识别能力-鸿蒙HarmonyOS技术社区

相机页面:

如何使用HarmonyOS面部识别能力-鸿蒙HarmonyOS技术社区

9. 完整代码示例

编写布局与样式

1.base/graphic/background_ability_main.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <shape xmlns:ohos="http://schemas.huawei.com/res/ohos"  
  3.        ohos:shape="rectangle">  
  4.     <solid  
  5.         ohos:color="#FFFFFF"/>  
  6. </shape> 

2.base/graphic/button_element.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <shape  
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"  
  4.     ohos:shape="rectangle">  
  5.     <corners  
  6.         ohos:radius="8vp"/>  
  7.     <solid  
  8.         ohos:color="#FF007DFE"/>  
  9. </shape> 

3.base/layout/ability_main.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <DirectionalLayout  
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"  
  4.     ohos:height="match_parent"  
  5.     ohos:width="match_parent"  
  6.     ohos:orientation="vertical">  
  7.   
  8.     <Text  
  9.         ohos:id="$+id:text_helloworld1"  
  10.         ohos:height="match_content"  
  11.         ohos:width="match_content"  
  12.         ohos:background_element="$graphic:background_ability_main"  
  13.         ohos:layout_alignment="horizontal_center"  
  14.         ohos:left_padding="80vp"  
  15.         ohos:right_padding="80vp"  
  16.         ohos:text="生物特征识别"  
  17.         ohos:text_size="30fp"  
  18.         ohos:top_padding="100vp"  
  19.         />  
  20.   
  21.     <Text  
  22.         ohos:id="$+id:text_status"  
  23.         ohos:height="100vp"  
  24.         ohos:width="match_parent"  
  25.         ohos:background_element="$graphic:background_ability_main"  
  26.         ohos:layout_alignment="center"  
  27.         ohos:text_alignment="center"  
  28.         ohos:max_text_lines="3"  
  29.         ohos:multiple_lines="true"  
  30.         ohos:margin="5vp"  
  31.         ohos:text=""  
  32.         ohos:text_font="serif"  
  33.         ohos:text_size="30fp"  
  34.         ohos:top_padding="5vp"  
  35.         ohos:visibility="invisible"  
  36.         />  
  37.   
  38.     <Button  
  39.         ohos:id="$+id:button_start"  
  40.         ohos:height="60vp"  
  41.         ohos:width="match_parent"  
  42.         ohos:align_parent_bottom="true"  
  43.         ohos:background_element="$graphic:button_element"  
  44.         ohos:layout_alignment="horizontal_center"  
  45.         ohos:left_padding="40vp"  
  46.         ohos:right_padding="40vp"  
  47.         ohos:text="开始人脸识别"  
  48.         ohos:text_color="#000000"  
  49.         ohos:text_size="30fp"  
  50.         ohos:left_margin="15vp"  
  51.         ohos:right_margin="15vp"  
  52.         ohos:top_margin="200vp"  
  53.        />  
  54.   
  55.     <Button  
  56.         ohos:id="$+id:button_cancel"  
  57.         ohos:height="60vp"  
  58.         ohos:width="match_parent"  
  59.         ohos:align_parent_bottom="true"  
  60.         ohos:background_element="$graphic:button_element"  
  61.         ohos:layout_alignment="horizontal_center"  
  62.         ohos:left_padding="40vp"  
  63.         ohos:right_padding="40vp"  
  64.         ohos:text="取消人脸识别"  
  65.         ohos:margin="15vp"  
  66.         ohos:text_color="#000000"  
  67.         ohos:text_size="30fp"  
  68.         />  
  69. </DirectionalLayout> 

4.base/layout/ability_open_camera.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"  
  3.                    ohos:height="match_parent"  
  4.                    ohos:width="match_parent">  
  5.   
  6.     <DependentLayout  
  7.         ohos:id="$+id:root_container"  
  8.         ohos:height="match_parent"  
  9.         ohos:width="match_parent">  
  10.   
  11.         <DirectionalLayout  
  12.             ohos:id="$+id:surface_container"  
  13.             ohos:height="match_parent"  
  14.             ohos:width="match_parent" />  
  15.         <DirectionalLayout  
  16.             ohos:width="match_parent"  
  17.             ohos:height="match_content"  
  18.             ohos:align_parent_bottom="$+id:root_container"  
  19.             ohos:bottom_margin="30vp"  
  20.             ohos:orientation="horizontal">  
  21.   
  22.             <Image  
  23.                 ohos:id="$+id:exit"  
  24.                 ohos:height="170px"  
  25.                 ohos:width="match_parent"  
  26.                 ohos:weight="1"  
  27.                 ohos:enabled="false"  
  28.                 ohos:layout_alignment="vertical_center"  
  29.                 ohos:scale_mode="center"  
  30.                 ohos:image_src="$media:ic_camera_back" />  
  31.   
  32.             <Image  
  33.                 ohos:id="$+id:tack_picture_btn"  
  34.                 ohos:height="170px"  
  35.                 ohos:width="match_parent"  
  36.                 ohos:weight="1"  
  37.                 ohos:enabled="false"  
  38.                 ohos:layout_alignment="vertical_center"  
  39.                 ohos:scale_mode="center"  
  40.                 ohos:image_src="$media:ic_camera_photo" />  
  41.   
  42.             <Image  
  43.                 ohos:id="$+id:switch_camera_btn"  
  44.                 ohos:height="170px"  
  45.                 ohos:width="match_parent"  
  46.                 ohos:weight="1"  
  47.                 ohos:enabled="false"  
  48.                 ohos:layout_alignment="vertical_center"  
  49.                 ohos:scale_mode="zoom_center"  
  50.                 ohos:image_src="$media:ic_camera_switch" />  
  51.         </DirectionalLayout>  
  52.     </DependentLayout>  
  53. </DirectionalLayout> 

功能逻辑代码

1.com/huawei/cookbook/slice/MainAbilitySlice.java

  1. package com.huawei.cookbook.slice;  
  2.   
  3. import com.huawei.cookbook.MainAbility;  
  4. import com.huawei.cookbook.OpenCamera;  
  5. import com.huawei.cookbook.ResourceTable;  
  6. import com.huawei.cookbook.util.FaceAuthResult;  
  7. import com.huawei.cookbook.util.LogUtils;  
  8.   
  9. import ohos.aafwk.ability.AbilitySlice;  
  10. import ohos.aafwk.content.Intent;  
  11. import ohos.aafwk.content.Operation;  
  12. import ohos.agp.components.Button;  
  13. import ohos.agp.components.Component;  
  14. import ohos.agp.components.Text;  
  15. import ohos.agp.utils.Color;  
  16. import ohos.biometrics.authentication.BiometricAuthentication;  
  17. import ohos.eventhandler.EventHandler;  
  18. import ohos.eventhandler.EventRunner;  
  19. import ohos.eventhandler.InnerEvent;  
  20.   
  21. import java.util.concurrent.LinkedBlockingQueue;  
  22. import java.util.concurrent.ThreadPoolExecutor;  
  23. import java.util.concurrent.TimeUnit;  
  24.   
  25. /**  
  26.  * MainAbilitySlice  
  27.  *  
  28.  * @since 2021-04-12  
  29.  */  
  30. public class MainAbilitySlice extends AbilitySlice {  
  31.     private static final int POOL_CORE_SIZE = 2;  
  32.     private static final int POOL_MAX_SIZE = 5;  
  33.     private static final int NO_FACE_RET = -1;  
  34.     private static final int KEEP_ALIVE_TIME = 3;  
  35.     private static final int QUEUE_SIZE = 6;  
  36.     private static final int RET_NOT_SUPPORTED = 1;  
  37.     private static final int RET_SAFE_LEVEL_NOT_SUPPORTED = 2;  
  38.     private static final int RET_NOT_LOCAL = 3;  
  39.     private EventRunner runner;  
  40.     private MyEventHandle myEventHandle;  
  41.     private BiometricAuthentication mBiometricAuthentication;  
  42.     /**  
  43.      * 新建线程进行认证,避免阻塞其他任务  
  44.      */  
  45.     private Runnable runnable = new Runnable() {  
  46.         private void initHandler() {  
  47.             runner = EventRunner.getMainEventRunner();  
  48.             if (runner == null) {  
  49.                 return;  
  50.             }  
  51.             myEventHandle = new MyEventHandle(runner);  
  52.         }  
  53.   
  54.         @Override  
  55.         public void run() {  
  56.             // 初始化myEventHandle  
  57.             initHandler();  
  58.             // 开始认证  
  59.             startAuth();  
  60.         }  
  61.     };  
  62.   
  63.     /**  
  64.      * onStart  
  65.      *  
  66.      * @param intent intent  
  67.      */  
  68.     @Override  
  69.     public void onStart(Intent intent) {  
  70.         super.onStart(intent);  
  71.         super.setUIContent(ResourceTable.Layout_ability_main);  
  72.         // 创建开始认证按钮,并添加点击事件  
  73.         createStartBtn();  
  74.         // 创建取消认证按钮,并添加点击事件  
  75.         createCancelBtn();  
  76.     }  
  77.   
  78.     /**  
  79.      * 创建取消按钮  
  80.      */  
  81.     private void createCancelBtn() {  
  82.         // 创建点击事件  
  83.         Component component = findComponentById(ResourceTable.Id_button_cancel);  
  84.         // 创建按钮  
  85.         Button cancelBtn = null;  
  86.         if (component != null && component instanceof Button) {  
  87.             cancelBtn = (Button) component;  
  88.             cancelBtn.setClickedListener(view -> {  
  89.                 if (mBiometricAuthentication != null) {  
  90.                     // 调用取消接口  
  91.                     int result = mBiometricAuthentication.cancelAuthenticationAction();  
  92.                     LogUtils.info("createCancelBtn:", result + "");  
  93.                 }  
  94.             });  
  95.         }  
  96.     }  
  97.   
  98.     /**  
  99.      * 创建开始识别的按钮点击事件  
  100.      */  
  101.     private void createStartBtn() {  
  102.         // 创建点击事件  
  103.         Component component = findComponentById(ResourceTable.Id_button_start);  
  104.         // 创建按钮  
  105.         Button featureBtn = null;  
  106.         if (component != null && component instanceof Button) {  
  107.             featureBtn = (Button) component;  
  108.             featureBtn.setClickedListener(view -> {  
  109.                 createStartListener();  
  110.             });  
  111.         }  
  112.     }  
  113.   
  114.     private void createStartListener() {  
  115.         // 提示用户人脸识别时将人脸对准摄像头  
  116.         getAndSetText(ResourceTable.Id_text_status, NO_FACE_RET, true);  
  117.         try {  
  118.             // 创建生物识别对象  
  119.             mBiometricAuthentication = BiometricAuthentication.getInstance(MainAbility.getMainAbility());  
  120.             // 检验设备是否有人脸识别功能  
  121.             // BiometricAuthentication.AuthType中有三个类别  
  122.             // 分别为AUTH_TYPE_BIOMETRIC_FINGERPRINT_ONLY指纹识别  
  123.             // AUTH_TYPE_BIOMETRIC_FACE_ONLY脸部识别  
  124.             // AUTH_TYPE_BIOMETRIC_ALL指纹和面部  
  125.             // BiometricAuthentication.SecureLevel 2D人脸识别建议使用SECURE_LEVEL_S2,3D人脸识别建议使用SECURE_LEVEL_S3  
  126.             int hasAuth = mBiometricAuthentication.checkAuthenticationAvailability(  
  127.                     BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,  
  128.                     BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2, true);  
  129.   
  130.             // hasAuth 0是支持,1是不支持,2安全级别不支持 3不是本地认证 4无人脸录入  
  131.             if (hasAuth == 0) {  
  132.                 ThreadPoolExecutor pool = new ThreadPoolExecutor(  
  133.                         POOL_CORE_SIZE, POOL_MAX_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,  
  134.                         new LinkedBlockingQueue<>(QUEUE_SIZE), new ThreadPoolExecutor.DiscardOldestPolicy());  
  135.                 pool.submit(runnable);  
  136.             } else {  
  137.                 // 人脸识别不支持或存在其他问题 ,直接回显页面,  
  138.                 // 在主线程不需要通过EventHandler发送回显任务  
  139.                 int retExcAuth = getRetExcAuth(hasAuth);  
  140.                 getAndSetText(ResourceTable.Id_text_status, retExcAuth, true);  
  141.             }  
  142.         } catch (IllegalAccessException e) {  
  143.             LogUtils.error("createStartBtn""IllegalAccessException when start auth");  
  144.         }  
  145.     }  
  146.   
  147.     /**  
  148.      * 开始认证  
  149.      */  
  150.     private void startAuth() {  
  151.         // retExcAuth 0认证成功 1:比对失败 2:取消认证 3认证超时 4:打开相机失败  
  152.         // 5:busy,可能上一个认证没有结束 6:入参错误 7:人脸认证锁定(达到错误认证次数了)  
  153.         // 8:没有录入人脸 100:其他错误。  
  154.         int retExcAuth = mBiometricAuthentication.execAuthenticationAction(  
  155.                 BiometricAuthentication.AuthType.AUTH_TYPE_BIOMETRIC_FACE_ONLY,  
  156.                 BiometricAuthentication.SecureLevel.SECURE_LEVEL_S2,  
  157.                 truefalsenull);  
  158.         // 将修改页面发送到主线程执行  
  159.         myEventHandle.sendEvent(retExcAuth);  
  160.     }  
  161.   
  162.     /**  
  163.      * 根据检验是否支持认证返回值获取提示code  
  164.      *  
  165.      * @param hasAuth 是否有认证能力  
  166.      * @return 返回认证码  
  167.      */  
  168.     private int getRetExcAuth(int hasAuth) {  
  169.         int retExcAuth;  
  170.         if (hasAuth == RET_NOT_SUPPORTED) {  
  171.             // 1是不支持2D人脸识别  
  172.             retExcAuth = FaceAuthResult.AUTH_2D_NOT_SUPPORTED;  
  173.         } else if (hasAuth == RET_SAFE_LEVEL_NOT_SUPPORTED) {  
  174.             // 安全级别不支持  
  175.             retExcAuth = FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED;  
  176.         } else if (hasAuth == RET_NOT_LOCAL) {  
  177.             // 是不是本地认证  
  178.             retExcAuth = FaceAuthResult.AUTH_NOT_LOCAL;  
  179.         } else {  
  180.             // 无人脸录入  
  181.             retExcAuth = FaceAuthResult.AUTH_NO_FACE;  
  182.         }  
  183.         return retExcAuth;  
  184.     }  
  185.   
  186.     /**  
  187.      * 获取并设置text  
  188.      *  
  189.      * @param textId 文本框id  
  190.      * @param retExcAuth 认证返回码  
  191.      * @param isVisible 是否显示  
  192.      */  
  193.     private void getAndSetText(int textId, int retExcAuth, boolean isVisible) {  
  194.         // 获取状态Text  
  195.         Component componentText = findComponentById(textId);  
  196.         if (componentText != null && componentText instanceof Text) {  
  197.             Text text = (Text) componentText;  
  198.             setTextValueAndColor(retExcAuth, text);  
  199.             if (isVisible) {  
  200.                 text.setVisibility(Component.VISIBLE);  
  201.             }  
  202.         }  
  203.     }  
  204.   
  205.     /**  
  206.      * 设置文本提示信息  
  207.      *  
  208.      * @param text 文本对象  
  209.      * @param textValue 文本值  
  210.      * @param color 文本颜色  
  211.      */  
  212.     private void setTextValueAndColor(Text text, String textValue, Color color) {  
  213.         text.setText(textValue);  
  214.         text.setTextColor(color);  
  215.     }  
  216.   
  217.     /**  
  218.      * 设置文本显示值和文本颜色  
  219.      *  
  220.      * @param retExcAuth 认证返回值  
  221.      * @param text 文本对象  
  222.      */  
  223.     private void setTextValueAndColor(int retExcAuth, Text text) {  
  224.         switch (retExcAuth) {  
  225.             case FaceAuthResult.AUTH_SUCCESS:  
  226.                 setTextValueAndColor(text, "认证成功", Color.GREEN);  
  227.                 // 页面跳转  
  228.                 toAuthAfterPage();  
  229.                 break;  
  230.             case FaceAuthResult.AUTH_FAIL:  
  231.                 setTextValueAndColor(text, "比对失败", Color.RED);  
  232.                 break;  
  233.             case FaceAuthResult.AUTH_CANCLE:  
  234.                 setTextValueAndColor(text, "取消认证", Color.RED);  
  235.                 break;  
  236.             case FaceAuthResult.AUTH_TIME_OUT:  
  237.                 setTextValueAndColor(text, "认证超时", Color.RED);  
  238.                 break;  
  239.             case FaceAuthResult.AUTH_OPEN_CAMERA_FAIL:  
  240.                 setTextValueAndColor(text, "打开相机失败", Color.RED);  
  241.                 break;  
  242.             case FaceAuthResult.AUTH_BUSY:  
  243.                 setTextValueAndColor(text, "busy,可能上一个认证没有结束", Color.RED);  
  244.                 break;  
  245.             case FaceAuthResult.AUTH_PARAM_ERROR:  
  246.                 setTextValueAndColor(text, "入参错误", Color.RED);  
  247.                 break;  
  248.             case FaceAuthResult.AUTH_FACE_LOCKED:  
  249.                 setTextValueAndColor(text, "人脸认证锁定(达到错误认证次数了)", Color.RED);  
  250.                 break;  
  251.             case FaceAuthResult.AUTH_NO_FACE:  
  252.                 setTextValueAndColor(text, "无人脸录入,请录入人脸。", Color.BLUE);  
  253.                 break;  
  254.             case FaceAuthResult.AUTH_OTHER_ERROR:  
  255.                 setTextValueAndColor(text, "其他错误。", Color.RED);  
  256.                 break;  
  257.             case FaceAuthResult.AUTH_2D_NOT_SUPPORTED:  
  258.                 setTextValueAndColor(text, "不支持2D人脸识别。", Color.BLUE);  
  259.                 break;  
  260.             case FaceAuthResult.AUTH_SAFE_LEVEL_NOT_SUPPORTED:  
  261.                 setTextValueAndColor(text, "安全级别不支持。", Color.BLUE);  
  262.                 break;  
  263.             case FaceAuthResult.AUTH_NOT_LOCAL:  
  264.                 setTextValueAndColor(text, "不是本地认证。", Color.BLUE);  
  265.                 break;  
  266.             default:  
  267.                 setTextValueAndColor(text, "开始认证,请将视线对准摄像头。。。。。。。", Color.BLUE);  
  268.                 break;  
  269.         }  
  270.     }  
  271.   
  272.     private void toAuthAfterPage() {  
  273.         Intent secondIntent = new Intent();  
  274.         // 指定待启动FA的bundleName和abilityName  
  275.         Operation operation = new Intent.OperationBuilder()  
  276.                 .withDeviceId("")  
  277.                 .withBundleName(getBundleName())  
  278.                 .withAbilityName(OpenCamera.class.getName())  
  279.                 .build();  
  280.         secondIntent.setOperation(operation);  
  281.         // 通过AbilitySlice的startAbility接口实现启动另一个页面  
  282.         startAbility(secondIntent);  
  283.     }  
  284.   
  285.     /**  
  286.      * 事件分发器  
  287.      *  
  288.      * @since 2021-04-12  
  289.      */  
  290.     private class MyEventHandle extends EventHandler {  
  291.         MyEventHandle(EventRunner runner) throws IllegalArgumentException {  
  292.             super(runner);  
  293.         }  
  294.   
  295.         @Override  
  296.         protected void processEvent(InnerEvent event) {  
  297.             super.processEvent(event);  
  298.             int eventId = event.eventId;  
  299.             getAndSetText(ResourceTable.Id_text_status, eventId, true);  
  300.         }  
  301.     }  
  302.   
  303.     @Override  
  304.     public void onStop() {  
  305.         mBiometricAuthentication.cancelAuthenticationAction();  
  306.         BiometricAuthentication.AuthenticationTips authenticationTips  
  307.                 = mBiometricAuthentication.getAuthenticationTips();  
  308.         String tips = authenticationTips.tipInfo;  
  309.     }  

2.com/huawei/cookbook/slice/OpenCameraSlice.java

  1. package com.huawei.cookbook.slice; 
  2.  
  3. import com.huawei.cookbook.ResourceTable; 
  4. import com.huawei.cookbook.util.LogUtils; 
  5. import com.huawei.cookbook.util.PermissionBridge; 
  6.  
  7. import ohos.aafwk.ability.AbilitySlice; 
  8. import ohos.aafwk.content.Intent; 
  9. import ohos.agp.components.Component; 
  10. import ohos.agp.components.ComponentContainer; 
  11. import ohos.agp.components.DirectionalLayout; 
  12. import ohos.agp.components.Image; 
  13. import ohos.agp.components.surfaceprovider.SurfaceProvider; 
  14. import ohos.agp.graphics.Surface; 
  15. import ohos.agp.graphics.SurfaceOps; 
  16. import ohos.agp.window.dialog.ToastDialog; 
  17. import ohos.app.Context; 
  18. import ohos.eventhandler.EventHandler; 
  19. import ohos.eventhandler.EventRunner; 
  20. import ohos.media.camera.CameraKit; 
  21. import ohos.media.camera.device.Camera; 
  22. import ohos.media.camera.device.CameraConfig; 
  23. import ohos.media.camera.device.CameraStateCallback; 
  24. import ohos.media.camera.device.FrameConfig; 
  25.  
  26. /** 
  27.  * 打开相机slice 
  28.  */ 
  29. public class OpenCameraSlice extends AbilitySlice implements PermissionBridge.OnPermissionStateListener { 
  30.     private static final String TAG = OpenCameraSlice.class.getName(); 
  31.  
  32.     private static final int SCREEN_WIDTH = 1080; 
  33.  
  34.     private static final int SCREEN_HEIGHT = 1920; 
  35.  
  36.     private static final int SLEEP_TIME = 200; 
  37.  
  38.     private EventHandler creamEventHandler; 
  39.  
  40.     private Image exitImage; 
  41.  
  42.     private SurfaceProvider surfaceProvider; 
  43.  
  44.     private Image switchCameraImage; 
  45.  
  46.     private boolean isCameraRear; 
  47.  
  48.     private Camera cameraDevice; 
  49.  
  50.     private Surface previewSurface; 
  51.  
  52.     @Override 
  53.     public void onStart(Intent intent) { 
  54.         super.onStart(intent); 
  55.         super.setUIContent(ResourceTable.Layout_ability_open_camera); 
  56.         new PermissionBridge().setOnPermissionStateListener(this); 
  57.     } 
  58.  
  59.     private void initSurface() { 
  60.         surfaceProvider = new SurfaceProvider(this); 
  61.         DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig( 
  62.                 ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT); 
  63.         surfaceProvider.setLayoutConfig(params); 
  64.         surfaceProvider.pinToZTop(false); 
  65.         // 添加SurfaceCallBack回调 
  66.         surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack()); 
  67.         // 将SurfaceProvider加入到布局中 
  68.         Component component = findComponentById(ResourceTable.Id_surface_container); 
  69.         if (component instanceof ComponentContainer) { 
  70.             ((ComponentContainer) component).addComponent(surfaceProvider); 
  71.         } 
  72.     } 
  73.  
  74.     private void initControlComponents() { 
  75.         // 退出拍照页面图标 
  76.         Component exitImageCom = findComponentById(ResourceTable.Id_exit); 
  77.         if (exitImageCom instanceof Image) { 
  78.             exitImage = (Image) exitImageCom; 
  79.             exitImage.setClickedListener(component -> terminate()); 
  80.         } 
  81.         // 切换前后置摄像头图标 
  82.         Component switchCameraImageCom = findComponentById(ResourceTable.Id_switch_camera_btn); 
  83.         if (switchCameraImageCom instanceof Image) { 
  84.             switchCameraImage = (Image) switchCameraImageCom; 
  85.             switchCameraImage.setClickedListener(component -> switchClicked()); 
  86.         } 
  87.     } 
  88.  
  89.     private void switchClicked() { 
  90.         isCameraRear = !isCameraRear; 
  91.         openCamera(); 
  92.     } 
  93.  
  94.     private void openCamera() { 
  95.         CameraKit cameraKit = CameraKit.getInstance(getApplicationContext()); 
  96.         String[] cameraLists = cameraKit.getCameraIds(); 
  97.         String cameraId = cameraLists.length > 1 && isCameraRear ? cameraLists[1] : cameraLists[0]; 
  98.         CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl(); 
  99.         cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler); 
  100.     } 
  101.  
  102.     private void showTips(Context context, String message) { 
  103.         getUITaskDispatcher().asyncDispatch(() -> { 
  104.             ToastDialog toastDialog = new ToastDialog(context); 
  105.             toastDialog.setAutoClosable(false); 
  106.             toastDialog.setContentText(message); 
  107.             toastDialog.show(); 
  108.         }); 
  109.     } 
  110.  
  111.     @Override 
  112.     public void onPermissionGranted() { 
  113.         getWindow().setTransparent(true); 
  114.         initSurface(); 
  115.         initControlComponents(); 
  116.         creamEventHandler = new EventHandler(EventRunner.create("======CameraBackground")); 
  117.     } 
  118.  
  119.     @Override 
  120.     public void onPermissionDenied() { 
  121.         showTips(OpenCameraSlice.this, "=======No permission"); 
  122.     } 
  123.  
  124.     /** 
  125.      * CameraStateCallbackImpl 
  126.      */ 
  127.     class CameraStateCallbackImpl extends CameraStateCallback { 
  128.         CameraStateCallbackImpl() { 
  129.         } 
  130.  
  131.         @Override 
  132.         public void onCreated(Camera camera) { 
  133.             // 获取预览 
  134.             previewSurface = surfaceProvider.getSurfaceOps().get().getSurface(); 
  135.             if (previewSurface == null) { 
  136.                 LogUtils.error(TAG, "create camera filed, preview surface is null"); 
  137.                 return
  138.             } 
  139.             // Wait until the preview surface is created. 
  140.             try { 
  141.                 Thread.sleep(SLEEP_TIME); 
  142.             } catch (InterruptedException exception) { 
  143.                 LogUtils.warn(TAG, "Waiting to be interrupted"); 
  144.             } 
  145.             CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder(); 
  146.             // 配置预览 
  147.             cameraConfigBuilder.addSurface(previewSurface); 
  148.             camera.configure(cameraConfigBuilder.build()); 
  149.             cameraDevice = camera; 
  150.             enableImageGroup(); 
  151.         } 
  152.  
  153.         @Override 
  154.         public void onConfigured(Camera camera) { 
  155.             FrameConfig.Builder framePreviewConfigBuilder 
  156.                     = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW); 
  157.             framePreviewConfigBuilder.addSurface(previewSurface); 
  158.             // 开启循环捕捉 
  159.             camera.triggerLoopingCapture(framePreviewConfigBuilder.build()); 
  160.         } 
  161.  
  162.         private void enableImageGroup() { 
  163.             if (!exitImage.isEnabled()) { 
  164.                 exitImage.setEnabled(true); 
  165.                 switchCameraImage.setEnabled(true); 
  166.             } 
  167.         } 
  168.     } 
  169.  
  170.     /** 
  171.      * SurfaceCallBack 
  172.      */ 
  173.     class SurfaceCallBack implements SurfaceOps.Callback { 
  174.         @Override 
  175.         public void surfaceCreated(SurfaceOps callbackSurfaceOps) { 
  176.             if (callbackSurfaceOps != null) { 
  177.                 callbackSurfaceOps.setFixedSize(SCREEN_HEIGHT, SCREEN_WIDTH); 
  178.             } 
  179.             openCamera(); 
  180.         } 
  181.  
  182.         @Override 
  183.         public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) { 
  184.         } 
  185.  
  186.         @Override 
  187.         public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) { 
  188.         } 
  189.     } 
  190.  
  191.     @Override 
  192.     public void onStop() { 
  193.         cameraDevice.release(); 
  194.     } 
  195. }    

3.com/huawei/cookbook/util/FaceAuthResult.java

  1. package com.huawei.cookbook.util;  
  2.   
  3. /**  
  4.  * 人脸认证返回码  
  5.  *  
  6.  * @since 2021-04-12  
  7.  */  
  8. public class FaceAuthResult {  
  9.     /**  
  10.      * 认证成功  
  11.      */  
  12.     public static final int AUTH_SUCCESS = 0;  
  13.     /**  
  14.      * 认证失败  
  15.      */  
  16.     public static final int AUTH_FAIL = 1;  
  17.     /**  
  18.      * 取消认证  
  19.      */  
  20.     public static final int AUTH_CANCLE = 2;  
  21.     /**  
  22.      * 认证超时  
  23.      */  
  24.     public static final int AUTH_TIME_OUT = 3;  
  25.     /**  
  26.      * 打开相机失败  
  27.      */  
  28.     public static final int AUTH_OPEN_CAMERA_FAIL = 4;  
  29.     /**  
  30.      * busy,可能上一个认证没有结束  
  31.      */  
  32.     public static final int AUTH_BUSY = 5;  
  33.     /**  
  34.      * 入参错误  
  35.      */  
  36.     public static final int AUTH_PARAM_ERROR = 6;  
  37.     /**  
  38.      * 人脸认证锁定(达到错误认证次数了)  
  39.      */  
  40.     public static final int AUTH_FACE_LOCKED = 7;  
  41.     /**  
  42.      * 没有录入人脸  
  43.      */  
  44.     public static final int AUTH_NO_FACE = 8;  
  45.     /**  
  46.      * 不支持2D人脸识别。  
  47.      */  
  48.     public static final int AUTH_2D_NOT_SUPPORTED = 9;  
  49.     /**  
  50.      * 安全级别不支持  
  51.      */  
  52.     public static final int AUTH_SAFE_LEVEL_NOT_SUPPORTED = 10;  
  53.     /**  
  54.      * 不是本地认证  
  55.      */  
  56.     public static final int AUTH_NOT_LOCAL = 11;  
  57.     /**  
  58.      * 其他问题  
  59.      */  
  60.     public static final int AUTH_OTHER_ERROR = 100;  
  61.   
  62.     private FaceAuthResult() {  
  63.         super();  
  64.     }  

4.com/huawei/cookbook/util/LogUtils.java

  1. package com.huawei.cookbook.util;  
  2.   
  3. import ohos.hiviewdfx.HiLog;  
  4. import ohos.hiviewdfx.HiLogLabel;  
  5.   
  6. /**  
  7.  * LogUtils  
  8.  *  
  9.  * @since 2021-04-12  
  10.  */  
  11. public class LogUtils {  
  12.     private static final String TAG_LOG = "LogUtil";  
  13.   
  14.     private static final HiLogLabel LABEL_LOG = new HiLogLabel(0, 0, LogUtils.TAG_LOG);  
  15.   
  16.     private static final String LOG_FORMAT = "%{public}s: %{public}s";  
  17.   
  18.     private LogUtils() {  
  19.     }  
  20.   
  21.     /**  
  22.      * Print debug log  
  23.      *  
  24.      * @param tag log tag  
  25.      * @param msg log message  
  26.      */  
  27.     public static void debug(String tag, String msg) {  
  28.         HiLog.debug(LABEL_LOG, LOG_FORMAT, tag, msg);  
  29.     }  
  30.   
  31.     /**  
  32.      * Print info log  
  33.      *  
  34.      * @param tag log tag  
  35.      * @param msg log message  
  36.      */  
  37.     public static void info(String tag, String msg) {  
  38.         HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg);  
  39.     }  
  40.   
  41.     /**  
  42.      * Print warn log  
  43.      *  
  44.      * @param tag log tag  
  45.      * @param msg log message  
  46.      */  
  47.     public static void warn(String tag, String msg) {  
  48.         HiLog.warn(LABEL_LOG, LOG_FORMAT, tag, msg);  
  49.     }  
  50.   
  51.     /**  
  52.      * Print error log  
  53.      *  
  54.      * @param tag log tag  
  55.      * @param msg log message  
  56.      */  
  57.     public static void error(String tag, String msg) {  
  58.         HiLog.error(LABEL_LOG, LOG_FORMAT, tag, msg);  
  59.     }  

5.com/huawei/cookbook/util/PermissionBridge.java

  1. package com.huawei.cookbook.util;  
  2.   
  3. import ohos.eventhandler.EventHandler;  
  4. import ohos.eventhandler.EventRunner;  
  5. import ohos.eventhandler.InnerEvent;  
  6.   
  7. /**  
  8.  * PermissionBridge  
  9.  *  
  10.  * @since 2021-04-12  
  11.  */  
  12. public class PermissionBridge {  
  13.     /**  
  14.      * permission handler granted  
  15.      */  
  16.     public static final int EVENT_PERMISSION_GRANTED = 0x0000023;  
  17.   
  18.     /**  
  19.      * permission handler denied  
  20.      */  
  21.     public static final int EVENT_PERMISSION_DENIED = 0x0000024;  
  22.   
  23.     private static final String TAG = PermissionBridge.class.getSimpleName();  
  24.   
  25.     private static OnPermissionStateListener onPermissionStateListener;  
  26.   
  27.     private static EventHandler handler = new EventHandler(EventRunner.current()) {  
  28.         @Override  
  29.         protected void processEvent(InnerEvent event) {  
  30.             switch (event.eventId) {  
  31.                 case EVENT_PERMISSION_GRANTED:  
  32.                     onPermissionStateListener.onPermissionGranted();  
  33.                     break;  
  34.                 case EVENT_PERMISSION_DENIED:  
  35.                     onPermissionStateListener.onPermissionDenied();  
  36.                     break;  
  37.                 default:  
  38.                     LogUtils.info(TAG, "EventHandler Undefined Event");  
  39.                     break;  
  40.             }  
  41.         }  
  42.     };  
  43.   
  44.     /**  
  45.      * setOnPermissionStateListener  
  46.      *  
  47.      * @param permissionStateListener OnPermissionStateListener  
  48.      */  
  49.     public void setOnPermissionStateListener(OnPermissionStateListener permissionStateListener) {  
  50.         onPermissionStateListener = permissionStateListener;  
  51.     }  
  52.   
  53.     /**  
  54.      * OnPermissionStateListener  
  55.      *  
  56.      * @since 2021-04-12  
  57.      */  
  58.     public interface OnPermissionStateListener {  
  59.         /**  
  60.          * 当授权时  
  61.          */  
  62.         void onPermissionGranted();  
  63.   
  64.         /**  
  65.          * 当拒绝授权时触发  
  66.          */  
  67.         void onPermissionDenied();  
  68.     }  
  69.   
  70.     /**  
  71.      * getHandler  
  72.      *  
  73.      * @return EventHandler  
  74.      */  
  75.     public static EventHandler getHandler() {  
  76.         return handler;  
  77.     }  

6.com/huawei/cookbook/MainAbility.java

  1. package com.huawei.cookbook;  
  2.   
  3. import com.huawei.cookbook.slice.MainAbilitySlice;  
  4.   
  5. import ohos.aafwk.ability.Ability;  
  6. import ohos.aafwk.ability.IDataAbilityObserver;  
  7. import ohos.aafwk.content.Intent;  
  8.   
  9. /**  
  10.  * MainAbility  
  11.  *  
  12.  * @since 2021-04-12  
  13.  */  
  14. public class MainAbility extends Ability {  
  15.     /**  
  16.      * 声明静态变量,用于获取生物识别对象  
  17.      */  
  18.     private static MainAbility myAbility;  
  19.   
  20.     /**  
  21.      * 私有构造  
  22.      */  
  23.     public MainAbility() {  
  24.         myAbility = this;  
  25.     }  
  26.   
  27.     /**  
  28.      * 获取ability  
  29.      *  
  30.      * @return MainAbility  
  31.      */  
  32.     public static MainAbility getMainAbility() {  
  33.         return myAbility;  
  34.     }  
  35.   
  36.     @Override  
  37.     public void onStart(Intent intent) {  
  38.         super.onStart(intent);  
  39.         super.setMainRoute(MainAbilitySlice.class.getName());  
  40.     }  

7.com/huawei/cookbook/MyApplication.java

  1. package com.huawei.cookbook;  
  2.   
  3. import ohos.aafwk.ability.AbilityPackage;  
  4.   
  5. /**  
  6.  * MyApplication  
  7.  *  
  8.  * @since 2021-04-12  
  9.  */  
  10. public class MyApplication extends AbilityPackage {  
  11.     @Override  
  12.     public void onInitialize() {  
  13.         super.onInitialize();  
  14.     }  

8.com/huawei/cookbook/OpenCamera.java

  1. package com.huawei.cookbook;  
  2.   
  3. import com.huawei.cookbook.slice.OpenCameraSlice;  
  4. import com.huawei.cookbook.util.PermissionBridge;  
  5.   
  6. import ohos.aafwk.ability.Ability;  
  7. import ohos.aafwk.content.Intent;  
  8. import ohos.bundle.IBundleManager;  
  9. import ohos.security.SystemPermission;  
  10.   
  11. import java.util.Arrays;  
  12. import java.util.List;  
  13. import java.util.stream.Collectors;  
  14.   
  15. /**  
  16.  * 打开相机ability  
  17.  *  
  18.  * @since 2021-04-12  
  19.  */  
  20. public class OpenCamera extends Ability {  
  21.     /**  
  22.      * permission handler granted  
  23.      */  
  24.     private static final int EVENT_PERMISSION_GRANTED = 0x0000023;  
  25.   
  26.     /**  
  27.      * permission handler denied  
  28.      */  
  29.     private static final int EVENT_PERMISSION_DENIED = 0x0000024;  
  30.     private static final int PERMISSION_REQUEST_CODE = 0;  
  31.   
  32.     @Override  
  33.     public void onStart(Intent intent) {  
  34.         super.onStart(intent);  
  35.         super.setMainRoute(OpenCameraSlice.class.getName());  
  36.         requestPermission();  
  37.     }  
  38.   
  39.     private void requestPermission() {  
  40.         String[] permissions = {  
  41.             // 存储权限  
  42.             SystemPermission.WRITE_USER_STORAGE,  
  43.             // 相机权限  
  44.             SystemPermission.CAMERA  
  45.         };  
  46.         List permissionFiltereds = Arrays.stream(permissions)  
  47.                 .filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED)  
  48.                 .collect(Collectors.toList());  
  49.         if (permissionFiltereds.isEmpty()) {  
  50.             PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED);  
  51.             return;  
  52.         }  
  53.         requestPermissionsFromUser(permissionFiltereds.toArray(new String[permissionFiltereds.size()]),  
  54.                 PERMISSION_REQUEST_CODE);  
  55.     }  
  56.   
  57.     @Override  
  58.     public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {  
  59.         if (permissions == null || permissions.length == 0 || grantResults == null || grantResults.length == 0) {  
  60.             return;  
  61.         }  
  62.         for (int grantResult : grantResults) {  
  63.             if (grantResult != IBundleManager.PERMISSION_GRANTED) {  
  64.                 PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_DENIED);  
  65.                 terminateAbility();  
  66.                 return;  
  67.             }  
  68.         }  
  69.         PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED);  
  70.     }  

9.打开相机页面图标

返回图标:

如何使用HarmonyOS面部识别能力-鸿蒙HarmonyOS技术社区

拍照图标:

如何使用HarmonyOS面部识别能力-鸿蒙HarmonyOS技术社区

切换前后置摄像头图标:

如何使用HarmonyOS面部识别能力-鸿蒙HarmonyOS技术社区

10. 恭喜您

恭喜你已经完成了基于面容识别的HarmonyOS应用开发的Codelab,并且学到了:

1.人脸识别的开发。

2.相机的打开。

3.线程间通信开发EventHandler。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com