HarmonyOS 7 + Core Vision Kit-PixelMap Native:图像超分结果的局部锐度收益与振铃拒绝【鸿蒙心迹】
图像超分最容易制造一种错觉:输出尺寸变大、边缘变硬,开发者就把它归入“增强成功”。真正放到相册修复、票据留档或商品素材中,判断没有这么轻松。细线周围可能出现一圈亮边,文字横画可能被拉成双线,平滑墙面也可能多出原图没有的颗粒。它们在缩略图里不明显,放大后却会直接影响可用性。
这篇文章设计一个名为 EdgeGuard 的演示工程。它不讨论模型如何训练,而是把系统返回的超分 PixelMap 当成候选结果:先检查尺寸,再在三个预先登记的局部区域中计算锐度收益、振铃比例和噪声增量,最后决定提交增强图还是保留原图。演示任务固定为 SR-AUDIT-0085,输入为 960×640,输出为 1920×1280,页面名为 SuperResolutionAuditPage。
文中的数字用于说明验收逻辑,不冒充真实设备基准。真正上线时,应在目标机型、真实素材分布和当前 SDK 上重新标定阈值。

一、尺寸变大不是业务验收结论
HarmonyOS 7(API 26)的 Core Vision Kit 提供 imageSuperResolution。官方接口由 ImageSRAnalyzer.create() 创建分析器,process() 接收一张图片,返回包含增强 PixelMap 的 ISPResponse,使用结束后通过 destroy() 释放服务。接口合同很清楚,但它回答的是“能力有没有正常返回”,没有替业务回答“这张增强图是否值得保存”。
于是,EdgeGuard 把流程拆成两个层次。系统能力层只关心创建、处理和销毁;业务验收层关心候选结果有没有通过局部质量门禁。两层不能混在一起,否则一次 process() 成功就会被误写成 COMPLETED,页面也会提前展示“增强完成”。
本例的状态路径是:
DECODED → UPSCALED → SCORING → ACCEPTED → COMMITTED
如果任何局部指标越界,路径则是:
DECODED → UPSCALED → SCORING → REJECTED → KEEP_ORIGINAL
这里特意保留 ACCEPTED 和 COMMITTED 两个状态。前者表示算法候选通过了质量规则,后者表示页面和保存逻辑已经原子地切换到新结果。两者之间仍可能发生页面离开、任务被替换或资源释放,不能直接合并。
二、先冻结一份可回放的数据合同
质量门禁若只写“看起来更清楚”,调试时几乎无法复现。本例在用户点击“执行超分”前就冻结任务快照:任务 ID、输入尺寸、三个 ROI、阈值版本、当前代次以及预期输出归属都写入同一个对象。
三个区域并不承担同一种职责:
ROI-TEXT覆盖票据编号,观察细字边缘是否真正变清楚。ROI-WIRE覆盖屋檐电线,观察高反差细线附近是否出现振铃。ROI-WALL覆盖平滑墙面,观察增强后是否凭空增加高频噪点。
本批演示阈值为:锐度收益至少 +12%,振铃比例不高于 3%,噪声增量不高于 6%。演示结果为锐度收益 +18.4%、振铃 2.1%、噪声增量 4.2%,所以状态进入 ACCEPTED。这些值是文图共同使用的演示数据,而不是 Core Vision Kit 承诺的质量指标。
这段代码解决什么问题:把一次超分请求需要的局部区域、阈值和代次冻结为不可变任务,避免页面状态变化后仍用新参数解释旧结果。
type RoiKind = 'TEXT_EDGE' | 'THIN_WIRE' | 'FLAT_WALL';
interface NormalizedRoi {
id: string;
kind: RoiKind;
x: number;
y: number;
width: number;
height: number;
}
interface AuditThresholds {
minSharpnessGain: number;
maxRingingRatio: number;
maxNoiseIncrease: number;
version: string;
}
interface SuperResolutionJob {
taskId: string;
generation: number;
inputSize: Size;
rois: ReadonlyArray<NormalizedRoi>;
thresholds: Readonly<AuditThresholds>;
}
const job: SuperResolutionJob = {
taskId: 'SR-AUDIT-0085',
generation: 85,
inputSize: { width: 960, height: 640 },
rois: [
{ id: 'ROI-TEXT', kind: 'TEXT_EDGE', x: 0.08, y: 0.64, width: 0.28, height: 0.18 },
{ id: 'ROI-WIRE', kind: 'THIN_WIRE', x: 0.52, y: 0.12, width: 0.34, height: 0.26 },
{ id: 'ROI-WALL', kind: 'FLAT_WALL', x: 0.58, y: 0.54, width: 0.30, height: 0.24 }
],
thresholds: {
minSharpnessGain: 0.12,
maxRingingRatio: 0.03,
maxNoiseIncrease: 0.06,
version: 'sr-gate-v4'
}
};
ROI 使用 0~1 的归一化坐标,而不是把 960×640 的像素位置直接存入配置。输出图尺寸变化后,同一语义区域可以按各自宽高重新映射。实际项目中还要校验 x + width ≤ 1、y + height ≤ 1,并拒绝面积过小的区域;否则几个像素的噪声就可能把指标放大。
generation 不是装饰字段。用户重新选择图片时,新任务会取得新的代次,旧任务即使稍后返回也没有提交资格。阈值版本同样需要保存,因为运营素材更新后可能调整标准,历史结果必须知道当时使用的是哪套规则。
三、系统调用只负责产生候选 PixelMap
官方文档明确说明,图像超分从 API 26 起提供,Stage 模型中可用;process() 一次只接收一张图片。工程层不要在一个分析器上私自构造“批量并发”,更不要在异常分支漏掉 destroy()。
这段代码解决什么问题:把分析器创建、单图处理和销毁放入同一个资源边界,确保异常时也能成对释放。
import { imageSuperResolution, visionBase } from '@kit.CoreVisionKit';
import { image } from '@kit.ImageKit';
class SuperResolutionRunner {
async run(input: image.PixelMap): Promise<image.PixelMap> {
const analyzer = await imageSuperResolution.ImageSRAnalyzer.create();
try {
const imageData: visionBase.ImageData = { pixelMap: input };
const request: visionBase.Request = { inputData: imageData };
const response = await analyzer.process(request);
return response.pixelMap;
} finally {
await analyzer.destroy();
}
}
}
这里不在 finally 中释放输入图,因为输入 PixelMap 的所有权属于调用方。Runner 只拥有分析器,返回的候选图则交给上层审计器。谁创建、谁释放,边界写清后才不容易出现双重释放。若页面取消任务,上层仍要等待本次 Promise 收口,再根据代次丢弃候选图并释放它,不能把“取消 UI”误当成底层调用已经停止。
项目结构与核心状态如下面的演示配图所示。图中是与本文数据一致的界面设计稿,用于说明代码和日志位置,并非真实 DevEco Studio 测试证据。

四、局部评分为什么放在 Native 层
读取整张 1920×1280 RGBA 数据后再在 ArkTS 层遍历,容易制造一次大对象搬运;而 ROI 评分只需要局部像素。示例因此通过 PixelMap Native 获取宽、高、rowStride 与 RGBA 缓冲,在 C++ 中完成亮度梯度、过冲像素和高频残差统计,最后只把三个数返回 ArkTS。
锐度收益使用候选 ROI 与原图 ROI 的平均梯度差。振铃比例统计强边缘外侧出现反向过冲的像素占比。噪声增量只在 ROI-WALL 的低梯度区域计算局部残差。它们都不是行业通用金标准,只是一组可解释、可复查的应用验收信号。
这段代码解决什么问题:在 Native 层按 rowStride 安全读取 PixelMap,并把评分限制在给定 ROI 内,避免把内存对齐字节误当成有效像素。
AuditScore ScoreRegion(OH_PixelmapNative* pixelMap, const PixelRect& roi)
{
OH_Pixelmap_ImageInfo* info = nullptr;
OH_PixelmapImageInfo_Create(&info);
if (OH_PixelmapNative_GetImageInfo(pixelMap, info) != IMAGE_SUCCESS) {
OH_PixelmapImageInfo_Release(info);
throw std::runtime_error("image info unavailable");
}
uint32_t width = 0, height = 0, rowStride = 0;
int32_t pixelFormat = 0;
OH_PixelmapImageInfo_GetWidth(info, &width);
OH_PixelmapImageInfo_GetHeight(info, &height);
OH_PixelmapImageInfo_GetRowStride(info, &rowStride);
OH_PixelmapImageInfo_GetPixelFormat(info, &pixelFormat);
OH_PixelmapImageInfo_Release(info);
if (pixelFormat != PIXEL_FORMAT_RGBA_8888 || !roi.inside(width, height)) {
throw std::invalid_argument("unsupported pixel map or roi");
}
size_t bufferSize = static_cast<size_t>(rowStride) * height;
std::vector<uint8_t> pixels(bufferSize);
if (OH_PixelmapNative_ReadPixels(pixelMap, pixels.data(), &bufferSize) != IMAGE_SUCCESS) {
throw std::runtime_error("pixel read failed");
}
return ComputeGradientRingingAndNoise(pixels, rowStride, roi);
}
rowStride 可能大于“宽度乘每像素字节数”,这正是很多手写像素循环容易踩的边界。示例还要求格式为 RGBA_8888;如果真实工程接收更多格式,应在解码阶段统一格式,或为每种格式提供独立读取策略,不能靠错误的通道偏移勉强运行。
Native 对象同样有成对释放要求。OH_PixelmapImageInfo_Create 与 OH_PixelmapImageInfo_Release 必须配对,临时像素缓冲使用 RAII 容器管理。评分失败不等于候选图可继续提交,上层应把它归为 AUDIT_ERROR 并保留原图。
五、三个数字怎样变成一次原子提交
本例的合并结果为:
sharpnessGain = 0.184ringingRatio = 0.021noiseIncrease = 0.042
三个条件需要同时满足。不要给它们做简单加权平均:锐度提升很高不应抵消严重振铃,噪声很低也不应掩盖文字双边。每个阈值都是独立的否决项。
这段代码解决什么问题:在候选结果提交前同时检查任务代次、输出尺寸和三个质量阈值,拒绝旧任务或不合格结果覆盖当前页面。
interface AuditMetrics {
sharpnessGain: number;
ringingRatio: number;
noiseIncrease: number;
}
class ResultCommitGate {
private currentGeneration: number = 85;
canCommit(job: SuperResolutionJob, outputSize: Size,
metrics: AuditMetrics): boolean {
const expected = {
width: job.inputSize.width * 2,
height: job.inputSize.height * 2
};
return job.generation === this.currentGeneration &&
outputSize.width === expected.width &&
outputSize.height === expected.height &&
metrics.sharpnessGain >= job.thresholds.minSharpnessGain &&
metrics.ringingRatio <= job.thresholds.maxRingingRatio &&
metrics.noiseIncrease <= job.thresholds.maxNoiseIncrease;
}
replaceGeneration(next: number): void {
this.currentGeneration = next;
}
}
尺寸检查在这里是业务示例约束。官方文档描述输出像素同步放大,但实际项目仍应以返回 PixelMap 的图像信息为准,再根据当前能力合同设计验证;不要把本文的 2× 边长写成跨版本永远不变的事实。
提交成功后,页面才把 candidatePixelMap 移入 displayPixelMap,并释放上一张展示图。提交失败时则释放候选图,原图继续显示。资源转移应和状态更新放在一个串行临界区内,否则 UI 已切换但旧图提前释放,会留下偶发空白。
六、运行页先展示判定,不急着展示“大图”
SuperResolutionAuditPage 的主视图不把整块空间都给图片。上半区展示原图/候选图对比,下半区明确列出任务 ID、尺寸、阈值版本和三个指标。用户首先需要知道“为什么被接受”,而不是只看到一个更大的缩略图。

演示页时间统一为 17:18,状态栏显示 Wi-Fi、5G、信号与 78% 电量。任务 SR-AUDIT-0085 的结果状态为 ACCEPTED,输出仍标注为“候选”,直到提交动作完成。红色标注只圈出振铃 2.1% 与阈值 ≤3% 的关系,不在每个数字旁都画箭头。
这也是文图一致性容易忽略的地方:正文写的是 +18.4% / 2.1% / +4.2%,日志、运行页和诊断页就不能出现另一组“更好看”的数字。技术配图不是装饰,它承担了把状态合同外显的作用。
七、诊断页要留下拒绝依据和资源结局
只展示最终 ACCEPTED 仍不够。诊断页按时间线记录:17:18:06.112 解码完成、17:18:06.741 超分返回、17:18:06.768 开始三个 ROI 评分、17:18:06.804 通过质量门禁、17:18:06.826 完成提交。总耗时 714 ms 是演示值,不用于宣称设备性能。

诊断页还要回答两个资源问题:分析器是否销毁,未被采用的 PixelMap 是否释放。本例在结果卡中显示 Analyzer DESTROYED、Candidate MOVED、Old display RELEASED。其中 MOVED 表示候选图的所有权已经移交给展示层,并不意味着它无需释放;页面离开时仍要由展示层收口。
如果评分失败,页面应显示具体否决项。例如振铃达到 4.6% 时,状态进入 REJECTED_RINGING,随后执行 KEEP_ORIGINAL。不要把它归为“超分失败”,因为系统能力可能正常返回,失败的是业务采用条件。把两类错误分开,才能判断下一步应该重试能力、调整素材,还是修改验收阈值。
八、容易出现的四种误判
第一种是只比较输出尺寸。放大成功说明数据通路完整,不说明细节可信。
第二种是用整图平均锐度。大片天空会稀释文字区域的变化,强纹理背景又会把噪声误当成细节。ROI 必须来自业务关注区域。
第三种是阈值跟着页面走。用户切换素材后,旧任务如果读取到新阈值,就失去了可回放性。任务快照需要冻结阈值版本。
第四种是把示例指标当成通用标准。票据文字、老照片人像和游戏贴图对振铃、噪声的容忍完全不同。真正的规则应通过离线样本集标定,并记录误接收、误拒绝和人工复核结果。
九、发布前的验收清单
上线前至少检查以下边界:输入 PixelMap 被谁持有;输出 PixelMap 在拒绝路径是否释放;页面离开后旧代次能否提交;ROI 是否越界;像素格式是否满足评分器;rowStride 是否按实际值读取;分析器是否在异常路径销毁;阈值版本是否随任务记录;评分错误是否与能力错误分开;演示数据是否被误写成实机性能结论。
性能方面,局部评分不是越多越好。三个 ROI 已能覆盖文本边缘、细线振铃和平坦区噪声,再增加几十个区域只会延长像素读取与计算时间。若业务需要更复杂的感知评价,应把它放到离线验收或专门的质量服务中,不应堵塞用户每次预览。
十、边界与结论
Core Vision Kit 提供的是清晰可靠的能力入口:创建分析器、处理一张图、取得增强 PixelMap、释放分析器。工程价值发生在能力返回之后。开发者要决定哪些区域值得观察、怎样区分锐度与伪影、何时拒绝候选、谁负责释放资源,以及旧任务为什么没有资格覆盖新页面。
EdgeGuard 的核心不是发明一个“万能画质分数”,而是让采用决定可解释:SR-AUDIT-0085 在 sr-gate-v4 下,以 +18.4% 锐度收益、2.1% 振铃和 4.2% 噪声增量通过三个独立门禁,再由代次与尺寸检查完成提交。换一类素材,阈值和 ROI 都应重建;不变的是“候选先审计,资源有归属,提交有资格”这条工程原则。
十一、阈值不是拍脑袋写进代码
演示中的 12% / 3% / 6% 只是把门禁逻辑具体化。真实项目要先建立小型标定集,再决定阈值。标定集不需要一开始就有几万张图片,但要覆盖业务中真正困难的材料:低码率截图、压缩多次的社交图片、带细小文字的票据、逆光人像、规则网格、平滑墙面和密集树叶。每一类素材都要同时保留原图、超分候选和人工判断。
人工判断不能只问“哪张更清楚”。更有价值的问题是:文字有没有双边;细线有没有亮暗回声;肤色区域有没有塑料感;原本平滑的背景有没有颗粒;保存后再次解码是否仍然成立。标注结果至少分为 ACCEPT、REJECT_RINGING、REJECT_NOISE、REJECT_DETAIL 和 UNCERTAIN。不确定样本不能强塞进接受或拒绝,否则阈值会被模糊标签牵着走。
随后离线计算每张样本的三个指标,观察不同类别的分布。票据文字可能要求更高锐度收益,却对轻微噪声相对宽容;人像正好相反。若各类别差异很大,应按素材类型配置阈值,而不是追求一个覆盖全部场景的数字。阈值版本发生变化时,sr-gate-v4 要升级为新版本,旧任务继续保留原版本,不能在后台悄悄重新解释历史结果。
线上可以抽样记录指标区间和最终采用结果,但不要上传用户原图或 ROI 像素。对隐私素材,遥测只保留匿名任务类型、阈值版本、耗时桶、拒绝原因和设备能力信息。质量观察需要证据,却不应以扩大数据采集为代价。
十二、错误矩阵要区分“能力失败”和“采用失败”
一次任务至少可能在四个阶段失败。创建分析器失败属于能力初始化问题;process() 抛出异常属于能力执行问题;像素读取或格式校验失败属于审计基础设施问题;指标越界则属于业务采用失败。它们在 UI 上都可能表现为“没有换成增强图”,日志却必须使用不同状态。
CREATE_ERROR 与 PROCESS_ERROR 可以提示稍后重试,并记录官方错误码。AUDIT_ERROR 应保留原图,同时把像素格式、ROI 和 Native 返回值写入诊断信息。REJECTED_RINGING、REJECTED_NOISE 与 REJECTED_DETAIL 不应该弹系统错误,它们说明门禁正常工作。用户只需看到“已保留原图”,开发者则能在详情页知道哪条规则生效。
超时也要分层。页面可以设置体验超时,在到点后停止等待并恢复按钮,但底层 Promise 未必因此取消。若调用稍后返回,代次门禁负责拒绝提交并释放结果。不要在体验超时发生时立刻释放仍被底层使用的输入 PixelMap;正确顺序是标记任务失去提交资格,等待调用收口,再释放任务持有的资源。
应用进入后台时,同样不建议强行中断未知状态的 Native 读取。可以暂停新的评分请求,让当前评分完成后检查页面令牌。页面已销毁就释放候选,不再发送 UI 状态。这样处理比在多个生命周期回调中随意置空引用更可预测。
十三、评分器自身也需要防回归
质量门禁看起来像辅助代码,实际上会决定用户看到哪张图片。它应该拥有独立测试集。最基础的单元测试包括:纯色图的梯度接近零;人工构造的黑白边缘能产生稳定梯度;在边缘外加一圈亮线后振铃比例上升;给平坦区域叠加随机噪声后噪声增量上升;相同 ROI 在输入与二倍尺寸输出上的归一化映射保持一致。
还要测试内存布局。构造 rowStride 大于有效行宽的缓冲,确认评分器跳过行尾填充;传入非 RGBA_8888 格式时明确拒绝;ROI 贴近右下角时不越界;宽或高为零时不进入循环。Native 层错误若直接越界,影响的不只是一次评分,可能导致进程退出。
端到端测试则检查资源结局。连续选择十张图片,上一代任务都不得覆盖当前任务;接受路径只有展示层持有候选;拒绝路径候选立即释放;页面退出后分析器数量回到零。HiLog 中的 created=1 destroyed=1 只是调试辅助,最终仍应结合内存工具和长时间操作验证。
十四、如何把诊断信息留给下一次排查
建议为每个任务生成一份轻量审计记录,包括任务 ID、代次、输入输出尺寸、ROI 配置摘要、阈值版本、三个指标、最终状态、各阶段耗时和资源结局。记录不保存图片内容,默认只保留最近若干条,避免诊断数据无限增长。
当用户反馈“增强后字有重影”时,开发者可以用任务 ID 找到 ROI-TEXT 与 ROI-WIRE 的判定。如果当时振铃为 2.9%,虽然没有超过 3%,但多次反馈都集中在临界区域,就有证据调整特定素材类别的阈值。相反,如果振铃只有 0.8%,问题可能发生在后续编码、缩放展示或平台二次压缩,排查方向不应继续停留在超分模型。
诊断记录还应写清结果是否经过编码回读。内存中的 PixelMap 通过门禁,不代表保存为 JPEG 后仍保持相同质量。若产品最终交付的是文件,应把编码、再次解码和必要的轻量复检纳入另一道门禁。本文只处理内存候选的局部质量,不把文件链路混入同一个结论。
十五、参考资料
- HarmonyOS Core Vision Kit
imageSuperResolutionAPI:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/core-vision-image-super-resolution-api - HarmonyOS Image Kit PixelMap Native 位图操作:https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/pixelmap-c
- HarmonyOS AI 开放能力总览:https://developer.huawei.com/consumer/cn/harmonyos-ai
更多推荐




所有评论(0)