粗大メモ置き場

個人用,たまーに来訪者を意識する雑記メモ

matplotlibでアニメーションを作成,保存

一口にアニメーションといっても時間毎に図の更新がみたいだけの場合とその様子を動画に保存したい場合とがある。

閲覧用のアニメーション

plt.pause(interval)を用いることでノンブロッキングで現在の画像を更新していくことができる。

subplotsを用いた場合

ちょっと長くなるのが難点

import numpy as np
import matplotlib.pyplot as plt

# make data
data = [[i, np.sin(i/50)] for i in range(100)]

# prepare figure
fig,ax = plt.subplots(1,1)
ax.set_xlim(min([x[0] for x in data]),max([x[0] for x in data]))
ax.set_ylim(min([x[1] for x in data]),max([x[1] for x in data]))
xdata = []
ydata = []
line, = ax.plot(xdata,ydata)

# iterate
for dat in data:
  print(dat)
  xdata.append(dat[0])
  ydata.append(dat[1])
  line.set_data(xdata,ydata)
  
  plt.pause(0.1) # 10ms late

plt.figure()でやる方法

こっちのほうが楽。

import numpy as np
import matplotlib.pyplot as plt

# make data
data = [[i, np.sin(i/50)] for i in range(100)]

# prepare figure
xdata = []
ydata = []

# iterate
for dat in data:
  fig = plt.figure(1)
  xdata.append(dat[0])
  ydata.append(dat[1])
  plt.plot(xdata,ydata)
  plt.pause(0.1) # 10ms late
  plt.clf()

matplotlib.animationを使って図を保存する方法

matplotlibにはanimationという図を作成,保存するためのモジュールが存在する。

そのうち2つの手法が存在しイメージとしては以下のような感じである。

  • ArtistAnimation:図をリスト形式で溜め込んで最後にまとめる。重い…?
  • FuncAnimation:逐次的に図を作って追加していく。軽い…?

こちらはリアルタイムに描写というよりは,その後の保存に重点を置いている。一応plt.pause()とも連携できるはずだが,結構重いのでその辺は覚悟したほうが良いかも。

imagemagicのインストール

windows環境でやったのでLinuxの人は別の箇所を探して欲しいが,ffmpegとimagemagicをインストールすることでgifやmp4などの形式で動画を保存することができる。 実はimagemagicのインストール時にffmpegをインストールできるので実質imagemagicをインストールすれば良い。

以下のブログの手順に基づいてすすめていこう。 matplotlib のanimation を保存 - はしくれエンジニアもどきのメモ

1.imagemagicのインストール

こちらのダウンロードページに行き,Windows Binaryの一番上を選択してダウンロードすれば良い。 ダウンロード後はいろいろ聞かれるが基本的にYesで進めていけば良い。

2.matplotlibにimagemagicの場所を教える

お使いの環境のmatplotlibrcというファイルを操作する。ファイルの場所を確かめるには以下のコマンドを実行すると良い。

import matplotlib
matplotlib.matplotlib_fname()

例えば以下のような感じの場所に置いてある。 'C:\\<Python path>\\lib\\site-packages\\matplotlib\\mpl-data\\matplotlibrc'

そしたらメモ帳などで開いて,# animation.convert_path:となっているところの#をはずしてコメントアウトを解除し,

animation.convert_path: C:\Program Files\ImageMagick-7.0.1-Q16\magick.exe #みんなの環境だと多分違うぞ

のようにimagemagicのexeファイルのパスを与える。(エクスプローラから自分で調べること!

ArtistAnimationで図を作成

ArtistAnimationの図の作り方は非常にわかりやすく,matlabのようにplot情報をリストに入れていき,最後にそれらをつなげて動画にする。 自分でもコードなど書いたが,こちらのブログが大変わかりやすく,自分で書くまでもないと思ったのでコードを借りて以下に表記する。どうもありがとうございます。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

fig = plt.figure() #figure objectを取得.
ax = fig.add_subplot(111)

x = np.arange(0,10,1)

ims = [] #Line2D objectを入れるリストを用意
for time in range(x.shape[0]):
    im = ax.plot(x[0:time] ,x[0:time] ** 2) #Line2D objectを取得
    ims.append(im) #imsにappendする

#ArtistAnimation機能で,imsの中の画像を繋ぎ合わせる.
ani = animation.ArtistAnimation(fig, ims, interval = 100)

#imagemagickを使って,gif画像を保存
ani.save("test.gif", writer="imagemagick")

FuncAnimationで動画を作成保存する

こちらは,Animationの生成規則を与えて,逐次これを計算させるものになる。先程と同様の方がわかりやすい解説を上げていてくださり,まずはこちらを試してみると良いだろう。

以下に軽く私の理解を述べておく。URL先のコードで以下のように呼ばれているFuncAnimationでは,

  • 最初にキャンバスとなるfigureのハンドルfigをわたし
  • 逐次処理を実行するupdate()関数を渡す
  • updateに渡す引数を決めるfargsというのがオプションで存在し,
  • 画像間のinterval時間
  • 最大繰り返しフレーム数framesを渡す
ani = animation.FuncAnimation(fig, update, fargs = (x, y, ax1, ax2), interval = 100, frames = 10)

という風になっている。従ってframeが0から9に至るまで,すなわち,

for i in range(frame):
   update(i,args)

という感じになっているはず。 この他にもinit()という初期化関数を渡したりできる。

自分Verとしては例えば以下のようなものになる。ただし,ランダムな点をPlotしようとするので挙動はきっとクソ。 (Originalではちゃんとplotすべきデータがあったが簡略化させていただいた)

fig, ax = plt.subplots()
line, = ax.plot([], [], 'r',label='trajectory')
line2, = ax.plot([], [], 'kx',label='current position')
ax.grid()
xdata, ydata = [], []
plt.xlabel('x [m]')
plt.ylabel('y [m]')
plt.grid ='on' 



#def data_gen():
data =  [[x[0],x[1]] for x in np.random.rand(100,2)]

def init():
    ax.set_ylim(-0.2, 1.2)
    ax.set_xlim(-1.2, 0.2)
    line.set_data(xdata, ydata)
    #line2.set_data(xdata, ydata)
    return line,

def run(i):
    # update the data
    x, y = data[i]
    xdata.append(x)
    ydata.append(y)    

    line.set_data(xdata, ydata)
    line2.set_data(x,y)
    plt.legend(loc='upper left')

ani = FuncAnimation(fig, run,  blit=False, interval=10, frames = int(len(data))-1,
                            repeat=False, init_func=init)

ani.save("plot2.gif",writer='imagemagick')
plt.show()

f:id:ossyaritoori:20181201234856g:plain
こういう感じに出来上がるってわけ