☰
区域差分信息熵RDE:超分图像感知质量评估的Python实现与避坑指南
2026/10/12 11:57:50 网站建设 项目流程

简介:区域差分信息熵(RDE)是一种面向超分辨率图像感知质量评估的专用指标,它通过分析图像局部区域的灰度统计与信息分布,刻画人眼对细节纹理的敏感程度,弥补PSNR、SSIM等传统像素级指标与主观感受之间的偏差。这份Python源码包围绕RDE算法提供了完整可运行的实现,主程序RDIE.py负责核心计算,demo.py演示具体调用方式,README.md说明算法原理与使用步骤,60张PNG图片覆盖不同场景下的超分对比结果,另有1张JPG流程图展示整体评估管线。压缩包共64个文件,总大小25.23MB,imgs、hr、sr等目录分别存放原始图、高分辨率参考图与超分辨率重建图,方便读者对照观察指标差异。源码注释详细、结构清晰,适合计算机视觉方向的学生用于课程设计、毕业设计,也能帮助研究人员与工程师快速掌握RDE这一感知质量评价方法,并集成到自己的超分模型评估流程中。目前已有38人学习,对于希望从代码层面理解图像感知质量评价的新手来说,是一份实用的学习工具。

1. 区域差分信息熵:为什么超分图像评价不能只信 PSNR

做超分辨率重建的人,十有八九都遇到过这个场景:PSNR 涨了 0.3 dB,肉眼看上去却更糊了;SSIM 报出高分,但纹理细节全是伪影。传统像素级指标和人类视觉感知之间的鸿沟,在做图像超分评估时暴露得特别明显。区域差分信息熵(Regional Difference Entropy, RDE)这个 Python 源码包,走的就是另一条路:它不看像素误差,而是分析图像局部区域的统计特性,把「图像局部结构的复杂度」量化成一个可比较的分数。简单说,它衡量的是图像在局部区域上「变化得有规律还是杂乱无章」,分数越低,说明结构越清晰、越接近自然图像的分布。

这套源码适合谁?给学生做课程设计、毕业论文做图像质量评估对比实验,给研究人员做超分模型的感知质量评测,给工程师在模型选型时多一个参考维度。它不是替代 PSNR/SSIM,而是补上它们缺失的那块拼图——感知质量。下面从原理到跑通,再到踩坑,一篇讲完。

2. RDE 的数学原理与代码结构:先搞懂它在算什么

2.1 区域差分信息熵的定义:差分、区域、熵三个概念怎么组合

RDE 名字拆开就三部分:区域(Regional)、差分(Difference)、信息熵(Entropy)。理解这三个词各自的含义,再组合起来,算法逻辑就清楚了。

差分:对图像做差分操作,最常见的是相邻像素灰度差,也可以是一阶梯度。差分结果能反映图像在局部区域内的变化强度。一幅清晰的超分图像,边缘处差分值大且集中;一幅过度平滑的图像,差分值普遍偏小;而带伪影的图像,差分值会出现大量异常跳动。差分这一步的作用,就是把「结构信息」从像素值里抽出来。

区域:不是整张图算一个熵值,而是把图像切成若干局部块,每个块单独计算。人眼对质量的感知本来就是局部化的——看一张人脸超分图,眼睛区域的锐利程度和背景区域是否干净,是分开判断的。全局单一指标会把局部缺陷平均掉,区域化就是为了保住这种空间分辨能力。

信息熵:这就是信息论里的香农熵。把差分值看成随机变量,统计其概率分布,熵值H = -Σ p(x) * log2(p(x))。熵的含义是「不确定性」:差分值分布越集中,熵越小,结构越有序;分布越均匀,熵越大,图像越接近随机噪声或过平滑。正常自然图像的局部差分分布有明确峰谷,超分引入的伪影会让分布变散,RDE 就能感知到这种统计差异。

三者组合的逻辑是这样的:把图像划分成区块 → 每个区块内计算差分 → 统计差分直方图并计算熵 → 汇总所有区块的熵值得到最终 RDE 分数。源码里实现的就是这一套流程。

2.2 源码包结构与核心文件职责

解压这个 zip,目录结构大致如下:

code1128/ ├── RDIE.py # 核心算法实现 ├── demo.py # 示例运行脚本 ├── PipeLine.jpg # 算法流程图 ├── imgs/ │ ├── hr/ # 高分辨率参考图 │ └── sr/ # 超分重建结果图 └── README.md # 说明文档

RDIE.py 是核心模块,实现了 RDE 的完整计算流程。demo.py 是跑通流程的入口脚本,它读取 hr 和 sr 两张图,调用 RDIE.py 里的函数,计算并打印出 RDE 分数。PipeLine.jpg 是算法流程图,图上已经把「输入图像 → 分块 → 差分 → 熵计算 → 分数输出」的流水线画了出来。

2.3 RDIE.py 核心代码逐段拆解

打开 RDIE.py,核心实现大致可以抽象成下面几段逻辑。注意,我这里给出的是源码核心逻辑的还原,帮助理解算法结构:

import numpy as np import cv2 def calculate_rde(image, block_size=32, diff_mode='horizontal'): """ 计算单张图像的 RDE 分数 :param image: 输入灰度图,shape=(H, W),dtype=uint8 :param block_size: 分块尺寸,默认 32x32 :param diff_mode: 差分模式,'horizontal' 或 'vertical' :return: rde_score(float), block_entropies(list) """ h, w = image.shape # 将图像裁剪为 block_size 的整数倍,保证分块完整 h_crop = h - h % block_size w_crop = w - w % block_size image = image[:h_crop, :w_crop] block_entropies = [] num_blocks_h = h_crop // block_size num_blocks_w = w_crop // block_size for i in range(num_blocks_h): for j in range(num_blocks_w): block = image[ i*block_size:(i+1)*block_size, j*block_size:(j+1)*block_size ] # 差分:当前行减去上一行(或当前列减上一列) if diff_mode == 'horizontal': diff = np.diff(block.astype(int), axis=1) else: diff = np.diff(block.astype(int), axis=0) # 统计差分值的直方图,区间的下限和上限根据 8bit 图设定 hist = np.histogram(diff, bins=256, range=(-255, 255))[0] # 转成概率分布 prob = hist / hist.sum() if hist.sum() > 0 else np.zeros_like(hist) # 剔除零概率项,计算信息熵 prob_nonzero = prob[prob > 0] entropy = -np.sum(prob_nonzero * np.log2(prob_nonzero)) block_entropies.append(entropy) # 最终分数取所有块的均值 rde_score = np.mean(block_entropies) return rde_score, block_entropies

这段逻辑里有几个关键的实现细节值得注意:

  • np.diff沿横向计算相邻像素灰度差,astype(int)是必须的——uint8 相减会溢出,比如 0 减 255 会包回 1,导致差分分布完全错误。这是一类非常经典的图像处理 bug。
  • np.histogram的 range 参数设置为 (-255, 255),8bit 图像相邻像素差的理论范围就是 [-255, 255]。把全部可能取值纳入然后剔零概率项,保证不会漏掉大梯度的边缘。
  • block_size参数会影响 RDE 的敏感度。块越小时空分辨能力越强,但每个块内统计样本越少,熵估计越不稳定;块越大熵估计越稳,但局部缺陷越容易被平均掉。

2.4 为什么这个指标能感知「PSNR 骗不了」的质量差异

PSNR 的数学本质是逐像素 MSE 的对数变换,它对像素值的小幅系统性偏移极其敏感。超分模型只要把输出整体往原图方向「抹」平均一些,PSNR 就会显著提升——因为均值接近了参考图,可纹理细节也被抹没了。

RDE 走的是差分统计的路,对「绝对像素值差不敏感」,对「结构分布敏感」。过度平滑图块的差分分布窄而尖,熵低;伪影图块的差分分布散而宽,熵高。这两类视觉上完全不同的退化方式,在 RDE 上会产生方向相反的响应。我最初跑这个指标的时候,拿 ESRGAN 和双三次插值的结果对比,RDE 的排序和主观视觉排序一致,而 PSNR 的排序是反的——双三次插值因为和原图平均像素更接近,PSNR 反而更高。这就是 RDE 作为感知质量指标的价值所在。

3. 跑通 demo 与集成到自己的评估流程:命令、参数与输出解读

3.1 环境准备:依赖安装与版本说明

源码依赖 NumPy 和 OpenCV,都是 Python 图像处理的标准库。安装命令如下:

pip install numpy opencv-python

版本方面,NumPy 1.19 以上版本均可,OpenCV 4.x 系列没问题。如果只算 RDE 不涉及可视化,不装 matplotlib 也能跑。我在 Python 3.8 和 3.11 两个环境分别测过,没有兼容性问题。

参数说明:OpenCV 的imread默认按 BGR 读图,demo 里如果直接读彩色图,需要先转成灰度图再进 RDE 计算,这一点源码里已经处理好了。如果你用自己下载的超分图,需要注意原始图像是否带了 alpha 通道,PNG 带透明通道时cv2.imread会读成 4 通道,直接转灰度会出现异常结果。

3.2 demo.py 的运行流程与输出判读

进入code1128目录,直接执行:

python demo.py

demo 做的事情分四步。第一步,读入 imgs/hr 和 imgs/sr 目录下对应的图像对;第二步,将图像转灰度并调用 RDIE.py 中的函数;第三步,分别计算 HR 参考图和 SR 重建图的 RDE 分数;第四步,把两者分数和差打印到控制台。

输出大致是这样:

HR image RDE score: 5.8321 SR image RDE score: 6.4578 Delta: +0.6257

看输出的时候有个关键认知:RDE 分数不是「越小越好」或「越大越好」的单向指标,它描述的是图像局部差分结构的统计分布特征。读取时建议结合方向来判断:

  • 如果 SR 图与 HR 图的内容整体一致,只是细节略有损失,SR 的 RDE 通常比 HR 略低。过度平滑导致差分分布变窄,熵减小。
  • 如果 SR 图引入明显伪影或振铃效应,SR 的 RDE 会高于 HR。伪影导致差分分布变散,熵增大。
  • 最理想状态是 SR 的 RDE 与 HR 的 RDE 接近,说明重建结果的局部结构复杂度与原始图匹配。

所以 demo 输出的 Delta 值才是核心参考量。绝对值 0.1 以内属优秀重建,0.5 以内可以接受,超过 1.0 通常意味着明显的感知质量偏差。

3.3 批量评估一组超分图的 RDE:改造 demo 脚本

实际使用场景往往需要对比多个模型或同一模型不同 epoch 的输出。手动一张张跑不现实,我把 demo 改造为一个批量脚本,支持传入目录并输出 CSV 结果:

import os import csv import cv2 import numpy as np def evaluate_pair(hr_path, sr_path, block_size=32): hr = cv2.imread(hr_path, cv2.IMREAD_GRAYSCALE) sr = cv2.imread(sr_path, cv2.IMREAD_GRAYSCALE) hr = cv2.resize(hr, (sr.shape[1], sr.shape[0]), interpolation=cv2.INTER_LANCZOS4) from RDIE import calculate_rde hr_score, _ = calculate_rde(hr, block_size=block_size) sr_score, _ = calculate_rde(sr, block_size=block_size) return hr_score, sr_score def batch_evaluate(result_dir, reference_path, output_csv): rows = [] for filename in sorted(os.listdir(result_dir)): if filename.lower().endswith(('.png', '.jpg', '.bmp')): sr_path = os.path.join(result_dir, filename) hr_score, sr_score = evaluate_pair(reference_path, sr_path) rows.append([filename, f'{hr_score:.4f}', f'{sr_score:.4f}', f'{sr_score - hr_score:.4f}']) with open(output_csv, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['filename', 'HR_RDE', 'SR_RDE', 'Delta']) writer.writerows(rows) print(f'Done. Result saved to {output_csv}') if __name__ == '__main__': batch_evaluate('test_results/', 'reference_hr.png', 'rde_scores.csv')

改造逻辑里有一个必须注意的点:evaluate_pair函数中对 HR 做了一次cv2.resize,将 HR 缩放到与 SR 相同的空间尺寸。这是必要的对齐操作,因为超分输出和 HR 参考图的分辨率往往不一致,直接比较不同尺寸图像的 RDE 毫无意义。

另一个重要问题是block_size的选择。demo 默认 32×32 在大多数 4 倍超分场景下表现稳定。如果你评估的是 2 倍超分输出,图像尺寸较小,建议块大小调到 16,保证足够的块数量用于统计。判断块数量是否足够有个实操经验:如果图像分块后少于 20 个块,RDE 得分的方差会很大,同样模型跑两次结果可能差很多。遇到这种情况,把小图放大到 512×512 以上再计算更可靠。

3.4 结合其他指标交叉验证:RDE、PSNR、SSIM 的互补用法

RDE 不该单打独斗,我更推荐把它加入评估组合拳。PSNR 看像素还原精度,SSIM 看结构相似度,RDE 看局部差分统计的感知一致性。三个指标一起上,能筛掉的坑比任何一个单独用都多。

我在实际对比两个超分模型时养成的习惯是,每个模型跑一组验证集图像,然后每组算出三个指标的均值,做成一个对比表:

  • 模型 A 的 PSNR 高 0.2 dB,SSIM 高 0.005,但 RDE Delta 高 0.8。
  • 模型 B 的 PSNR 略低,SSIM 略低,但 RDE Delta 只有 0.15。

单看 PSNR/SSIM 会选 A,但 A 在视觉上是锐化过度、伪影偏重的典型表现。RDE 把这一真相暴露出来,最终结论自然反转。这种交叉验证方式在论文实验部分也更好写:既能看到传统的精度指标,又能证明你多考虑了一层感知维度。

4. 避坑与常见问题:我在跑 RDE 时踩过的五个真实坑

4.1 输入图像尺寸不一致导致指标失真

现象:同一张 HR 图配不同尺寸的 SR 图,RDE 分数忽高忽低,Delta 值混乱,得不出稳定结论。

原因:代码里分块数量由图像尺寸决定。512×512 的图按 32×32 分块能分出 256 个块,256×256 只有 64 个块,样本量不同,熵估计的稳定度完全不一样。另外不同尺寸图像的差分分布天然不同,小图经过下采样后高频信息本就减少,RDE 直接可比较性低。

解决:先cv2.resize将 HR 和 SR 统一到相同尺寸再计算。统一尺寸时使用 INTER_LANCZOS4 插值,它在缩放过程中对高频保持相对友好。对齐之后 RDE 分数才具备可比较性。

4.2 彩色图直接进算法导致结果异常

现象:demo 跑通没问题,换成自己的彩色超分图后,RDE 分数比预期高出一截,且所有图都差不多。

原因:看一眼代码,cv2.imread默认读成 3 通道 BGR。源码按单通道灰度图计算,如果传入了 3 通道数组,np.diff会沿着最后一个维度计算通道间的差值——B 通道减 G 通道、G 通道减 R 通道的差值被当成图像差分算进了直方图,结果完全错误。

解决:读图时显式指定灰度模式,cv2.imread(path, cv2.IMREAD_GRAYSCALE),或者在读完后用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)转换。这是我疏忽过的事,后来养成了习惯,凡是图像质量指标的代码,入口第一行永远是显式指定灰度。

4.3 uint8 溢出导致差分直方图变量全部乱掉

现象:高亮区域和边缘区域附近的熵值异常偏大,肉眼看起来一副图块质量不错,但它的 RDE 分数反而高于明显带伪影的图块。

原因:np.diff处理 uint8 数组时不做类型提升。像素值 0 和 255 做差,数学结果应为 -255,uint8 溢出后变成 1。大量边缘像素的差分值被错误映射到小数值区间,直方图分布扭曲,熵计算跟着失真。

解决:差分前务必做astype(int)转型。这个坑坑了我两轮才意识到,第一轮发现结果不对劲,第二轮逐块调试到np.diff那行才看明白。在读任何一个图像处理的源码包时,先把数组 dtype 链梳理一遍,能省出大量调试时间。

4.4 block_size 的选择对结论有决定性影响

现象:同一组超分图像,用 block_size=8 时模型 A 胜出,用 block_size=64 时模型 B 反超。

原因:块越小熵估计的方差越大,块越大局部细节越容易被牺牲掉。block_size=8 在 512×512 图上能分出 4096 个块,但从每块仅 64 个像素估计直方图,统计噪声很大;block_size=64 则把 64×64 区域内的所有结构信息揉成一个熵值,局部伪影被大量正常区域稀释。

解决:选 block_size 的原则是保证每块内至少包含一个完整的纹理单元或边缘结构。4 倍超分图像的最小纹理尺度通常不低于 16 像素,我一般设为 32 并和 16、64 一起各跑一遍,看结论是否一致。三个尺度结论一致,说明评估结果是稳健的;不一致就说明图像本身质量处于模糊地带,需要人工目检介入。

4.5 HR 与 SR 内容不对齐,RDE 比较完全失效

现象:SR 图比 HR 图有 1~2 像素的平移偏移,RDE Delta 值突然飙升到 2.0 以上,判为严重质量劣化。但目测 SR 图细节和 HR 几乎无差。

原因:RDE 的计算依赖局部差分统计,平移 1~2 个像素虽然不改变整张图的统计分布,但在「分块对应」的语义下,SR 的某一块对应的是 HR 的另一块。HR 图里原本在同一区块内的规则结构,在 SR 里被切分到两个相邻区块,每个区块的熵分布都会发生变化,累积起来 Delta 就被放大了。

解决:评估前先做对齐。如果 SR 是超分模型的直接输出,与 HR 存在亚像素偏移是常见现象。这时候运行一次基于相位相关的配准,或干脆以 SR 为基准,对 HR 做相同的裁剪和缩放变换。对齐后再算 RDE 才是公平的口径对比。

5. 把 RDE 做成评估管线里的一个模块:进阶用法与验证技巧

RDE 源码包提供了一个独立的单图像评估函数,但要让它真正融入工作流,还要做一层封装。这里给一个进阶的实践方案:把 RDE 封装成一个可用import导入的评估模块,统一接口、统一参数管理。

# quality_metrics.py import numpy as np import cv2 from RDIE import calculate_rde class RDEMetric: def __init__(self, block_size=32, diff_mode='horizontal'): self.block_size = block_size self.diff_mode = diff_mode def __call__(self, hr: np.ndarray, sr: np.ndarray) -> dict: # 统一尺寸 if hr.shape[:2] != sr.shape[:2]: hr = cv2.resize(hr, (sr.shape[1], sr.shape[0]), interpolation=cv2.INTER_LANCZOS4) # 统一灰度 if len(hr.shape) == 3: hr = cv2.cvtColor(hr, cv2.COLOR_BGR2GRAY) if len(sr.shape) == 3: sr = cv2.cvtColor(sr, cv2.COLOR_BGR2GRAY) # 统一 dtype hr = np.ascontiguousarray(hr, dtype=np.uint8) sr = np.ascontiguousarray(sr, dtype=np.uint8) hr_score, hr_blocks = calculate_rde( hr, block_size=self.block_size, diff_mode=self.diff_mode) sr_score, sr_blocks = calculate_rde( sr, block_size=self.block_size, diff_mode=self.diff_mode) return { 'hr_rde': hr_score, 'sr_rde': sr_score, 'delta': sr_score - hr_score, 'block_count': len(hr_blocks) }

封装之后,在训练或推理脚本里就能这样用:

from quality_metrics import RDEMetric rdem = RDEMetric(block_size=32) result = rdem(hr_image, sr_image) print(result['delta'])

这个封装解决了三个实际问题。第一,所有输入图像被统一到灰度、统一到相同尺寸、统一到 uint8,杜绝了章节 4 里那些典型 bug 的再次发生。第二,block_size 和 diff_mode 作为构造参数传入,跑多组对比实验时切换参数不用改函数体。第三,返回值包含块数量,方便你做置信度判断——块数量不足时结果谨慎参考。

另一个值得做的验证技巧是「噪声扰动自检」。取一张清晰的 HR 图,对它添加不同程度的高斯噪声(sigma 从 0 到 20),观察 RDE 分数的变化趋势。如果 RDE 分数随噪声强度单调递增,说明算法的差分熵对退化敏感,工作正常;如果曲线出现平台期或上下跳动,说明代码有潜在问题,回查直方图计算的实现。这个自检只要两分钟就能跑完,能筛掉大多数实现层面的 bug。

最后说一个我的个人习惯。从那以后,每当我评估一个超分模型或者调一次后处理参数,我都会强制走一遍「RDE + PSNR + SSIM」三件套,并把 RDE 的 Delta 值和 block_count 一起记录到实验表格里。数据积累到一定量后,模型的性格特征会浮现出来——有些模型永远 PSNR 高但 RDE 偏差大,说明它做的是像素平均式重建;有些模型 PSNR 略低但 RDE 贴合 HR,说明它在纹理结构上更接近真实图像。这套方法论已经帮我避开了不少「指标漂亮、肉眼翻车」的模型选择失误。希望这个 RDE 源码包也能成为你评估流程里可靠的一把尺子,帮你把超分模型选择的依据落到视觉实际上去。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询