#PSNRとは - 類似度の指標である
PSNRとは, 簡単に言うと, 2枚の画像がどれだけ相違っているかを表す指標のことである. ここで「2枚の画像」がという点が一つの大事なポイントであり, 例えば1枚の画像だけをみて, それが画質が良い悪いを判断する指標ではないことは注意したい. あくまで, 相対的に画質を評価する際の指標であるのだ. このことを頭に置き, 定義を以下に述べていく.
扱う画像は8[bit]であるとする. このとき, 画素値は0から255の値をとるので, 画素値の最大値は255となる. よって, PSNRは次式で求められる.
PSNR = 10\log_{10}\Bigl(\frac{255^2}{MSE}\Bigr)
この式におけるMSEとは平均二乗誤差のことであり、以下の式で算出される。
MSE = \frac{1}{Color × N} \sum_{i=0}^{Color}\sum_{j=1}^{N} E(i,j)^2
ただし, Colorは画素を構成している色の数であるので3(i = 0でred, i=1でgreen, i = 2でblueとする), Nは全画素数, E(i,j)は2枚の画像間の, j番目の画素の色iにおける画素値の差とする.
式で定義をするとややこしくなるが, 意味として考えると見通しが良く, 2枚の画像の画素値の差をRGB値それぞれで計算して, それの平均値を求めているに過ぎない. つまり, 2枚の画像の「相違が少ない」とMSEは小さくなり, 「相違が大きい」とMSEは大きくなる. よって, 「相違が小さい」ならばPSNRは「大きく」なり, 「相違が大きい」ならばPSNRは「小さく」なると考えられる. (分かりやすさのために「相違」という言葉を用いたが, この相違とは2枚の画像の画素値の差のことである. )
#PSNRの1つの使い所
前章の冒頭で, PSNRとはあくまで相対的な画質評価であると述べた. では, どこでPSNRを使えるのかと疑問を抱くと思われる. なので, このPSNRがどのような場面で活躍するのかを述べる. その1つとして, ある画像を圧縮し, その画像の画質評価をする際に役立つと言うことができるだろう. (画像を圧縮していくと, 当然ながらノイズが生じるので, PSNRの値は小さくなっていく.)
2019.8.31 訂正済み
(訂正前)画像を圧縮していくと・・・PSNRの値は「大きく」なっていく.
(訂正後)画像を圧縮していくと・・・PSNRの値は「小さく」なっていく.
#実際にPSNRを算出してみる
以下の3つの画像を提示する. 左から, (1)元画像, (2)74%圧縮画像, (3)92%圧縮画像, とする. ここで74%圧縮とは, 元の画像のデータ量を74%削減したという意味で用いている.
元画像 | 74%圧縮 (PSNR : 40.0) | 92%圧縮 (PSNR : 31.6) |
---|---|---|
上の3つの画像を見れば, 元画像と比べ圧縮した画像は, 「主観的に」画質が落ちているように見えるだろう. このとき客観的な証拠としてPSNRを用いることができる.
ここで, 次章に載せたコードを用いてPSNRを計算し, その結果を述べていく. (1)と(2)を用いて計算したPSNRが40.0, (1)と(3)ではPSNRが31.6となった. これより, (2)の方が(3)と比べてPSNRの値が大きいので, (2)の方が画質が良い(ノイズが少ない)と客観的に言うことができるだろう.
#使用したソースコード
import cv2
import numpy as np
import math
#画像の読み込み
Original = cv2.imread('Original_Image.jpg') #元画像
Distorted = cv2.imread('Distorted_10.jpg') #圧縮した画像
#画素値の読み込み
pixel_value_Ori = Original.flatten().astype(float)
pixel_value_Dis = Distorted.flatten().astype(float)
#画素情報の取得
imageHeight, imageWidth, BPP = Original.shape
#画素数
N = imageHeight * imageWidth
#1画素あたりRGB3つの情報がある.
addr = N * BPP
#RGB画素値の差の2乗の総和
sumR=0
sumG=0
sumB=0
#差の2乗の総和を計算
for i in range(addr):
if(i%3==0):
sumB += pow ( (pixel_value_Ori[i]-pixel_value_Dis[i]), 2 )
elif(i%3==1):
sumG += pow ( (pixel_value_Ori[i]-pixel_value_Dis[i]), 2 )
else:
sumR += pow ( (pixel_value_Ori[i]-pixel_value_Dis[i]), 2 )
#PSNRを求める
MSE =(sumR + sumG + sumB) / (3 * N )
PSNR = 10 * math.log(255*255/MSE,10)
print('PSNR',PSNR)