图像超分辨率重建这个方向,我断断续续跟了几年,从早期简单的双三次插值一路看到现在各种深度网络堆叠,说实话大部分方案的思路已经趋于同质化——要么堆更深的卷积,要么换更花哨的注意力机制,真正在“上采样”这个最基础环节做文章的反而不多。最近一段时间我把注意力放回到上采样算子本身,重点研究了联合双边上采样(Joint Bilateral Upsampling,下文简称 JBU)在超分重建里的用法,越挖越觉得这块被低估了。JBU 最早是在深度图修复和联合滤波场景里被提出来的,核心思想是借助一张高分辨率引导图来指导低分辨率信号的上采样过程,让边缘和纹理在放大时不被糊掉。把它迁移到图像超分辨率重建里,恰好能解决传统插值上采样“边缘发虚、纹理丢失”的老毛病,也能给深度网络提供一个更干净的初始重建基底。
这篇内容我想把 JBU 联合双边上采样在超分重建中的整套思路掰开揉碎讲清楚:它到底解决什么问题、背后的数学原理怎么理解、参数怎么选、实操流程怎么走、踩过哪些坑。适合已经了解超分基本概念、想在上采样环节做优化的朋友,也适合刚入门、想搞明白“为什么不用简单双三次插值”的新手。全文会围绕原理拆解、方案设计、实操实现、问题排查四个大块展开,中间穿插我自己实测的参数和代码片段,尽量做到看完就能动手复现。
1. 内容整体设计与思路拆解
1.1 为什么盯上上采样这个环节
先说说我为什么把目光放在上采样而不是网络结构上。图像超分辨率重建的完整链路大致可以拆成三步:特征提取、上采样、重建微调。过去几年大量工作集中在特征提取网络的设计上,残差块、密集连接、注意力模块轮番上阵,收益确实有,但边际递减很明显。而上采样这一步,很多方案要么直接用亚像素卷积(PixelShuffle)一次性放大,要么用转置卷积,本质上都是在低分辨率特征图上做插值再卷积,边缘处的振铃和模糊问题始终存在。
我做过一组对比实验,同一套轻量特征提取网络,只替换上采样方式:双三次插值、转置卷积、亚像素卷积、JBU 四种方案,在同样的训练轮次下比较边缘区域的 PSNR 和视觉锐度。结果很能说明问题——双三次插值最快但边缘最糊,转置卷积容易产生棋盘格伪影,亚像素卷积表现均衡但在强边缘处仍有轻微振铃,而 JBU 借助引导图的高频信息,在边缘区域的锐度保持上明显更好。这就是我把 JBU 单独拎出来研究的直接动机:它不是在网络容量上做加法,而是在“如何利用已有高频信息”上做文章,思路更本质。
1.2 JBU 的核心思想与方案选型考量
JBU 的核心可以一句话概括:低分辨率信号的上采样值,由高分辨率引导图中空间邻近且像素值相似的像素共同加权决定。传统双边上采样只考虑低分辨率图自身的空间距离和值域相似度,而 JBU 多引入了一张高分辨率引导图,把引导图的空间距离和值域相似度也纳入权重计算。这样做的妙处在于,引导图里清晰的边缘结构会被“传递”到上采样结果中,低分辨率信号负责提供整体色调和低频信息,引导图负责提供高频边缘定位,两者互补。
方案选型上我考虑过三条路线。第一条是纯传统 JBU,用低分辨率图自身做引导,本质退化成普通双边上采样,效果有限。第二条是用高分辨率原图做引导,但这在真实超分任务里不现实,因为推理时根本没有高分辨率原图。第三条是我最终采用的:用低分辨率图经过一次基础插值放大后的结果作为引导图,再配合 JBU 做精细化上采样。这条路线的好处是引导图在推理阶段可以自给自足,不需要外部高分辨率输入,同时基础插值已经提供了粗略的高频轮廓,JBU 在此基础上做边缘对齐和锐化,实测下来是可行性和效果之间比较平衡的选择。
这里要解释一下为什么不用高分辨率原图做引导——很多人第一反应会觉得“有高分辨率图做引导肯定效果最好”,但超分任务的本质就是从低分辨率恢复高分辨率,推理阶段高分辨率图是不存在的。如果训练时用原图引导、推理时用插值图引导,训练和推理的引导图分布不一致,模型会严重过拟合到训练时的理想引导图上,实际部署效果崩掉。这个坑我早期踩过,训练集 PSNR 很漂亮,换真实低分辨率输入直接翻车。所以引导图的构造必须保证训练和推理一致,这是方案设计的第一原则。
1.3 整体链路的分层设计
把 JBU 嵌进超分链路,我采用的是分层设计:底层做基础插值得到粗上采样结果和引导图,中层用 JBU 做边缘感知的精细化上采样,上层接一个轻量卷积网络做残差微调和伪影抑制。这样分层的好处是每一层职责清晰,底层保证不引入额外的高频幻觉,中层负责把边缘对齐做扎实,上层只做小幅修正,避免网络承担过重的上采样任务。
具体来说,低分辨率输入先经过双三次插值放大到目标尺寸,得到粗上采样图,这张图同时充当 JBU 的引导图。然后 JBU 以低分辨率原图为信号源、以粗上采样图为引导,逐像素计算加权上采样值。JBU 输出再和粗上采样图做一个残差相加,送入后面的轻量网络做微调。整个链路里 JBU 是确定性算子,没有可学习参数,网络只负责微调,这样训练稳定、收敛快,也不容易过拟合。这个设计取舍背后的逻辑是:把“确定性的几何对齐”交给传统算子,把“不确定性的纹理生成”交给网络,各司其职。
2. 核心细节解析与实操要点
2.1 JBU 权重计算的数学拆解
JBU 的权重由两部分乘积构成:空间核和值域核。对目标高分辨率坐标 $(i,j)$,它对应的低分辨率坐标约为 $(i/s, j/s)$,其中 $s$ 是放大倍数。JBU 在该低分辨率坐标的邻域内取若干像素,每个邻域像素 $(k,l)$ 的权重为:
$$w(i,j,k,l) = \exp\left(-\frac{(i/s-k)^2+(j/s-l)^2}{2\sigma_s^2}\right) \cdot \exp\left(-\frac{(I_{guide}(i,j)-I_{low}(k,l))^2}{2\sigma_r^2}\right)$$
前半部分是空间高斯核,衡量高分辨率目标点映射回低分辨率后与邻域像素的空间距离;后半部分是值域高斯核,衡量引导图在该点的像素值与低分辨率邻域像素值的差异。最终上采样值为邻域像素按权重归一化后的加权平均。
这里有个容易混淆的点:值域核里比较的是引导图的高分辨率像素值和低分辨率邻域像素值,两者分辨率不同但都是同一场景的亮度信息,可以直接比较。我第一次实现时误把引导图也降采样后再比较,结果边缘引导完全失效,因为降采样把高频信息抹掉了,值域核退化成常数。这个细节在不少简化实现里被忽略,但对效果影响很大,务必保证值域核用的是引导图原始高分辨率像素值。
2.2 空间核与值域核的参数选择
$\sigma_s$ 和 $\sigma_r$ 是 JBU 的两个命门参数。$\sigma_s$ 控制空间邻域范围,取值太小则邻域覆盖不足、上采样结果出现块状感,取值太大则跨边缘采样、边缘被抹平。$\sigma_r$ 控制值域相似度容忍度,取值太小则只有极相似像素参与加权、结果噪点多,取值太大则不同亮度像素混入、边缘对比度下降。
我实测下来的一组经验值:放大倍数 $s=2$ 时,$\sigma_s$ 取 1.5 到 2.0,$\sigma_r$ 取 0.08 到 0.12(像素值归一化到 0 到 1 区间);$s=4$ 时,$\sigma_s$ 取 2.5 到 3.5,$\sigma_r$ 取 0.10 到 0.15。放大倍数越大,空间核要相应放宽,否则邻域覆盖不到足够的低分辨率像素。值域核则和放大倍数关系不大,主要取决于图像本身的对比度和噪声水平,噪声大的图要适当放大 $\sigma_r$。
| 放大倍数 | 空间核 σs | 值域核 σr | 邻域半径 | 适用场景 |
|---|---|---|---|---|
| 2x | 1.5–2.0 | 0.08–0.12 | 2 | 低噪、边缘清晰 |
| 3x | 2.0–2.8 | 0.09–0.13 | 2–3 | 一般场景 |
| 4x | 2.5–3.5 | 0.10–0.15 | 3 | 高噪、纹理丰富 |
| 8x | 3.5–5.0 | 0.12–0.18 | 3–4 | 大幅放大、需强平滑 |
邻域半径的选择也有讲究。理论上邻域越大越精确,但计算量是半径的平方增长。我一般取 $3\sigma_s$ 向上取整作为邻域半径,这样能覆盖 99% 以上的高斯权重,再大就是浪费算力。实测 $s=4$ 时半径取 3 已经足够,再往上 PSNR 提升不到 0.05dB,但耗时翻倍,不划算。
2.3 引导图构造的三个关键约束
引导图构造是 JBU 在超分里能否落地的关键,我总结了三个必须满足的约束。第一是训练推理一致性,前面提过,引导图必须用推理阶段也能获得的信息构造,也就是低分辨率图的基础插值结果,绝不能用高分辨率原图。第二是引导图质量下限,基础插值方法不能太差,双三次插值是个稳妥选择,双线性插值边缘太糊会导致 JBU 值域核判断失准,我试过双线性引导,边缘锐度比双三次引导低约 0.3dB。第三是引导图与信号源的配准,引导图是低分辨率图放大来的,和低分辨率原图在几何上天然对齐,但如果中间做了任何裁剪、旋转、缩放,必须保证两者坐标映射严格一致,否则值域核比较的是错位像素,边缘引导直接失效。
注意:引导图构造时不要做任何锐化或增强预处理。我早期为了让引导图“看起来更清晰”,给它加了一层 unsharp mask,结果 JBU 值域核被锐化产生的过冲像素误导,边缘处出现明显光晕。引导图要的是几何结构准确,不是视觉锐利,这个认知转变花了我不少调试时间。
2.4 计算复杂度与加速手段
JBU 的朴素实现复杂度是 $O(N \cdot r^2)$,$N$ 是高分辨率像素总数,$r$ 是邻域半径。4 倍放大、半径 3 的情况下,每个高分辨率像素要算 49 次权重,一张 512×512 的图放大到 2048×2048,总计算量约 2 亿次权重计算,纯 Python 循环跑起来要几十秒,完全不可用。
加速手段我试过三种。第一种是向量化,用 NumPy 把邻域偏移展开成数组运算,能快一个数量级,但内存占用高,大图容易爆内存。第二种是分离近似,把二维高斯核拆成两个一维核分别做,复杂度降到 $O(N \cdot r)$,速度快但精度略降,边缘处会有轻微方向性偏差。第三种是我最终采用的查表加预计算:值域核的指数项用查找表近似,空间核因为坐标映射规律可以预计算成固定模板,实际运行时只做查表和乘加。这套组合下来,4 倍放大单张 512×512 图耗时压到 1 秒以内,满足实用要求。
import numpy as np def build_spatial_lut(scale, sigma_s, radius): # 预计算空间核查找表,键为整数偏移 offsets = np.arange(-radius, radius + 1) lut = {} for dy in offsets: for dx in offsets: dist2 = (dx / scale) ** 2 + (dy / scale) ** 2 lut[(dy, dx)] = np.exp(-dist2 / (2 * sigma_s ** 2)) return lut def jbu_upsample(low, guide, scale, sigma_s, sigma_r, radius): h, w = guide.shape spatial_lut = build_spatial_lut(scale, sigma_s, radius) out = np.zeros_like(guide, dtype=np.float64) weight_sum = np.zeros_like(guide, dtype=np.float64) for dy in range(-radius, radius + 1): for dx in range(-radius, radius + 1): # 高分辨率坐标映射回低分辨率 ys = np.clip((np.arange(h) + dy) // scale, 0, low.shape[0] - 1) xs = np.clip((np.arange(w) + dx) // scale, 0, low.shape[1] - 1) sampled = low[np.ix_(ys, xs)] range_w = np.exp(-((guide - sampled) ** 2) / (2 * sigma_r ** 2)) w_total = spatial_lut[(dy, dx)] * range_w out += w_total * sampled weight_sum += w_total return out / np.maximum(weight_sum, 1e-8)上面这段是我简化后的核心实现,实际工程里会把空间查找表和坐标映射进一步预计算成索引数组,避免循环里重复算除法。注意np.maximum(weight_sum, 1e-8)这个保护,权重和理论上不会为零,但浮点误差下极端情况可能出问题,加个下限更稳。
3. 实操过程与核心环节实现
3.1 环境准备与依赖确认
实操环境我用的是一台普通开发机,Python 3.9 加 NumPy 1.24,图像读写用 OpenCV,评估指标用 scikit-image 里的 PSNR 和 SSIM 实现。不需要 GPU,JBU 本身是 CPU 算子,后面的轻量微调网络如果要用 GPU 训练再另说,但纯 JBU 上采样环节 CPU 足够。依赖清单很简单:numpy、opencv-python、scikit-image,版本不用太挑,近两年的版本都兼容。
数据准备上,我建议先用标准超分测试集里的小图做验证,比如常见的 512×512 左右的自然图像,先降采样再放大,和原图对比算指标。这样能快速判断 JBU 参数是否合理,不用一上来就搭完整训练流程。我最初就是拿几张纹理丰富的图做单图验证,调好参数后再扩展到批量。
3.2 单图验证的完整流程
单图验证流程分五步。第一步,读入高分辨率原图,转成 float 并归一化到 0 到 1。第二步,用双三次插值降采样 $s$ 倍,得到低分辨率输入,模拟真实超分场景。第三步,对低分辨率输入做双三次插值放大 $s$ 倍,得到粗上采样图和引导图。第四步,调用 JBU 做精细化上采样。第五步,分别计算粗上采样图和 JBU 结果相对原图的 PSNR 和 SSIM,对比提升幅度。
import cv2 import numpy as np from skimage.metrics import peak_signal_noise_ratio as psnr from skimage.metrics import structural_similarity as ssim def single_image_test(hr_path, scale=4, sigma_s=3.0, sigma_r=0.12, radius=3): hr = cv2.imread(hr_path, cv2.IMREAD_GRAYSCALE).astype(np.float64) / 255.0 h, w = hr.shape # 降采样模拟低分辨率输入 lr = cv2.resize(hr, (w // scale, h // scale), interpolation=cv2.INTER_CUBIC) # 粗上采样作为引导图 guide = cv2.resize(lr, (w, h), interpolation=cv2.INTER_CUBIC) # JBU 精细化上采样 jbu_out = jbu_upsample(lr, guide, scale, sigma_s, sigma_r, radius) jbu_out = np.clip(jbu_out, 0, 1) print(f"粗上采样 PSNR: {psnr(hr, guide, data_range=1.0):.3f} dB") print(f"JBU 上采样 PSNR: {psnr(hr, jbu_out, data_range=1.0):.3f} dB") print(f"粗上采样 SSIM: {ssim(hr, guide, data_range=1.0):.4f}") print(f"JBU 上采样 SSIM: {ssim(hr, jbu_out, data_range=1.0):.4f}") return guide, jbu_out实测一组数据:4 倍放大、$\sigma_s=3.0$、$\sigma_r=0.12$、半径 3 的条件下,纹理丰富的自然图像上,粗上采样 PSNR 约 28.5dB,JBU 后约 29.6dB,提升约 1.1dB;SSIM 从 0.82 提到 0.87 左右。边缘密集的图像提升更明显,能到 1.5dB 以上;平滑区域为主的图像提升小一些,0.5dB 左右。这个提升幅度看起来不大,但视觉上边缘锐度的改善比数字更直观,尤其是斜向边缘和细纹理,粗上采样的锯齿和模糊在 JBU 后明显收敛。
3.3 参数扫描与最优组合确定
单图验证只能看个大概,要确定参数得做扫描。我的做法是固定放大倍数,在 $\sigma_s$ 和 $\sigma_r$ 的二维网格上跑一批图,记录平均 PSNR,画成热力图找峰值区域。扫描范围 $\sigma_s$ 从 1.0 到 5.0 步长 0.5,$\sigma_r$ 从 0.05 到 0.20 步长 0.01,每组合跑 10 张图取平均。
扫描结果有个规律:PSNR 对 $\sigma_r$ 比对 $\sigma_s$ 更敏感,$\sigma_r$ 偏离最优值 0.03 以上 PSNR 就掉 0.3dB 以上,而 $\sigma_s$ 在最优值附近 ±0.5 范围内 PSNR 波动不到 0.1dB。所以调参时先粗调 $\sigma_s$ 定个大概,再细调 $\sigma_r$ 找峰值。另外不同图像内容的最优 $\sigma_r$ 有差异,纹理图偏小、平滑图偏大,实际部署如果图内容未知,取中间值 0.12 比较稳妥。
提示:参数扫描时记得固定随机种子和图像列表,我早期扫描时每次随机抽图,结果两次扫描的最优参数差了不少,排查半天才发现是抽样波动导致的,不是参数本身的问题。
3.4 接入轻量微调网络的完整链路
JBU 单独用已经能出效果,但接一个轻量网络做残差微调还能再提一截。我的做法是:JBU 输出和粗上采样图做残差相加得到基础重建,再送进一个三层卷积的小网络,网络学习的是“JBU 结果到原图”的残差。网络结构很轻,三层 3×3 卷积,通道数 32,激活用 ReLU,最后一层不加激活直接输出残差,和基础重建相加得到最终结果。
训练数据用常见的超分训练集降采样构造,损失用 L1,优化器 Adam,学习率 1e-4,batch size 16,训练 50 个 epoch 左右收敛。这里的关键是网络只学残差,不承担上采样任务,所以收敛很快,也不容易过拟合。我对比过让网络直接从低分辨率图上采样(不用 JBU),同样网络容量下 PSNR 比 JBU 加微调低约 0.4dB,而且训练轮次要多一倍才收敛,说明 JBU 确实给网络减了负。
| 方案 | PSNR (dB) | SSIM | 训练轮次 | 单图推理耗时 |
|---|---|---|---|---|
| 双三次插值 | 28.5 | 0.820 | 0 | 5ms |
| 转置卷积 | 29.0 | 0.845 | 80 | 12ms |
| 亚像素卷积 | 29.3 | 0.858 | 80 | 10ms |
| JBU 单独 | 29.6 | 0.870 | 0 | 800ms |
| JBU + 轻量微调 | 30.0 | 0.882 | 50 | 820ms |
这张表是我在一批测试图上的平均结果,能看出 JBU 加微调在指标上领先,代价是推理耗时比纯网络方案高不少,主要花在 JBU 的 CPU 计算上。如果对实时性要求高,可以把 JBU 的邻域半径调小或者用分离近似加速,指标会掉一点但速度能快两三倍。
3.5 彩色图像的处理细节
前面讲的都是灰度图,彩色图要分通道处理。我的做法是转到 YCbCr 空间,只对 Y 通道做 JBU 上采样,Cb 和 Cr 通道直接用双三次插值放大。原因是人眼对亮度细节敏感、对色度细节不敏感,色度通道做 JBU 收益很小但计算量翻三倍,不划算。实测 Y 通道 JBU 加色度双三次,和三个通道都做 JBU 相比,PSNR 差不到 0.05dB,但耗时只有三分之一。
转换时注意 OpenCV 的 YCbCr 范围和归一化要统一,我习惯先把 BGR 转 float 归一化到 0 到 1,再转 YCbCr,处理完再转回 BGR 并 clip 到 0 到 1。中间任何一步范围搞错都会导致色偏,这个坑很基础但容易犯,尤其是混用不同库的转换函数时。
4. 常见问题与排查技巧实录
4.1 边缘出现光晕或过冲
这是 JBU 最典型的问题,表现为强边缘两侧出现亮暗交替的带状伪影。根因通常是 $\sigma_r$ 设得过大,值域核把边缘两侧差异较大的像素也纳入了加权,导致边缘被“拉平”后又因为归一化产生过冲。解决办法是调小 $\sigma_r$,一般降到 0.08 到 0.10 之间光晕就明显减轻。另一个可能原因是引导图本身有锐化过冲,前面提过,引导图不要做锐化预处理。
如果调小 $\sigma_r$ 后光晕还在,检查一下引导图和信号源的坐标映射是否严格对齐。我遇到过一次引导图在构造时多做了半像素偏移的 resize,导致值域核比较的是错位像素,边缘处权重计算全乱,光晕非常严重。排查方法很简单:拿一张纯色图加一条竖直边缘做测试,如果 JBU 结果在边缘处出现对称的亮暗带,基本就是配准问题。
4.2 平滑区域出现块状感
平滑区域(比如天空、墙面)JBU 后出现隐约的方块纹理,这通常是 $\sigma_s$ 太小或邻域半径不足导致的。空间核覆盖范围不够时,每个高分辨率像素只受很少几个低分辨率像素影响,放大后块状感就出来了。解决办法是增大 $\sigma_s$ 或半径,让邻域覆盖更充分。实测 $\sigma_s$ 从 1.5 提到 2.5,块状感基本消失,代价是边缘锐度略降,需要和 $\sigma_r$ 配合调。
还有一种块状感来自低分辨率图本身的压缩块效应,降采样前如果原图有 JPEG 压缩痕迹,放大后块效应会被 JBU 放大。这种情况建议在 JBU 前对低分辨率图做一次轻度去块滤波,或者直接换质量更高的源图,JBU 本身不负责去压缩伪影。
4.3 计算速度慢到不可用
纯 Python 循环实现慢是必然的,前面给的向量化版本在 512×512 放大到 2048×2048 时大约 1 到 2 秒,如果还嫌慢,可以上分离近似或者把 JBU 写成 C 扩展。我用 Cython 重写过一版核心循环,速度比 NumPy 向量化再快约 3 倍,单图 4 倍放大压到 300ms 以内。如果只是做实验验证,NumPy 版本够用;如果要集成到产品链路,建议用 C++ 或 Cython 重写。
另一个提速思路是降低 JBU 的作用范围。观察发现 JBU 的收益主要集中在边缘区域,平滑区域用双三次插值和 JBU 差别很小。所以可以先做边缘检测,只对边缘邻域内的像素跑 JBU,其余像素直接用双三次结果。这样能省掉大部分计算,实测整体耗时降 60% 以上,指标只掉 0.05dB 左右,是个很实用的工程折中。
4.4 训练时指标虚高、推理翻车
这个前面提过,根因是引导图构造在训练和推理时不一致。排查方法是:训练时打印引导图的构造方式,确认它只依赖低分辨率输入;推理时用同样的构造逻辑,对比两者引导图的统计分布(均值、方差、梯度幅值),如果差异明显就是不一致。我早期用原图引导训练,推理用插值图引导,两者梯度幅值差了一个数量级,模型完全学偏了。
修复方法就是把引导图构造统一成“低分辨率图基础插值放大”,训练推理共用一套代码路径,不要写两份。这个原则听起来简单,但工程里很容易因为训练脚本和推理脚本分开维护而出现偏差,建议把引导图构造封装成独立函数,两边都调同一个。
4.5 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决方向 |
|---|---|---|---|
| 边缘光晕/过冲 | σr 过大或引导图锐化 | 纯色加边缘测试图验证 | 调小 σr,去掉引导图锐化 |
| 平滑区块状感 | σs 过小或半径不足 | 观察块状纹理周期 | 增大 σs 或半径 |
| 边缘引导失效 | 引导图与信号源错位 | 检查坐标映射和 resize 参数 | 统一坐标映射,避免半像素偏移 |
| 推理效果远差于训练 | 引导图构造不一致 | 对比训练推理引导图统计量 | 统一引导图构造代码路径 |
| 计算耗时过长 | 朴素循环或半径过大 | 计时各环节 | 向量化、分离近似、边缘选择性 JBU |
| 彩色图色偏 | YCbCr 范围或归一化不统一 | 检查转换前后数值范围 | 统一归一化和转换函数 |
4.6 几个容易被忽略的实操心得
第一个心得是关于边界处理。JBU 在图像边界处邻域会越界,我的实现里用 clip 把坐标限制在有效范围内,这会导致边界像素的邻域不对称,边缘处可能有轻微偏差。更严谨的做法是边界处镜像填充或者按有效邻域重新归一化权重,但实测下来 clip 的偏差在视觉上几乎不可见,除非做严格的定量对比,否则不必过度处理。
第二个心得是关于多尺度 JBU。单尺度 JBU 在 4 倍以上放大时边缘引导会变弱,因为低分辨率图的高频信息本身有限。我试过金字塔式多尺度 JBU,先在 2 倍尺度做一次 JBU,再在 4 倍尺度用 2 倍结果做引导,逐级放大,边缘保持比单尺度好一些,但计算量翻倍,收益约 0.2dB,性价比一般,看场景取舍。
第三个心得是关于评估方式。PSNR 和 SSIM 对 JBU 的提升反映得不够充分,因为 JBU 主要改善的是边缘锐度和纹理清晰度,这些在全局指标里被平滑区域稀释了。我习惯额外算一个边缘区域的局部 PSNR,或者直接看放大后的边缘截图对比,视觉判断往往比全局指标更能反映 JBU 的价值。做方案选型时不要只盯全局 PSNR,否则容易低估 JBU 这类边缘优化方案。
这套 JBU 联合双边上采样的方案我从原理验证到工程落地折腾了挺长时间,中间踩的坑大多集中在引导图构造和参数配合上,反而算法本身的理解没花太多功夫。它在超分链路里的定位很明确:不是替代深度网络,而是在上采样这个基础环节提供一个边缘感知的、确定性的精细化基底,让后面的网络少干脏活。如果你的超分方案在边缘锐度上一直不满意,又不想无脑堆网络深度,JBU 这个方向值得花时间试试,尤其是它对算力要求不高、实现门槛低,拿来快速验证想法很合适。