大家好,我是陈杨,8 年前端老兵转型鸿蒙开发,也是一名鸿蒙极客。从前端到鸿蒙,我靠的是 “三天上手 ArkTS” 的技术嗅觉,以及 “居安思危” 的转型魄力。这三年,我不玩虚的,封装了开源组件库「莓创图表」,拿过创新赛大奖,更带着团队上架了 11 款自研 APP,涵盖工具、效率、创意等多个领域。想体验我的作品?欢迎搜索体验:指令魔方、JLPT、REFLEX PRO、国潮纸刻、Wss 直连、ZenithDocs Pro、圣诞相册、CSS 特效。

之前给大家分享了鸿蒙的人脸检测功能,今天继续深挖 Core Vision Kit 的相关能力 —— 人脸比对。这个功能和指令魔方无关,但娱乐性和实用性拉满,比如社交 APP 里的 “明星脸相似度对比”、亲友间的 “颜值匹配度测试”,甚至可以做趣味互动小程序。今天就用通俗的语言,带大家拆解它的实现逻辑,附上可直接复用的代码,新手也能快速落地。

一、先搞懂:人脸比对到底能做啥?

简单说,人脸比对就是 “让 APP 判断两张图片里的人是不是同一个,还能给出相似度”。它的核心作用有两个:

  1. 身份判断:返回 “是同一个人” 或 “不是同一个人” 的明确结果;
  2. 相似度评分:用 0-1 之间的数值表示两张人脸的相似程度,数值越接近 1,相似度越高。

它的适用场景特别适合娱乐和轻社交类产品:

  • 明星脸对比:用户上传自己的照片,和明星照片比对,看相似度多少;
  • 亲友互动:情侣、闺蜜上传合照,生成 “颜值匹配度” 报告,用于社交分享;
  • 趣味测试:比如 “穿越回古代像哪个名人”,通过比对预设图片给出结果;
  • 轻量身份验证:比如 APP 登录时,比对当前拍摄的人脸和预留照片是否一致(注意:正式验证场景需搭配更严谨的安全方案)。

不过要注意几个约束:目前只支持 1v1 比对(一次只能比两张图);不支持模拟器,必须用真实鸿蒙设备测试;图片质量要过关(建议 720p 以上),太模糊会影响比对准确性。

二、核心逻辑:从 “选两张图” 到 “出结果” 的 5 步走

人脸比对的开发流程和之前的视觉类功能一脉相承,核心就 5 个步骤:

  1. 初始化人脸比对服务:打开比对功能的 “开关”,准备好所需资源;
  2. 选择两张图片:用户从图库一次性选两张需要比对的图片;
  3. 图片格式转换:把两张图片都转换为人脸比对能识别的 PixelMap 格式;
  4. 调用比对接口:传入两张图片,等待比对结果;
  5. 解析结果:把返回的相似度数值转换成百分比,搭配 “是否为同一人” 的结论显示在页面上。

三、可直接复用的代码(趣味实战版)

下面的代码是基于鸿蒙官方示例优化的简化版,增加了更通俗的结果展示,去掉了冗余逻辑,新手可以直接复制到项目里使用。

// 导入需要的工具包
import { faceComparator } from '@kit.CoreVisionKit';
import { image } from '@kit.ImageKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { fileIo } from '@kit.CoreFileKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';

const TAG = "FaceCompareDemo";

@Entry
@Component
struct FaceComparePage {
  // 第一张待比对图片(PixelMap格式)
  @State image1: PixelMap | undefined = undefined;
  // 第二张待比对图片(PixelMap格式)
  @State image2: PixelMap | undefined = undefined;
  // 比对结果(相似度+是否同一人)
  @State compareResult: string = "选择两张图片后,点击「开始比对」获取结果...";
  // 图片资源对象
  private imageSource: image.ImageSource | undefined = undefined;

  // 页面加载时初始化比对服务
  async aboutToAppear(): Promise<void> {
    const initResult = await faceComparator.init();
    hilog.info(0x0000, TAG, `人脸比对服务初始化结果:${initResult}`);
  }

  // 页面销毁时释放服务,避免占用内存
  async aboutToDisappear(): Promise<void> {
    await faceComparator.release();
    hilog.info(0x0000, TAG, '人脸比对服务已释放');
  }

  build() {
    Column({ space: 20 }) {
      // 显示第一张图片
      Image(this.image1)
        .objectFit(ImageFit.Contain)
        .height('25%')
        .width('45%')
        .border({ width: 2, color: 0x317AE7, radius: 8 })
        .backgroundColor('#F5F5F5')
        .accessibilityDescription("比对图片1")

      // 显示第二张图片
      Image(this.image2)
        .objectFit(ImageFit.Contain)
        .height('25%')
        .width('45%')
        .border({ width: 2, color: 0x317AE7, radius: 8 })
        .backgroundColor('#F5F5F5')
        .accessibilityDescription("比对图片2")

      // 结果展示区域(支持复制)
      Text(this.compareResult)
        .copyOption(CopyOptions.LocalDevice)
        .fontSize(18)
        .fontWeight(FontWeight.Medium)
        .textAlign(TextAlign.Center)
        .width('90%')
        .height('15%')
        .border({ width: 1, color: '#E0E0E0', radius: 8 })
        .padding(10)

      // 选择图片按钮(一次性选两张)
      Button('选择两张图片')
        .type(ButtonType.Capsule)
        .backgroundColor(0x317AE7)
        .fontColor(Color.White)
        .width('90%')
        .height(45)
        .onClick(() => this.selectTwoImages())

      // 开始比对按钮
      Button('开始人脸比对')
        .type(ButtonType.Capsule)
        .backgroundColor(0x317AE7)
        .fontColor(Color.White)
        .width('90%')
        .height(45)
        .onClick(() => this.startFaceCompare())
    }
    .padding(20)
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  // 第一步:从图库一次性选择两张图片
  private async selectTwoImages() {
    try {
      const photoPicker = new photoAccessHelper.PhotoViewPicker();
      const selectResult = await photoPicker.select({
        MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE, // 只选图片
        maxSelectNumber: 2 // 最多选2张,刚好满足比对需求
      });

      const imageUris = selectResult.photoUris;
      if (imageUris.length !== 2) {
        this.compareResult = "请选择两张图片进行比对!";
        return;
      }

      // 转换两张图片的格式
      await this.loadImageToPixelMap(imageUris[0], 1);
      await this.loadImageToPixelMap(imageUris[1], 2);

      this.compareResult = "已选中两张图片,点击「开始比对」获取结果...";
    } catch (err: BusinessError | any) {
      hilog.error(0x0000, TAG, `选图失败:${err.message}`);
      this.compareResult = `选图失败:${err.message}(错误码:${err.code})`;
    }
  }

  // 第二步:将图片转换为人脸比对能识别的PixelMap格式
  private async loadImageToPixelMap(uri: string, imageIndex: number) {
    try {
      // 打开图片文件
      const file = await fileIo.open(uri, fileIo.OpenMode.READ_ONLY);
      // 创建图片资源对象
      this.imageSource = image.createImageSource(file.fd);
      // 转换为PixelMap格式
      const pixelMap = await this.imageSource.createPixelMap();
      // 关闭文件,避免资源泄露
      await fileIo.close(file);

      // 根据索引赋值给对应的图片变量
      if (imageIndex === 1) {
        this.image1 = pixelMap;
      } else {
        this.image2 = pixelMap;
      }
    } catch (err: BusinessError | any) {
      hilog.error(0x0000, TAG, `图片加载失败:${err.message}`);
      this.compareResult = `第${imageIndex}张图片加载失败:${err.message}`;
    }
  }

  // 第三步:调用人脸比对接口,解析结果
  private startFaceCompare() {
    // 先判断是否选中了两张图片
    if (!this.image1 || !this.image2) {
      this.compareResult = "请先选择两张图片进行比对!";
      return;
    }

    this.compareResult = "正在比对中,请稍候...";

    // 配置比对参数:传入两张图片的PixelMap
    const visionInfo1: faceComparator.VisionInfo = { pixelMap: this.image1 };
    const visionInfo2: faceComparator.VisionInfo = { pixelMap: this.image2 };

    // 调用比对接口
    faceComparator.compareFaces(visionInfo1, visionInfo2)
      .then((result: faceComparator.FaceCompareResult) => {
        // 转换相似度为百分比(保留2位小数)
        const similarityPercent = (result.similarity * 100).toFixed(2);
        // 判断是否为同一人,并生成通俗结论
        const isSamePerson = result.isSamePerson;
        let resultText = `相似度:${similarityPercent}%\n`;

        if (isSamePerson) {
          resultText += "✅ 判定为同一人!";
          // 增加趣味描述
          if (parseFloat(similarityPercent) >= 95) {
            resultText += "\n相似度超高,大概率是同一张照片~";
          } else if (parseFloat(similarityPercent) >= 80) {
            resultText += "\n相似度很高,确定是同一个人!";
          } else {
            resultText += "\n虽然相似度一般,但判定为同一人~";
          }
        } else {
          resultText += "❌ 判定为不同的人!";
          // 增加趣味描述
          if (parseFloat(similarityPercent) >= 70) {
            resultText += "\n相似度不低,难道是失散多年的亲友?";
          } else if (parseFloat(similarityPercent) >= 50) {
            resultText += "\n有点像哦,可能是颜值同款~";
          } else {
            resultText += "\n相似度较低,是完全不同的两个人!";
          }
        }

        // 显示结果
        this.compareResult = resultText;
        hilog.info(0x0000, TAG, `比对成功:${resultText}`);
      })
      .catch((error: BusinessError) => {
        // 处理错误
        this.compareResult = `比对失败:${error.message}(错误码:${error.code})`;
        hilog.error(0x0000, TAG, `比对失败:${error.message},错误码:${error.code}`);
      });
  }
}

代码关键部分解析

  1. 初始化与释放:faceComparator.init() 初始化服务,faceComparator.release() 释放资源,和之前的视觉服务逻辑一致,避免内存泄漏;
  2. 图片选择:通过 maxSelectNumber: 2 限制一次性选两张图,刚好满足 1v1 比对的需求,不用用户分两次选择;
  3. 图片转换:还是老规矩,人脸比对只支持 PixelMap 格式,转换后要关闭文件,避免资源泄露;
  4. 核心比对接口:faceComparator.compareFaces(visionInfo1, visionInfo2) 是核心,传入两张图片的配置,返回 FaceCompareResult 对象;
  5. 结果解析:
    • result.similarity:相似度数值(0-1),转换为百分比更直观;
    • result.isSamePerson:布尔值,直接告诉你 “是不是同一个人”;
    • 增加趣味描述:根据相似度数值添加不同的文案,让功能更有娱乐性,适合社交分享;
  6. 错误处理:捕获选图失败、图片加载失败、比对服务异常等情况,给出清晰提示。

四、实战效果演示

给大家看看实际的使用流程,更直观:

五、避坑指南:这些问题一定要注意

  1. 设备支持:不支持模拟器!必须用真实的鸿蒙手机、平板测试,否则会报 “服务不可用”;
  2. 图片要求:两张图片分辨率建议 720p 以上,宽度 100px-10000px,高度 224px-15210px,高宽比例不要超过 10:1,太模糊或尺寸不符合的图片会影响比对 accuracy;
  3. 选图数量:必须选两张图片,少选或多选都会提示错误,代码里要做数量判断;
  4. 资源释放:页面销毁时一定要调用 faceComparator.release(),否则会占用设备资源;
  5. 结果用途:该功能适合娱乐场景,不适合金融、安防等需要严格身份验证的场景,正式验证需搭配专业方案。

六、开发总结:新手也能快速落地

人脸比对的开发难度不高,核心流程和之前的人脸检测、OCR 高度相似,只要掌握了 “选图 - 转格式 - 调用接口 - 解析结果” 的套路,半天就能落地功能。

如果想扩展功能,还可以加这些:

  1. 明星脸库:预设一批明星、名人的图片,用户只需上传自己的照片,就能自动和库中图片比对,找出最像的明星;
  2. 分享功能:把比对结果生成图片,支持分享到社交平台;
  3. 多图比对:先选一张基准图片,再选多张图片,批量比对找出和基准图最像的;
  4. 结果校准:允许用户手动调整相似度阈值(比如把判定 “同一人” 的阈值从 80% 调到 85%)。

虽然这个功能没用到指令魔方里,但它的娱乐性和社交属性很强,适合做趣味小程序或 APP 的附加功能。如果你正在开发这类产品,直接拿上面的代码改一改就能用~ 有问题欢迎留言交流,也可以关注我,后续会分享更多鸿蒙实用技术!

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐