HarmonyOS 6 轻相机应用开发4:物品分类功能实现
引言
之前章节实现了基础的贴纸和滤镜效果,接下来就要开始接入AI能力。如果说滤镜是艺术的装点,那么 AI 识别就是相机的"智慧之眼"。
今天,我们将挑战更高级的能力:全能识物 。我们将探讨如何基于 MindSpore Lite 端侧推理引擎,在 HarmonyOS 6 上实现毫秒级的物品检测与分类,并让结果实时跃然于预览之上。
技术方案:一机多模的动态架构
在"轻相机"的设计中,我们没有为每种识别写一套逻辑,而是构建了一个名为 FaceDetector(实为通用推理器)的 C++ 基类。
它通过 NAPI 暴露了 switchAiMode 接口,允许我们在运行时动态切换模型文件。只需一行代码,相机就能从"认脸"切换到"识物":
typescript
// ArkTS 层发起模式切换
CameraController.switchAiMode("models/detect_regular_nms_quant.ms", 320, 320);
底层 C++ 引擎会自动解析新模型的 Input/Output 维度,重新配置内存池,并继续接收来自预览流的 YUV 指针进行异步推理。
模型与数据集:百类识物
我们采用了一个经过量化的 SSD(Single Shot MultiBox Detector) 模型。该模型不仅能返回物体的位置(Bounding Box),还能根据内置的 OBJECT_LABELS 词典返回高达 300+ 类的分类标签(如:Cat, Bottle, Mobile phone, Cup 等)。
这些标签被定义在 ObjectDetectionUtil.ets 中,作为后期视觉渲染的依据。
核心算法:SSD 坐标解码与 NMS 抑制
由于神经网络输出的是原始的张量(Tensor),我们需要在 ArkTS 层进行数学解码。
1. SSD 坐标解码
模型输出的坐标通常是相对于 Anchor(锚点)的偏移量。我们需要根据输入比例(如 320x320)计算回真实坐标:
typescript
static decodeSSD(reg: number[], cls: number[], anchors: SSDAnchor[], scoreThresh: number): DetectedObject[] {
const results: DetectedObject[] = [];
for (let i = 0; i < anchors.length; i++) {
let score = 1.0 / (1.0 + Math.exp(-cls[i])); // Sigmoid 激活
if (score > scoreThresh) {
const p = i * 4;
const cx = reg[p] + anchors[i].x_center;
const cy = reg[p + 1] + anchors[i].y_center;
// ... 转换为标准化矩形坐标 [x1, y1, x2, y2]
}
}
return results;
}
2. 非极大值抑制(NMS)
为了解决同一个物体被多个检测框覆盖的问题,我们引入了 NMS 算法,保留置信度最高且重叠度(IOU)最小的候选框。
ArkTS 实战:实时识物与视觉反馈
识别结果通过 registerFaceListener(通用 AI 回调)异步返回给 UI。我们在主界面的 Canvas 叠加层上进行动态绘制:
typescript
private renderAIOverlay() {
if (!this.context) return;
this.context.clearRect(0, 0, this.compWidth, this.compHeight);
if (this.aiMode === 'object') {
this.detectedObjects.forEach((obj) => {
// 绘制定制化的青色识别框
this.context.lineWidth = 3;
this.context.strokeStyle = '#22D3EE';
this.context.strokeRect(x, y, w, h);
// 绘制半透明标签底色
this.context.fillStyle = 'rgba(34,211,238,0.8)';
this.context.fillRect(x, y - 30, textWidth + 16, 30);
// 绘制识别名称与置信度
this.context.fillStyle = Color.Black;
this.context.fillText(`${obj.label} ${(obj.score*100).toFixed(0)}%`, x + 8, y - 8);
});
}
}
为了保证渲染性能,我们将 renderAIOverlay 放在 @Watch('onAIUpdate') 中,只有当 AI 结果真实变化时,才触发 Canvas 的重绘,显著降低了预览时的 CPU 占用。
进阶:如何将 AI 识别框"拍"进照片?
很多开发者发现,预览时有框,但拍出来的照片却空空如也。
在"轻相机"中,我们利用 OffscreenCanvas(离屏画布) 解决了这一痛点。在执行 saveCapturedPhoto 时,我们将原始图片数据解析为 PixelMap,然后在其上层同步绘制当前的识别框,最后再执行编码保存:
typescript
// 1. 绘制原始底图
ctx.drawImage(bgPixelMap, 0, 0, imgW, imgH);
// 2. 按照比例缩放并叠加 AI 识别信息
if (this.saveAIOverlays) {
this.detectedObjects.forEach(obj => {
ctx.strokeRect(obj.x1 * imgW, obj.y1 * imgH, ...);
});
}
// 3. 统一打包保存
const finalPixelMap = ctx.getPixelMap(0, 0, imgW, imgH);
效果展示
点击AI按钮出现选择AI智能模式后选择全物识别,当识别到物品时会通过框线标记出来:


总结
到这里,"轻相机"系列的核心功能------从底层预览到高阶滤镜,再到实时贴纸与 AI 识物------已经全部落实。
通过这四篇文章的实战,我们深度探索了 HarmonyOS 6 在 Native 渲染与本地 AI 推理方面的强大底蕴。希望这一系列能够成为你探索鸿蒙应用开发的助推器,开发出更多具有"智慧灵魂"的作品。