目的
本章では、
理論
これまでの章では、ガウシアン平滑化やメディアン平滑化など、多くの画像平滑化手法を見てきた。これらは少量のノイズを除去するうえである程度有効だった。これらの手法では、ピクセルの周囲の小さな近傍を取り、ガウシアン重み付き平均や値のメディアンなどの演算を行って中心要素を置き換えた。要するに、あるピクセルのノイズ除去はその近傍に対してローカルに行われていた。
ノイズには次のような性質がある。ノイズは一般に平均が0の確率変数とみなされる。ノイズの乗ったピクセル \(p = p_0 + n\) を考える。ここで \(p_0\) はピクセルの真値、\(n\) はそのピクセルのノイズである。異なる画像から同じピクセルを多数(たとえば \(N\) 個)取り、その平均を計算できる。ノイズの平均は0なので、理想的には \(p = p_0\) が得られるはずである。
簡単な実験で自分で確認できる。固定カメラを数秒間ある場所に向けておく。これにより多数のフレーム、つまり同じシーンの大量の画像が得られる。次に、動画内のすべてのフレームの平均を求めるコードを書く(今のあなたにとっては簡単すぎるはずだ)。最終結果と最初のフレームを比較する。ノイズが減少しているのがわかる。残念ながら、この単純な手法はカメラやシーンの動きに対して頑健ではない。また、多くの場合ノイズの乗った画像が1枚しか利用できない。
そこでアイデアは単純で、ノイズを平均化して除去するために、似た画像の集合が必要である。画像内の小さなウィンドウ(たとえば5x5のウィンドウ)を考える。同じパッチが画像内の別の場所に存在する可能性は高い。場合によってはその周囲の小さな近傍に存在することもある。これらの似たパッチをまとめて使い、その平均を求めてはどうだろうか。その特定のウィンドウについては、それで問題ない。下の例画像を見てほしい。
image
画像中の青いパッチは互いに似て見える。緑のパッチも似て見える。そこで、あるピクセルを取り、その周囲に小さなウィンドウを取り、画像内で似たウィンドウを探し、すべてのウィンドウを平均して、得られた結果でそのピクセルを置き換える。この手法がNon-Local Means Denoisingである。先に見た平滑化手法に比べて時間はかかるが、その結果は非常に良い。詳細とオンラインデモは追加資料の最初のリンクにある。
カラー画像の場合、画像はCIELAB色空間に変換され、その後LとABの各成分を別々にノイズ除去する。
OpenCVにおける画像のノイズ除去
OpenCVはこの手法の4つのバリエーションを提供している。
- cv.fastNlMeansDenoising() - 単一のグレースケール画像に対して機能する
- cv.fastNlMeansDenoisingColored() - カラー画像に対して機能する。
- cv.fastNlMeansDenoisingMulti() - 短時間に撮影された画像シーケンス(グレースケール画像)に対して機能する
- 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
plt.subplot(121),plt.imshow(img)
plt.subplot(122),plt.imshow(dst)
plt.show()
Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
Loads an image from a file.
void fastNlMeansDenoisingColored(InputArray src, OutputArray dst, float h=3, float hColor=3, int templateWindowSize=7, int searchWindowSize=21)
Modification of fastNlMeansDenoising function for colored images.
以下は結果を拡大したものである。入力画像には \(\sigma = 25\) のガウシアンノイズが乗っている。結果を見てほしい。
image
2. cv.fastNlMeansDenoisingMulti()
次に、同じ手法を動画に適用する。第1引数はノイズの乗ったフレームのリストである。第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
img = [cap.read()[1] for i in range(5)]
gray = [
cv.cvtColor(i, cv.COLOR_BGR2GRAY)
for i
in img]
gray = [np.float64(i) for i in gray]
noise = np.random.randn(*gray[1].shape)*10
noisy = [i+noise for i in gray]
noisy = [np.uint8(np.clip(i,0,255)) for i in noisy]
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()
Class for video capturing from video files, image sequences or cameras.
Definition videoio.hpp:786
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
Converts an image from one color space to another.
void fastNlMeansDenoisingMulti(InputArrayOfArrays srcImgs, OutputArray dst, int imgToDenoiseIndex, int temporalWindowSize, float h=3, int templateWindowSize=7, int searchWindowSize=21)
Modification of fastNlMeansDenoising function for images sequence where consecutive images have been ...
下の画像は、得られた結果を拡大したものである。
image
計算にはかなりの時間がかかる。結果では、1番目の画像が元のフレーム、2番目がノイズの乗ったもの、3番目がノイズ除去された画像である。
追加資料
- http://www.ipol.im/pub/art/2011/bcm_nlm/ (詳細やオンラインデモなどが掲載されている。ぜひ訪問することを強く推奨する。テスト画像はこのリンクから生成したものである)
- courseraのオンラインコース (最初の画像はここから取得した)