画質を評価する客観評価指標として、PSNR(Peak signal-to-noise ratio:ピーク信号対雑音比)がよく知られていますが、PSNRは必ずしも人間が感じる主観画質と評価値が一致しないことが知られています。
このPSNRよりも、人間の主観画質をより反映した客観評価指標としてSSIM(Structural similarity)がよく知られています。
今回は画像のSSIMについて紹介し、SSIMの測定を行うプログラムを紹介したいと思います。
SSIMとは
従来画像の品質評価によく使われるPSNRは、画像内の同位置の画素同士の値の差のみを参照しており、画像構造の類似度については考慮を行っていませんでした。
画像構造の類似度とは何かと言うと、例えば正解画像と評価画像の間で、評価画像の画素がいずれかの方向に全体的に1ピクセルずれてしまうような現象が発生したときに、人間の眼では違いが知覚しにくいものの、PSNR値では大幅に悪化してしまうようなことです。
SSIMでは、二つの画像の間の「輝度」「コントラスト」「構造」の3つの要素を比較し、それらを乗算することで最終的な評価値を算出します。
定義式は、非常に詳しく説明してくれているサイトが他にもありますので、ここでは端的によく使われるSSIMの定義式を紹介します。
SSIMは、画像内の小領域(Window)の中でそれぞれ算出され、Windowは少しずつスライドさせていきます。以下は、一つのWindowに対してのSSIMの計算式です。

上記をWindowの中に計算した後は、少しずつWindowをずらしあらゆるWindowでSSIMを算出した上で、最終的な平均値を結果として算出します。
SSIMは0のときに画質最低、1のときに画質最高を示します。
どれくらいの値があれば高品質かは難しいところですが、感覚的に0.9~0.95程度あれば高品質と言えるのではないかと思います。
画像のSSIM測定プログラム(Python+OpenCV)
動作環境:OpenCV 4.5.5
SSIMの測定の際に、RGBのようにカラーを複数持つ場合にどう測定するかは様々なやり方がありますが、今回は赤、緑、青ごとにそれぞれのSSIMを求めるプログラムとしました。
PSNRも算出できるようにしているので、SSIMとPSNRの値の違いを確認することも可能です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np
import cv2
print("OpenCV Version: " + str(cv2.__version__))
def AddGaussianNoise(image, mean, sigma):
noise = np.random.normal(mean, sigma, np.shape(image))
noisy_image = image + noise
noisy_image[noisy_image > 255] = 255
noisy_image[noisy_image < 0] = 0
noisy_image = noisy_image.astype(np.uint8) # Float -> Uint
return noisy_image
# Loading image data (COLOR)
gt_image = cv2.imread('data/lenna.png', cv2.IMREAD_COLOR)
# Adding Gaussian Noise
sigma = 2
noisy_image = AddGaussianNoise(gt_image, 0, sigma)
# Saving image
cv2.imwrite('noisy_image.jpg',noisy_image)
cv2.imwrite('gt_image.jpg',gt_image)
# Calculate PSNR with OpenCV (RGB)
PSNR_opencv, _ = cv2.quality.QualityPSNR_compute(noisy_image, gt_image)
print("PSNR Evaluation Results")
print(" PSNR OpenCV (Blue): " + str(PSNR_opencv[0]))
print(" PSNR OpenCV (Green): " + str(PSNR_opencv[1]))
print(" PSNR OpenCV (Red): " + str(PSNR_opencv[2]))
print(" PSNR OpenCV (RGB Average): " + str((PSNR_opencv[0] + PSNR_opencv[1] + PSNR_opencv[2]) / 3))
# Calculate SSIM with OpenCV (RGB)
SSIM_opencv, _ = cv2.quality.QualitySSIM_compute(noisy_image, gt_image)
print("SSIM Evaluation Results")
print(" SSIM OpenCV (Blue): " + str(SSIM_opencv[0]))
print(" SSIM OpenCV (Green): " + str(SSIM_opencv[1]))
print(" SSIM OpenCV (Red): " + str(SSIM_opencv[2]))
print(" SSIM OpenCV (RGB Average): " + str((SSIM_opencv[0] + SSIM_opencv[1] + SSIM_opencv[2]) / 3))
入力データとしては、以下の画像を用いました。

gt_imageがGroundTruthの画像(正解画像)であり、noisy_imageがノイズが付与された画像となります。画像には全チャネルにガウシアンノイズを付与しています。ガウシアンノイズに関してはこちらの記事を参照してください。
実行結果
σを段々大きくして、PSNRとSSIMの値を確認してみましょう。

今回の画像ではノイズレベルを高めていくにつれ、PSNRとSSIMの双方が悪化している傾向が見えました。
まとめ
画像のSSIMを測定し、画像の品質を評価するプログラムを紹介しました。
PSNRとSSIMは論文内での画質評価等にも使える著名かつ強力な客観評価手法です。今後はPSNRとSSIMで差が大きくなる画像で実験等も行ってみたいと思います。