在这里插入图片描述
在这里插入图片描述

一、前言思考

1.1 图像 AI 是端侧 AI 的"入口级"能力

手机 App 里最高频的 AI 场景,一半以上是图像:扫码、拍照翻译、文档扫描、人脸解锁、AR 导航、相册分类。图像 AI 之所以适合端侧:

  • 摄像头数据本来就在本地,端侧处理零上传成本、零隐私风险;
  • 目标检测/分类/OCR 的模型可以压到 10~100MB,端侧轻松承载;
  • 实时性要求高(人脸跟踪要 30fps),网络延迟不可接受。

1.2 本文要解决的核心问题

  1. 多个视觉能力(检测/分类/OCR/人脸)如何组织成高效管线;
  2. CameraKit 实时帧如何喂给 AI 模型并回渲结果;
  3. 多模型并发时如何管理资源不卡顿。

二、底层原理

2.1 实时视觉推理管道

摄像头(CameraKit) ──▶ 帧回调(30fps) ──▶ 预处理(缩放/归一化/NV21→RGB)
                                              │
                                              ▼
                                     ┌──▶ 目标检测模型(YOLO) ──▶ 检测框
                             并行 ──▶ ├──▶ 图像分类模型(分类) ──▶ 类别标签
                                     ├──▶ OCR模型(PP-OCR) ────▶ 文本行
                                     └──▶ 人脸检测模型 ────────▶ 人脸框/关键点
                                              │
                                              ▼
                                    结果合并 → 渲染 Overlay → 屏幕显示

关键设计:预处理单次、多模型共享。一帧数据只做一次缩放和颜色转换,四个模型复用同一份预处理结果,避免重复计算。

2.2 模型管线(Pipeline)而非单模型

单模型精度再高也有局限,真实场景需要多模型接力:

文字识别全流程:
  检测模型(找文字区域) → 方向分类(旋转校正) → 识别模型(逐行转文字)
阶段 模型 输出
文本检测 DB/DBNet 文字行 bounding box
方向分类 轻量分类器 0°/90°/180°/270°
文本识别 CRNN/SVTR 字符序列

管线设计让每一段都可以单独替换升级(检测模型升级不影响识别部分)。

2.3 结果渲染 Overlay

AI 结果叠加在相机预览上,要求低延迟、无闪烁

Stack {
  CameraPreview   // 底层: 相机画面
  CanvasOverlay   // 顶层: 检测框/文字/关键点
}

渲染要点:

  • 用 Canvas 绘制,避免频繁创建组件;
  • 坐标系转换:模型输出是归一化坐标 → 映射到预览视图坐标;
  • @State 管理框列表,变化时局部重绘。

三、实战落地

3.1 CameraKit 取帧 + 模型推理

import { camera } from '@kit.CameraKit';
import { mindSporeLite } from '@kit.MindSporeLite';

// 1. 创建图像模型(OCR 为例)
const model = new mindSporeLite.Model();
await model.loadModelFromFile({ modelPath: '/data/app/el1/ocr.ms' });

// 2. 相机会话取帧
const session = await camera.createSession(camera.SceneMode.NORMAL_PHOTO);
session.on('frameAvailable', async (frame: camera.Frame) => {
  // 3. 帧预处理(NV21 → 归一化张量)
  const input = preprocess(frame);  // 缩放 + 转RGB + 归一化

  // 4. 推理
  const outputs = model.predict([input]);

  // 5. 解析文字框
  const boxes = postprocess(outputs);

  // 6. 触发 UI 更新(必须在主线程)
  this.detections = boxes;
});

3.2 多模型并行 + 结果合并

// 用 TaskPool 把四个模型并行推理,避免串行耗时
import { taskpool } from '@kit.ArkTS';

@Concurrent
function runModels(frames: ArrayBuffer, modelPaths: string[]): string[] {
  const results: string[] = [];
  for (let i = 0; i < modelPaths.length; i++) {
    const model = new mindSporeLite.Model();
    model.loadModelFromFile({ modelPath: modelPaths[i] });
    const out = model.predict([frames]);
    results.push(JSON.stringify(out));
    model.free();
  }
  return results;
}

// 主线程调用
const result = await taskpool.execute(runModels, [frameData, modelPaths]);

注意:TaskPool 线程里创建模型要各自加载,模型加载是线程隔离的;推理前要预热(warmup 一次),否则首次推理慢。

3.3 拍照翻译完整流程(业务闭环)

// 1. 拍一张照
const image = await takePhoto();

// 2. OCR 识别文字
const textLines = await ocrRecognize(image);

// 3. 翻译每行(端侧翻译模型)
const translated = await translateLines(textLines);

// 4. 结果按原文位置回渲(Overlay 显示)
for (const line of translated) {
  overlay.addText(line.text, line.box, translatedText);
}

3.4 人脸识别 + 活体检测

// 人脸检测 + 关键点 + 活体
const faceResult = await faceDetect(image);  // 返回人脸框 + 72关键点
if (faceResult.faces.length === 0) {
  toast('未检测到人脸');
  return;
}
const liveness = await livenessCheck(faceResult.faces[0]);  // 眨眼/转头检测
if (liveness.passed) {
  // 特征比对(本地特征库,不传云端)
  const match = await faceCompare(feature, faceResult.faces[0].feature);
}

四、性能排查与优化

问题 表现 优化手段
预览卡顿 帧率 < 20fps 预处理降分辨率、推理移出主线程、跳过中间帧
检测延迟 结果滞后 1s 模型量化 INT8、批处理、帧队列丢弃
内存暴涨 多模型叠加 OOM 模型串行加载、共享预处理缓冲区
框抖动 检测框闪烁 跟踪平滑(IoU 关联 + 卡尔曼滤波)

4.1 掉帧策略(帧节流)

相机 30fps,但推理可能只有 15fps,需要队列 + 丢帧

let processing = false;
session.on('frameAvailable', (frame) => {
  if (processing) return;   // 正在处理上一帧,直接丢弃本帧
  processing = true;
  infer(frame).finally(() => { processing = false; });
});

4.2 预处理复用

四个模型共用一个预处理结果(缩放、颜色转换、归一化),能省 60% 预处理耗时:

const preprocessed = preprocessOnce(frame);  // 共享
const [det, cls, ocr, face] = await Promise.all([
  modelDet.predict([preprocessed.clone()]),
  modelCls.predict([preprocessed.clone()]),
  modelOcr.predict([preprocessed.clone()]),
  modelFace.predict([preprocessed.clone()])
]);

4.3 框平滑防抖

// 简单 IoU 关联 + 加权平均
function smoothBox(prev: Box, cur: Box): Box {
  const iou = calcIoU(prev, cur);
  if (iou < 0.3) return cur;           // 新目标,直接采用
  return {
    x: prev.x * 0.7 + cur.x * 0.3,     // 指数平滑
    y: prev.y * 0.7 + cur.y * 0.3,
    w: prev.w * 0.7 + cur.w * 0.3,
    h: prev.h * 0.7 + cur.h * 0.3
  };
}

五、总结

  1. 图像 AI 落地的关键是管线设计:检测→分类→识别多段接力,段段可替换。
  2. 实时性靠三招:预处理共享、推理移出主线程、帧节流丢帧
  3. Overlay 渲染要低延迟:Canvas 绘制 + 坐标换算 + 平滑防抖。
  4. 隐私优势是端侧图像 AI 的核心卖点:人脸特征、照片内容永不出设备

一句话记住:管线接力提精度,共享预处理提速度,丢帧节流保流畅,端侧处理保隐私。


🚀 演示功能优化(随项目同步更新)

本文对应的 ArkTS 演示页面已随项目整体优化,主要改进:

  1. 独立主题风格:深海青绿 · 渐变卡片 + 胶囊按钮,与其余章节演示页明显区分,不再千篇一律。
  2. 步骤回放动画:点击演示按钮后,结果行按 260~320ms/步 逐步展示,模拟真实推理过程。
  3. 运行态保护:演示过程中按钮置灰防重复触发,页面退出自动清理定时器。
  4. 结果摘要:演示结束后自动给出「一句话结论」,并 Toast 提示完成。
  5. AI 对话演示:新增 AiChatDemo(根目录 main.py 的 ArkTS 移植),真实 SSE 流式大模型请求,首页「★ AI Chat 流式对话演示」可进入。

对应页面:entry/src/main/ets/pages/ImageAiDemo.ets


🤖 AI 推理接入(v2 更新)

演示页已接入真实大模型推理:复用 main.py 的 GitCode Chat Completions SSE 流式接口(已封装为公共工具 utils/AiClient.ets,AiChatDemo / ImageAiDemo / VoiceAiDemo 共用)。

  1. AI 云端识别主卡:演示页顶部新增「🤖 AI 云端推理识别」卡片,内置 街景 / 文档 / 人像 三个场景,点击「开始 AI 推理识别」即真实请求 DeepSeek-V4-Flash,流式(打字机)生成「目标检测 + 置信度 + 场景/文本/人脸属性判断 + 端侧部署建议」识别报告。
  2. 端云协同演示:端侧用预置场景模拟视觉预处理(本页原有目标检测 / OCR / 人脸步骤回放保留),云端大模型负责语义级理解 —— 直观展示本篇「端云协同 AI」的工程形态。
  3. 一行接入:页面通过 AiClient.stream(system, user, { onDelta, onDone, onError }) 发起推理,返回取消函数在 aboutToDisappear 中调用,页面销毁自动断开请求、防止回调泄漏。

说明:main.py 接口仅支持文本生成,本页图像识别走「场景描述 → 云端大模型生成识别报告」的演示路径;真正的端侧视觉模型(目标检测 / OCR / 人脸)仍由模拟卡片演示。

Logo

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

更多推荐