セグメンテーションとは
画像のセグメンテーションとは,画像から注目している領域を切り取る ということです.例えば,細胞の画像が与えられたとき,その画像から細胞部分と背景部分とを区別することは,非常に重要です.
一見,二値化すれば終わりのように思いますが,画像処理の分野では,画像にノイズが含まれる場合を考慮する必要があり,簡単にはいきません. ここで力を発揮するのが,画像のセグメンテーションです.
画像のセグメンテーションには,いくつか手法があります.
- Snakes
- Level set 法
- Deep Learning に基づく方法
ここではその中でも,Level set 法と呼ばれる方法を紹介したいと思います.
Level set 法
Level set 法とは,Osher やSethean によって開発された,セグメンテーションの枠組みです.この枠組みでは,領域の内部,外部をLevel set 関数と呼ばれる陰関数$\phi$の符号によって表現します. 具体的には
\begin{equation} \phi(r,t) < 0 \ \ r \in \Omega \end{equation}
\begin{equation}
\phi(r,t) =0 \ \ r \in \partial \Omega
\end{equation}
\begin{equation}
\phi(r,t) >0 \ \ r \notin \Omega
\end{equation}
とします.Level set 関数には,領域の個数などの事前情報が必要ないといったメリットがあります.
Level set 法の更新則
ここで更新の仕方をざっと説明したいと思います.
\begin{align} \phi(r+dr,t+dt) - \phi(r,t) = 0 \end{align} \begin{align} \therefore \ \frac{\partial \phi}{\partial t} + v\cdot \nabla \phi =0 \end{align}
この式はHamilton-Jacobi 方程式と呼ばれ,様々なところで目にすると思います.
ここで,上の$v$は更新速度であり,評価関数の微分によって計算されます.
実装
大まかな理論に関して説明したところで,具体的に画像のセグメンテーションを行いましょう. 今回は,Github で公開されていた,Python のプログラムを用いてみます.
Chan-Vese Level set では以下のコスト関数を最小化します.
\begin{align} E(C) = \mu_1 \int_{\rm inside} |I(x,y)-c_1|dxdy + \mu_2 \int_{\rm outside} |I(x,y)-c_2|dxdy + \alpha \kappa \end{align}
この式の意味は
- 第一項: 内部の輝度値($c_1$)と画像の輝度値($I(x,y)$)との差分
- 第二項: 外部の輝度値($c_2$)と画像の輝度値($I(x,y)$)との差分
- 第三項: 曲線の曲率項(安定して更新するため)
です.つまり,内部と外部の輝度値を決めておくことで,外部の輪郭の抽出ができるということですね.
基本的にはchanvase.py を実行すれば可能なのですが,convergenceの関数のところで,以下のように修正しました.
# Convergence Test def convergence(p_mask, n_mask, thresh, c): diff = np.array(p_mask.astype(np.int)) - np.array(n_mask.astype(np.int)) ## modified n_diff = np.sum(np.abs(diff)) if n_diff < thresh: c = c + 1 else: c = 0 return c
ImportError: dlopen(/usr/local/lib/python2.7/site-packages/scipy/special/_ufuncs.so, 2): Library not loaded: /usr/local/opt/gcc/lib/gcc/5/libquadmath.0.dylib Referenced from: /usr/local/lib/python2.7/site-packages/scipy/special/_ufuncs.so Reason: image not found
sudo pip install --upgrade --force-reinstall scipy
実行結果
python chanvese.py iteration: 0 iteration: 50 iteration: 100 iteration: 150 iteration: 200 iteration: 250 iteration: 300 iteration: 350 iteration: 400 iteration: 450 iteration: 500 iteration: 550 iteration: 600 iteration: 650 iteration: 700 iteration: 750 iteration: 800 iteration: 850 iteration: 900 iteration: 950
上のコードをそのまま実行しました.
Rice 画像のセグメンテーション
Rice 画像
今回使うのはImageJのサンプルの,Rice 画像です.
if __name__ == "__main__": img = nd.imread('./../rice.png', flatten=True) mask = np.zeros(img.shape) shp = img.shape mask[30:shp[0]-30, 30:shp[1]-30] = 1 ##modified chanvese(img, mask, max_its=1000, display=True, alpha=1.0)
初期値
150回の更新
300回の更新
最終的な値
参考文献
- 倉爪 亮 レベルセット法とその実装法について
- Chan-Vese level-sets in Python: https://github.com/kevin-keraudren/chanvese