峯值信噪比(Peak Signal to Noise Ratio,PSNR)經常用來評價圖像質量。對於一張RGB三通道圖像,其計算公式如下:
先計算圖像與Ground Truth的均方誤差MSE:
再計算PSNR:
計算PSNR的Python代碼,網上有下面兩種:
import cv2
import numpy as np
import math
def psnr1(img1, img2):
mse = np.mean((img1 - img2) ** 2 )
if mse < 1.0e-10:
return 100
return 10 * math.log10(255.0**2/mse)
def psnr2(img1, img2):
mse = np.mean( (img1/255. - img2/255.) ** 2 )
if mse < 1.0e-10:
return 100
PIXEL_MAX = 1
return 20 * math.log10(PIXEL_MAX / math.sqrt(mse))
理論上,這兩種計算方式都對應上面的計算公式,在輸入圖像一樣的情況下,這兩段代碼的結果應該是一樣的。但是,在調用這段代碼的時候,我發現這兩者的結果卻相差很遠,同樣的圖片,psnr1的結果大概是29,而psnr2的結果是12。
gt = cv2.imread('1.jpg')
img= cv2.imread('2.jpg')
print(psnr1(gt,img))
print(psnr2(gt,img))
這是輸出的結果:
單看代碼的話完全看不出來任何問題,後來我輸出了這兩張圖像作差的結果,發現所有的值都是在0-255之間的,比如img1的一個像素值是30,img2的一個像素值是60,二者作差,本來應該是-30,但是結果卻是226,即對於負值,輸出要加上256。所以,問題就出在這行代碼上:
mse = np.mean((img1 - img2) ** 2 )
如果img1某個點的像素比img2小,而兩者差別又比較大,這個絕對值比較大的負值就會變成一個比較小的正值,MSE的結果也會偏小,那麼PSNR的值就會偏大。
只要把上面那行代碼改成mse = np.mean((img1/1.0 - img2/1.0) ** 2 )就可以了。
最後,我們發現這兩個結果是一樣的了。