人脸检测封面

相册应用里有个常见功能:检测图片里有没有人脸,有的话自动归到"人物相册"里,或者给人脸打标签。我们在做智能截图整理工具的时候,也需要判断截图里有没有人脸——有截图是聊天记录,有的是人脸照片,分类逻辑不一样。

HarmonyOS 7 的人脸检测能力在 VisionKit 里,端侧运行,速度很快。这篇就讲讲怎么把人脸检测集成到我们的图片处理流程里,从检测位置到多目标处理,再到隐私边界怎么把控。

一、真实开发中遇到的问题

最开始接人脸检测的时候,以为就是调个 API 返回一个坐标那么简单。实际用起来问题不少:

第一,一张图里多个人脸怎么处理?合影照片里有 5 个人,每个脸都要框出来,还是只取最大的那个?

第二,检测结果怎么和 UI 层结合?检测出来的坐标是相对于原图的,图片在屏幕上显示的时候缩放过,框框的位置要怎么换算才能对齐?

第三,人脸检测的准确率问题。侧脸、戴口罩、光线暗的时候,检测不到怎么办?要不要降级处理?

第四,隐私问题。人脸数据是敏感信息,本地检测没问题,但不能把人脸数据上传出去。这个边界要守住。

二、这个能力怎么接入

人脸检测的接入思路:加载图片 → 调检测接口 → 拿到人脸坐标列表 → 在 UI 上画框。

关键是理解检测结果的数据结构:返回的是一个人脸数组,每个人脸有位置坐标(boundingBox)、置信度(confidence),有的还返回关键点(眼睛、鼻子、嘴巴的位置)。

接入步骤:

1. 创建检测器:FaceDetector.create(),指定检测模式(快速/高精度)。

2. 传入图片:把 PixelMap 传进去,异步检测。

3. 解析结果:拿到人脸数组,遍历每个脸的坐标。

4. UI 渲染:把坐标换算成屏幕坐标,在图片上画矩形框。

坐标换算示意图

三、关键代码怎么写

下面是封装的人脸检测工具类,文件位置在 entry/src/main/ets/utils/FaceDetectUtil.ets

import { vision } from '@kit.CoreVisionKit';
import { image } from '@kit.CoreImageKit';

export class FaceDetectUtil {
  private static detector: vision.FaceDetector | null = null;

  // 初始化检测器
  static async init(): Promise<void> {
    if (!this.detector) {
      this.detector = await vision.FaceDetector.create({
        mode: vision.DetectMode.FAST, // 快速模式,够用就行
        maxDetectNum: 5 // 最多检测5张脸
      });
    }
  }

  // 检测图片中的人脸
  static async detectFaces(
    pixelMap: image.PixelMap
  ): Promise<FaceResult[]> {
    if (!this.detector) await this.init();

    try {
      const result = await this.detector!.detect(pixelMap);
      
      // 解析检测结果
      return result.faces.map((face: vision.Face) => ({
        x: face.boundingBox.left,
        y: face.boundingBox.top,
        width: face.boundingBox.width,
        height: face.boundingBox.height,
        confidence: face.confidence
      }));
    } catch (e) {
      console.error('Face detect failed: ' + e.message);
      return []; // 检测失败返回空数组
    }
  }

  // 判断图片中是否有人脸
  static async hasFace(pixelMap: image.PixelMap): Promise<boolean> {
    const faces = await this.detectFaces(pixelMap);
    return faces.length > 0;
  }

  // 释放资源
  static release() {
    if (this.detector) {
      this.detector.release();
      this.detector = null;
    }
  }
}

// 人脸结果类型
interface FaceResult {
  x: number;
  y: number;
  width: number;
  height: number;
  confidence: number;
}

这段代码的核心:detect 方法返回 faces 数组,每个 face 有 boundingBox 坐标。我们把坐标转成普通对象,方便 UI 层使用。hasFace 方法简单封装,只判断有没有人脸。

页面里怎么画人脸框?文件位置 pages/FaceDetectPage.ets

@Entry
@Component
struct FaceDetectPage {
  @State faces: FaceResult[] = [];
  @State imageWidth: number = 0;
  @State imageHeight: number = 0;

  async onImageLoaded(pixelMap: image.PixelMap) {
    // 获取图片原始尺寸
    const info = pixelMap.getImageInfoSync();
    this.imageWidth = info.width;
    this.imageHeight = info.height;
    
    // 执行人脸检测
    this.faces = await FaceDetectUtil.detectFaces(pixelMap);
  }

  build() {
    Stack() {
      // 原图
      Image($r('app.media.sample'))
        .objectFit(ImageFit.Contain)
      
      // 人脸框(用 Canvas 画)
      Canvas(this.context)
        .width('100%')
        .height('100%')
        .onReady(() => {
          this.drawFaceBoxes();
        })
    }
    .width('100%')
    .height('100%')
  }

  // 画人脸框
  private drawFaceBoxes() {
    const ctx = this.context.getContext();
    ctx.strokeStyle = '#00ff00';
    ctx.lineWidth = 2;
    
    // 坐标换算:原图坐标 → 屏幕坐标
    const scaleX = this.canvasWidth / this.imageWidth;
    const scaleY = this.canvasHeight / this.imageHeight;
    
    for (const face of this.faces) {
      const x = face.x * scaleX;
      const y = face.y * scaleY;
      const w = face.width * scaleX;
      const h = face.height * scaleY;
      
      ctx.strokeRect(x, y, w, h);
    }
  }
}

这里的关键是坐标换算:检测返回的坐标是相对于原图的,但图片在屏幕上显示的时候按比例缩放了,所以要乘上缩放比例,框才能对齐。

多目标人脸检测效果

四、运行过程中怎么处理异常

人脸检测的异常场景:

1. 图片中没有人:faces 为空数组。这不是错误,UI 上不画框就行。

2. 侧脸/遮挡:戴口罩、侧脸、光线暗,可能检测不到。这时候不要报错,就当没有人脸处理。

3. 多人脸:maxDetectNum 限制了最多检测几张。合影人多的时候,超过数量的脸检测不到。根据场景调整这个参数。

4. 大图处理:原图很大的时候检测慢。可以先把图片缩放到合适尺寸再检测,速度快很多,准确率影响不大。

五、实际开发中容易忽略的问题

1. 坐标原点:boundingBox 的原点是左上角,不是中心。画框的时候别搞反了,不然框的位置就偏了。

2. 检测模式选择:FAST 模式快但准确率一般,ACCURATE 模式慢但准。相册场景用 FAST 够了,人像编辑场景用 ACCURATE。

3. 人脸数据隐私:人脸数据是敏感信息。检测结果只在本地用,不要上传到服务器。如果要做人脸聚类,也要在本地做。

4. 检测时机:不要在图片加载的时候就同步检测,等图片显示完了再后台检测,不阻塞 UI。

5. 复用检测器:FaceDetector 实例不要每次都创建,创建一次复用。频繁创建销毁会有性能开销。

人脸检测是视觉处理里很基础的能力。看起来简单,但坐标换算、多目标处理、隐私边界这些细节,真做的时候都要考虑到。后面会把这个能力用到截图分类里——判断截图是人脸照片还是聊天记录。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐