メインコンテンツへスキップ
見出し画像

Pythonライブラリ(可視化):Matplotlib_アニメーションの作成

    KIYO

    1.概要

     前回記事ではMatplotlibの基礎を学びましたが今回は応用でAnimation(動画およびGif)を作成します。モジュールは"matplotlib.animation"を使用していきます。

    2.全般知識

    2-1.animationクラスの説明

     クラス大きく分けて”FuncAnimation”と”ArtistAnimation”があります。

    画像

    【Animationクラス】
    ●FuncAnimation:"function func"を連続で呼び出すことでAnimationを作成
     ー>毎回関数を変更して表示(リストで保持しないためメモリ小)
     ー>ループするため動画などに出力する場合は時間指定(frame)が必要
    ●ArtistAnimation:"fixed set of Artist objects"を使用したAnimation
     ー>グラフデータをリストで保持して1枚ずつ表示(メモリ大)

    2-2.ArtistAnimation

     ArtistAnimationは直感的に使用できます。作成したfigureを一度リストに入れて、そのリストをArtistAnimationで処理します。

    【Animationの引数】
    ●interval:動画のフレーム速度[ms] (※fps=1000÷interval) 

    [IN]
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    
    fig = plt.figure(figsize=(6,6)) 
    imgs = [] #各フレームの図を保存するためのリスト
    
    for i in range(100):
        array = np.linspace(0, i, 10) #0~iを10個に分割
        x = np.arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
        im = plt.plot(x, array)
        imgs.append(im) #imgsに各フレームの図を追加
        
    ani = animation.ArtistAnimation(fig, imgs, interval=100)    
    plt.show()
    ani.save('Animation_線形.gif', writer='pillow')
    
    [OUT]
    画像

    2-3.FuncAnimation

     FuncAnimationは癖を理解する必要があります。プロセスとしては先に関数を作成して、その関数をFuncAnimation(fig, <処理する関数>)で連続的に処理させます。引数および注意点を下記に記載しました。

    【FuncAnimationの注意点】
    ●FuncAnimation実行時に関数がループ処理されるため、関数にはループ用の引数を渡す必要がある
    ●plt.cla()で前のフレームをクリアしない場合は各フレームの図が重なって表示される
    ●ループ計算するため各処理でリストにデータを追加して表示させていけば動きがつながっているような図も作成可能である。
    ●各フレームで処理を削除するため装飾(ラベル、軸名、グリッドなど)は関数内に入れて毎回処理をさせる必要がある

    【Animationの引数】
    ●interval:動画のフレーム速度[ms] (※fps=1000÷interval) 
    ●frame:動画のフレーム数
     ー>ループの回数と同じ
     ー>リストを渡すとリスト内データがループする
    ●fargs:ループ用以外に引数がある関数を作成した場合、FuncAnimationの関数内に直接引数は渡せない。fargs=<配列(ListやTuple)>で値を渡すことで別の引数も使用することができる

      コードを何パターンか記載することで動きを確認しました。

    [IN]
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    
    fig = plt.figure(figsize=(6,6)) 
    plt.grid()
    
    def linearplot(frame_No):
        array = np.linspace(0, frame_No, 10) #0~iを10個に分割
        x = np.arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
        im = plt.plot(x, array)
    
    ani = animation.FuncAnimation(fig, linearplot, interval=100, frames=50) #
    from IPython.display import HTML
    HTML(ani.to_html5_video())
    
    [OUT]
    plt.cla()がないため図が重なる
    frames=50のため線の傾き(np.linspace)はMAX50
    [IN]
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    
    fig = plt.figure(figsize=(6,6)) 
    plt.grid()
    
    def linearplot(frame_No):
        plt.cla()
        array = np.linspace(0, frame_No, 10) #0~iを10個に分割
        x = np.arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
        im = plt.plot(x, array)
    
    ani = animation.FuncAnimation(fig, linearplot, interval=500, frames=10) #
    from IPython.display import HTML
    HTML(ani.to_html5_video())
    
    [OUT]
    plt.cla()があるため毎回新規でフレームが作成される
    frames=10のため線の傾き(np.linspace)はMAX10
    plt.grid()は関数の外にあるため、関数が繰り返されるタイミングでクリアになるため表示されない
    画像
    [IN]
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    import japanize_matplotlib
    
    fig = plt.figure(figsize=(6,6)) 
    
    def linearplot(frame_No):
        plt.cla()
        array = np.linspace(0, frame_No, 10) #0~iを10個に分割
        x = np.arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
        im = plt.plot(x, array)
        plt.grid()
        plt.xlabel('x軸'); plt.ylabel('y軸') 
        plt.title(f'フレームNo:{frame_No}', y=-0.15)
    
    ani = animation.FuncAnimation(fig, linearplot, interval=500, frames=[1,10,100,50,25])
    from IPython.display import HTML
    HTML(ani.to_html5_video())
    
    [OUT]
    framesにリストを入れており、リスト内のデータがループしている
    関数内で図を装飾した場合は各フレームにデータが残る
    画像

    3.Animationの出力

     Jupyterを使用した場合に"plt.show()"や"%matplotlib inline"でAnimationが表示されないことがあります。その対処法を紹介します。

    3ー1.JavaScript化:HTML(ani.to_jshtml())

     対処法としてアニメーションをJavaScript化します。

    [IN]
    from IPython.display import HTML
    HTML(ani.to_jshtml())

     具体的な使用例は下記の通りです。出力後はボタン付きの画面が現れるため再生ボタン(▶)を押すとアニメーションが動きます。

    [IN]
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    
    fig = plt.figure(figsize=(6,6)) 
    anims = [] #各フレームの図を保存するためのリスト
    
    for i in range(100):
        array = np.linspace(0, i, 10) #0~iを10個に分割
        x = np.arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
        im = plt.plot(x, array)
        anims.append(im) #animsに各フレームの図を追加
        
    ani = animation.ArtistAnimation(fig, anims, interval=100)    
    
    from IPython.display import HTML
    HTML(ani.to_jshtml())
    
    [OUT]

    3ー2.mp4化:HTML(ani.to_html5_video())

     対処法としてアニメーションをHTML5 video化します。

    [IN]
    from IPython.display import HTML
    HTML(ani.to_html5_video())

     具体的な使用例は下記の通りです。

    [IN]
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    
    fig = plt.figure(figsize=(6,6)) 
    anims = [] #各フレームの図を保存するためのリスト
    
    for i in range(100):
        array = np.linspace(0, i, 10) #0~iを10個に分割
        x = np.arange(10) #array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
        im = plt.plot(x, array)
        anims.append(im) #animsに各フレームの図を追加
        
    ani = animation.ArtistAnimation(fig, anims, interval=100)    
    
    from IPython.display import HTML
    HTML(ani.to_html5_video())
    
    [OUT]

    【参考:Windowsでのエラー対応】
     なお上記はGoogle Colab上から実行しており、自宅PCのWindowsから実行したら"ffmpegがない"とのエラーがでました。Windowsではffmpegを

    画像

     現在の環境はANACONDAを使用しているため環境設定でffmpegを追加しました。エラーは出なくなったけど動画が動かなかったので諦めました。
    (解決したら追記予定)

    画像

    4.Animationの保存

     保存はインスタンス化したアニメーションのsaveメソッドを使用します。保存形式によって記法が異なるため注意が必要です。

    [saveメソッド]
    ani.save(<保存するファイル名>, writer=<形式>)

    4ー1.Gif化

     Gifを保存する場合は拡張子をgifにして形式を"pillow"を選択します。

    [IN]
    ~省略~    
    ani = animation.ArtistAnimation(fig, imgs, interval=100)    
    ani.save('Animation_線形.gif', writer='pillow')
    
    [OUT]
    画像

    4ー2.mp4化

     動画形式で保存する場合は拡張子を"mp4"で実行すれば問題なくファイル生成できました(※ffmpegはANACONDAで設定済み)。
    (FuncAnimationではframe数に注意すること)

    [IN]
    ani.save('animation.mp4')

    5.Animation応用:基礎編

    5-1.画像を表示するアニメーション

     下記記事の「Pillowでやってみたシリーズ2:画像の結合」で使用した画像を利用してGifを作成してみました。

     前の応用として①globで画像パスを取得してリスト化、②画像を表示する関数を作成、③FuncAnimationを使用してframesに①のリストを渡す ことでgif画像を作成しました。

    [IN]
    import glob
    path = r"C:\Users\KIYO\Desktop\note\02_pythonライブラリ\note_pythonライブラリ_Pillow\sampleimages" #画像ファイルがあるディレクトリ
    path_files = glob.glob(path + "/*.jpg")
    
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    
    fig = plt.figure(figsize=(6,6)) 
    
    def showpictures(path_file):
        plt.cla()
        img = plt.imread(path_file) #画像を読み込む
        plt.imshow(img) #画像を表示
        plt.axis('off') #axisを消す
        
    ani = animation.FuncAnimation(fig, showpictures, interval=500, frames=path_files)
    ani.save('animation_girls.gif', writer='pillow')  
    
    [OUT]
    画像

    6.Animation応用:実践編

    6-1.1次元熱伝導の非定常計算

     下記記事にまとめましたので興味があればご確認ください。


    参考記事

    あとがき 

     取り急ぎ先だし

     





     
     
     

    KIYO

     
     
    普段は製造業で企画/開発/設計しております。記事はプログラミング・機械学習、IoT関係の記事をメインで作成し、なるべく1つの記事で知りたいことを網羅していきます。内容は学術的より実装・アウトプット(ほしくなるもの)を重視して作成しています。 面白そうな仕事があればやりたいです!

    あなたへのおすすめ