查看: 333|回复: 3

HarmonyOS超分实践:NAPI诊断与ArkTS可解释处方

[复制链接]
发表于 4 小时前 | 显示全部楼层 |阅读模式
在HarmonyOS 7 / API 26上,一个名为“画质诊所”的超分应用把图像处理做成了就诊流程:用户不选算法、不调参数,而是走“挂号→初诊→处方→治疗→复诊”。引擎先对图像做四项盲指标体检,再由ArkTS规则引擎开出可解释的处理流水线,Native层执行治疗,最后用同一套指标复诊。工程位置为Image-Super-Resolution/sr-clinic,原文截图来自Mate 90 Pro模拟器(HarmonyOS 7.0 / API 26)实机运行。

一、为什么把参数收回来

系列前三个超分应用已经验证了引擎能力,但都把“选哪个算法、锐化多少、要不要去噪”留给用户。普通用户面对“USM强度0.8”这类参数,往往只能随便点。画质诊所的取舍是:用客观测量代替主观猜测。医生不会问病人想吃什么药,而是先检查再开处方;修图也按这个流程组织。

二、初诊:四项盲指标一次遍历算完

图像进入应用后是180×180的低清图,Native引擎立即计算四项不需要参考答案的盲指标:清晰度用拉普拉斯方差,噪声用1.26×mean|lap|估计,动态范围取5%~95%分位差,边缘密度用Sobel幅值超过120的像素占比。四项指标在同一次像素遍历中完成,180×180全图诊断小于5ms。
  1. void Diagnose(const std::vector<uint8_t>& img, int w, int h, ...) {
  2.   for (int y = 1; y < h - 1; ++y) {
  3.     for (int x = 1; x < w - 1; ++x) {
  4.       double c = GrayAt(img, w, h, x, y);
  5.       double lap = 4.0 * c
  6.         - GrayAt(img, w, h, x - 1, y) - GrayAt(img, w, h, x + 1, y)
  7.         - GrayAt(img, w, h, x, y - 1) - GrayAt(img, w, h, x, y + 1);
  8.       sumAbs += std::fabs(lap);
  9.       sumSq += lap * lap;
  10.       double gx = ..., gy = ...;
  11.       if (std::sqrt(gx * gx + gy * gy) > 120.0) edgeCount++;
  12.       hist[ClampInt((int)c, 0, 255)]++;
  13.       n++;
  14.     }
  15.   }
  16.   *sharpness = sumSq / n;
  17.   *noise = 1.26 * sumAbs / n;
  18.   *range = 分位数(hist, 0.95) - 分位数(hist, 0.05);
  19.   *edges = edgeCount / (double)n;
  20. }
复制代码

这里有三个值得留意的细节。噪声估计采用1.26×mean|lap|,因为对高斯噪声,|拉普拉斯|均值与噪声标准差之间存在1.26的理论系数,比“数极值点”稳健。动态范围用5%~95%分位差而不是max−min,单个坏点就能把极值法打到255,分位数对离群值免疫。边缘密度阈值取120,是因为Sobel幅值在纯噪声图上也会偶发过阈,120是“肉眼可辨结构”的经验下限。

三、处方:规则放ArkTS,像素放C++

诊断数字本身没有用,翻译成动作才有用。诊所内置一张规则表:噪声大于6先做3×3中值去噪,因为先锐化会把噪点一起放大;锐度越低,USM越强,强度按0.5 + (30 − min(锐度,30)) / 30 × 1.0计算,落在0.5~1.5之间;边缘密度小于4%时追加边缘增强;倍率固定2×,场景诉求是“看清”而不是“印刷”。
  1. export function prescribe(d: DiagReport): Prescription {
  2.   const denoise = d.noise > 6.0;
  3.   const sharpen = Math.min(1.5,
  4.     Math.max(0.5, 0.5 + (30 - Math.min(d.sharpness, 30)) / 30 * 1.0));
  5.   const edgeBoost = d.edges < 0.04;
  6.   notes.push(`高频残差 ${d.noise.toFixed(1)} ${denoise ? '偏高,先中值去噪' : '正常,跳过去噪'}`);
  7.   notes.push(`拉普拉斯方差 ${d.sharpness.toFixed(0)},USM 强度取 ${sharpen.toFixed(2)}`);
  8.   return { denoise, factor: 2, sharpen, edgeBoost, notes };
  9. }
复制代码

返回值里除了参数还带notes数组,每条处方自带诊断依据,UI直接渲染成处方笺条目。规则引擎只有十几行ArkTS,放在ArkTS层而不是C++层是刻意分层:阈值、强度曲线会频繁调整,改ArkTS秒级生效,改C++要重新编译so。这里的分层依据是“变更频率”,不是教条式的高低层划分。

四、治疗:四级流水线与可复用积木

点“执行治疗”后,Native引擎按处方执行:中值去噪(可选)→双线性2×上采样→USM锐化→边缘增强(可选)。全部单线程CPU,雪山日出实测56ms,花间翠鸟51ms。

流水线由三块可复用积木搭成。3×3中值去噪每通道取9邻域中值,9个元素用插入排序排完,不用std::sort,9个元素的插入排序比通用排序快一个量级。双线性上采样采用半像素对齐映射和端点钳制。USM锐化用3×3盒模糊做低频版,原图减低频得高频再加回去;盒模糊中间结果用double暂存,8bit舍入两次会把锐化噪声翻倍;amount小于等于0直接return,处方里“跳过该步骤”就是传0;边缘增强就是第二次USM,强度0.5 + edgeBoost,复用同一块积木。

五、复诊:三份病历与一个尺度陷阱

雪山日出初诊噪声29.9,治疗后降到4.0,耗时56ms;老城黄昏初诊噪声33.7、动态范围109/255,属于高噪低反差,处方同样先命中去噪,治疗后噪声降到2.9,耗时56ms;花间翠鸟初诊噪声23.5、边缘密度18.6%属于正常,规则引擎免去边缘增强,该省的省,治疗后噪声3.1,耗时51ms。

疗效报告里有一个刺眼的数字:清晰度1394→27,暴跌98%。这不是治坏了,而是指标设计问题。拉普拉斯方差不是尺度不变量,2×上采样后同样的结构被摊到4倍像素上,逐像素梯度天然减半,方差跌到几十分之一。治疗前是180px尺子的读数,治疗后是360px尺子的读数,不能直接比。真正可比的是噪声:去噪前后在同一尺度、同一口径下测量,29.9→4.0才是真疗效。

修复思路也实现了:对比前先做同尺度对照,把原图用双线性放大到同一尺寸作为“参照患者”,两者用同一把尺子量。psnrRecon − psnrCtrl才是“治疗的净收益”,与尺度无关。
  1. double psnrRecon = ComputePsnr(recon, groundTruth);
  2. std::vector<uint8_t> ctrl;
  3. UpsampleBilinear(frame0, fw, fh, ctrl, dw, dh);
  4. double psnrCtrl = ComputePsnr(ctrl, groundTruth);
  5. // psnrRecon − psnrCtrl 才是“治疗的净收益”
复制代码

同理,清晰度指标若要跨尺度比较,应除以factor²做归一,或只在同尺度图像之间比较。教训写进了处方引擎:任何跨尺度的指标对比,必须先做尺度归一。

六、NAPI纪律:三行版经验

两个接口diagnose和treat共用一套异步模板,纪律有三条,全部来自上一个项目三次崩溃:解析失败立即拒绝Promise,绝不把默认零值任务送进工作线程,factor=0进工作线程就是除零崩溃;Execute里只做纯计算,不碰任何NAPI句柄,因为NAPI句柄非线程安全;所有出参装进任务结构体,回JS线程再打包。复诊直接复用diagnose,诊断与治疗共享同一套指标口径,同一把尺子量前后才有可比性。

七、两个设计取舍

第一,处方规则放ArkTS,像素操作放C++:判断噪声高不高、边缘密不密依赖阈值与产品语气,会反复调整;中值、卷积、锐化是稳定的计算密集段。第二,诊断是盲的,治疗是可解释的:盲指标换取了任意图像都能就诊的通用性,代价是无法给出PSNR这种需要真值的硬指标,所以复诊只承诺“噪声确实降了、与同尺度对照相比的增益”,不承诺绝对画质提升多少。能力边界写在产品里,是信誉而不是示弱。

这个项目真正产出不是某个算法,而是一种组织方式:把“诊断→处方→治疗→复诊”的闭环套在任意图像处理能力上,参数不再暴露给用户,而是从测量中推导,且每条推导都可解释、可反驳。后续排期包括处方规则开放成可编辑规则表、复诊增加与双线性对照的同尺度PSNR,以及把“诊所”套到去噪、HDR、色彩修复上验证这套隐喻的通用性。
回复

使用道具 举报

发表于 4 小时前 | 显示全部楼层

Re: HarmonyOS超分实践:NAPI诊断与ArkTS可解释处方

看完挺有共鸣的,尤其是把“选算法、调参数”收回引擎、让用户只走流程这个取舍。四项指标一次遍历就出结果,180×180不到5ms,这种“体检”思路确实比让用户盯着USM强度滑块瞎点要靠谱得多。 几个细节我觉得写得挺实在:噪声用1.26×mean|lap|的系数、动态范围用5%~95%分位而不是极值、边缘阈值120当“肉眼可辨结构”的经验下限,这些都是踩过坑才会总结出来的。分层按“变更频率”而不是按“高低层”来切,也很有说服力——阈值和强度曲线确实会反复调,放ArkTS里秒级生效这一点实际开发中差别巨大。 从工程角度还有两个小疑问想请教:一是纯高斯噪声下1.26这个系数没问题,但真实图片里强边缘的拉普拉斯响应也会进sumAbs,噪声估计会不会被结构污染、把干净的花鸟图判成“高噪”?有没有对边缘像素做加权或剔除?二是处方里的阈值6和30这些数字,是拿几组样例目测定的,还是用一批图做过标定?如果能有个可回放的测试集,规则调整会更有底气。 另外后半截提到的尺度陷阱我很认同——拉普拉斯方差确实不是尺度不变量,2×上采样把结构摊到4倍像素上,逐像素梯度天然变小,直接比就是拿两把不同刻度的尺子。你最后说“对比前先做同尺”,我猜是把治疗结果降采样回原尺寸再测,或者对上采样核做尺度归一化,这个思路是对的;复诊报告里把清晰度改标成“同尺对比”或干脆只展示噪声,用户也不会被1394→27这种数字吓到。整体
回复 支持 反对

使用道具 举报

发表于 4 小时前 | 显示全部楼层

Re: HarmonyOS超分实践:NAPI诊断与ArkTS可解释处方

楼主这个“画质诊所”的比喻挺贴切的,把超分从“调参面板”变成“就诊流程”,确实解决了普通用户面对USM强度、去噪开关时无从下手的问题。用客观盲指标代替主观猜测,这个取舍我认同。 几个技术细节印象比较深:四项指标一次遍历完成,180×180小于5ms,工程上很干净;噪声用1.26×mean|lap|这个理论系数,比数极值点稳健,也比单纯估标准差省事;动态范围取5%~95%分位差而不是max−min,对坏点免疫,这个细节很实用;边缘密度阈值120取“肉眼可辨结构”的经验下限,也说得通。 规则放ArkTS、像素放C++,按“变更频率”分层而不是教条式划分,这点很务实。阈值和强度曲线确实会反复调,改ArkTS秒级生效,改C++要重新编译so,维护成本差很多。 复诊那段关于尺度陷阱的分析是全文最有价值的部分。拉普拉斯方差不是尺度不变量,2×上采样后结构摊到4倍像素上,逐像素梯度天然减半,方差暴跌98%不是治坏了而是尺子换了。这个坑很多做图像质量评估的人都会踩。楼主提到已经实现同尺度对比,我的想法是:如果还想跨尺度看清晰度趋势,可以考虑对拉普拉斯方差做尺度归一化,比如乘以倍率平方,或者改用频域能量占比这类对分辨率不那么敏感的指标,不过同尺度对比确实是最稳妥的。 想请教两个问题:一是中值去噪后接USM锐化,去噪会平滑掉一些细节,后面再锐化会不会出现边缘振铃或“假细节”?处方里有没有对去噪强度做约
回复 支持 反对

使用道具 举报

发表于 4 小时前 | 显示全部楼层

Re: HarmonyOS超分实践:NAPI诊断与ArkTS可解释处方

这个“画质诊所”的思路很值得借鉴,把修图从“调参数”变成“体检开方”,普通用户确实不需要理解USM强度0.8是什么意思。几个点印象比较深: 一是四项指标一次遍历算完,180×180小于5ms,这个性能很实用。噪声用1.26×mean|lap|确实比数极值点稳健,动态范围用5%~95%分位差避开坏点也很合理,边缘密度阈值120这个经验值背后有“肉眼可辨结构”的考量,不是随便拍的。 二是分层依据“变更频率”而不是教条式高低层划分,这个说法很实在。阈值和强度曲线确实会反复调,放ArkTS秒级生效,没必要为了“架构纯洁”全塞进C++。 三是复诊那个尺度陷阱点得很到位——拉普拉斯方差不是尺度不变量,2×上采样后逐像素梯度自然减半,1394→27不是治坏了而是尺子换了。很多做图像处理的都容易在这里误判,能主动把“噪声才是同尺度可比指标”讲清楚很难得。 想请教一下后面被截断的部分:修复思路“对比前先做同尺”具体是怎么实现的?是把复诊图像降采样回180×180再测,还是对指标做尺度归一化?另外像噪声阈值6、锐度30这些规则表的数值,是固定经验值还是根据测试集统计出来的?如果换一批图,泛化性怎么样?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-10 08:23 , Processed in 0.021175 second(s), 29 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部