.. _Filtering: 画像の平滑化 *********************** 目的 ======= 学習内容: * 画像をぼかすための様々なローパスフィルタ * 自作したフィルタの適用(2D convolution) 2D Convolution (画像のフィルタリング) =============================================== 1次元信号と同様,画像に対してローパスフィルタ(LPF)やハイパスフィルタ(HPF)によるフィルタリングを適用することができます.LPFはノイズ除去や画像をぼかすために,HPFは画像中のエッジ検出に使われます. **cv2.filter2D()** 関数は入力画像とカーネル(フィルタ)のconvolutionを計算します.以下に5x5サイズの平均値フィルタに使うカーネルを示します: .. math:: K = \frac{1}{25} \begin{bmatrix} 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \end{bmatrix} 上記のカーネルを使ったフィルタリングを行うと: 各画素に対してその画素を中心にした5x5のウィンドウを選択し,ウィンドウ内の全画素の画素値の合計を25で割ります.この処理はウィンドウ内の画素値の平均を取る処理です.この計算を全画素に対して適用し,フィルタリングした画像を作ります.以下のコードを実行してください: :: import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('opencv_logo.png') kernel = np.ones((5,5),np.float32)/25 dst = cv2.filter2D(img,-1,kernel) plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(dst),plt.title('Averaging') plt.xticks([]), plt.yticks([]) plt.show() 結果: .. image:: images/filter.jpg :alt: Averaging Filter :align: center 画像のぼかし (平滑化) ================================== 画像のぼかしはローパスフィルタのカーネルを重畳積分することで実現でき,画像中のノイズ除去などに使います.画像中の高周波成分(エッジやノイズ)を消すことで結果として画像全体がぼけます(エッジをぼけさせない画像のぼかし方もあります).OpenCVが用意しているのは4種類のぼかし方です. 1. 平均 -------------- 平均を取るには正規化された箱型フィルタを使います. カーネルの範囲内にある全画素の画素値の平均をとります. **cv2.blur()** か **cv2.boxFilter()** を使います.カーネルの縦幅,横幅を指定する必要が有ります.このカーネルに関する詳細についてはドキュメントを参照してください.3x3の箱型フィルタは以下のようになります: .. math:: K = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix} .. note:: 正規化された箱型フィルタを使いたくなければ **cv2.boxFilter()** 関数の引数に ``normalize=False`` を指定してください. 5x5の箱型フィルタを使った例を以下に示します: :: import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('opencv_logo.png') blur = cv2.blur(img,(5,5)) plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(blur),plt.title('Blurred') plt.xticks([]), plt.yticks([]) plt.show() 結果: .. image:: images/blur.jpg :alt: Averaging Filter :align: center 2. ガウシアンフィルタ ---------------------- 箱型フィルタがカーネル内のフィルタ係数が一様だったのに対して,ガウシアンフィルタは注目画素との距離に応じて重みを変えるガウシアンカーネルを採用します. **cv2.GaussianBlur()** 関数を使います.カーネルの縦幅と横幅(どちらも奇数)に加え,ガウシアンの標準偏差値sigmaX(横方向)とsigmaY(縦方向)を指定する必要があります.sigmaXしか指定されなければ,sigmaYはsigmaXと同じだとみなされます.どちらの値も0にした場合,カーネルのサイズから自動的に計算されます.ガウシアンフィルタは白色雑音の除去に適しています. **cv2.getGaussianKernel()** 関数を使えば,ガウシアンフィルタを作成することができます. 上記のコードを編集すればガウシアンフィルタを試せます: :: blur = cv2.GaussianBlur(img,(5,5),0) 結果: .. image:: images/gaussian.jpg :alt: Gaussian Blurring :align: center 3. 中央値フィルタ -------------------- **cv2.medianBlur()** 関数はカーネル内の全画素の中央値を計算します.ごま塩ノイズのようなノイズに対して効果的です.箱型フィルタとガウシアンフィルタの出力結果は原画像中には存在しない画素値を出力とするのに対して,中央値フィルタの出力は常に原画像中から選ばれています.そのためごま塩ノイズのような特異なノイズに対して効果的です.カーネルサイズは奇数でなければいけません. この例では原画像に対して50%のノイズを追加した劣化画像に中央値フィルタを適用しています.: :: median = cv2.medianBlur(img,5) 結果: .. image:: images/median.jpg :alt: Median Blurring :align: center 4. バイラテラルフィルタ ----------------------- 前述したように,フィルタリングは一般的にエッジまでぼかしてしまいますが, **cv2.bilateralFilter()** によって使えるバイラテラルフィルタはエッジを保存しながら画像をぼかすことができます.しかし,上記のフィルタリングに比べて処理速度が遅いという欠点があります.既に紹介したガウシアンフィルタは注目がその近傍領域に対して重み付け平均した値を出力します.これはガウシアンフィルタが注目画素の近傍の画素のみを考慮した関数であることを意味します.近傍領域内の画素が似たような値を持っているか否か,注目画素がエッジ上に存在するか否かなどは考慮されません.結果としてガウシアンフィルタはエッジの劣化が不可避です. バイラテラルフィルタも同様にガウシアンフィルタを採用していますが,画素値の差を考慮した関数として別のガウシアンフィルタも同時に使用します.一つ目のガウシアンフィルタはフィルタリングに使用する画素は '空間的に近い位置にある'ことを保証してくれます.一方で,二つ目のガウシアンフィルタは注目画素に似た画素値を持つ画素の値のみ考慮してフィルタリングすることを保証します.結果としてバイラテラルフィルタはエッジを保存した画像のぼかしを実現できることになります. 以下の例はバイラテラルフィルタの使い方です(各引数の詳細はOpenCVのドキュメントを参照してください). :: blur = cv2.bilateralFilter(img,9,75,75) 結果: .. image:: images/bilateral.jpg :alt: Bilateral Filtering :align: center 表面上のテクスチャは見えなくなりましたが,エッジが保存されているのが分かります. 補足資料 ====================== 1. バイラテラルフィルタの詳細な資料は `ここ `_ で読めます. 課題 =========== ガウシアンノイズを足した劣化画像とごま塩ノイズを足した劣化画像に対して,箱型フィルタ,ガウシアンフィルタ,中央値フィルタ,バイラテラルフィルタを使って,ノイズレベルの変化に対して各フィルタによる結果画像がどのように変わるか試してください.