087、超分模型评估指标PSNR、SSIM、LPIPS与NIQE的数学原理与代码实现上周调一个EDSR模型在Set5上PSNR刷到了32.1dB心里美滋滋准备发论文。结果审稿人一句“请提供LPIPS和NIQE指标”直接把我打回原形。更尴尬的是我跑出来的LPIPS居然比bicubic插值还高——这意味着我的“超分模型”在感知质量上还不如直接放大。排查了半天发现是代码里把图像归一化到[0,1]后直接算LPIPS而预训练模型期望的输入是[-1,1]。这种坑踩一次记一辈子。今天就把这四个指标的数学原理和代码实现掰开揉碎讲清楚顺便把那些容易翻车的地方标出来。PSNR最古老也最容易被骂的指标PSNR全称Peak Signal-to-Noise Ratio翻译成人话就是“峰值信号和噪声的比值”。数学上长这样PSNR 10 * log10(MAX^2 / MSE)其中MAX是像素最大值8位图就是255MSE是均方误差。注意这里MSE是在像素空间算的所以PSNR天然对像素级对齐敏感——你平移一个像素PSNR能掉好几个dB。实际写代码时有个细节很多人直接拿整张图算MSE但超分任务通常要裁掉边界像素比如shave border因为边界处的卷积填充会导致误差。我一般裁掉6个像素这个值取决于模型下采样倍数和卷积核大小。importnumpyasnpfromskimage.metricsimportpeak_signal_noise_ratiodefcalculate_psnr(img1,img2,border6,max_val255.0):# 这里踩过坑img1和img2必须是uint8或者float在[0,255]# 别这样写直接传[0,1]的float进去skimage内部会默认max_val1ifimg1.max()1.0:img1img1*255.0img2img2*255.0# 裁掉边界不然PSNR虚高ifborder0:img1img1[border:-border,border:-border,:]img2img2[border:-border,border:-border,:]# 别用skimage的默认实现它不裁边界msenp.mean((img1-img2)**2)ifmse0:returnfloat(inf)return10*np.log10(max_val**2/mse)PSNR的致命缺陷它完全不关心人眼感知。一张图稍微模糊一点PSNR可能只掉0.5dB但人眼觉得“这图糊了”。反过来GAN生成的图PSNR可能比SRCNN还低但人眼觉得更清晰。所以现在顶会论文里PSNR基本只作为“保真度”的参考真正决定论文生死的往往是后面几个指标。SSIM结构相似性比PSNR靠谱一点SSIM的出发点是人眼对亮度、对比度、结构三个维度敏感。数学公式看起来吓人其实就是三个分量的乘积SSIM(x,y) [l(x,y)]^α * [c(x,y)]^β * [s(x,y)]^γ其中l是亮度比较c是对比度比较s是结构比较。实际实现时通常取αβγ1简化成SSIM (2μxμy C1)(2σxy C2) / (μx^2 μy^2 C1)(σx^2 σy^2 C2)C1和C2是稳定常数防止分母为0。注意这里μ和σ都是在局部窗口通常11x11内用高斯加权计算的不是全局统计量。写代码时最容易翻车的是通道处理。很多人直接把RGB三通道拼起来算SSIM但正确做法是每个通道单独算然后取平均。另外skimage的ssim实现有个坑它默认data_range255如果你传了[0,1]的float进去结果会完全不对。fromskimage.metricsimportstructural_similarityasssimdefcalculate_ssim(img1,img2,border6,channel_axis-1):# 别这样写直接传RGB三通道skimage默认按灰度图处理# 必须指定channel_axis参数告诉它哪个维度是通道ifimg1.max()1.0:img1(img1*255).astype(np.uint8)img2(img2*255).astype(np.uint8)ifborder0:img1img1[border:-border,border:-border,:]img2img2[border:-border,border:-border,:]# win_size必须是奇数默认7但建议用11# data_range如果不指定skimage会从dtype推断uint8就是255returnssim(img1,img2,win_size11,data_range255,channel_axischannel_axis)SSIM比PSNR好一点但它对纹理细节的感知仍然有限。一张图如果纹理被平滑了但边缘保持得好SSIM可能还挺高。这就引出了下一个指标。LPIPS终于开始学人眼了LPIPSLearned Perceptual Image Patch Similarity是2018年Zhang等人的工作核心思想用预训练好的VGG网络提取特征在特征空间里算距离。因为VGG是在ImageNet上训的它的特征空间天然对“猫长什么样”“纹理是什么”有感知。数学上很简单LPIPS Σ (w_l * ||φ_l(x) - φ_l(y)||_2)其中φ_l是VGG第l层的特征w_l是学出来的权重或者直接用L2距离不加权。注意这里的特征要先做通道归一化除以通道的L2范数再在空间维度上平均。实现时最坑的是预处理。VGG要求输入是[-1,1]范围并且要BGR顺序。很多人直接把[0,1]的RGB图丢进去结果LPIPS算出来完全没意义。importtorchimportlpips# 这里踩过坑lpips库默认用AlexNet但VGG效果更好loss_fnlpips.LPIPS(netvgg,verboseFalse)defcalculate_lpips(img1,img2,devicecuda):# 输入是numpy数组范围[0,1]形状[H,W,C]# 别这样写直接转tensor不归一化img1_ttorch.from_numpy(img1).permute(2,0,1).unsqueeze(0).float().to(device)img2_ttorch.from_numpy(img2).permute(2,0,1).unsqueeze(0).float().to(device)# 关键归一化到[-1,1]lpips内部不做这个img1_timg1_t*2-1img2_timg2_t*2-1withtorch.no_grad():distloss_fn(img1_t,img2_t)returndist.item()LPIPS的值越小表示越相似。一般超分模型的LPIPS在0.1-0.3之间GAN能到0.1以下而bicubic插值通常在0.3以上。注意LPIPS对图像尺寸敏感最好把输入resize到256x256再算不然不同尺寸的结果不可比。NIQE没有参考图怎么办前面三个指标都需要GTGround Truth但实际应用中哪有那么多高清原图NIQENatural Image Quality Evaluator就是为这个场景设计的——它只靠一张图就能评估质量。原理听起来玄乎先在一堆高质量自然图像上拟合一个多元高斯模型MVG然后在待测图像上提取同样的特征计算两个MVG分布之间的距离。特征包括空间域的局部对比度、频域的能量分布等。数学上NIQE是两个MVG之间的马氏距离NIQE sqrt((ν1 - ν2)^T * (Σ1 Σ2)/2 * (ν1 - ν2))其中ν是均值向量Σ是协方差矩阵。实现时直接用piq库最省事但要注意它内部对图像做了归一化输入范围[0,1]或[0,255]都行。frompiqimportniqedefcalculate_niqe(img,devicecuda):# 输入numpy数组[H,W,C]范围[0,1]# 别这样写直接传uint8piq内部会做归一化但可能和你预期不一致img_ttorch.from_numpy(img).permute(2,0,1).unsqueeze(0).float().to(device)withtorch.no_grad():# piq的niqe要求输入在[0,1]范围scoreniqe(img_t,data_range1.0)returnscore.item()NIQE的值越低表示质量越好。自然图像的NIQE通常在3-5之间超分模型生成的图像如果能到5以下就算不错了。但注意NIQE有个致命弱点它对GAN生成的伪影不敏感。一张图如果全是高频噪声但纹理丰富NIQE可能反而给高分。实战中的血泪教训指标打架是常态。我见过一个模型PSNR比baseline高0.3dB但LPIPS反而更差。这时候要看你的应用场景如果是医疗影像PSNR优先如果是视频娱乐LPIPS更重要。裁边问题必须统一。不同论文裁的边界数不一样有的裁4有的裁8。比较结果时一定要确认裁边策略一致否则PSNR能差0.5dB。颜色空间陷阱。PSNR和SSIM在RGB空间算没问题但LPIPS的预训练模型是在sRGB上训的如果你用了不同的颜色空间比如YCbCr结果会偏移。batch size影响LPIPS。LPIPS的预训练模型有batch normalization虽然eval模式会冻结BN但不同batch size下数值稳定性不同。建议每次只算一对图像。NIQE的参考模型。piq库的NIQE默认使用在KADID-10k上拟合的MVG参数这个数据集偏向自然图像。如果你处理的是医学图像或遥感图像最好自己重新拟合MVG。最后说句实在话这四个指标没有一个完美的。PSNR和SSIM是“保真度”指标LPIPS是“感知质量”指标NIQE是“无参考”指标。发论文时最好四个都报审稿人挑不出毛病。但做产品时最终还是要靠人眼主观评价——毕竟用户不会看PSNR他们只看“清不清楚”。