GuidedFilter

Bilateral Filterよりも高速化に動作するエッジ保存型のフィルターGuided Filterを実装したので紹介します.

概要

Guided Filterは,Bilateral Filterのようなエッジ保存型のフィルターで高速な動作が特徴です.

Guided Filterでノイズ除去した結果.

一枚の画像に対して平滑化処理を行うBilateral Filterの機能に加えて,Guided Filterでは色の類似性を考慮するためのガイド画像$I$とフィルタリングされる入力画像$p$を分けて持つことができます($I=p$であれば,Bilateral Filterのように動作します).

OpenCV 3.0であれば,Guided Filterが既に実装されていますので以下のようにして呼び出せます.

OpenCV Guided Filter
1
2
3
4
5
6
7
cv::Mat I = cv::imread("guidance.png");
cv::Mat p = I;
int r = 8;
double eps = 255 * 0.3;
cv::Mat q = guidedFilter(I, p, r, eps);

手元のPython用のOpenCVが2.4であることと,Guided Filterを高速化したFast Guided Filterを実装するためには,中身をいじらなければいけなかったので自前で実装することにしました(デモ用のPythonコードはGitHubで公開しています).
以下では,簡単にGuided Filterの仕組みについて触れたいと思います.

一般的な重み付フィルタ

出力画像を$q$とすると,一般的な重みのフィルタリング処理は以下のように書けます.

$q_i = \sum_j W_{ij} (I) p_j$

ここで,$j$は求めたいピクセル$i$の近傍ピクセル,$W_{ij}$はガイド画像$I$を入力とした重み関数でピクセル$i$,ピクセル$j$間の重みを表します.

例えば,Bilateral Filterの場合,以下のような重みを使っていることに相当します.

$W_{ij}^{bf} (I)= \frac{1}{K_i} G_{\sigma_s} (x_i) G_{\sigma_r} (I_i)$

ここで,$G$はGauss関数を表し,$\sigma_s$と$\sigma_r$で座標と色の類似性を考慮しつつ重みを計算しています.一般的なGaussian Filterでは,色に関する重みが無いので処理を$x$,$y$座標成分に分けて計算することで高速化できますが,色の部分が入るとそれが出来ないので計算が重くなります.

Guided Filter

Guided Filterでは,ガイド画像$I$と出力画像$q$に対して以下のような線形変換モデルを仮定します.

$q_i = a_k I_i + b_k, \forall i \in \omega_k$

ここで,$\omega_k$はピクセル$k$の周辺領域を表し,一定の線形変換パラメータ$a_k$,$b_k$で周辺領域内の$I_i$,$q_i$の関係を表現しています.周辺領域$\omega_k$は一辺$r$の四角形で$\sigma_s$と同様に座標が近い所では線形変換も類似していることを考慮しています.

未知である出力画像とガイド画像の関係性を推定することは出来ないので,入力画像$p$と出力画像$q$がある程度近いと仮定して,$p$と$I$の関係性から$a_k$,$b_k$を推定します.
$a_k$,$b_k$は周辺領域のピクセルに関して,以下のエラー関数を最小化する形で得られます.

$E (a_k, b_k) = \sum_{i \in \omega_k} ((a_k I_i + b_k - p_i)^2 + \epsilon a_k^2)$

ここで,$\epsilon$は正則化パラメータでどのくらいガイド画像の影響を考慮するか($\sigma_r$に相当)を考慮します.解の詳細は省きますが,ここから$a_k$,$b_k$が求まります.

最終的な出力は,求めた$a_k$,$b_k$を使って以下のように計算できます.

$q_i = \bar{a}_i I_i + \bar{b}_i$

ここで,$\bar{a}_i$,$\bar{b}_i$は$\omega_i$内の$a_k$,$b_k$を平均化した物で$\bar{a}_i=\frac{1}{\omega} \sum_{k \in \omega_i} a_k$,$\bar{b}_i= \frac{1}{\omega} \sum_{k \in \omega_i} b_k$で計算できます.

この計算過程はシンプルな平均処理が基本となっていますので高速に動作します.後述のFast Guided Filterでは,ダウンサンプルした画像を利用してさらに処理を高速化します.

Fast Guided Filter

Fast Guided Filterでは,ダウンサンプルした画像を利用してさらに処理を高速化します.アイデアは非常にシンプルでダウンサンプルした画像で$\bar{a}_i^{down}$,$\bar{b}_i^{down}$を計算しておき,最終的な出力をする段階で$\bar{a}_i=f_{up}(\bar{a}_i^{down})$のように線形変換パラメータをアップサンプリングして元のサイズで$q_i = \bar{a}_i I_i + \bar{b}_i$を計算します.

処理の重い部分をダウンサンプルした画像上で行うので高速に動作します.$\bar{a}_i$,$\bar{b}_i$は元々ぼかされることが前提ですし,エッジ保存の部分は元の解像度のガイド画像$I_i$が担当していますので出力の質はあまり落ちません.各フィルタの計算速度に関してですが,自前の実装では以下のようになりました.

Guided Filterのパフォーマンス.

画像サイズ$1200 \times 800$でPythonで扱うには割と大き目ですが,0.1秒以内でエッジ保存フィルタをかけることが出来ています.Bilateral Filter (OpenCVのbilateralFilter関数)では,半径$r$の増加につれて計算時間も増加してしまっていますが,Guided Filterではほとんど変化していません.さらに,Fast Guided Filterでは5倍程度の高速化が見られました(著者らのページでは10倍以上高速化できるとありました).どちらにせよ,半径$r$にあまり依存しないので$r=100$でフィルタをかけるという荒業も出来ます(Bilateral Filterをかけるには勇気がいる設定).

まとめ

Guided Filterの概略を紹介しましたが,論文ではガイド画像と入力画像を分けられる性質を利用して,特徴強調・フラッシュ画像を利用してノイズ除去・高品質な$\alpha$マット生成の応用にも触れられていますので興味があるば参照されると良いかと思います.現状画像での応用が目立っていますが,3次元メッシュでの計算にも応用できれば面白そうです.

参考文献

[1] Guided Image Filtering:
   http://research.microsoft.com/en-us/um/people/kahe/eccv10/

[2] Fast Guided Filter:
   http://arxiv.org/abs/1505.00996