鸿蒙人脸比对实战:快速实现 “相似度检测” 功能,娱乐社交必备
人脸比对的开发难度不高,核心流程和之前的人脸检测、OCR 高度相似,只要掌握了 “选图 - 转格式 - 调用接口 - 解析结果” 的套路,半天就能落地功能。明星脸库:预设一批明星、名人的图片,用户只需上传自己的照片,就能自动和库中图片比对,找出最像的明星;分享功能:把比对结果生成图片,支持分享到社交平台;多图比对:先选一张基准图片,再选多张图片,批量比对找出和基准图最像的;结果校准:允许用户手动调
大家好,我是陈杨,8 年前端老兵转型鸿蒙开发,也是一名鸿蒙极客。从前端到鸿蒙,我靠的是 “三天上手 ArkTS” 的技术嗅觉,以及 “居安思危” 的转型魄力。这三年,我不玩虚的,封装了开源组件库「莓创图表」,拿过创新赛大奖,更带着团队上架了 11 款自研 APP,涵盖工具、效率、创意等多个领域。想体验我的作品?欢迎搜索体验:指令魔方、JLPT、REFLEX PRO、国潮纸刻、Wss 直连、ZenithDocs Pro、圣诞相册、CSS 特效。
之前给大家分享了鸿蒙的人脸检测功能,今天继续深挖 Core Vision Kit 的相关能力 —— 人脸比对。这个功能和指令魔方无关,但娱乐性和实用性拉满,比如社交 APP 里的 “明星脸相似度对比”、亲友间的 “颜值匹配度测试”,甚至可以做趣味互动小程序。今天就用通俗的语言,带大家拆解它的实现逻辑,附上可直接复用的代码,新手也能快速落地。
一、先搞懂:人脸比对到底能做啥?
简单说,人脸比对就是 “让 APP 判断两张图片里的人是不是同一个,还能给出相似度”。它的核心作用有两个:
- 身份判断:返回 “是同一个人” 或 “不是同一个人” 的明确结果;
- 相似度评分:用 0-1 之间的数值表示两张人脸的相似程度,数值越接近 1,相似度越高。
它的适用场景特别适合娱乐和轻社交类产品:
- 明星脸对比:用户上传自己的照片,和明星照片比对,看相似度多少;
- 亲友互动:情侣、闺蜜上传合照,生成 “颜值匹配度” 报告,用于社交分享;
- 趣味测试:比如 “穿越回古代像哪个名人”,通过比对预设图片给出结果;
- 轻量身份验证:比如 APP 登录时,比对当前拍摄的人脸和预留照片是否一致(注意:正式验证场景需搭配更严谨的安全方案)。
不过要注意几个约束:目前只支持 1v1 比对(一次只能比两张图);不支持模拟器,必须用真实鸿蒙设备测试;图片质量要过关(建议 720p 以上),太模糊会影响比对准确性。
二、核心逻辑:从 “选两张图” 到 “出结果” 的 5 步走
人脸比对的开发流程和之前的视觉类功能一脉相承,核心就 5 个步骤:
- 初始化人脸比对服务:打开比对功能的 “开关”,准备好所需资源;
- 选择两张图片:用户从图库一次性选两张需要比对的图片;
- 图片格式转换:把两张图片都转换为人脸比对能识别的 PixelMap 格式;
- 调用比对接口:传入两张图片,等待比对结果;
- 解析结果:把返回的相似度数值转换成百分比,搭配 “是否为同一人” 的结论显示在页面上。
三、可直接复用的代码(趣味实战版)
下面的代码是基于鸿蒙官方示例优化的简化版,增加了更通俗的结果展示,去掉了冗余逻辑,新手可以直接复制到项目里使用。
// 导入需要的工具包
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}`);
});
}
}
代码关键部分解析
- 初始化与释放:
faceComparator.init()初始化服务,faceComparator.release()释放资源,和之前的视觉服务逻辑一致,避免内存泄漏; - 图片选择:通过
maxSelectNumber: 2限制一次性选两张图,刚好满足 1v1 比对的需求,不用用户分两次选择; - 图片转换:还是老规矩,人脸比对只支持
PixelMap格式,转换后要关闭文件,避免资源泄露; - 核心比对接口:
faceComparator.compareFaces(visionInfo1, visionInfo2)是核心,传入两张图片的配置,返回FaceCompareResult对象; - 结果解析:
result.similarity:相似度数值(0-1),转换为百分比更直观;result.isSamePerson:布尔值,直接告诉你 “是不是同一个人”;- 增加趣味描述:根据相似度数值添加不同的文案,让功能更有娱乐性,适合社交分享;
- 错误处理:捕获选图失败、图片加载失败、比对服务异常等情况,给出清晰提示。
四、实战效果演示
给大家看看实际的使用流程,更直观:

五、避坑指南:这些问题一定要注意
- 设备支持:不支持模拟器!必须用真实的鸿蒙手机、平板测试,否则会报 “服务不可用”;
- 图片要求:两张图片分辨率建议 720p 以上,宽度 100px-10000px,高度 224px-15210px,高宽比例不要超过 10:1,太模糊或尺寸不符合的图片会影响比对 accuracy;
- 选图数量:必须选两张图片,少选或多选都会提示错误,代码里要做数量判断;
- 资源释放:页面销毁时一定要调用
faceComparator.release(),否则会占用设备资源; - 结果用途:该功能适合娱乐场景,不适合金融、安防等需要严格身份验证的场景,正式验证需搭配专业方案。
六、开发总结:新手也能快速落地
人脸比对的开发难度不高,核心流程和之前的人脸检测、OCR 高度相似,只要掌握了 “选图 - 转格式 - 调用接口 - 解析结果” 的套路,半天就能落地功能。
如果想扩展功能,还可以加这些:
- 明星脸库:预设一批明星、名人的图片,用户只需上传自己的照片,就能自动和库中图片比对,找出最像的明星;
- 分享功能:把比对结果生成图片,支持分享到社交平台;
- 多图比对:先选一张基准图片,再选多张图片,批量比对找出和基准图最像的;
- 结果校准:允许用户手动调整相似度阈值(比如把判定 “同一人” 的阈值从 80% 调到 85%)。
虽然这个功能没用到指令魔方里,但它的娱乐性和社交属性很强,适合做趣味小程序或 APP 的附加功能。如果你正在开发这类产品,直接拿上面的代码改一改就能用~ 有问题欢迎留言交流,也可以关注我,后续会分享更多鸿蒙实用技术!
更多推荐


所有评论(0)