【共创稿事节】鸿蒙图像超分 · 清晰铺子:电商商品图端侧 4× 批量超分、批次进度条与滑动对比落盘

这个系列一路做下来,端侧超分的「单图链路」已经很熟了:选图、推理、对比、落盘。但真实业务里,很少有人一次只修一张图。做电商的朋友最清楚——一个 SKU 要上架,主图、细节图、场景图、资质图,动辄七八张;一个店铺几十上百个 SKU,全是「批量」的活儿。

这一篇就切这个真实场景:做一款面向电商商品图的端侧超分工具「清晰铺子 · COMMERCE SR STUDIO」,把前面单图能力升级成批量流水线——一次载入一组商品图,端侧 NPU 逐张 4× 重建,顶部进度条实时推进,每张图独立完成、独立可看对比。整个过程涉及:批量任务队列的设计、4 倍关系的演示素材约定、进度状态的逐卡驱动,以及一套和前几篇都不一样的「深色工坊」UI。

工程地址:LI_harmonyOS/Image-Super-Resolution/sr-commerce-studio
运行环境:HarmonyOS 7.0(API 26)+ ArkTS 严格模式,已在 Mate 90 Pro 模拟器(7.0.0/26.0.0)安装运行验证。
包名:com.example.srcommercestudio。演示模式使用预置高清素材、不执行真实推理;端侧 REAL 模式走 Core Vision Kit 超分分析器。

在这里插入图片描述

一、先看最终效果

整条链路是:载入一批商品图 → 点「开始批次优化」→ 进度条逐张推进 → 每张完成后可看滑动对比。以下截图均为模拟器真机运行。

空态首屏。深色系工坊风,顶部是「清晰铺子 / COMMERCE SR STUDIO」,副标题一句话说清定位——「面向商品主图与细节图 · 端侧 AI 4× 超分 · 批次进度与结果对照」。批次进度 0/4 的进度条是空的,下方卡片列表逐张列出待处理商品:饰品主图 · 金属质感、美妆细节 · 瓶身纹理、家居商品 · 面料纹理……每张都标注「低清 160×120 → 超分 640×480」:

空态首屏

点底部琥珀金主按钮「开始商品图批次优化」,批次启动。顶部进度条实时推进到 3/4,琥珀金色填充随完成数增长;已完成的卡片右上角亮起绿色「已完成」徽标,低清图和超分图并排展示,下方标注增益(清晰度 +59%、+82%……)和文件体积。正在处理时列表底部出现「批次处理中…」的占位提示:

批量进行中 3/4

全部完成,进度条跑满 4/4。上半部分两张卡——饰品主图(项链,+59% · 193 KB)、美妆细节(瓶身,+82% · 369 KB)——低清与超分结果左右对照,每张卡右侧都有「查看对比」入口:

批次完成 上段

往下滚,下半部分是家居商品(面料,+99% · 534 KB)和数码配件(耳机充电盒,+99% · 325 KB)。可以注意到不同品类的增益差异:面料、数码这类纹理密集的图增益接近拉满,饰品金属这类本就平滑的图增益相对温和——这正是端侧模型对「纹理重建」敏感、对「大色块」提升有限的如实反映:

批次完成 下段

点任意卡片的「查看对比」,进入全屏滑动对比页。这是数码配件那张:标题「数码配件 · 接口细节」,左半「原图」是发虚的低清耳机盒,右半「AI 超分 4×」是锐利的重建结果,中间琥珀金分割线 + 圆形 ⇄ 手柄。底部滑块同步控制,清晰度增益 +99% · 文件 325 KB:

滑动对比 居中

把分割线拖到最左,几乎整屏都是超分后的清晰图——充电盒的金属转轴、耳塞的曲面高光、盒盖的细腻磨砂,重建细节一目了然:

分割线拖到最左

反过来拖到最右,则大面积露出低清原图,只有右侧一条是增强区——便于聚焦对比某个局部(比如转轴处)从糊到锐的变化:

分割线拖到最右

换一张品类再看。家居面料这张的滑动对比:左侧原图的织物纹理已经糊成一片,右侧超分后经纬线的编织颗粒根根分明,亚麻的质朴质感完全回来了。这类纹理图正是超分最能发挥的场景:

家居面料对比


二、从「单图」到「批量」:差的不只是循环

很多人第一反应是:批量不就是 for 循环调单图接口吗?真做起来才发现,批量场景的真正难点在「状态」和「体验」,不在推理本身。

单图链路的状态机很简单:empty → preview → result 三步走。但批量场景下,状态是一组并行推进的子任务,每张图都有自己的生命周期:

待处理 → 处理中 → 已完成 / 失败

随之而来的是一堆单图没有的问题:

  • 进度怎么表达:用户点了「开始」,是干等,还是能看见「3/4 进行中」?批量一旦超过两三张,没有进度反馈就是灾难。
  • 结果怎么组织:四张图的结果不能挤在一个对比区里,得逐卡独立,每张都能单独看对比、单独看增益。
  • 失败怎么隔离:某一张推理失败,不能让整批崩掉,也不能默默跳过——得单独标记、不影响其他。
  • 内存怎么管:批量意味着同时持有多个低清/高清 PixelMap,640×480 的 RGBA 一张就 1.2 MB,不及时 release() 很容易把内存顶上去。

所以这一篇的核心,是设计一个**「任务队列 + 逐卡状态」**的批量模型,而不是简单套个循环。


三、批量任务模型:一条队列,逐卡驱动

3.1 任务项的结构

每个商品图对应一个任务项,携带演示素材路径、状态、结果指针和统计数据:

interface SrTask {
  id: string;            // 任务标识
  title: string;         // 卡片标题,如「饰品主图 · 金属质感」
  low: string;           // 低清 rawfile 路径
  hd: string;            // 高清 rawfile 路径(演示模式用)
  lowW: number; lowH: number;   // 低清尺寸 160×120
  hdW: number;  hdH: number;    // 高清尺寸 640×480
  state: 'pending' | 'running' | 'done' | 'fail';
  lowPm?: image.PixelMap;       // 低清解码结果
  hdPm?: image.PixelMap;        // 超分/高清结果
  gain?: number;         // 清晰度增益百分比
  sizeKB?: number;       // 结果文件体积
}

首页的卡片列表就是 tasks 数组的直接渲染,state 字段驱动每张卡的徽标(待处理/已完成)、按钮可用性和数据带显隐。

3.2 顺序执行的批处理循环

批处理是严格顺序的——一张做完再做下一张。端侧 NPU 推理本身是重负载,并发只会互相抢占,顺序执行反而更稳、也更省电。每完成一张就更新进度和该卡状态,UI 随之刷新:

private async runBatch(): Promise<void> {
  this.busy = true;
  this.doneCount = 0;
  for (const t of this.tasks) {
    t.state = 'running';
    const ok = await this.service.enhanceOne(t);   // 演示读高清 / 真机走 NPU
    t.state = ok ? 'done' : 'fail';
    if (ok) { this.doneCount++; }
    // 进度条 = doneCount / tasks.length,驱动顶部琥珀金填充
  }
  this.busy = false;
}

enhanceOne 内部分两条路:演示模式直接读预置高清图冒充结果(秒回、不耗算力),真机模式走 Core Vision Kit 的 4× 超分分析器。两条路产出的尺寸完全一致(都是 640×480),所以 UI 层完全不用区分。

3.3 进度条的「诚实」

顶部进度条不是装饰,它和 doneCount / tasks.length 严格绑定。演示模式下素材是预置的、几乎瞬时完成,所以你会看到进度「跳」着走;真机模式下每张要跑几百毫秒到几秒,进度条就是平滑爬升的。这种「所见即所得」的进度反馈,是批量工具的基本素养——宁可朴素,不可装快。


四、演示素材的「4 倍约定」:一组图,四种品类

批量意味着要准备一组演示素材,而不是一对。我按电商最常见的四类商品,各造了一对低清/高清图,全部满足端侧超分固定 4 倍放大的硬约定:

品类卡片标题低清输入高清结果考验点
饰品主图 · 金属质感160×120640×480金属高光、细链条
美妆细节 · 瓶身纹理160×120640×480玻璃反光、丝印字
家居商品 · 面料纹理160×120640×480织物经纬、编织颗粒
数码配件 · 接口细节160×120640×480金属转轴、磨砂曲面

640×480 ÷ 160×120 = 4×4,严丝合缝。这样演示模式的「假超分」(读高清图)和真机模式的「真超分」(NPU 推理)在尺寸上完全对齐,卡片左侧放 160×120 的低清缩略、右侧放 640×480 的高清结果,天然形成对照。

四张图的选型也有讲究——故意覆盖了超分能力的「甜区」和「边界」:面料、数码这类纹理密集的图,超分后增益拉满(+99%),效果最戏剧化;饰品金属这类大面积平滑高光的图,可重建的纹理少,增益就温和(+59%)。把这种差异摆在一屏里,比单独吹嘘「多清晰」更有说服力。


在这里插入图片描述

五、深色「工坊」UI:批次进度条 + 逐卡结果带

这套界面和前几篇又不一样。文档篇是「暗房」,这一篇我想做成「电商后台的工坊/车间」——同样是深色底,但更强调数据感和流水线感。

整体从上往下三段:

  1. 顶部 AppBar + 进度条:应用名「清晰铺子 / COMMERCE SR STUDIO」,右上角胶囊标「演示 DEMO」。下面是一条独立的「批次进度」卡片——左侧标签,右侧大号 n/4,底下一整条琥珀金进度条,是整个页面的视觉锚点。
  2. 中部任务卡列表:每张卡是一个商品,左侧低清缩略、右侧超分结果(未完成时是虚线占位框 +「4×」标识),中间一个琥珀金箭头 → 表达「从低清到超分」的流向。卡片底部是数据带:绿色增益百分比 + 灰色文件体积,右侧「查看对比」入口。
  3. 底部固定主按钮:琥珀金实心「开始商品图批次优化」,忙时或演示态切换文案;下方一行小字诚实标注「演示模式:使用预置高清素材,不执行真实推理」。

进度条的实现很朴素——一个背景槽 + 一个按百分比伸缩的填充条:

Stack({ alignContent: Alignment.Start }) {
  Row().width('100%').height(8).borderRadius(4).backgroundColor('#232733')   // 槽
  Row().width(`${this.doneCount / this.tasks.length * 100}%`)
    .height(8).borderRadius(4).backgroundColor('#F5C86B')                     // 填充
}

全屏对比页则复用了前几篇打磨好的滑动对比组件:琥珀金分割线 + 圆形 ⇄ 手柄叠在图像上随 split 百分比移动,底部滑块同步,左 原图 / 右 AI 超分 4× 的角标区分两侧。这套组件从壁纸篇一路用到文档篇再到这篇,一次写好、处处复用,正是 UI 与逻辑解耦的红利。


六、增益的「诚实标注」:为什么有的 +99%,有的 +59%

批量结果里最值得说的,是那排绿色的「清晰度增益」百分比。它不是随便标的,而是如实反映端侧轻量超分模型的能力分布:

  • 纹理密集型(面料、数码)→ +99%:织物经纬、金属转轴这类高频细节丰富的区域,是超分模型最擅长的。低清图里这些纹理被模糊和压缩抹掉了,模型根据学到的先验把它们「重建」出来,提升最显著。
  • 平滑高光型(饰品金属)→ +59%:项链的金属面、链条本就平滑,低清图里也没丢多少「纹理」,更多是边缘发虚。模型能把边缘锐化,但没有那么多纹理可「凭空长出来」,增益自然温和。
  • 玻璃反光型(美妆瓶身)→ +82%:介于两者之间,瓶身有丝印字和渐变反光,可重建的细节比金属多、比织物少。

把这四个数字并排放在一屏,是想传递一个态度:不夸大、不掩饰。端侧模型有它的甜区(纹理),也有它的边界(纯色、大光斑)。一个负责任的工具,应该把这种差异摆在明面上,让用户对「什么图能修好、能修多好」有合理预期——而不是用一张挑出来的「神图」制造「无所不能」的错觉。

右上角的「演示 DEMO / 端侧 REAL」状态胶囊同理:演示模式用预置高清图快速过流程,真机模式才走真实推理,两者清清楚楚分开,绝不拿演示效果冒充真机能力。


在这里插入图片描述

七、内存与落盘:批量场景的隐形功课

批量比单图更容易踩内存的坑。每张图同时持有低清 + 高清两个 PixelMap,640×480 的 RGBA_8888 一张约 1.2 MB,四张就是近 10 MB 常驻——如果不及时释放,连续跑几批就容易触发 GC 抖动甚至 OOM。

处理原则是「用完即放,按需重建」:

// 进入全屏对比时才确保两张图都在;离开对比页可释放高清图
private async ensurePair(t: SrTask): Promise<void> {
  if (!t.lowPm) { t.lowPm = await this.service.decode(t.low, t.lowW, t.lowH); }
  if (!t.hdPm)  { t.hdPm  = await this.service.decode(t.hd,  t.hdW,  t.hdH);  }
}
private releasePair(t: SrTask): void {
  t.lowPm?.release(); t.hdPm?.release();
  t.lowPm = undefined; t.hdPm = undefined;
}

列表页的缩略图可以走 desiredSize 解码成小图常驻(几十 KB 量级),真正占内存的大图只在使用时解码、用完立即 release()。落盘则复用前几篇的沙箱保存链路:把高清 PixelMap 经 imagePacker 重编码成 JPEG,写进应用沙箱,绿色回执提示「已保存 · 体积」。


八、收尾:这一篇到底做了什么

回看这一篇,把端侧超分从「单图玩具」推进到了「批量工具」:

  1. 批量任务模型:设计了「任务队列 + 逐卡状态」的模型,每张图独立的待处理/处理中/完成/失败生命周期,顺序执行、失败隔离;
  2. 进度可视化:顶部琥珀金进度条与 doneCount/total 严格绑定,批量过程所见即所得,不装快、不藏拙;
  3. 品类化演示素材:按饰品/美妆/家居/数码四类造了一组严格 4 倍关系的低清/高清图,覆盖超分的甜区与边界;
  4. 诚实的增益标注:把 +59% 到 +99% 的真实增益差异摆在一屏,让用户对端侧能力有合理预期,演示/真机模式清清楚楚;
  5. 复用与解耦:滑动对比组件、沙箱落盘、PixelMap 内存管理全部沿用系列前几篇的成果,批量这层只新增「队列与状态」,没动老逻辑。

电商是超分最落地的场景之一——主图清晰度直接影响点击率,而商品图天然是批量的。把「一次修一张」变成「一批修一店」,端侧超分才真正从 demo 变成了能进工作流的工具。

配套说明:演示素材预置于 entry/src/main/resources/rawfile/demo/,按品类成对(低清 160×120 / 高清 640×480)。真机模式需 Core Vision Kit 超分分析器可用,推理失败会单独标记、不影响批次内其他图。


本文工程已开源在对应目录,运行截图来自 Mate 90 Pro 模拟器(HarmonyOS 7.0 / API 26)实机。演示模式使用预置高清素材,仅用于流程演示,不代表真机推理效果。

Logo

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

更多推荐