本日は画像品質を評価する評価指標の一つであるMSE(Mean Squared Error: 最小二乗誤差)を紹介し、画像のMSEを計算するプログラムを紹介したいと思います。
前回グレースケール画像でMSEを算出したので、今回はカラー画像を対象に、各チャネルごとのMSEを算したいと思います。
MSE(Mean Squared Error: 最小二乗誤差)
最小二乗誤差は、正解値と評価したい値の差の二乗から計算される評価指標です。もし、評価したい対象と正解が完全に一致する場合、MSEは0となります。
MSEは以下の式で計算されます。

画像処理であればNは画像のピクセル数が対応します。以下に4画素の画像でMSEを計算する場合の計算例を示します。

MSEは単なる画素値の差の二乗をベースとした指標のため、人間の視覚的な感覚と、劣化具合が必ずしも一致しないという問題はありますが、非常に簡単に計算することが可能です。
以下で、MSEを計算するプログラムを見ていきます。
画像のMSE測定プログラム(Python+OpenCV)
ソースコード全体
動作環境:OpenCV 4.5.5
確認のためにNumpyとOpenCVの双方で結果を算出しています。OpenCVにはMSEを算出するための関数が既に用意されているので、OpenCVを使うのが基本的には楽だと思います。
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
noisy_image = AddGaussianNoise(gt_image, 0, 30)
# Saving image
cv2.imwrite('noisy_image.jpg',noisy_image)
cv2.imwrite('gt_image.jpg',gt_image)
# Calculate MSE with numpy (RGB)
gt_image_blue, gt_image_green, gt_image_red = cv2.split(gt_image)
noisy_image_blue, noisy_image_green, noisy_image_red = cv2.split(noisy_image)
error_blue= np.sum((noisy_image_blue.astype(float) - gt_image_blue.astype(float)) ** 2)
MSE_blue_numpy = error_blue / (float(noisy_image.shape[0] * noisy_image.shape[1]))
print("MSE Numpy (blue): " + str(MSE_blue_numpy))
error_green = np.sum((noisy_image_green.astype(float) - gt_image_green.astype(float)) ** 2)
MSE_green_numpy = error_green / (float(noisy_image.shape[0] * noisy_image.shape[1]))
print("MSE Numpy (green): " + str(MSE_green_numpy))
error_red = np.sum((noisy_image_red.astype(float) - gt_image_red.astype(float)) ** 2)
MSE_red_numpy = error_red / (float(noisy_image.shape[0] * noisy_image.shape[1]))
print("MSE Numpy (red): " + str(MSE_red_numpy));
# Calculate MSE with OpenCV (RGB)
MSE_opencv, _ = cv2.quality.QualityMSE_compute(noisy_image, gt_image)
print("MSE OpenCV (blue): " + str(MSE_opencv[0]))
print("MSE OpenCV (green): " + str(MSE_opencv[1]))
print("MSE OpenCV (red): " + str(MSE_opencv[2]))
入力データとしては、以下の画像を用いました。
gt_imageがGroundTruthの画像(正解画像)であり、noisy_imageがノイズが付与された画像となります。画像には全チャネルにガウシアンノイズを付与しています。ガウシアンノイズに関してはこちらの記事を参照してください。

実行結果
1) ノイズレベル σ=30
■ 正解画像

■ ノイズ画像

■ MSE測定結果
MSE Numpy (blue): 898.2626652892562
MSE Numpy (green): 829.8355785123968
MSE Numpy (red): 811.0753305785124
MSE OpenCV (blue): 898.2626652892562
MSE OpenCV (green): 829.8355785123968
MSE OpenCV (red): 811.0753305785124
NumpyとOpenCVで測定結果が一致することがわかりました。
2) ノイズレベル σ=100
■ 正解画像

■ ノイズ画像

■ MSE測定結果
MSE Numpy (blue): 6451.726900826447
MSE Numpy (green): 6042.388987603305
MSE Numpy (red): 5820.100599173554
MSE OpenCV (blue): 6451.726900826447
MSE OpenCV (green): 6042.388987603306
MSE OpenCV (red): 5820.100599173554
ノイズを大きくするとMSEの値も大きくなることがわかります。
まとめ
今回はMSEを用いて画像を評価する方法を紹介しました。次回はMSEを使った評価指標であるPSNRを紹介したいと思います。