主体分割能力在鸿蒙开发中的运用案例
我们发现,虽然大家在群里都很活跃,但是在线下实际上可能都没有见过面,现代互联网的发展,让更多人之间的关系成为了“网游”。我们说线下好友聚会能有合照,那网友咋办?所有我们开发了一款App,叫“卡通云合影”(上架中)。在我们的“卡通云合影”应用中,有一个核心功能是**“抠图选人”**。用户从相册选择一张包含人物的照片。App 自动识别照片中的人物,并将他们从背景中分离出来(抠图)。用户点击选择需要保留
在移动应用开发中,抠图(即从图片中提取特定主体)是一个非常高频且具有挑战性的需求。在过去,这通常需要依赖服务端庞大的 AI 模型或集成复杂的第三方 SDK。
而在 HarmonyOS Next 中,华为通过 Core Vision Kit(基础视觉服务) 将这一能力下沉到了系统层面,提供了主体分割(Subject Segmentation) API。开发者只需几行代码,即可在端侧实现高精度、低延时的自动抠图功能。
本文将结合本项目中的**“卡通云合影”**功能,详细介绍如何在鸿蒙应用中集成和使用主体分割能力。
1. 场景介绍:卡通云合影
我们发现,虽然大家在群里都很活跃,但是在线下实际上可能都没有见过面,现代互联网的发展,让更多人之间的关系成为了“网游”。我们说线下好友聚会能有合照,那网友咋办?所有我们开发了一款App,叫“卡通云合影”(上架中)。在我们的“卡通云合影”应用中,有一个核心功能是**“抠图选人”**。
交互流程如下:
- 用户从相册选择一张包含人物的照片。
- App 自动识别照片中的人物,并将他们从背景中分离出来(抠图)。
- 用户点击选择需要保留的人像。
- 被选中的人像将被合成到卡通背景中。
为了实现第 2 步的“自动抠图”,我们正是使用了 HarmonyOS 的主体分割能力。
2. 核心 API 概览
主体分割能力主要由 @kit.CoreVisionKit 模块提供。
2.1 关键接口
subjectSegmentation.init(): 初始化分割服务。subjectSegmentation.doSegmentation(visionInfo, config): 执行分割操作。subjectSegmentation.release(): 释放资源(建议在不再使用时调用)。
2.2 关键参数
VisionInfo: 包含待处理的图片信息,主要是pixelMap。SegmentationConfig: 配置分割行为。maxCount: 最大分割主体数量。enableSubjectDetails: 是否输出主体的详细信息(如位置框)。enableSubjectForegroundImage: 关键参数,设置为true才会返回抠好的前景图(PixelMap)。
3. 实战代码解析
以下代码片段摘自本项目中的 entry/src/main/ets/pages/PhotoStep2Page.ets,展示了完整的调用流程。
3.1 引入模块
首先,我们需要引入 Core Vision Kit 和 Image Kit:
import { subjectSegmentation } from '@kit.CoreVisionKit';
import { image } from '@kit.ImageKit';
3.2 实现抠图方法
我们在页面组件中定义了一个 segmentPerson 方法,用于处理图片分割。
/**
* 执行人像分割
* 该方法负责调用 Core Vision Kit 的主体分割能力,识别图片中的人像主体。
*
* @param imageId 图片的唯一标识ID,用于在 selectedImages 数组中定位目标图片
*/
private async segmentPerson(imageId: string): Promise<void> {
// 1. 查找目标图片
// 遍历 selectedImages 数组,找到 ID 匹配的图片对象索引
const imageIndex = this.selectedImages.findIndex(img => img.id === imageId)
// 如果未找到(例如图片已被删除),直接返回
if (imageIndex === -1) return
try {
// 开启处理状态,UI 上会显示 Loading 动画
this.isProcessing = true
// 2. 初始化主体分割服务
// 这是一个异步操作,用于加载底层 AI 模型和资源。
// 建议放在 try-catch 中,以捕获初始化失败的情况(如设备不支持)。
await subjectSegmentation.init()
// 3. 构建 VisionInfo 对象
// VisionInfo 是 AI 能力的输入结构体。
// 这里将从相册选择并转换好的 PixelMap (this.selectedImages[imageIndex].originalImage) 传入。
const visionInfo: subjectSegmentation.VisionInfo = {
pixelMap: this.selectedImages[imageIndex].originalImage
}
// 4. 配置 SegmentationConfig
// SegmentationConfig 用于控制分割行为。
const config: subjectSegmentation.SegmentationConfig = {
maxCount: 10, // 最大分割主体数量:限制最多识别 10 个人物,避免过多干扰项
enableSubjectDetails: true, // 启用主体详情:设置为 true 后,返回结果包含位置信息等详细数据
enableSubjectForegroundImage: true // 【关键参数】启用前景图输出:必须设置为 true,否则结果中不会包含抠好的人像 PixelMap
}
// 5. 调用核心接口 doSegmentation
// 这是一个耗时操作,执行实际的 AI 推理和图像分割。
const result: subjectSegmentation.SegmentationResult =
await subjectSegmentation.doSegmentation(visionInfo, config)
// 6. 处理分割结果
// 检查结果对象及其 subjectDetails 数组是否存在且非空
if (result && result.subjectDetails && result.subjectDetails.length > 0) {
const subjects: image.PixelMap[] = []
// 遍历所有识别出的主体
for (const d of result.subjectDetails) {
// 如果该主体包含前景图(即抠好的透明背景图),则加入列表
if (d.foregroundImage) {
subjects.push(d.foregroundImage)
}
}
// 7. 更新 UI 数据
if (subjects.length > 0) {
// 获取原始图片对象
const base = this.selectedImages[imageIndex]
// 创建 selectedImages 的副本,遵循不可变数据更新原则(尽管这里直接赋值给了状态变量)
const updated = [...this.selectedImages]
// 将识别出的第一个主体更新到原位置
// 这里我们将原图对象更新为包含分割后图像(segmentedImage)的新对象
updated[imageIndex] = {
id: base.id,
originalImage: base.originalImage,
segmentedImage: subjects[0], // 设置第一个分割结果
isSelected: false // 默认不选中
}
// 如果识别出多个主体(例如合影中有 2 个人),
// 我们将剩余的主体作为新的可选项添加到列表中,方便用户单独选择。
for (let i = 1; i < subjects.length; i++) {
updated.push({
id: `${base.id}_${i}`, // 生成新的唯一 ID
originalImage: base.originalImage, // 共享原图引用
segmentedImage: subjects[i], // 设置对应的分割结果
isSelected: false
})
}
// 更新状态变量,触发 UI 刷新
this.selectedImages = updated
hm.toast(`人像分割完成,共检测到 ${subjects.length} 个主体`)
} else {
// 虽然识别到了主体信息,但没有成功提取出前景图
hm.toast('未检测到人像')
}
} else {
// 未识别到任何主体
hm.toast('未检测到人像')
}
} catch (error) {
// 捕获并处理所有异常,如初始化失败、推理超时等
console.error('人像分割失败:', error)
hm.toast('人像分割失败')
} finally {
// 无论成功还是失败,都必须关闭 Loading 状态
this.isProcessing = false
// 最佳实践:建议在此处调用 subjectSegmentation.release() 释放 AI 引擎占用的内存资源
// subjectSegmentation.release()
}
}
3.3 结果展示
分割成功后,result.subjectDetails[i].foregroundImage 就是我们需要的透明背景 PixelMap。在 ArkUI 中,可以直接通过 Image 组件显示它:
// 这里的 segmentedImage 就是上面获取到的 foregroundImage
Image(personImage.segmentedImage)
.width(80)
.height(80)
.objectFit(ImageFit.Cover)
.border({
width: personImage.isSelected ? 3 : 1,
color: personImage.isSelected ? $r('app.color.primary_color') : $r('app.color.border_color')
})
4. 最佳实践与注意事项
结合官方文档和项目开发经验,以下几点需要特别注意:
-
图片质量与比例:
- 输入图片建议分辨率在 720p 以上,以保证分割精度。
- 图片宽高应在 [20px, 9000px] 范围内。
- 高宽比限制:建议高宽比小于 3:1。如果图片过于细长(如长截图),可能会导致识别失败或精度下降。
-
主体占比:
- 文档指出,物体占比不小于原图大小的 5‰ 才会被认定为主体。如果人物太小(例如远景合影),可能无法被识别。
-
异步处理:
- 图像处理是耗时操作,务必使用
async/await或Promise,并在 UI 上给予用户 Loading 提示(如本项目中的isProcessing状态),防止应用假死。
- 图像处理是耗时操作,务必使用
-
资源管理:
PixelMap占用内存较大。在处理完逻辑后,对于不再需要的临时 PixelMap,建议及时释放内存。- 虽然本项目示例中未显式展示
release,但规范的做法是在业务流程结束或页面销毁(aboutToDisappear)时调用subjectSegmentation.release()。
-
模拟器限制:
- 该 AI 能力通常依赖端侧 NPU,当前不支持在模拟器上运行。开发调试时请务必使用真机。
5. 总结
通过集成 HarmonyOS 的主体分割能力,我们在“卡通云合影”项目中轻松实现了智能抠图功能,极大地提升了用户体验,且无需引入庞大的第三方库。这体现了 HarmonyOS “原生智能”的优势——让 AI 能力像调用普通 API 一样简单。
6. 最终效果

更多推荐

所有评论(0)