6
5

More than 5 years have passed since last update.

Bokeh対話機能を用いた画像処理結果の可視化

Posted at

本日は

ちょっとタイトルが悪いですが,Bokehの対話機能,つまり,スライダーを用いてパラメータを動かせるようなアプリケーションの練習として,画像処理の様子を可視化しましょう.

題材は scipy レクチャーノートから拝借します.

例えば

において, 画像をシャープにするコードがあります.

#一部抜粋
f = scipy.misc.face(gray=True).astype(float)
blurred_f = ndimage.gaussian_filter(f, 3)

filter_blurred_f = ndimage.gaussian_filter(blurred_f, 1)

alpha = 30
sharpened = blurred_f + alpha * (blurred_f - filter_blurred_f)

上記のコードにあるalphablurred_f = ndimage.gaussian_filter(f, 3) の3に当たる値をsigmaとし,これらを動かした時の画像の変化の様子を可視化しましょう.

実装例

上でロジックは何をするべきかを決めましたので, 可視化部分をbokehを用いて実装してみましょう.個人的にも実務でも再利用していきたいというのもあって,真面目に可視化用のclassを実装しています.
(bokehもmatplotlibでもそうなんですが,サンプルがベタがきで書いてあってオブジェクト指向で実装をしていないのが若干不満です.そんな人たちは自分で実装せいやという事なのだろうか?)

sharpe.py
from scipy import ndimage as ndi
import numpy as np
import scipy
from bokeh.io import curdoc
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Slider, TextInput
from bokeh.layouts import row, widgetbox
from bokeh.layouts import gridplot
from bokeh.client import push_session


class ImageViewer():

    def __init__(self, target):
        self.target = target[::-1]
        self.source1 = ColumnDataSource(data=dict(image=[self.target]))
        self.alpha = Slider(title="alpha", value=30,
                            start=10, end=50, step=1)
        self.sigma = Slider(title="sigma", value=3,
                            start=1, end=20, step=1)

        self.fig1 = self.define_figure('image')
        self.regist_image(self.fig1,self.source1)

        blurred = ndi.gaussian_filter(self.target, sigma=self.sigma.value)
        self.source2 = ColumnDataSource(data=dict(image=[blurred]))
        self.fig2 = self.define_figure('blurred')
        self.regist_image(self.fig2,self.source2)


        filtered = ndi.gaussian_filter(blurred, sigma=1)
        sharped = blurred+self.alpha.value*(blurred-filtered)
        sharped = sharped.astype(np.uint8)
        self.source3 = ColumnDataSource(data=dict(image=[sharped]))
        self.fig3 = self.define_figure('sharped')
        self.regist_image(self.fig3,self.source3)

        widget_list = [self.alpha, self.sigma]
        for widget in widget_list:
            widget.on_change('value', self.update_data)
        inputs = widgetbox(*[widget_list])
        self.plot = row(inputs, gridplot(
            [[self.fig1, self.fig2, self.fig3]]), width=600)

    def define_figure(self, title):
        return figure(title=title,
                      plot_width=self.target.shape[1]//2,
                      x_range=[0, self.target.shape[1]],
                      plot_height=self.target.shape[0]//2,
                      y_range=[0, self.target.shape[0]])

    def regist_image(self, fig, source):
        fig.image('image',
                  x=0, y=0,
                  dh=self.target.shape[0], dw=self.target.shape[1],
                  source=source, palette='Greys256')

    def update_data(self, attr, old, new):
        blurred = ndi.gaussian_filter(self.target, sigma=int(self.sigma.value))
        filtered = ndi.gaussian_filter(blurred, sigma=1)
        sharped = blurred+self.alpha.value*(blurred-filtered)
        sharped = sharped.astype(np.uint8)
        self.source2.data = dict(image=[blurred])
        self.source3.data = dict(image=[sharped])
        self.fig1.title.text = '{} {}'.format(
            self.sigma.value, self.alpha.value)


def main():
    target = scipy.misc.face(gray=True)
    viewer = ImageViewer(target)
    document = curdoc()
    document.add_root(viewer.plot)

main()

実装の方針としては次の通り,

  • 画像処理対象画像オブジェクトをインスタンスの引数とするクラスを作ります.
  • そして __init__ 内部でクリクリ動かすスライダー, 画像を表示する為の領域の確保のためにfigureオブジェクトを作ります(define_figure メソッド).
  • update_data メソッドでスライダーをクリクリ動かすごとにどういう動作をさせるかを定義しています.
  • matplotlibで言う所の ax.imshow() に相当するのが regist_image メソッドです.
  • main 関数内で document を定義してそこに plot を add する.

適宜メソッドの内部を書き換える事で対話機能を用いた可視化ツールを作る事ができるでしょう.

実行例

$ bokeh serve --show sharpe.py

ブラウザが立ち上がり下図のようなものが出ます.

スクリーンショット 0029-11-21 午後7.09.55.png

結果がレクチャーノートと微妙に異なるのは
f = scipy.misc.face(gray=True).astype(float) としていないからです.
修正は読者の練習問題とします.

ともかく,ここでは左上にあるスライダーを動かして変化の様子をみてみましょう.グリグリ動かすと画像が変化していきます.

matplotlib+ipywidgetsと比べて

これぐらいのツールであれば jupyter notebook を起動して ipywidgets と matplotlib の描画機能を利用してできるのですが, スライダーを動かした時の挙動が結構カクカクします.
体感的としては今回のbokehでの実装の方が相対的にスムーズに動きます.とはいえ超絶ヌルヌル動くかといえばそうでもないです.

jupyter notebook で物足りない方はbokehにトライしてみてくださいまし.

参考文献

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5