第5回 平滑化
本題に入る前に、必ず
連絡 の動画を見てください。
Google Colabへのリンク
1. リサイズとノイズ追加
動画の解説を参照
今回行う「平滑化」では隣り合ったピクセルの色を混ぜて均(なら)すことによって画像をぼかします。
それに伴って、夜間撮影などで起こるザラつき (
参考) を消すことができます。
もともとザラつきのある画像を探すのは難しいので、ここでは普通の画像を元にしてノイズを追加する処理を行います。
さらに、以降の項目でのノイズの低減の効果を確認しやすくするため、横方向のサイズが800ピクセルになるように大きさを調整します。
(処理の順番は「サイズ変更」→「ノイズ追加」です)
import cv2
import numpy as np
from google.colab import files
file = files.upload()
filename = next(iter(file))
img = cv2.imread(filename)
h, w = img.shape[:2]
new_w = 800
new_h = int(h * (new_w / w))
img1 = cv2.resize(img, (new_w, new_h))
for i in range(round(new_w * new_h * 0.01)):
x = np.random.randint(0, new_w)
y = np.random.randint(0, new_h)
img1[y, x] = [np.random.randint(0, 256),
np.random.randint(0, 256),
np.random.randint(0, 256)]
cv2.imwrite('ex1.png', img1)
(前回までのようにJPG形式で保存すると画像が劣化し、本来のノイズの乗り方とは異なった画像になってしまうため、ここではPNG形式で保存しています。
実写の写真のような画像では、JPG形式に比べてPNG形式の方がファイルサイズは大きくなりますが、PNG形式では画像の劣化は起こりません)
元画像の選び方についての注意
今回確認したいことは「平滑化によるノイズの薄まりかた」「平滑化によるエッジのぼやけ方」の2つです。
そのため、元画像には
ほぼ単色の広いエリアがあり、くっきりしたエッジがあって、それなりに解像度が大きいものを使ってください。
(画像検索して小さく表示されているものをダウンロードしたりすると、いわゆる「サムネイル」という縮小された画像になってしまいます)
理想は例えばこのような画像です。
課題1
上記の太枠の説明の条件を満たす元画像を元にして、横幅が800ピクセルのノイズ入り画像 ex1.pngを出力してください。
2. 移動平均フィルタ
動画の解説を参照
移動平均フィルタでは、ピクセルのまわりのピクセルの色を平均して変換後の画像のピクセルの色を決めます。
OpenCVでは「blur」関数で移動平均フィルタをかけられます。第1引数が画像の変数、第2引数が平均を取るために考慮される範囲 (
カーネル) です。
このようにすると、3×3の範囲の色を平均してその中心の位置のピクセルの色が決まります。
img2_3 = cv2.blur(img1, (3, 3))
このようにすると、5×5の範囲の色を平均してその中心の位置のピクセルの色が決まります。
img2_5 = cv2.blur(img1, (5, 5))
カーネルが大きくなるほどぼやけ方が大きくなるため、エッジは曖昧になりノイズは目立たなくなります。
img2_3 = cv2.blur(img1, (3, 3))
img2_5 = cv2.blur(img1, (5, 5))
img2_7 = cv2.blur(img1, (7, 7))
img2_9 = cv2.blur(img1, (9, 9))
cv2.imwrite('ex2_3.png', img2_3)
cv2.imwrite('ex2_5.png', img2_5)
cv2.imwrite('ex2_7.png', img2_7)
cv2.imwrite('ex2_9.png', img2_9)
課題2
課題1で作成したノイズ入り画像を元にして、カーネルが3×3~9×9の移動平均フィルタをかけた画像 ex2_3.png ~ ex2_9.png を出力してください。
※ カーネルが大きいほどぼやけ方が大きくなるはずです。出力された画像を比較して確認してください。
3. ガウシアンフィルタ
動画の解説を参照
移動平均フィルタでは周辺のピクセルの色を混ぜて使うので、カーネルを大きくするとエッジがぼやけてしまいます。これは、元ピクセルからの距離と関係なく色を混ぜてしまっているためです。
もう少し工夫して、中心のピクセルの影響は強く、そこから離れるにしたがって影響が弱くなるように「重みをつけた平均」を取れば、カーネルを大きくしてもぼやけ方を抑えられます。
このようなフィルタにはいくつか種類がありますが、ガウシアンフィルタではその名の通り「ガウス関数」を重みとして使用します。
ガウス関数とは
\(
\begin{eqnarray}
\frac{1}{\sqrt{2\pi\sigma^2}}\exp\left(-\frac{x^2+y^2}{2\sigma^2}\right)
\end{eqnarray}
\)
のような形をした関数 (expはexponentialの略で、ネイピア数 \(e\) の累乗) です。
下のグラフはカーネルが9x9の場合の2種類の \(\sigma\) についてガウス関数の値を示したものです。\(\sigma\) が大きくなると横に広がり (青)、小さくなると尖った形になります (赤)。
標準偏差 (\(\sigma\))
が小さいと色の計算で中心の画素の影響がより大きくなるので、ぼやけ方は小さくなり、標準偏差が大きくなると中心から離れたピクセルも影響してくるようになるので移動平均の結果に近く、ぼやけ方は大きくなります。
OpenCVでは「GaussianBlur」関数でガウシアンフィルタをかけられます。
第1引数は画像の変数、第2引数はカーネル、第3引数が標準偏差です。
カーネルが同じであれば、標準偏差が大きいほどぼやけ方が大きくなります。
img3_1 = cv2.GaussianBlur(img1, (9, 9), 1)
img3_5 = cv2.GaussianBlur(img1, (9, 9), 5)
img3_9 = cv2.GaussianBlur(img1, (9, 9), 9)
cv2.imwrite('ex3_1.png', img3_1)
cv2.imwrite('ex3_5.png', img3_5)
cv2.imwrite('ex3_9.png', img3_9)
課題3
課題1で作成したノイズ入り画像を元にして、カーネルが9×9で標準偏差が 1, 5, 9 のガウシアンフィルタをかけた画像 ex3_1.png ~ ex3_9.png を出力してください。
※ 標準偏差が大きいほどぼやけ方が大きくなるはずです。出力された画像を比較して確認してください。
4. 双方向フィルタ
動画の解説を参照
ガウシアンフィルタでは、中心のピクセルからの距離に応じてそのピクセルの色をどれだけ計算に入れるかという「重み」を決めましたが、双方向フィルタでは距離だけでなく色がどれだけ近いかも重みの計算に含めます。
つまり、そのピクセルの色が中心のピクセルに近ければ影響が大きく、色の違いが大きければ影響が小さくなるように重みを決めます。
大雑把に言えば「多少遠い場所にあっても似た色なら平均の計算に優先して使う」という処理になるので、全体としてはぼかしつつもエッジの情報を保持するようなぼかし方ができます。
OpenCVでは「bilateralFilter」関数で双方向フィルタをかけられます。
第1引数が画像の変数、第2引数がカーネル (1つの値で縦横両方を表す)、第3引数が色空間の標準偏差、第4引数が座標空間の標準偏差です。
img4_75 = cv2.bilateralFilter(img1, 9, 75, 75)
img4_255 = cv2.bilateralFilter(img1, 9, 255, 75)
cv2.imwrite('ex4_75.png', img4_75)
cv2.imwrite('ex4_255.png', img4_255)
課題4
課題1で作成したノイズ入り画像を元にして、カーネルが9×9、座標空間の標準偏差が9で、色空間の標準偏差が75, 255の双方向フィルタをかけた画像 ex4_75.png, ex4_255.png を出力してください。
※ 色空間の標準偏差が大きい「ex4_255.png」の方がぼやけ方が大きくなるはずです。
※ これと前項の課題で出力した「ex3_9.png」を比較すると、「ex4_255.png」の方がエッジがくっきりしているはずです。出力された画像を比較して確認してください。
5. メディアンフィルタ
動画の解説を参照
メディアンフィルタでは、平均を取るのではなく、カーネル内のピクセルの色成分の中央値を取って出力画像の色を決めます。
カーネルが5x5の場合なら、考慮する25個のピクセルの色成分を昇順に並べ替え、中央番目、つまり13番目 (プログラミングの数え方的には12番目) のピクセルの色を採用します。
(図は赤成分です。緑・青成分についても同様にして中央番目の値を使います)

移動平均フィルタ、ガウシアンフィルタ、双方向フィルタはいずれもノイズを「まぜて薄める」という方法でしたが、メディアンフィルタでは採用された1つのピクセル以外は完全に無視されます。
採用されるのはそのなかで「最も平凡な色」なので、ノイズの色が採用されることはほぼありません。
そのため、カーネルが小さくてもほとんどのノイズを消せます。
OpenCVでは「medianBlur」関数でメディアンフィルタをかけられます。
第1引数が画像の変数、第2引数がカーネル (1つの値で縦横両方を表す) です。
img5_3 = cv2.medianBlur(img1, 3)
img5_5 = cv2.medianBlur(img1, 5)
img5_7 = cv2.medianBlur(img1, 7)
cv2.imwrite('ex5_3.png', img5_3)
cv2.imwrite('ex5_5.png', img5_5)
cv2.imwrite('ex5_7.png', img5_7)
課題5
課題1で作成したノイズ入り画像を元にして、カーネルが3×3 ~ 7×7 のメディアンフィルタをかけた画像 ex5_3.png ~ ex5_7.png を出力してください。
※ いずれもほぼノイズが消えているはずです。
※ カーネルが大きくなると、パステル画のようなのっぺりした画像になります。
提出
動画の解説を参照
今回作成したノートブックを「imgprc2024@gmail.com」と共有してください。
※ 第2回で共有した、画像共有用の「画像処理」フォルダの中に「第5回」フォルダを作り、その中に今回の元画像と出力画像 (計14枚) を入れてください。
※ 出力画像を保存するときは、
出力時と同じ名前で保存してください (特に今回は画像が多いので、名前がイレギュラーだと同類の画像どうしを比較しにくくなるため)。
※ 課題の再提出の際は、ノートブックの再度の共有はせずにチャットで連絡してください。