【共创稿事节】鸿蒙图像超分 · 文档增强工作台:把内置演示图换成纸面文档、深色暗房 UI、滑动对比一键落盘
【共创稿事节】鸿蒙图像超分 · 文档增强工作台把内置演示图换成纸面文档、深色暗房 UI、滑动对比一键落盘前面几篇把端侧超分的「能力链路」跑通了单图重建、批量落盘、相册直选、壁纸适配。这一篇不堆新能力而是回头解决一个很容易被忽略、却最影响「演示观感」的问题——内置演示素材与应用主题不匹配。「清晰卷」这款 App 主打的是文档/纸面内容超分讲义小字、收据标签、白板照片让模糊的印刷体重新可读。可它的内置演示图却是一张城市夜景——霓虹、月亮、万家灯火。用户一点「载入演示图」看到的和「文档增强」八竿子打不着。这就像卖扫描仪的柜台摆着一张风景明信片当样品。这一篇就做一件事但把它做透用一张合成的纸面文档替换掉那张「文不对题」的风景演示图顺带把整套 UI 从「蓝色卡片模板」重构成「深色暗房风」让演示素材、应用名、视觉风格三者第一次真正对齐。整个过程涉及素材的程序化合成、rawfile 资源的加载链路、低清/高清图对的尺寸约定、以及一套和第五篇截然不同的深色界面。工程地址LI_harmonyOS/Image-Super-Resolution/paper-clarity-studio运行环境HarmonyOS 7.0API 26 ArkTS 严格模式已在 Mate 90 Pro 模拟器7.0.0/26.0.0安装运行验证。包名com.example.paperclaritystudio为读取相册照片申请了ohos.permission.READ_IMAGEVIDEOuser_grant含 reason/usedScene。一、先看最终效果整条链路只有四步选文档类型 → 载入演示图或相册选图→ 端侧超分 → 滑动对比并保存。以下截图均为模拟器真机运行。空态首屏深色系预览区是虚线描边的「尚未载入文档」占位右上角胶囊实时标注当前是「端侧 REAL」还是「演示 DEMO」点「载入演示图」预览区立刻铺出那张纸面文档——标题《关于提高大学生心理健康水平的几点思考》、三节正文、一张发生率调查表全部清晰可辨。这就是本篇的核心演示素材第一次和「文档增强」主题对上了点「开始图像超分」进入对比态左侧「原图」低清发虚、右侧「演示」超分后清晰中间一条琥珀金分割线 圆手柄。下方滑块同步控制弹出的 toast 提示「演示对比已生成」把分割线向左拖右侧高清区扩大可以清楚看到表格里的数字12.5、15.8、18.2……从发虚到锐利的差别。底部主按钮文案也随状态切换为「保存高清图到沙箱」拖到最左几乎整屏都是超分后的清晰文档正文笔画和表格线的边缘重建一目了然点「保存高清图到沙箱」数据带下方亮起绿色回执「已保存到应用沙箱 · 324 KB」toast 提示「增强图片已保存」二、问题出在哪一张「文不对题」的演示图「清晰卷」的功能定义非常聚焦——文档超分。但它的内置演示图是从一个风景 Demo 里直接搬过来的城市夜景主题错位用户想验证的是「小字能不能变清晰」结果给的是「夜景能不能放大」两者评测维度完全不同。文档超分看的是笔画边缘、表格细线、数字锐度风景超分看的是色块、光斑、远景层次。用风景图验证文档能力结论没有意义。观感割裂应用名叫「清晰卷 · DOCUMENT ENHANCER」预设里写着「讲义书页 / 收据标签 / 白板照片」结果演示图是夜景——第一眼就让人怀疑「这到底是不是做文档的」。更要命的是这张夜景图是写死在代码常量里的// 改之前演示素材指向风景图constDEMO_LOW:stringdemo/low_city.png;constDEMO_HD:stringdemo/hd_city.png;constDEMO_LOW_W:number160;constDEMO_LOW_H:number120;// 4:3 横版constDEMO_HD_W:number640;constDEMO_HD_H:number480;也就是说换素材不只是「丢两张图进 rawfile」那么简单——图的宽高比、解码尺寸、低清/高清的倍数关系全都和代码常量强耦合。文档是竖版约 1:1.41 的 A4 比例风景是横版4:3直接替换文件不改尺寸预览就会拉伸变形。这正是本篇要系统性处理的地方。三、第一步程序化合成一张「以假乱真」的纸面文档与其去网上找一张带版权风险的文档照不如用代码直接画一张。好处是内容可控、分辨率可控、还能顺便批量生成各种降质版本做横向测试。我用 Python PIL 写了个生成器分两步。3.1 先画一张高清「母版」母版按 A4 竖版比例1240×1754合成把真实纸面文档的几个关键特征都模拟出来内容结构居中大标题 三节正文引言/现状/对策 一张带边框的数据表格纸面质感暖灰底色234,231,224 随机噪点避免「死白」的数字感不均匀光照左下角压暗、右上角提亮模拟手机拍摄时手部遮挡的阴影——这是真实文档照最典型的退化轻微失焦整体GaussianBlur(0.6)模拟手持拍摄的轻微跑焦。defbuild_master():imgImage.new(RGB,(W,H),(234,231,224))# 暖灰纸面dImageDraw.Draw(img)# … 随机噪点打底 …# 标题 / 三节正文 / 表格逐段绘制# 不均匀光照用竖向渐变 L 通道 高斯模糊左下压暗lightImage.new(L,(W,H),0)ldImageDraw.Draw(light)foriinrange(0,H,6):ratioi/H ld.rectangle([0,i,W,i6],fillint(30-60*ratio))lightlight.filter(ImageFilter.GaussianBlur(120))imgImage.composite(img,Image.blend(img,dark,0.45),light)returnimg.filter(ImageFilter.GaussianBlur(0.6))# 轻微失焦合成出来的母版结构和观感都贴近真实讲义3.2 再从母版「退化」出低清版超分要对比就得有一对同源、同构图、仅分辨率/质量不同的图。低清版直接从母版「退化」而来一个degrade()函数搞定所有档位defdegrade(img,out,target_h,quality,noise,blur):rtarget_h/img.height mimg.resize((int(img.width*r),target_h),Image.LANCZOS)# 降采样mm.filter(ImageFilter.GaussianBlur(blur))# 模糊# … 叠加噪点 …mImageEnhance.Contrast(m).enhance(0.92)# 降对比m.save(out,JPEG,qualityquality)# 压缩我一口气生了三档方便横向对比不同输入质量下的超分表现档位尺寸JPEG 质量用途轻度 720p509×72085看小幅提升中度 480p339×48055对比最明显推荐重度 360p254×36028加噪点极限测试三档并排看退化程度递进正文从「能读」到「发虚」再到「几乎看不清」中度 480p 这一档正文笔画已经开始粘连、表格数字发虚但版面结构完整——是验证超分重建能力的最佳输入。四、第二步生成内置演示素材接通加载链路有了图接下来把它变成 App 的内置演示素材。这里有个关键约定要讲清楚。4.1 低清/高清的「4 倍」约定Core Vision Kit 的端侧超分是固定 4 倍放大输入W×H输出4W×4H。所以内置演示素材必须是一对严格 4 倍关系的图低清输入doc_paper.png160×226演示模式下的「原图」高清结果hd_doc_paper.png640×904恰好 4×演示模式下的「超分输出」640×904 ÷ 160×226 4×4严丝合缝。这样演示模式的「假超分」直接读高清图和真机模式的「真超分」NPU 4× 推理在尺寸上完全一致UI 不用为两种模式做区分。LOW_W,LOW_H160,226# 低清输入HD_W,HD_H640,904# 超分结果 4xlowmaster.resize((LOW_W,LOW_H),Image.LANCZOS).filter(ImageFilter.GaussianBlur(0.7))low.save(OUT/doc_paper.png,PNG)hdmaster.resize((HD_W,HD_H),Image.LANCZOS)hd.save(OUT/hd_doc_paper.png,PNG)两张图落到entry/src/main/resources/rawfile/demo/目录。低清输入特意再叠一层 0.7 的高斯模糊让「待超分」的发虚感更明显4.2 改常量完成切换素材就位后切换只需要改PhotoLabService.ets顶部的六个常量——把风景图的文件名和尺寸换成文档图的// 改之后演示素材指向文档图竖版 A4 比例constDEMO_LOW:stringdemo/doc_paper.png;constDEMO_HD:stringdemo/hd_doc_paper.png;constDEMO_LOW_W:number160;constDEMO_LOW_H:number226;// 竖版constDEMO_HD_W:number640;constDEMO_HD_H:number904;这就是「文件名 宽高比 解码尺寸」三者强耦合的具体体现少改一个要么图片找不到解码失败返回 null要么尺寸错预览拉伸。改对之后下游两个方法自动跟着切换无需再动载入演示图——读低清版作为「原图」publicasyncloadDemo():PromiseLoadedImage|null{constpixelMapawaitthis.decodeRawfile(DEMO_LOW,DEMO_LOW_W,DEMO_LOW_H);if(!pixelMap){returnnull;}constinfoawaitpixelMap.getImageInfo();return{lowPm:pixelMap,lowW:info.size.width,lowH:info.size.height,srcW:info.size.width,srcH:info.size.height};}演示模式「超分」——直接读高清版冒充推理结果}elseif(!useReal){highPmawaitthis.decodeRawfile(DEMO_HD,DEMO_HD_W,DEMO_HD_H);}而decodeRawfile这条底层解码链路是素材无关的从 rawfile 读字节流、按目标尺寸解码成 RGBA_8888 的 PixelMap对风景图和文档图一视同仁——所以这次替换完全没有触碰解码逻辑privateasyncdecodeRawfile(name:string,width:number,height:number){constcontent:Uint8Arrayawaitthis.context.resourceManager.getRawFileContent(name);constsourceimage.createImageSource(content.buffer);constpixelMapawaitsource.createPixelMap({desiredSize:{width:width,height:height},desiredPixelFormat:image.PixelMapFormat.RGBA_8888});awaitsource.release();returnpixelMap;}整个替换过程业务代码只改了 6 行常量符合「素材与逻辑解耦」的设计——这也是前几篇打下的底子。五、第三步把 UI 从「蓝色卡片」重构成「深色暗房」换素材的同时我顺手把整套界面也重写了。原因很直接之前的 UI 和第五篇是同一套模板——顶部渐变横幅、白色圆角卡片、蓝色主按钮布局和配色几乎一样。既然是新的应用、新的主题视觉上就该彻底区分开。重构思路是把它做成一个「扫描暗房」墨黑底色#0B0D12营造暗房氛围琥珀金#F5C86B做点缀像暗房里那盏安全灯。整体结构从上往下是四段顶部深色 AppBar应用名「清晰卷」 英文小字「DOCUMENT ENHANCER」右侧一颗胶囊用绿/金圆点实时区分「端侧 REAL / 演示 DEMO」中部主对比区深色容器#14171E承载图像空态是虚线描边占位分隔线和手柄用琥珀金数据带 预设输入/输出/增益三个数据用竖线分隔成一条横带文档类型讲义书页/收据标签/白板照片做成深色分段选项卡底部固定操作栏琥珀金实心主按钮开始超分/保存下方三个次要按钮相册/演示/重置平铺。分隔线和对比手柄的绘制是这套 UI 里最有「暗房感」的细节——一条 2px 的琥珀金竖线 一个实心圆手柄叠在图像上随split百分比移动Column().width(2).height(PREVIEW_HEIGHT).backgroundColor(#F5C86B).position({x:this.previewWidth*this.split/100-1,y:0})Text(⇄).width(30).height(30).fontColor(#14161C).backgroundColor(#F5C86B).borderRadius(15).position({x:this.previewWidth*this.split/100-15,y:PREVIEW_HEIGHT/2-15})底部主按钮则根据状态动态切换文案和可用性——忙时置灰显示「处理中…」结果态显示「保存高清图到沙箱」平时是「开始图像超分」Button(this.busy?处理中…:(this.stageresult?保存高清图到沙箱:开始图像超分)).backgroundColor(this.busy||(this.stage!result!this.lowPm)?#2A303C:#F5C86B).fontColor(this.busy||(this.stage!result!this.lowPm)?#6E7482:#14161C).enabled(!this.busy(this.stageresult||this.lowPm!null))值得强调的是这次 UI 重构只动了视觉层业务逻辑一行没改。服务调用、stage状态机empty/preview/result、PixelMap的release()内存管理、滑动对比的split计算全部原样保留。这也再次验证了前几篇「UI 与逻辑分离」的价值——换皮肤不伤筋动骨。六、横向测试三档输入下的超分表现素材和界面都就绪后我用前面生成的三档降质图做了一轮横向验证。把它们传进模拟器相册用「从相册选择」逐档导入跑超分看效果720p 轻度原图本就接近可读超分后主要是笔画边缘更锐、表格细线更利落提升偏「润色」480p 中度差异最戏剧化——发虚粘连的正文重新根根分明表格里的「18.2」「13.6」等小数从糊成一团到清晰可读增益带上的百分比也最高360p 重度噪点原图已接近不可读超分能找回版面结构和大字标题但密集小字难免有涂抹感——这也如实反映了端侧轻量模型的能力边界不会为了演示效果而伪装成「无所不能」。应用右上角那颗「端侧 REAL / 演示 DEMO」的状态胶囊正是为了把这种「诚实」摆在明面上真实模式只展示分析器返回的结果推理失败就明确提示绝不拿演示图冒充真机效果。七、收尾这一篇到底做了什么回看这一篇没有引入新的超分能力做的都是「对齐」的功夫素材对齐主题把内置演示图从城市夜景换成纸面文档让「文档增强」这个卖点第一次有了匹配的样品素材与代码解耦厘清了 rawfile 演示素材「文件名 宽高比 解码尺寸 4 倍关系」的耦合点替换只改 6 行常量视觉对齐气质UI 从蓝色卡片模板重构为深色暗房风与第五篇彻底区分且不伤业务逻辑测试对齐真实程序化合成 三档退化既能批量造图又能横向对比不同输入质量下的超分表现诚实标注真实/演示模式。很多时候一个 Demo 给别人的「专业感」恰恰藏在这些细节里演示素材是不是贴合主题、界面是不是有辨识度、测试数据是不是站得住脚。这一篇把这三件事一次性补齐了。配套脚本test_images/gen_test_images.py合成文档母版 三档降质、test_images/make_demo_assets.py生成 rawfile 演示素材。想换文档内容、调分辨率或噪点强度改参数重跑即可。本文工程与脚本均已开源在对应目录运行截图来自 Mate 90 Pro 模拟器HarmonyOS 7.0 / API 26实机。