首页/新闻资讯/正文详情

【共创稿事节】鸿蒙应用图像超分·双镜头细节放大镜:端侧 AI 超分与普通放大的同屏实时对比

发布时间:2026/9/26 6:15:22 来源:云帆数科 栏目:资讯中心
【共创稿事节】鸿蒙应用图像超分·双镜头细节放大镜:端侧 AI 超分与普通放大的同屏实时对比
【共创稿事节】鸿蒙应用图像超分·双镜头细节放大镜端侧 AI 超分与普通放大的同屏实时对比本文是图像超分系列的第三篇。前两篇我们分别完成了「4 倍高清重建」主流程和「老照片修复」对比滑块这一篇我们把超分能力做成一个更直观、更有演示张力的形态——超分双镜头·细节对比放大镜手指按住图片移动两枚圆形镜头实时跟手左镜头是普通双线性放大 4× 的效果右镜头是端侧 AI 超分 4× 的效果同一取样位置、同一放大倍率差距一眼可见。页面下方的指标卡还会实时给出当前位置的「细节增益」百分比。文章配套完整可运行的工程源码sr-detail-loupe在 DevEco Studio 中 Sync 通过后直接运行到真机即可体验。全部推理与像素计算都在端侧完成数据不出设备。一、最终效果先看为快应用启动后进入「城市夜景 · 灯火」示例。图片本体是 160×120 的低清原图故意保留马赛克感两枚镜头默认悬停在图片中心左镜头灰色边框普通放大 4×双线性插值边缘发糊、锯齿感明显右镜头蓝色边框AI 超分 4×边缘干净、结构完整下方指标卡给出输入分辨率 / 超分分辨率 / 细节增益三项实时数据。按住图片拖动取样圆点跟手移动两枚镜头实时刷新当前取样位置的放大内容。把镜头拖到帆船边缘时对比最强烈左镜头里帆的斜边是一级一级的楼梯右镜头里则是平滑的斜线此时细节增益 10%镜头组会自动避开图片边缘手指靠近顶部时镜头自动落到下方靠近左右边时整体被夹回可视区内。每枚镜头下方都有标签普通放大 4×/AI 超分 4×截图、录屏时观众不需要解释就能看懂内置四组示例城市夜景、花朵特写、山峦日出、海边风景点「换一张」循环切换。不同素材的增益差异本身就是很好的讲解素材——花瓣、海面这类大面积平滑区域增益只有 0%1%而帆船、灯火这类高频细节区域可以到 10% 以上二、交互与产品设计思路放大镜类 Demo 最容易做成两张图左右一摆的静态对比观众感知不到实时。这一版的设计原则是让差距跟着手指走。镜头常驻打开页面双镜头就显示在图片中心不需要先做一次手势才出现录屏第一帧就有内容跟手节流手势帧 40ms 节流取样、放大、能量计算、双帧生成全链路跑完仍然流畅抬手保留手指抬起后镜头停在最后取样位置方便对着某个位置讲解、截图量化指标光靠肉眼说更清楚不够有说服力指标卡的「细节增益」把差距变成一个可变动的数字拖到哪里涨到哪里演示效果非常好双模式演示模式用同源预置高清素材无需 NPU任何设备都能跑出完整效果真机模式切换到ImageSRAnalyzer端侧推理接口语义完全一致useReal一个布尔值切换实现。三、工程结构sr-detail-loupe/ └── entry/src/main/ets/ ├── common/ │ ├── LoupeModels.ets // 示例数据模型 四组「低清/高清」成对素材 │ ├── PixelOps.ets // 像素工具读取/裁剪/双线性放大/梯度能量/转 PixelMap │ └── LoupeService.ets // 双模式镜头服务一次取样产出双帧 增益 ├── entryability/ │ └── EntryAbility.ets └── pages/ └── Index.ets // 主页面展示区 双镜头 指标卡 手势素材放在resources/rawfile/demo/下命名成对low_city.png160×120与hd_city.png640×480高清图是低清图同一画面的 4× 超分结果保证两枚镜头看到的是同一块区域。四、核心实现详解4.1 数据模型成对素材是一切的前提exportinterfaceLoupeDemo{id:string;low:string;// 低清输入展示原图hd:string;// 预置 AI 超分结果演示模式的「AI 镜头」数据源title:string;lowW:number;// 160×120lowH:number;hdW:number;// 640×480恰好是低清的 4×hdH:number;}为什么高清素材必须是低清的整数倍同源图因为镜头取样时低清窗口和高清窗口要做坐标一一对应低清图上以(cx, cy)为中心取 40×40 的窗口高清图上就以(cx×4, cy×4)为中心取 160×160 的窗口。两帧内容严格对齐对比才有意义。4.2 双模式服务一次取样产出双帧LoupeService是整个应用的核心。loupeAt(cx, cy)接收低清图坐标一次调用返回LensPair双线性帧 AI 帧 增益值publicasyncloupeAt(cx:number,cy:number,useReal:boolean):PromiseLensPair|null{// 1. 坐标夹取保证取样窗口不越界constclampedXMath.max(SAMPLE_SIDE/2,Math.min(this.lowW-SAMPLE_SIDE/2,cx));// 2. 低清源裁 40×40 → 手动双线性放大 4× →「普通放大」帧constlowRegionextractSquare(this.lowBuf,this.lowW,this.lowH,clampedX,clampedY,SAMPLE_SIDE);constbilinearDatabilinearUpscale(lowRegion,SAMPLE_SIDE,UPSCALE);// 3. 高清源坐标 ×4 后裁 160×160 →「AI 超分」帧consthdCxclampedX*(this.hdW/this.lowW);consthdRegionextractSquare(this.hdBuf,this.hdW,this.hdH,hdCx,hdCy,SAMPLE_SIDE*UPSCALE);// 4. 双帧各自算梯度能量得出细节增益consteBilineargradientEnergy(bilinearData,LENS_SIDE,LENS_SIDE);consteAigradientEnergy(hdRegion,LENS_SIDE,LENS_SIDE);letgainMath.round((eAi/eBilinear-1)*100);// 5. RGBA 缓冲转 PixelMap 供 Image 组件显示constbilinearPmawaittoPixelMap(bilinearData,LENS_SIDE);constaiPmawaittoPixelMap(hdRegion,LENS_SIDE);returnnewLensPair(bilinearPm,aiPm,gain);}真机模式下prepare()阶段会对整张 160×120 低清图执行一次ImageSRAnalyzer.process()把 NPU 重建结果缓存为hdBuf之后每一次镜头取样都只是内存里的区域裁剪一次推理全程流畅——而不是每拖动一帧都去调一次推理接口。这是放大镜场景下最重要的性能决策if(useRealthis.analyzer){hdSourceawaitthis.superResolve(lowRaw);// 整图一次 NPU 超分}if(!hdSource){hdSourceawaitthis.decodeRawfile(demo.hd,demo.hdW,demo.hdH);// 回退预置素材}ImageSRAnalyzer是重资源对象随页面生命周期在aboutToDisappear()里destroy()模式切换演示 ↔ 真机也走destroy()→create()的完整流程失败自动回退演示模式并提示保证任何设备上都不会白屏。4.3 像素工具把普通放大的口径握在自己手里PixelOps.ets提供五个纯函数。为什么不用Image组件自带的插值放大来做普通放大镜头因为组件插值的行为受渲染管线影响不可控也不可量化。手动双线性插值只有 40×40 → 160×160 的计算量开销极小却让两种口径完全可控/** 双线性插值放大正方形区域RGBA用于「普通放大 4×」基线 */exportfunctionbilinearUpscale(src:Uint8Array,side:number,scale:number):Uint8Array|null{constdstSideside*scale;constoutnewUint8Array(dstSide*dstSide*4);for(letdy0;dydstSide;dy){constgy(dy0.5)/scale-0.5;// 中心对齐采样避免整体偏移consty0Math.max(0,Math.min(side-1,Math.floor(gy)));constfyMath.max(0,Math.min(1,gy-y0));for(letdx0;dxdstSide;dx){// ... 对 RGB 三通道分别做 2×2 邻域加权out[oc]clampByte(top(bot-top)*fy);}}returnout;}「细节增益」的口径是梯度能量邻域像素亮度差的绝对值之和值越高代表边缘越锐利、细节越丰富。这与第一篇的SharpnessMeter是同一套算法两篇文章的指标可以互相印证exportfunctiongradientEnergy(data:Uint8Array,w:number,h:number):number{letsum0;for(lety0;yh-1;y){for(letx0;xw-1;x){constl0.299*r0.587*g0.114*b;// 当前点亮度sumMath.abs(lr-l)Math.abs(ld-l);// 右邻差 下邻差}}returnsum;}AI 超分重建出更多高频细节能量更高平滑区域两种放大都没东西可放增益自然趋近 0——这正好解释了截图中海面 0%、帆船 10% 的差异。4.4 手势与坐标换算三个必须踩对的坑放大镜跟手的实现只有几十行但坐标换算有三个坑踩错一个镜头就会飘。坑一手势坐标是屏幕系组件定位是组件系。FingerInfo.globalX/globalY相对窗口而镜头position()相对父组件。需要在手势开始时记录展示区在窗口中的偏移每次事件里做减法privatecaptureAreaOffset():void{constdensitydisplay.getDefaultDisplaySync().densityPixels;constrectthis.getUIContext().getComponentUtils().getRectangleById(lensArea);// windowOffset 单位是 px除以像素密度换算成与 vp 口径一致的值this.areaWindowXNumber(rect.windowOffset.x)/density;this.areaWindowYNumber(rect.windowOffset.y)/density;}privateonLensPan(fingerX:number,fingerY:number,isStart:boolean):void{constxfingerX-this.areaWindowX;// 屏幕坐标 → 组件坐标constyfingerY-this.areaWindowY;if(x0||y0||xthis.areaWidth||yIMG_HEIGHT){return;// 拖出展示区不响应}this.loupeXx;this.loupeYy;// 40ms 节流后把组件坐标反算回低清图像素坐标去取样constcxx/this.areaWidth*demo.lowW;constcyy/IMG_HEIGHT*demo.lowH;this.refreshLens(cx,cy);}注意windowOffset的单位是 px而组件布局用 vp两者之间要除以densityPixels否则在有密度缩放的屏幕上镜头永远差一个固定偏移。坑二手势方向选择。桌面预览器与真机对PanDirection.All的触发判定不一致横竖都要跟手的场景下可能出现按住了但手势不触发。本工程实际使用PanDirection.Horizontal配合distance: 5各设备表现一致稳定.gesture(PanGesture({fingers:1,direction:PanDirection.Horizontal,distance:5}).onActionStart((event:GestureEvent){this.captureAreaOffset();// 每次手势开始重新校准偏移constfingerevent.fingerList[0];if(finger){this.onLensPan(finger.globalX,finger.globalY,true);}}).onActionUpdate((event:GestureEvent){constfingerevent.fingerList[0];if(finger){this.onLensPan(finger.globalX,finger.globalY,false);}}))坑三Builder 与 State 的配合。镜头内容直接读State的 PixelMap帧就绪即刻渲染释放上一帧后立刻赋新值避免 PixelMap 泄漏privateasyncrefreshLens(cx:number,cy:number):Promisevoid{constpairawaitthis.service.loupeAt(cx,cy,this.useReal);if(!pair){return;}if(this.bilinearPm){this.bilinearPm.release();}// 释放上一帧if(this.aiPm){this.aiPm.release();}this.bilinearPmpair.bilinear;this.aiPmpair.ai;this.gainpair.gain;}另外 ArkTS 的Builder返回void不能链式调用.position()镜头定位要写在Builder内部组件上或外层容器上。镜头组的翻面逻辑也很简单手指靠近顶部时loupeY - LENS_SIZE - 34 8镜头落到取样点下方否则悬在上方。4.5 布局组织展示区是一个Stack底层低清Image其上是镜头Row两枚 128vp 圆形镜头并排和取样指示Circle最上层是加载遮罩。.clip(true)保证镜头移出图片边缘时被裁掉.id(lensArea)供getRectangleById定位。指标卡是标准三列Row 竖直Divider数字随State实时刷新。五、踩坑清单速查问题现象解法手势坐标错位镜头总偏移一个固定距离FingerInfo.globalX/globalY是窗口坐标需减去展示区windowOffsetpx→vp 除以densityPixels手势不触发按住图片无反应PanDirection.All部分环境判定不稳改用PanDirection.Horizontaldistance: 5拖动卡顿镜头刷新跟不上手每帧全图推理不可行改为整图一次推理 40ms 节流 40×40 小窗运算PixelMap 泄漏长时间拖动内存上涨每帧赋值前release()上一帧页面销毁统一releaseFrames()Builder报错void上链式.position()编译失败定位属性写在Builder内部组件上createPixelMap假同步拿到空图像该 API 是异步的必须await对象字面量当类型arkts-no-obj-literals-as-types用interface显式声明LoupeDemo、LensPair均如此NPU 不可用真机模式创建失败create()失败自动回退演示模式toast提示不白屏六、运行指南DevEco Studio 打开sr-detail-loupe工程等待 Sync 完成真机连接并开启开发者模式签名配置完成后直接 Run启动即进入演示模式按住图片拖动体验双镜头跟手点「切换真机模式」走端侧 NPU 推理链路低清图 160×120 远小于 2048 上限整图一次推理点「换一张」在四组示例间循环。七、总结这一篇把超分从看结果做成了摸差距同一个位置、同一个倍率普通放大与端侧 AI 超分并排跟手差距不再需要语言描述。技术上值得带走的三点放大镜场景的性能公式整图一次推理 小窗内存裁剪 手势节流把 O(每帧推理) 降为 O(每帧裁剪)对比实验要控制变量自研双线性插值保证普通放大口径完全可控梯度能量给出可复现的量化指标坐标系统一是跟手体验的命门窗口系手势↔ 组件系布局↔ 像素系取样三套坐标换算基准与单位密度必须逐一核对。系列至此超分能力已经覆盖「重建 → 修复对比 → 实时对比」三种形态。

相关推荐

计算机专业开题报告怎么写?从选题到答辩的全流程拆解
计算机专业开题报告怎么写?从选题到答辩的全流程拆解

开题报告这东西,几乎所有计算机专业本科生都躲不过,但你问十个刚写完的人,可能有八个都说不清它到底有什么用。有人把它当成任务表格,填完了事;有人把它当成毕业设计的前奏曲,随便写写等导师点头&#xff1… · 2026/9/26 6:15:16

视频下载工具全指南:从B站到优酷的合法下载与版权边界
视频下载工具全指南:从B站到优酷的合法下载与版权边界

前阵子有个朋友找我帮忙,说想把自己收藏的几百个B站视频整理到本地硬盘里,方便剪片子的时候当素材用。我跟他说这事不难,但得先聊清楚“哪些下载路径合法、哪些工具靠谱、哪些内容不建议碰”这三件事,不然很容易自己给自己挖坑。这… · 2026/9/26 6:15:16

计算机毕业设计开题报告全攻略:选题、写作框架与避坑指南
计算机毕业设计开题报告全攻略:选题、写作框架与避坑指南

计算机技术专业的毕设开题,是很多人第一次真正独立面对完整技术链路的节点。开题报告这东西,在一部分同学眼里是“导师走流程”,但实际上评审老师和导师在开题阶段要判断的问题非常少:这个题目你做得出来、你规划得清楚、你读够了… · 2026/9/26 6:15:16

Remotely Save 插件 Koofr(PRO)同步功能使用指南:基于 Koofr 原生 API 的 OAuth2 连接与同步实战
Remotely Save 插件 Koofr(PRO)同步功能使用指南:基于 Koofr 原生 API 的 OAuth2 连接与同步实战

数据同步 【免费下载链接】remotely-save Sync notes between local and cloud with smart conflict: S3 (Amazon S3/Cloudflare R2/Backblaze B2/...), Dropbox, webdav (NextCloud/InfiniCLOUD/Synology/...), OneDrive, Google Drive (GDrive), Box, pCloud, Yandex Disk, K… · 2026/9/26 6:50:28

NgRx ESLint 插件 prefer-action-creator 规则详解:用 Action Creator 替代 Action Class
NgRx ESLint 插件 prefer-action-creator 规则详解:用 Action Creator 替代 Action Class

前端状态管理 【免费下载链接】platform Reactive State for Angular 项目地址: https://gitcode.com/gh_mirrors/pl/platform 点击查看 免费下载 导读 prefer-action-creator 是 NgRx ESLint 插件中针对 store 模块提供的一条代码风格(suggestion&… · 2026/9/26 6:50:28

同步电机与构网型变流器频率稳定性仿真:从VSG控制到参数扫描
同步电机与构网型变流器频率稳定性仿真:从VSG控制到参数扫描

1. 同步电机与构网型变流器,为什么总被放在一起研究做新能源并网仿真的同行,这两年一定没少听见“构网型”三个字。光伏、风电通过电力电子变流器接入电网后,系统里的同步电机占比越来越低,旋转设备提供的转子惯量和阻尼效应都被削… · 2026/9/26 6:50:22

金融服务系统落地全解析:账户、对账、风控与合规
金融服务系统落地全解析:账户、对账、风控与合规

金融服务这几年听起来特别热,但真正动手做过的人都知道,一个能上线、敢跑真实资金流的系统,和“看起来功能齐全”的Demo完全是两码事。我过去几年一直在做金融科技侧的基础服务建设,从账户体系到清结算再到风控合规,踩… · 2026/9/26 6:50:22

用一套Agent Skills自动化小红书获客:从关键词到线索的完整流水线
用一套Agent Skills自动化小红书获客:从关键词到线索的完整流水线

1. 找客户这件事,为什么值得用一整套 Skills 来跑1.1 先说一个我实际经历过的时间黑洞两个月前帮一个做女性个护的品牌搭小红书获客流程,对方给的诉求特别朴素:每天有人在小红书上问“这个在哪买”“有没有链接”,但我们根本盯不过… · 2026/9/26 6:50:22

MCP协议实战:从概念到FastMCP与LangChain集成
MCP协议实战:从概念到FastMCP与LangChain集成

1. 从一次踩坑说起:MCP 到底解决了什么问题去年下半年我接手了一个内部工具链整合的活儿,需求说起来很简单:让团队里的 AI 助手能直接读取我们自研工单系统的数据,顺便把设计稿的标注信息也拉进来。当时我的第一反应是写几个函数调… · 2026/9/26 6:50:22

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码