研究開発日誌

CG研究・開発のちょっとしたメモ書き

XDoG: An eXtended difference-of-Gaussians compendium including advanced image stylization

2015-01-27 NPR

XDoG (Extended Difference of Gaussians)で画像をstylizationする論文XDoG: An eXtended difference-of-Gaussians compendium including advanced image stylizationを読んだのでご紹介します.

概要

XDoGは,DoG (Difference of Gaussians)にシャープ化・閾値処理をして拡張した物になります.DoGは一般的に,ノイズが多い画像に対しても安定してエッジを抽出することができます.この論文では,さらにシャープ化・閾値処理を加えることにより,インクイラストのような減色化処理を実現しています.

DoG (Difference of Gaussians)

ここでは,あまり詳しく式の導出には触れませんが,DoGは,LoG (Laplacian of Gaussians)の近似モデルになっています.Laplacianの処理が入ると,x方向,y方向に処理を分離できるGaussianフィルタの長所が消えてしまうので,大小2つのカーネルサイズのGaussianフィルタの差分でLoGを近似します.

小さいカーネルサイズのGaussianフィルタを$G_{\sigma} (x)$,大きいカーネルサイズのGaussianフィルタを$G_{k \sigma} (x)$とすると,DoGフィルタ$D_{\sigma, k} (x)$は以下のように表すことができます.

$D_{\sigma, k} (x) = G_{\sigma} (x) - G_{k \sigma} (x)$

ここで,$k$はカーネルサイズの比率を表し,一般的に$k=1.6$の値が使われています.

DoGのカーネル

実際にDoGフィルタを使ってエッジ抽出を行う際は,$D_{\sigma, k} (x)$のzero-crossingを求めます.ただ,単純に$D_{\sigma, k} (x)=0$の部分を求めてしまうと,色が変化していない部分も拾ってしまうので,$D_{\sigma, k} (x) < \epsilon$ ($\epsilon < 0$)で閾値処理を行います.

DoGによるエッジ抽出

以下,簡単ではありますがPython+OpenCVによるDoGによるエッジ抽出のコードです.

## DoG filter.
#  @param  img      input gray image.
#  @param  sigma    sigma for small Gaussian filter.
#  @param  k_sigma  large/small sigma (Gaussian filter).
def DoG(img, sigma, k_sigma):
    sigma_large = sigma * k_sigma

    G_small = cv2.GaussianBlur(img,(0, 0), sigma)
    G_large = cv2.GaussianBlur(img,(0, 0), sigma_large)

    D = G_small - G_large
    return D

## DoG edge detection.
#  @param  img      input gray image.
#  @param  sigma    sigma for small Gaussian filter.
#  @param  k_sigma  large/small sigma (Gaussian filter).
#  @param  epsilon  threshold value for edge detection.
def DoGEdge(img, sigma, k_sigma, epsilon):
    D = DoG(img, sigma, k_sigma)
    retval, D_edge = cv2.threshold(D, epsilon, 1.0, cv2.THRESH_BINARY)
    return D_edge

OpenCVの関数をGaussianフィルタ関数をそのまま使っているだけの単純な処理です. その他のエッジ抽出手法との比較は,ここでは省略しますが,Gaussianフィルタの効果でノイズが軽減された状態でエッジを抽出できます.

XDoG (Extended Difference of Gaussians)

XDoGでは,DoGにシャープ化・閾値処理を加えてインクイラストのような2値画像を生成できます.まず,シャープ化の処理ですが,小さいカーネルサイズのGaussianフィルタ画像$G_{k \sigma} (x)$にスケールしたDoGフィルタ画像$D_{\sigma, k} (x)$を足し合わせることにより実現しています.

$S_{\sigma, k, p}(x) = G_{\sigma} (x) + p \cdot D_{\sigma, k} (x)$      $= (1+p) \cdot G_{\sigma} (x) - p \cdot G_{k \sigma} (x)$

ここで,$p$はシャープ化のパラメータで,DoGフィルタ画像$D_{\sigma, k} (x)$にかかるスケーリングを調整しています.

インクイラストを生成する際には,閾値処理を行い画像を減色化しています.先ほどの処理で得られたシャープ画像$S_{\sigma, k, p}(x)$と輝度画像$I(x)$の積$S_{\sigma, k, p}*I$に対して以下のような関数でソフトな閾値処理を行います.

$T_{\epsilon, \phi}(u) = 1 + tanh (\phi \cdot (u - \epsilon)) (u < \epsilon)$

$u \geq \epsilon$の場合は,$T_{\epsilon, \phi}(u) = 1$になります.この関数をプロットしてみると,以下のような形をしています.

閾値処理関数のプロット

$T_{\epsilon, \phi}(u) = 1$の接続部分で傾きが不連続になるのはいいのかどうか少し疑問が残ります.結局のところ,Toonシェーディング(ブログ内記事)のようなカラーマップを使うのと同じですので,細かい部分はあまり気にしないようにします.

XDoGによるインクイラスト

以下が,XDoGのコードになります.

## Sharp image from scaled DoG signal.
#  @param  img        input gray image.
#  @param  sigma      sigma for small Gaussian filter.
#  @param  k_sigma    large/small sigma (Gaussian filter).
#  @param  p          scale parameter for DoG signal to make sharp.
def sharpImage(img, sigma, k_sigma, p):
    sigma_large = sigma * k_sigma

    G_small = cv2.GaussianBlur(img,(0, 0), sigma)
    G_large = cv2.GaussianBlur(img,(0, 0), sigma_large)

    S = (1+p) * G_small - p * G_large
    return S

## Soft threshold function to make ink rendering style.
#  @param  img        input gray image.
#  @param  epsilon    threshold value between dark and bright.
#  @param  phi        soft thresholding parameter.
def softThreshold(SI, epsilon, phi):
    T = np.zeros(SI.shape)
    SI_bright = SI >= epsilon
    SI_dark = SI < epsilon

    T[SI_bright] = 1.0
    T[SI_dark] = 1.0 + np.tanh( phi * (SI[SI_dark] - epsilon))
    return T

## XDoG filter.
#  @param  img        input gray image.
#  @param  sigma      sigma for sharpImage.
#  @param  k_sigma    large/small sigma for sharpImage.
#  @param  p          scale parameter for sharpImage.
#  @param  epsilon    threshold value for softThreshold.
#  @param  phi        soft thresholding parameter for softThreshold.
def XDoG(img, sigma, k_sigma, p, epsilon, phi):
    S = sharpImage(img, sigma, k_sigma, p)
    SI = np.multiply(img, S)
    T = softThreshold(SI, epsilon, phi)
    return T

DoGによるエッジ抽出と比較すると,明暗を分けるフィルタ処理に置き換わっています.DoGと共通の性質ですが,$\sigma$の値を調整して抽象化度合を制御することができます.

XDoGの抽象化制御

論文の中ではさらに,FDoG (Flow-based DoG), FXDoG (Flow-based XDoG)といった流れ場を考慮したフィルタ処理についても解説されていますので,興味がありましたら参照されると良いかと思います.

参考文献

[1] XDoG: An eXtended difference-of-Gaussians compendium including advanced image stylization:    http://www.sciencedirect.com/science/article/pii/S009784931200043X