8
7

More than 5 years have passed since last update.

[python]画質評価としてのPSNR

Last updated at Posted at 2019-06-08

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)
8
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7