1. ホーム

OpenCV-Pythonによる画像デノイジング|fifty-nine

2022-03-17 08:56:38

対象

この章では

  • 画像からノイズを除去するために使用される非局所的平均ノイズ除去アルゴリズムを学びます。
  • cv.fastNlMeansDenoising(), cv.fastNlMeansDenoisingColored() などの様々な関数を見ることができます.

理論編

これまでの章では、ガウスぼかし、メディアンぼかしなど、何とかしてわずかなノイズを除去する画像平滑化技術を多く見てきました。これらの手法では、画素の周辺を小さく取り、ガウス加重平均や値の中央値などの演算を行い、中心要素を置き換えます。つまり、画素におけるノイズの除去は、その周囲の局所的な現象である。そこにはノイズの本質がある。

通常、ノイズは平均がゼロの確率変数と考えられている。p=p_0+n$で、$p_0$がその画素の真の値、$n$がその画素のノイズであると考える。異なる画像から多数の同一画素(N個とする)を取り出して、その平均値を計算することができる。理想的には、ノイズの平均値は0なので、$p = p_0$となるはずである。

簡単な設定で、自分で検証することができます。スチルカメラをある位置で数秒間保持する。そうすると、同じシーンの多くのフレームまたは多くの画像が得られます。次に、ビデオ内のすべてのフレームの平均を求めるコードを書きます(これはもう簡単すぎるくらい簡単なはずです)。最終的な結果を最初のフレームと比較してみてください。ノイズが減少しているのがわかると思います。残念ながら、この単純な方法は、カメラやシーンの動きに対して堅牢ではありません。多くの場合、ノイズの多い画像は1枚だけです。

そこで考え方は簡単で、ノイズを平均化するために類似した画像のセットが必要になります。画像に小さな窓(例えば5×5の窓)を設けることを考えます。ほとんどの場合、同じパッチが画像内の他の場所にある可能性があります。時にはその周りの小さなコミュニティーにも。これらの類似したパッチを一緒に使って、その平均値を求めたらどうでしょうか。その特定のウィンドウについては、それでいいのです。次の画像例をご覧ください。

画像の青いパッチは似ていますね。緑のパッチは似ています。そこで、ある画素を取り出し、その周りの小さな窓を取得し、画像内の似たような窓を探し、すべての窓を平均化し、その結果で画素を置き換えます。この方法が非局所的平均ノイズ除去法(quot)です。これまで見てきたぼかし技術よりも時間がかかりますが、結果は非常に良好です。より詳細な情報とオンラインデモは、他のリソースへの最初のリンクで見ることができます。

カラー画像の場合は、CIELAB色空間に変換した後、L成分とAB成分に別々にノイズリダクションを適用します。

OpenCVによる画像ノイズ除去

OpenCVはこの手法の4つのバリエーションを提供しています。

  1. cv.fastNlMeansDenoising ()-グレースケール画像1枚を処理する
  2. cv.fastNlMeansDenoisingColored ()-カラー画像を処理します。
  3. cv.fastNlMeansDenoisingMulti ()-短時間に撮影された画像シーケンス(グレースケール画像)を処理する。
  4. cv.fastNlMeansDenoisingColoredMulti ()-上記と同じですが、カラー画像用です。

一般的なパラメータは

  • h:フィルターの強さを決定するパラメーター。hの値が高いほどノイズ除去がうまくいきますが、画像のディテールもなくなります。(10に設定可能)
  • hForColorComponents: h と同じですが、カラー画像にのみ適用されます。(通常はhと同じ)
  • templateWindowSize:奇数であること。(7に設定することを推奨)
  • searchWindowSize: 奇数であること。(21を推奨)

これらのパラメータの詳細については、他のリソースの最初のリンクを参照してください。ここでは、2と3のデモを行います。残りは、あなたにお任せします。

  1. cv.fastNlMeansDenoisingColored () 前述のように、カラー画像からノイズを除去するために使用されます。(ノイズはガウシアンであってもよい)。以下の例を参照してください。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('die.png')
dst = cv.fastNlMeansDenoisingColored(img,None,10,10,7,21)
plt.subplot(121),plt.imshow(img)
plt.subplot(122),plt.imshow(dst)
plt.show()

結果を拡大したものがこちらです。私の入力画像のガウスノイズは $σ= 25$ です。結果を見るには

  1. cv.fastNlMeansDenoisingMulti () では、同じ方法を動画に適用してみます。最初のパラメータは、ノイズフレームのリストです。2番目のパラメータ imgToDenoiseIndex はノイズ除去を行うフレームを指定し、これには入力リスト中のフレームのインデックスを渡します。3番目は temporalWindowSize で、ノイズ除去に使用する近傍のフレーム数を指定する。変わっているはずです。その場合、合計で使うのは temporalWindowSize フレームで、中央のフレームがノイズ除去の対象となるフレームです。例えば、5つのフレームのリストを入力として渡します。作る imgToDenoiseIndex = 2,temporalWindowSize = 3 . 次に frame-1 frame-2 frame-3 デノイジング frame-2 . 例を見てみましょう。
    import numpy as np
    import cv2 as cv
    from matplotlib import pyplot as plt
    cap = cv.VideoCapture('vtest.avi')
    # Create a list of 5 frames
    img = [cap.read()[1] for i in xrange(5)]
    # Convert all to grayscale
    gray = [cv.cvtColor(i, cv.COLOR_BGR2GRAY) for i in img]
    # Convert all to float64
    gray = [np.float64(i) for i in gray]
    # Create noise with variance of 25
    noise = np.random.randn(*gray[1].shape)*10
    # Add noise to the image
    noisy = [i+noise for i in gray]
    # Convert to unit8
    noisy = [np.uint8(np.clip(i,0,255)) for i in noisy]
    # Noise reduction for the third frame
    dst = cv.fastNlMeansDenoisingMulti(noisy, 2, 5, None, 4, 7, 35)
    plt.subplot(131),plt.imshow(gray[2],'gray')
    plt.subplot(132),plt.imshow(noisy[2],'gray')
    plt.subplot(133),plt.imshow(dst,'gray')
    plt.show()
    
    

計算にはかなりの時間がかかる。その結果、1枚目の画像はオリジナルのフレーム、2枚目はノイズの多いフレーム、3枚目はノイズ除去された画像となります。