拍一张就懂你在看啥?”——HarmonyOS 图像识别与智能标签系统设计(Camera Kit × ImageClassifier)
本文介绍了如何在鸿蒙系统中实现端侧图像分类功能,主要分为三个步骤:图像采集、模型推理和结果展示。采用Camera Kit进行图像采集,MindSpore Lite进行端侧推理,并通过自定义ImageClassifier封装模型加载与预测过程。文章详细说明了权限配置、会话搭建、模型加载、图像预处理和推理执行的代码实现,提供了完整的开发思路和关键代码片段,适合鸿蒙开发者学习端侧AI应用开发。
我是兰瓶Coding,一枚刚踏入鸿蒙领域的转型小白,原是移动开发中级,如下是我学习笔记《零基础学鸿蒙》,若对你所有帮助,还请不吝啬的给个大大的赞~
前言
目标很直白:把摄像头采集到的画面,实时(或按需)丢给模型推理,然后把“它是什么”变成用户能一眼看懂的标签与置信度。下面按你的大纲来:图像采集 → 模型推理 → 结果展示;技术栈选 Camera Kit 做取流,MindSpore Lite 做端侧推理,并用一个我们自定义的 ImageClassifier 封装器承接模型加载与预测(你也可以把这个类就当作“ImageClassifier”能力)。涉及到的官方能力与 API 我都标了出处,便于你对照文档实现与扩展。(华为开发者)
架构一眼懂
- Camera:
@kit.CameraKit建会话 → 绑定 Preview 输出(给 UI)+ ImageReceiver(给推理)。(华为开发者) - Preprocess:
@kit.ImageKit把Image/PixelMap统一到模型输入尺寸,做 resize / color convert / normalize。(华为开发者) - Classifier(ImageClassifier):基于 @ohos.ai.mindSporeLite;负责 模型加载、tensor 填充、predict、Top-K。提供 CPU → NNRt/NPU 的可选加速(视设备能力与模型而定)。(华为开发者)
- UI 展示:ArkUI 页面上显示相机预览 + 右上角 Top-K 标签;点击卡片可展开完整标签列表或“历史识别记录”。
一、图像采集:稳定、低延迟地拿到像素
1)权限与能力声明(必做)
ohos.permission.CAMERA(拍摄)+ 如需落盘到媒体库,再加媒体库读写权限。- 机能来自 Camera Kit;预览/拍照/录像都走同一会话体系。(华为开发者)
2)会话搭建思路
- 创建
CameraManager,选择后置/前置摄像头。 - 创建
PreviewOutput(给 UI 组件 Surface)与ImageReceiver(给推理线程吃帧)。 - 组装
Session,start()即可跑预览并回调帧。
HarmonyOS 的相机最佳实践里有“拍照与取流”的完整流程,
ImageReceiver则由 Image Kit 提供,它可以从Surface中接收帧(YUV/JPEG)。(华为开发者)
关键片段(ArkTS,思路示例):
import { camera } from '@kit.CameraKit';
import { image } from '@kit.ImageKit';
let camMgr: camera.CameraManager;
let session: camera.CaptureSession;
let preview: camera.PreviewOutput;
let receiver: image.ImageReceiver;
async function setupCamera(surfaceForUI: any /* e.g. XComponent surface */) {
camMgr = camera.getCameraManager();
const cam = camMgr.getSupportedCameras()[0]; // 简化演示:挑第一个
// 1) 预览输出(绑定到 UI 的 Surface)
preview = camMgr.createPreviewOutput(surfaceForUI);
// 2) 推理用的 ImageReceiver(从摄像头会话拿帧)
receiver = image.createImageReceiver({
width: 640, height: 480, // 取个推理友好分辨率
format: image.ImageFormat.YUV_420_SP, // 常见 YUV
capacity: 4
});
const inferSurface = receiver.getReceivingSurface();
// 3) 组装会话
session = camMgr.createCaptureSession();
await session.beginConfig();
await session.addInput(camMgr.createCameraInput(cam));
await session.addOutput(preview);
await session.addOutput(camMgr.createPreviewOutput(inferSurface)); // 给推理流
await session.commitConfig();
await session.start();
// 4) 帧回调(你可以按 5~10FPS 抽帧推理)
receiver.on('imageArrival', () => onImageArrival());
}
ImageReceiver/ImageFormat/getReceivingSurface()等能力见 Image Kit;预览/拍照 Session/Output 能力见 Camera Kit。(华为开发者)
二、模型推理(ImageClassifier):装进 MindSpore Lite,跑在端侧
1)为什么选 MindSpore Lite?
HarmonyOS 内置的端侧推理引擎,支持 ArkTS 直调;提供 模型加载、张量输入/输出、预测 的统一接口,并能利用 CPU/NNRt/NPU 等后端(取决于设备与模型)。官方还提供了图像分类 Codelab 示例与开源 Demo,直接拿来改就行。(华为开发者)
文档要点:ArkTS 里通过
@ohos.ai.mindSporeLite加载.ms模型,设置上下文(线程数/设备类型),predict()返回输出张量,随后解析为概率分布并取 Top-K。(华为开发者)
2)工程准备
-
把模型(如
mobilenetv2.ms)与labels.txt放到resources/rawfile。 -
在
syscap.json里加上{ "development": { "addedSysCaps": ["SystemCapability.AI.MindSporeLite"] } } -
UI 层引入
@kit.MindSporeLiteKit(ArkTS 绑定)。(华为开发者)
3)封装一个 ImageClassifier(加载→预处理→预测→Top-K)
// classifier/ImageClassifier.ets
import { mindSporeLite as msl } from '@kit.MindSporeLiteKit';
import { image } from '@kit.ImageKit';
type TopK = { label: string; prob: number };
export class ImageClassifier {
private model?: msl.Model;
private labels: string[] = [];
private inputW = 224; private inputH = 224; // 以 MobileNet 为例
private mean = [0.485, 0.456, 0.406];
private std = [0.229, 0.224, 0.225];
async load(modelBuf: ArrayBuffer, labelsText: string) {
const ctx = msl.createContext({ // 线程、后端按需配置
deviceType: msl.DeviceType.CPU, // 设备若支持 NNRt/NPU,可切换
threadNum: 2
});
this.model = await msl.loadModelFromBuffer(modelBuf, ctx);
this.labels = labelsText.split('\n').filter(Boolean);
}
// 把 PixelMap 预处理成 Float32Array(NHWC) 或 NCHW(看模型)
async preprocess(pix: image.PixelMap): Promise<Float32Array> {
const resized = await pix.scaleToFit({ // ImageKit 支持缩放/裁剪
size: { width: this.inputW, height: this.inputH }
});
const info = resized.getImageInfo();
const pixels = new Uint8Array(info.size.width * info.size.height * 4);
await resized.readPixelsToBuffer(pixels.buffer); // RGBA
const out = new Float32Array(this.inputW * this.inputH * 3);
for (let i = 0, o = 0; i < pixels.length; i += 4) {
const r = pixels[i] / 255;
const g = pixels[i+1] / 255;
const b = pixels[i+2] / 255;
out[o++] = (r - this.mean[0]) / this.std[0];
out[o++] = (g - this.mean[1]) / this.std[1];
out[o++] = (b - this.mean[2]) / this.std[2];
}
return out;
}
async predictNHWC(input: Float32Array, topK = 3): Promise<TopK[]> {
if (!this.model) throw new Error('model not loaded');
const inputs = this.model.getInputs();
// NHWC: [1, H, W, 3]
inputs[0].setData(input.buffer);
const outputs = await this.model.predict(inputs);
const logits = new Float32Array(outputs[0].getData());
return this.softmaxTopK(logits, topK);
}
private softmaxTopK(logits: Float32Array, k: number): TopK[] {
const max = Math.max(...logits);
const exps = logits.map(v => Math.exp(v - max));
const sum = exps.reduce((a,b)=>a+b, 0);
const probs = exps.map(v => v / sum);
// 取 Top-K
const idx = [...probs.keys()].sort((a,b)=>probs[b]-probs[a]).slice(0, k);
return idx.map(i => ({ label: this.labels[i] ?? `#${i}`, prob: probs[i] }));
}
}
以上流程与官方“ArkTS 实现图像分类”的开发步骤一致:加载模型 → 准备输入张量 → 调用
predict()→ 解析输出。你也可以直接参考开源 Demo 的目录结构与代码组织。(华为开发者)
4)把相机帧喂给分类器
把 ImageReceiver 拿到的 Image 转成 PixelMap(或先转 JPEG/ARGB)后做预处理,再调用 predictNHWC()。建议抽帧(如每秒 5 次),并把推理放到 TaskPool/Worker,避免占用 UI 线程。
import { image } from '@kit.ImageKit';
import { ImageClassifier } from './classifier/ImageClassifier';
const clf = new ImageClassifier();
let busy = false;
async function onImageArrival() {
if (busy) return; // 抽帧+限流
busy = true;
try {
const img: image.Image = await receiver.readImage();
const pix = await image.createPixelMap(img); // 简化:按机型可能需要先做YUV->RGB
const input = await clf.preprocess(pix);
const top3 = await clf.predictNHWC(input, 3);
updateUI(top3); // 在页面状态里更新
img.release(); pix.release();
} catch (e) {
// 日志&容错
} finally {
busy = false;
}
}
三、结果展示:把“模型话术”变成人话
1)UI 设计要点
- 叠加层:在预览画面上方用
Positioned/Row贴一个半透明卡片,展示 Top-K(标签 + 置信度条); - 交互:点击卡片可展开完整概率列表;新增“截图/保存结果到相册”;
- 状态:用
@State缓存最近 N 次结果,支持“结果历史”。
页面示意(片段):
@Entry
@Component
struct LiveClassifyPage {
@State topk: {label: string; prob: number}[] = [];
build() {
Stack() {
// 1) 相机预览(XComponent 或 ImageSurface)
CameraPreview() // 这里是你封装的预览组件
// 2) 结果叠加
Column() {
ForEach(this.topk, (it) => {
Row() {
Text(it.label).fontSize(16).layoutWeight(1)
Progress({ value: Math.round(it.prob * 100) })
.width('40%')
}
.padding(8).backgroundColor('#66000000').borderRadius(12)
.margin({ bottom: 6 })
})
}.align(Alignment.TopEnd).padding(12)
}
}
}
端到端打通(落地 checklist)
- 相机:按最佳实践创建 Session,绑定 UI 预览 +
ImageReceiver,注册imageArrival回调。(华为开发者) - 图像处理:用 Image Kit 把帧转
PixelMap并 resize/normalize,注意 YUV→RGB 的转换策略(不同机型输出格式不同)。(华为开发者) - 推理:
@ohos.ai.mindSporeLite加载.ms,设置上下文,predict()得到 logits,做 softmax/Top-K。(华为开发者) - 展示:UI 叠加层展示 Top-K,提供“历史记录/截图/分享”。
- 性能:抽帧(5~10FPS)+ Worker 推理 + 预热模型(首帧前先跑一次 dummy)+ 输入尺寸与后端(CPU/NNRt)调优。(华为开发者)
性能与稳定性小抄
- 吞吐:优先 减小输入分辨率(如 224×224 / 256×256),抽帧限频;
- 延迟:模型预热;在可用设备上启用 NNRt/NPU 后端(若模型兼容);(华为开发者)
- 内存:复用
PixelMap/ 预分配Float32Array; - 容错:相机热插拔、权限拒绝、
ImageReceiver容量溢出时的降级处理; - 模型:用量化/裁剪的轻量模型(MobileNet、ShuffleNet、EfficientNet-Lite 等);MindSpore Lite 支持多框架模型经转换为
.ms。(华为开发者)
可扩展:从“单标签”到“智能标签系统”
- 多任务:在同一帧上并行跑分类 + 目标检测(检测到的 bbox 再跑局部分类);
- 本地标签库:允许业务上传/下发
labels.txt; - 业务规则:给标签绑定跳转动作/推荐卡片(例如识别到“咖啡”,弹出“附近咖啡店”);
- 离线包:模型与标签随版本迭代,放
rawfile或首次启动下发到filesDir。
参考与可复用资源(强烈建议对照实现)
- Camera Kit:相机会话/输出/最佳实践(ArkTS)。(华为开发者)
- Image Kit:
ImageReceiver/PixelMap/scale/读写像素等(ArkTS)。(华为开发者) - MindSpore Lite(ArkTS)图像分类指南:从模型加载到推理完整流程;含接口说明与示例。(华为开发者)
- 官方开源示例:MindSporeLiteArkTS(图片分类 Demo)。(Gitee)
- MindSpore Lite Kit 简介:后端加速、模型格式/转换与端侧优化。(华为开发者)
…
(未完待续)
更多推荐





所有评论(0)