jupyter notebook で ipywidgets を使ってインタラクティブにパラメータを操作してついでに画像処理もしてみる

  • 8
    いいね
  • 0
    コメント

matplotlib とかで jupyter notebook に関数のグラフをプロットするのはいいけど値を変更して試行錯誤するたびにいちいち Ctrl + Enter とかするのがちとめんどくさい。
最近流行りのリアクティブ(もう古い?)的なノリで値変更を動的に結果に反映させたいのは世の常ですよね。
探してみたところ、 ipywidgets というライブラリが jupyter notebook 向けに開発されているらしいので使ってみることに。

https://github.com/ipython/ipywidgets

いわゆるスライダーやテキストボックスなどの UI コンポーネントを通してのインタラクティブな挙動も可能になるようです。

環境

以下の環境で試しました。

  • OS X El Capitan
  • Python3.6 with venv
  • fish shell

準備

まずは venv で仮想環境を構築しておく。
virtualenv な人だったり bash な人は適宜読み替えて下さい。

$ python3 -m venv ipywidgets_env
$ source ipywidgets_env/bin/activate.fish # fish 用

インストール

普通に jupyter をインストール。

(ipywidgets_env) $ pip install jupyter

依存関係として一緒に ipywidgets が入ってきたことを軽く確認。
なければないで pip install で入れてあげればいいだけな気がします。

(ipywidgets_env) $ pip list | grep ipywidgets
ipywidgets (5.2.2)

さらに jupyter に対し下記のコマンドを実行し、描画用拡張機能を有効にしておく。

(ipywidgets_env) $ jupyter nbextension enable --py widgetsnbextension --sys-prefix

挙動確認

一応 ipywidgets を試すだけの環境は整ったので早速 jupyter notebook 上で試してみます。

(ipywidgets_env) $ jupyter notebook

起動後、開いたブラウザで新規にノートブックを作成し、新規セルに a * x + b という計算結果を返す関数を書いてみる。

def f(x, a, b):
    return a * x + b

この関数においてのパラメータをスライダーの UI コンポーネントで操作するには下記のように上記の関数 f およびパラメータとなる x, a, b のそれぞれの値域を interact 関数に渡してあげる。

from ipywidgets import interact
interact(f, x=(-10,10, 1), a=(-10,10, 1), b=(-10,10, 1))

ブラウザ上の実行結果は以下のようになる。

スクリーンショット 2017-01-04 0.52.27.png

画面上にスライダーが現れた。
ツマミを適当にちょっといじってみる。

スクリーンショット 2017-01-04 0.54.12.png

最初 x = 0, a = 0, b = 0 で結果が 0 だったのが x = 10, a = 4, b = -10 にすると結果にすぐに反映され 30 と表示される。
こうして ipywigdets による値の動的反映が確認できた。

画像の2値化しきい値をスライダーで調節しつつ結果に反映する

ちょっとした応用として、画像処理の定番である2値化のしきい値を自前で操作しつつ結果に反映する、といったことを試してみようと思います。
使うのは自分のアイコン画像。

my_github.jpeg

とりあえずですが Python での画像処理ツールである Pillow を使います。
既に入れてあれば下記のコマンドを実行する必要はないです。

(ipywidgets_env) $ pip install Pillow

まずは Pillow をインポートしてグローバル変数 img にグレスケの255階調画像をロードしておく。

from PIL import Image
img = Image.open('picture.jpeg').convert('L')

my_github_gray.png

@interact
def binarize(th: (0, 255, 1)):
    return img.point(lambda p: 255 if p > th else 0)

ipywidgets で動的操作する関数 binarize を定義し、しきい値変数 th によって2値化後の画像を返す関数を作り、 interact デコレータを付与してあげることでも同様に ipywidgets を利用することができます。
上記セルを実行すると、

スクリーンショット 2017-01-08 22.23.49.png

スクリーンショット 2017-01-08 22.24.08.png

このように jupyter notebook 上でしきい値スライダーを動かしつつ動的に2値化操作が行えるようになります。

ついでに GitHub の画像 diff もどきもやってみる

スライダーの使い方はわかったので有名な GitHub の画像差分による確認方法の真似事もやってみます。
Pillow にまだそこまで詳しくなく、部分画像の彩度を調節する方法がわからなかったので画像の上に画像を重ねる無駄な方法でやってしまいました。

orig = Image.open('picture.jpeg')

@interact
def github_diff(percentage: (0, 100, 1)):
    orig_copied = orig.copy()
    box = (int(img.width / 100 * percentage), 0, img.width, img.height)
    img_cropped = img.crop(box)
    orig_copied.paste(img_cropped, box)
    return orig_copied

orig にオリジナル画像をロード後、スライダーで指定された割合に基づいて左側にオリジナル画像を、右側にグレスケ画像を描画した画像を動的に表示します。
paste が破壊的なメソッドらしく、オリジナル画像のコピーを値変更ごとにとってます。
もっといいやり方あったら教えてほしいです。

スクリーンショット 2017-01-09 14.14.56.png

スクリーンショット 2017-01-09 14.15.11.png

まとめ

ただでさえ実験・検証に向いている jupyter notebook が更に使いやすくなりますね!