平均値フィルタを用いた画像のノイズ除去プログラム(Python+OpenCV)

本日は平均値フィルタを用いた画像のノイズ除去について紹介したいと思います。

平均値フィルタ

平均値フィルタは、ある特定画素の周囲n×n画素の平均値を計算し、当該画素を平均値で置き換えることで、ノイズの除去等を行うことができる画像処理のフィルタです。

一番小さいサイズのものでは、3×3のフィルタを使うことが多いです。例えば以下の図のように、ある入力画像の各画素位置を中心とした周囲3×3の画素について平均値を計算し、その値で出力画像の画素値を置き換えます。

画像の端の方の画素を処理する場合には、フィルタが画像の外にはみ出すことがあるため、そこは計算しないなどの例外処理が必要となります。

平均値フィルタの効果

一般的にはノイズの除去に有益なフィルタと考えられています。

例えば0~255の画素で示される画像の中で、一画素だけ255のような飛び抜けた値が入っている場合には、このような画素はノイズであったりすることがあります。

このような画素を周囲画素の値の平均値で置き換えることで、画像全体の画素値の変化を滑らかにし、ノイズの除去等をすることが可能になります。

フィルタのサイズは3×3に限定されず、5×5や7×7などアレンジ可能です。 フィルタが大きければ大きいほど、画素値の変化は滑らかになりますが、副作用として画像全体の鮮鋭な部分もぼかされてしまい、ピンボケしたような画像になってしまうこともあるので注意が必要です。

画像への平均値フィルタによるノイズ除去プログラム(Python+OpenCV)

平均値フィルタはOpenCVの関数で簡単に実現することができます。以下にPythonとOpenCVで書いたプログラムを紹介します。

動作環境:OpenCV 4.5.5

import numpy as np
import cv2
import sys
def main():
print("OpenCV Version: " + str(cv2.__version__))
# Loading image data (COLOR)
filename = "data/lenna.png"
gt_image = cv2.imread(filename, cv2.IMREAD_COLOR)
if gt_image is None:
print("Cannot find image data : " + filename)
sys.exit()
# Generation of noisy image
noisy_image = AddGaussianNoise(gt_image, 0, 30)
# Denoising with average filter
# Average Filter (3*3)
kernel1 = np.array([[1/9, 1/9, 1/9],
[1/9, 1/9, 1/9],
[1/9, 1/9, 1/9]])
denoised_image1 = cv2.filter2D(noisy_image, -1, kernel1)
# Average Filter (5*5)
kernel2 = np.array([[1/25, 1/25, 1/25, 1/25, 1/25],
[1/25, 1/25, 1/25, 1/25, 1/25],
[1/25, 1/25, 1/25, 1/25, 1/25],
[1/25, 1/25, 1/25, 1/25, 1/25],
[1/25, 1/25, 1/25, 1/25, 1/25]])
denoised_image2 = cv2.filter2D(noisy_image, -1, kernel2)
# Average Filter (7*7)
kernel3 = np.array([[1/49, 1/49, 1/49, 1/49, 1/49, 1/49, 1/49],
[1/49, 1/49, 1/49, 1/49, 1/49, 1/49, 1/49],
[1/49, 1/49, 1/49, 1/49, 1/49, 1/49, 1/49],
[1/49, 1/49, 1/49, 1/49, 1/49, 1/49, 1/49],
[1/49, 1/49, 1/49, 1/49, 1/49, 1/49, 1/49],
[1/49, 1/49, 1/49, 1/49, 1/49, 1/49, 1/49],
[1/49, 1/49, 1/49, 1/49, 1/49, 1/49, 1/49]])
denoised_image3 = cv2.filter2D(noisy_image, -1, kernel3)
# Evaluation with PSNR and SSIM
PSNREval(gt_image, noisy_image, 255)
SSIMEval(gt_image, noisy_image)
PSNREval(gt_image, denoised_image1, 255)
SSIMEval(gt_image, denoised_image1)
PSNREval(gt_image, denoised_image2, 255)
SSIMEval(gt_image, denoised_image2)
PSNREval(gt_image, denoised_image3, 255)
SSIMEval(gt_image, denoised_image3)
cv2.imwrite('noisy_image.png',noisy_image)
cv2.imwrite('denoised_image1.png',denoised_image1)
cv2.imwrite('denoised_image2.png',denoised_image2)
cv2.imwrite('denoised_image3.png',denoised_image3)
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
def PSNREval(image1, image2, R):
PSNR_opencv, _ = cv2.quality.QualityPSNR_compute(image1, image2)
print("PSNR Evaluation Results")
print(" PSNR OpenCV (Blue): " + str(round(PSNR_opencv[0],3)))
print(" PSNR OpenCV (Green): " + str(round(PSNR_opencv[1],3)))
print(" PSNR OpenCV (Red): " + str(round(PSNR_opencv[2],3)))
print(" PSNR OpenCV (RGB Average): " + str(round(((PSNR_opencv[0] + PSNR_opencv[1] + PSNR_opencv[2]) / 3),3)))
def SSIMEval(image1, image2):
SSIM_opencv, _ = cv2.quality.QualitySSIM_compute(image1, image2)
print("SSIM Evaluation Results")
print(" SSIM OpenCV (Blue): " + str(round(SSIM_opencv[0],3)))
print(" SSIM OpenCV (Green): " + str(round(SSIM_opencv[1],3)))
print(" SSIM OpenCV (Red): " + str(round(SSIM_opencv[2],3)))
print(" SSIM OpenCV (RGB Average): " + str(round(((SSIM_opencv[0] + SSIM_opencv[1] + SSIM_opencv[2]) / 3),3)))
if __name__ == "__main__":
main()

入力データとしては、以下の画像を用いました。

プログラム中のgt_imageがGroundTruthの画像(正解画像)であり、noisy_imageがノイズが付与された画像となります。ノイズ画像には全チャネルにガウシアンノイズを付与しています。ガウシアンノイズに関してはこちらの記事を参照してください。

今回は、このnoisy_imageに対して平均値フィルタを適用することで、どれだけ正解に近づけることができるのか(ノイズ除去の効果)を確認します。

効果測定の評価指標としてはPSNRとSSIMと採用しました。PSNRとSSIMについてはこちらの記事をご参照ください。

画像のPSNR(ピーク信号対雑音比)の測定(Python+OpenCV)

画像のSSIM(structural similarity)の測定(Python+OpenCV)

プログラム実行結果

ノイズレベルσを段々大きくして、PSNRとSSIMの値を確認してみましょう。

ノイズレベルは19行目のnoisy_image = AddGaussianNoise(gt_image, 0, 30)の「30」の箇所を変えることで変更可能です。

実行すると以下のような出力が得られます。

PSNR Evaluation Results
   PSNR OpenCV (Blue): 18.588
   PSNR OpenCV (Green): 18.912
   PSNR OpenCV (Red): 19.064
   PSNR OpenCV (RGB Average): 18.855
SSIM Evaluation Results
   SSIM OpenCV (Blue): 0.256
   SSIM OpenCV (Green): 0.349
   SSIM OpenCV (Red): 0.31
   SSIM OpenCV (RGB Average): 0.305
PSNR Evaluation Results
   PSNR OpenCV (Blue): 26.2
   PSNR OpenCV (Green): 25.162
   PSNR OpenCV (Red): 26.158
   PSNR OpenCV (RGB Average): 25.84
SSIM Evaluation Results
   SSIM OpenCV (Blue): 0.601
   SSIM OpenCV (Green): 0.649
   SSIM OpenCV (Red): 0.651
   SSIM OpenCV (RGB Average): 0.634
PSNR Evaluation Results
   PSNR OpenCV (Blue): 26.293
   PSNR OpenCV (Green): 23.923
   PSNR OpenCV (Red): 25.259
   PSNR OpenCV (RGB Average): 25.159
SSIM Evaluation Results
   SSIM OpenCV (Blue): 0.684
   SSIM OpenCV (Green): 0.678
   SSIM OpenCV (Red): 0.702
   SSIM OpenCV (RGB Average): 0.688
PSNR Evaluation Results
   PSNR OpenCV (Blue): 25.439
   PSNR OpenCV (Green): 22.616
   PSNR OpenCV (Red): 23.917
   PSNR OpenCV (RGB Average): 23.991
SSIM Evaluation Results
   SSIM OpenCV (Blue): 0.677
   SSIM OpenCV (Green): 0.644
   SSIM OpenCV (Red): 0.674
   SSIM OpenCV (RGB Average): 0.665

上から順に

  • ノイズ画像のPSNR
  • ノイズ画像のSSIM
  • 3×3の平均値フィルタ適用時のPSNR
  • 3×3の平均値フィルタ適用値のSSIM
  • 5×5の平均値フィルタ適用値のPSNR
  • 5×5の平均値フィルタ適用値のSSIM
  • 7×7の平均値フィルタ適用値のPSNR
  • 7×7の平均値フィルタ適用値のSSIM

となります。

各ノイズレベルでの結果を表にまとめたものが以下となります。PSNRとSSIM共に値が大きいほど品質が良い(正解画像に類似している)ことを示します。

興味深いことに、ノイズレベルが小さい(σ=3)の場合は、平均値フィルタを掛けると画質は劣化するという結果が出ました。これは、ノイズが少ないのにも関わらず平均値フィルタを掛けることで、ノイズ除去による改善よりも、副作用である画像全体がボケてしまうという部分が強く出てしまったためだと思います。

一方、ノイズレベルが大きめのものでは改善効果が見られ、3×3のフィルタで特に良い効果が得られているケースが多いようです。

そのため、単にフィルタのサイズを大きくすることが品質の改善には繋がらないこともわかります。

以下にσ=30の場合の各画像の比較も示します。7×7などでは少々ボケ過ぎているような傾向がみられるようです。

まとめ

本日は平均値フィルタを用いたノイズ除去を紹介しました。

このようなノイズ除去ができるフィルタは多数提案されていますので、他も今後紹介してみたいと思います。

スポンサーリンク

シェアする

フォローする