4
7

More than 1 year has passed since last update.

VSCodeのなかNotebookのなかでMatplotlibアニメーションを動かしてみた

Last updated at Posted at 2022-08-11

解決したい問題

「ゼロから作るDeep Learning」斎藤康毅著 O'Reilly刊の「5.6 Affine/Softmaxレイヤの実装」を読んでいて引っかかった。「Affine変換って何だっけ?」 Qiitaで アフィン変換 を検索したら下記の記事を見つけた。

とてもためになりそうな記事だが、Affine変換の学習を始める前に僕の関心は脇道に逸れてしまった。というのも、この記事に次のようなアニメーションが貼ってあったから。

shiftX

このアニメーションをどうやって作るのか、作り方を知りたい!と思った。記事にPythonコードが掲載されていたからそれを写経しさえすればすぐ作れそうだった。ただしわたしは自分好みのPython環境でアニメーションを動かしたいと思った。すなわち

  1. IDEとしてVisual Studio Codeを使いたい。もちろんPython for VSCodeを入れて。
  2. VSCodeの中で Notebook すなわちファイル名が *.ipynb であるファイルをエディタで作って実行したい。
  3. Notebookをエディタで開き、pythonコードを選んで Ctrl+Enter で run する。するとJupyter Server:Localがpythonコードを実行して、その結果としてアニメーションがNotebookの中で動く。こういう操作をしたい。
  4. ただし作業プロジェクトのために固有のpython仮想環境をpipenvで作って使いたい。具体的にいえば "$ pipenv install opencv-python" でインストールした cv2 ライブラリを VSCodeの中のNotebookの中のpythonコードがimportできるようにしたい。そのためにはpipenvで作った仮想環境の中のPythonインタプレタを使ってVSCodeのなかNotebookのなかのpythonコードを実行することが必要である。

試行錯誤したあげく望ましい実行環境を構築することができた。どのように環境を作ったか、ここに自分のためにメモします。ちなみにわたしが使っているOSはmacOS v12.3です。

準備1 pipenv

何はともあれmacOSにPython3をインストールした。具体的な手順はこちらを参照のこと。

pipenvをインストールした。Python仮想環境を作れるように。具体的な手順はこちらを参照のこと。

準備2 本プロジェクトのためにディレクトリと仮想環境を作った

$ mkdir ~/Matplotlib_animation_with_Notebook_in_VSCode
$ cd ~/Matplotlib_animation_with_Notebook_in_VSCode
$ export PRJDIR=$(pwd)

以下でプロジェクトのディレクトリを示す必要がある場合、文を短くするため$PRJDIRと記すことにする。

本プロジェクトのためにPython仮想環境を作った。

$ cd $PRJDIR
$ pipenv --python 3
Creating a virtualenv for this project...
Pipfile: /Users/kazurayam/github/Matplotlib_animation_with_Notebook_in_VSCode/Pipfile
Using /usr/local/bin/python3.9 (3.9.13) to create virtualenv...
⠋ Creating virtual environment...

✔ Successfully created virtual environment!
Virtualenv location: /Users/kazurayam/.local/share/virtualenvs/Matplotlib_animation_with_Notebook_in_VSCo-zSMskZBS

上記コマンドによってPython仮想環境が作られるが、それがどのパスにあるかを調べるには次のコマンドを実行する。

$ pipenv --venv
/Users/kazurayam/.local/share/virtualenvs/Matplotlib_animation_with_Notebook_in_VSCo-zSMskZBS

自分がきっと必要とすると分かっているパッケージをプロジェクト専用の仮想環境にインストールした。

$ cd $PRJDIR
$ pipenv install numpy
$ pipenv install matplotlib
$ pipenv install notebook
$ pipenv install opencv-python

どれも大きいライブラリなので数分かかった。

準備3 どのPythonインタープレタが使われたかを確認するためのスクリプト print_sys.py

print_sys.pyprint_sys.ipynbを書いた。中身はたった2行のpythonコードである。

import sys
print(sys.executable)

sys.executableはpythonコードを実行するのに使われているPythonインタープレタのファイルパスを示す。

Step1 マシンのデフォルトのPython環境でどう動くか

素朴なやり方。コマンドラインでprint_sys.pyを実行してみた。

$ cd $PRJDIR
$ python print_sys.py
/Users/kazurayam/.pyenv/versions/anaconda3-4.4.0/bin/python

このパスが示すPythonインタープレタはわたしのマシンのデフォルトであって、このプロジェクト専用の仮想環境の中にあるPythonインタープレタではない。これではダメなのだ。

Ste2 プロジェクト専用のPython仮想環境でどう動くか

pipenv runを介してprint_sys.pyを実行してみた。

$ cd $PRJDIR
$ pipenv run python print_sys.py
/Users/kazurayam/.local/share/virtualenvs/Matplotlib_animation_with_Notebook_in_VSCo-zSMskZBS/bin/python

Step1で使われたPythonインタープレタとStep2のPythonインタープレタのパスが違う。Step1のパスは仮想環境ではない。Step2のパスがこのプロジェクトのために準備した仮想環境の中のPythonインタープレタである。こっちが好みだ。

Step3 Jupyterでどう動くか

コマンドラインでJupyterを起動した。

$ cd PRJDIR
$ jupyter notebook

そのあとJupyterの中でprint_sys.ipynbをCtrl+Enterして実行した。

jupyter_print_sys

Step1と同じパスが表示された。これは予想通りだ。

Step4 Jupyter+仮想環境でどう動くか

コマンドラインでpipenv runを介してJupyterを起動した。

$ cd PRJDIR
$ pipenv run jupyter notebook

そのあとJupyterの中でprint_sys.ipynbをCtrl+Enterで実行した。

jupyter_pipenv_run

はてな? Step2と同じく仮想環境のPythonインタープレタのパスが表示されるだろうと予想していたが、そうはならなかった。Step1と同じパスが表示された。なぜか?

コマンドラインからJupyterを起動するときにプロジェクト固有の仮想環境の中のPythonインタープレタを使いたい時、どこをどうすべきなのか、わたしはわからない。きっと方法があるのだろうがわたしはまだ知らない。

本記事の目標はVSCodeの中でNotebookの中でMatplotlibのアニメーションを動かすことなので、Jupyterの中でNotebookを動かす場合のことには深入りしません。

Step5 VSCodeでNotebookを実行するために必要な設定

デスクトップアイコンをダブルクリックしてVSCodeを起動し、その中でNotebook print_sys.ipynb を開いた。pythonコードを実行してみた。

VSCode_print_sys_out_of_venv

上のスクリーリーンショットの中でAと印した箇所を見てほしい。Step1と同じPythonインタープレタのパスが表示されている。これは本プロジェクトのための仮想環境ではなくて、わたしのマシンのデフォルトになっているPythonインタープレタのパスだ。

Bと印した箇所を見てほしい。import cv2がエラーになっている。私のマシンのデフォルトになっているPython実行環境にopencv-pythonをインストールしていないから、エラーになっているのだ。

VSCodeでNotebook print_sys.ipynb を開いたとき、マシンのデフォルトのPythonインタープレタではなく、このプロジェクト専用の仮想環境のなかのPythonインタープレタを使いたい。そのことをどうやってVSCodeに指図すればいいのだろうか?ーーーあちこちググった。あげく下記の1行を見つけた。

Next, select a kernel using the kernel picker in the top right.

スクリーンショットにCと印した箇所つまりNotebookを開いた画面の右上に kernel picker がある、これをクリックせよ、といっている。

クリックしてみると、選択可能なPythonインタープレタのパス候補が表示された。その中にこのプロジェクト専用の仮想環境の中のPythonインタープレタ(その中にopencv-pythonがインストール済みである)も列挙されていた。

VSCode_print_sys_specify_kernel

候補リストの中からプロジェクト専用の仮想環境を選んだ。そうしたら警告ダイアログが表示された。

VSCode_ipykernel_is_required

Running cells with 'Python 3.9.13 ('Matplotlib_animation_with_Notebook_in_VSCo-zSMskZBS')' requires ipykernel package.
Run the following command to install 'ipykernel' into the Python environment.
Command: '/Users/kazurayam/.local/share/virtualenvs/Matplotlib_animation_with_Notebook_in_VSCo-zSMskZBS/bin/python -m pip install ipykernel -U --force-reinstall'

Notebookの中のpythonコードを実行するためにipykernelパッケージが必要だからインストールしなさいとのこと。そこでメッセージに表示された通りのコマンドを実行して、プロジェクト専用の仮想環境にipykernelをインストールした。

$ /Users/kazurayam/.local/share/virtualenvs/Matplotlib_animation_with_Notebook_in_VSCo-zSMskZBS/bin/python -m pip install ipykernel -U --force-reinstall

プロジェクト専用のPython仮想環境にipykernelがインストールされた。VSCodeの中でNotebookを開き、仮想環境のPythonインタープレタを指定するとipykernelが動いて、Notebookのpythonコードを実行できるようになった。仮想環境にインストール済みの他のパッケージ(numpyやcv2)もimportできるようになった。前進した。

cv2 imported

Step6 アニメーションするPythonコードを書いた

shiftX_saveGif.ipynb というNotebookを書いた。このコードを書くにあたって「Qiita 完全に理解するアフィン変換 @koshain2」の成果を拝借した。ただしQiita記事はAffine変換を解説することに注力していて、Matplotlibのアニメーションを利用する方法は説明していない。そこでわたしはアニメーションするためのコードを補った。

# 水平移動
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import cv2

# 一つのsubplotを作る。それはfigureとaxesとから成る
fig, ax = plt.subplots()

# JPEG画像ファイルを読み込む
image = cv2.imread("gorilla.jpg")[:,:,::-1]

# 画像を水平に移動する関数(画像を返す)
# image : 画像
# shift : 画像をX軸方向にシフトする幅、単位はピクセル. Eg: 20 : X軸方向に20ピクセルだけ移動する
def shift_x(image, shift):
    h, w = image.shape[:2]
    src = np.array([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0]], np.float32)
    dest = src.copy()
    dest[:,0] += shift
    # 変換行列を導き出す
    transformation_matrix = cv2.getAffineTransform(src, dest)
    # 画像に対して変換行列を適用する
    dst = cv2.warpAffine(image, transformation_matrix, (w, h))
    return dst # 変換された結果としての画像を返す

# frameを描画する関数
def update(x):
    transformed = shift_x(image, x)
    plt.imshow(transformed)

# update関数をcallしてアニメーションを描画する
animation = FuncAnimation(fig, update,
    frames=np.append(np.arange(0,200,20), np.arange(200, 0, -20)), interval=100)


# GIFファイルに保存する
animation.save("shiftX.gif")

このpythonコードはJPEG画像ファイルを読み込みXY座標平面に配置し、画像を徐々に水平方向にスライドさせる、そういうアニメーションを作り、GIFファイルに出力する。これを実行したら shiftX.gif ファイルができた。GIFファイルをダブルクリックすればそれ単体で眺めることができる。この通り:

GIF

ただしこのスクリプトではNotebookのなかでアニメーションが動くのを見ることはできない。

Step7 Notebookの中でMatplotlibアニメーションが描画されない。

shiftX_without_ipympl.ipynbを書いた。このコードはGIFファイルをsaveするのではなくplt.show()する。つまりNotebookを開いたなかでアニメーションを動かすことを狙った。

...

#animation.save("shiftX.gif")
plt.show()

実行してみると、axesがあるけどfigureが空っぽな座標平面がプロットされた。JPEG画像が水平方向に移動するアニメーションが表示されることを期待したがそうはならなかった。

anim not acting

このNotebookを(VSCodeではなくて)Jupyterのなかでも実行してみたが、Jupyterでもアニメーションは表示されなかった。

Step8 ipymplパッケージを導入したらついに動いた

MatplotlibのアニメーションをNotebookの中で動かしたいが、まだ何かが足りない。何だろう?ググったらMatplotlibプロジェクトが提供する ipymplパッケージ が必要だと分かった。

ipympl enables using the interactive features of matplotlib in Jupyter Notebooks, Jupyter Lab, Google Colab, VSCode notebooks, Google Colab

仮想環境にipymplをインストールした。

$ cd $PRJDIR
$ pipenv install ipympl

そしてNotebookファイルの先頭にマジックを1行追記した。

%matplotlib ipympl

こういうコードになった。

shiftX_with_ipympl.ipynb

%matplotlib ipympl
# 水平移動
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import cv2

# 一つのsubplotを作る。それはfigureとaxesとから成る
fig, ax = plt.subplots()

# JPEG画像ファイルを読み込む
image = cv2.imread("gorilla.jpg")[:,:,::-1]

# 画像を水平に移動する関数(画像を返す)
# image : 画像
# shift : 画像をX軸方向にシフトする幅、単位はピクセル. Eg: 20 : X軸方向に20ピクセルだけ移動する
def shift_x(image, shift):
    h, w = image.shape[:2]
    src = np.array([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0]], np.float32)
    dest = src.copy()
    dest[:,0] += shift
    # 変換行列を導き出す
    transformation_matrix = cv2.getAffineTransform(src, dest)
    # 画像に対して変換行列を適用する
    dst = cv2.warpAffine(image, transformation_matrix, (w, h))
    return dst # 変換された結果としての画像を返す

# frameを描画する関数
def update(x):
    transformed = shift_x(image, x)
    plt.imshow(transformed)

# update関数をcallしてアニメーションを描画する
animation = FuncAnimation(fig, update,
    frames=np.append(np.arange(0,200,20), np.arange(200, 0, -20)), interval=100)

plt.show()

実行してみた。ついにVSCodeの中でNotebookのなかでMatplotlibのアニメーションが表示された。

VSCode_anim_in_action

結論

いくつかのパッケージを追加しNotebookの設定を工夫することにより、VSCodeのなかNotebookのなかでMatplotlibアニメーションを動かすことに成功した。

今回わたしが実装したアニメーションの動きは残念ながら滑らかでない。JPEG画像を変換する処理をmatplotlib.animation.ArtistAnimationではなくmatplotlib.animation.FuncAnimationで実装したことが要因だと思う。コードに工夫の余地が大いにある。しかし今のところわたしはこれで満足だ。

GitHubレポジトリ

ソースコードがここにあります。

4
7
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
4
7