2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Google Colabで画像からGIF動画を作りインライン表示させる3つの方法

Last updated at Posted at 2021-12-28

概要

Google ColabでGIF動画を作りインライン表示させるまでの3つの方法(pillow, imageio, imagemagickのconvert)を紹介する。Google Colabで作成したコードは、こちらにあります。

方法

  1. GIF動画にしたい画像ファイルをGoogle Colabにアップロード、またはGoogle Colab内で作成
  2. globで画像ファイル名を取得
  3. GIF動画を作り保存(pillow、imageio、imagemagickのconvert)(3種類紹介しますが、どれか1つ使うと良いです。)
  4. 保存したGIF動画をIPythonで開きインライン表示

1. GIF動画にしたい画像をGoogle Colabにアップロード、またはGoogle Colab内で作成

  1. まず、Google Colabを立ち上げます。

  2. 下図の青枠からGIF動画にしたい画像をアップロードするか、Google Colab内でコードを書きその場で画像を作成します。JPG画像を用意した人はPNG画像に変換してください。pillowの公式サイトによると、JPG画像は対応していないためです。変換方法は、jpgをpngに変換しても透明度がいじれなかったからなんとかしたを参照してください。
    GIF動画に使う画像は、01.png, 02.png, ..., 60.pngのようにGIF画像に使う枚数の最大桁数で0埋めした通し番号の名前を付けると良いです。
    (組み込み関数sorted()を用いたsorted(glob.glob('*.png'))で順番を管理しやすくなります。0埋めにしないファイル名でsorted(glob.glob('*.png'))を使うと、1.png, 10.png, 2.png, ...となってしまいます。回避する方法があるので、方法の0埋めにしていないファイル名を読み込みソートするで紹介します。)
    図1-4.png

  3. ここで、画像が置いてあるか確認するため、セルに!lsと入力して実行してください。自分が用意した画像ファイル名が表示されて入ればOKです。(セルの先頭に「!」を付与するとコマンドとして認識されます。)

用意した画像ファイル名が表示されない場合画像が置いてあるパスと今いるパスが異なる場合があります。セルに``!pwd``を入力して実行してください。今いるパスが分かります。立ち上げた後パスの移動をしていないと``/content``と返ってくると思います。画像があるパスを確認して異なっていたら、``%cd /画像のパス``で画像のパスまで移動してください。(ここは、「``!``」を使った``!cd /画像のパス``では、パスの移動はできません。詳しくは、[!cd と %cd の違い](https://qiita.com/bear_montblanc/items/64d7efd9e4ea7caa98f9)を参照してください。)
1. ちなみに``!ls *.png``がGIF動画に使う画像です。「``*``」は任意の長さの任意の文字列を表す[ワイルドカード文字](https://support.microsoft.com/ja-jp/office/ワイルドカード文字の例-939e153f-bd30-47e4-a763-61897c87b3f4)の一種で、``.png``で終わるファイル名が表示されます。

今回サンプルとして、時計の秒針をイメージしたも60枚のPNG画像を用意しています。

サンプルの複数画像
# サンプルの複数画像(時計の秒針をイメージしたもの)
import numpy as np
import matplotlib.pyplot as plt


def get_rotate_coord(angle, coord):
    theta = np.radians(angle)  # °からラジアンに変換
    c, s = np.cos(theta), np.sin(theta)
    R = np.array([[c, -s], [s, c]])  # 2次元の回転行列
    rotate_coord = np.dot(R, coord)  # 回転後の座標を取得
    return rotate_coord

start_coord = np.array([0, 10])  # この座標の原点を軸に回転させる
for sec in range(60):
    fig = plt.figure(figsize=(5, 5))
    ax = fig.add_subplot(1, 1, 1)
    rotate_coord = get_rotate_coord(-6*sec, start_coord)
    ax.plot((0, rotate_coord[0]), (0, rotate_coord[1]), lw=3)
    circle = plt.Circle((0, 0), 11, fill=False)  # 円をプロット
    ax.add_artist(circle)
    ax.set_title(sec)
    ax.set_xlim(-12, 12)
    ax.set_ylim(-12, 12)
    ax.axis('off')
    plt.savefig('{:0>2}.png'.format(sec))  # 0埋め2桁のファイル名
    plt.close()

2. globで画像ファイル名を取得

0埋めにしたファイル名を読み込みソートする

0埋めにしたファイル名を読み込みソートする
import glob


filenames = sorted(glob.glob('*.png'))

print(filenames)とすると、['00.png', '01.png', '02.png',..., '60.png']と画像ファイル名が読み込まれているのが分かります。

0埋めにしていないファイル名を読み込みソートする

0埋めにしていない通し番号の画像ファイル名を読み込む場合は、natsortのnatsorted()を使うと順番に読み込めます。

0埋めにしていないファイル名を読み込みソートする
import glob
from natsort import natsorted


filenames = natsorted(glob.glob('*.png'))

3. GIF動画を作り保存(pillow、imageio、imagemagickのconvert)

3つの方法を紹介します。どれか1つ使えば良いです。

pillow編

GIF動画を作り保存(pillow編)
# pillow編
import PIL


# GIF動画の作成
images = [PIL.Image.open(filename) for filename in filenames]  # PNGファイルを開く
out_filename = 'image_pillow.gif'  # GIF動画のファイル名
'''
パラメータ
--------
image[0] : GIF動画の先頭に使う画像
out_filename : 保存するGIF動画のファイル名
save_all : 画像を全て保存するか(GIF動画を作るので画像を全て保存するTrueを選択する。)
append_images : 2枚目以降のGIF動画に使う画像のリスト
               (images[1:]とスライスを使うと、2枚目以降の画像のリストになる。)
duration : 表示間隔(単位は1/1000秒)
loop : ループ回数(0は無限ループ)
--------
'''
images[0].save(out_filename, 
               save_all=True, append_images=images[1:], duration=1000, loop=0)

imageio編

GIF動画を作り保存(imageio編)
# imageio編
import imageio


# GIF動画の作成
images = [imageio.imread(filename) for filename in filenames]  # PNGファイルを開く
out_filename = 'image_imageio.gif'  # GIF動画のファイル名
'''
パラメータ
--------
out_filename : 保存するGIF動画のファイル名
images : GIF動画に使う画像のリスト
duration : 表示間隔(単位は秒)
loop : ループ回数(0は無限ループ)
--------
'''
imageio.mimsave(out_filename, images, duration=1.00, loop=0)

imagemagickのconvert編

GIF動画を作り保存(imagemagickのconvert編)
# imagemagickのconvert編
!sudo apt-get install imagemagick  # imagemagickをインストール


# GIF動画の作成
filenames_str = ' '.join(filenames)  # 00.png 01.png 02.png…60.pngの文字列にする
out_filename = 'image_imagemagick.gif'  # GIF動画のファイル名
'''
パラメータ
--------
delay : 表示間隔(単位は1/100秒)
loop : ループ回数(0は無限ループ)
filenames_str : GIF動画に使う画像の名前が入った文字列
out_filename : 保存するGIF動画のファイル名
--------
'''
!convert -delay 100 -loop 0 $filenames_str $out_filename

4. 保存したGIF動画をIPythonで開きインライン表示

保存したGIF動画をIPythonで開きインライン表示
# pillowで作成したGIF動画(3種類のどれでも良い)
import IPython


out_filename = 'image_pillow.gif'
IPython.display.Image(out_filename, format='png')

出力結果
image.gif
時計の秒針みたいですね。Google Colabにインライン表示できていれば成功です。

途中で一定期間静止させたGIF動画の作成方法

一定期間静止させたい画像の表示枚数を増やすことで静止させることができます。つまり、方法の2. globで画像ファイル名を取得のコードのみを書き換え、その他は同じ手順です。

ファイル名と追加したい枚数を指定するための関数append_filenameを以下のように定義します。

append_filename
def append_filename(filenames, filename, flame_num):
    '''
    パラメータ
    --------
    filenames : GIF画像用のファイル名が入ったリスト
    filename : 枚数を追加したいファイル名
    flame_num : 追加する枚数
    --------
    '''
    if filename in filenames:  # filenamesにfilenameが存在するか確認(次行のエラー対策)
        index_filename = filenames.index(filename)
        for _ in range(flame_num):
            filenames.insert(index_filename, filename)
    else:
        print('ファイル名{}は、filenamesにはないです。'.format(filename))
    return filenames

コードの説明です。

  • index_filenameで枚数を増やしたいファイル名のindexを取得します。

  • filenames.insert(index_filename, filename)でそのindexの1つ前に枚数を増やしたいファイル名と同じファイル名を加えます。この同じファイル名の追加を複数回繰り返すとindexが変わってしまうと一見思いますが、最初に指定したindexのindex_filenameには追加したファイル名の1つ前に常に追加できるので問題ないです。
    ここは、今回作ったコードで作るGIF動画が生きる場面です。例えば、filenames=['00.png', '01.png', '02.png', '03.png']に対して、append_filename(filenames=filenames, filename='02.png', flame_num=3)とするとどうなるでしょうか。
    以下のように追加されていく過程が分かります。このように、3枚の02.pngが追加されて、filenames=['00.png', '01.png', '02.png', '02.png', '02.png', '02.png', '03.png']となります。
    image_imageio-2.gif

早速、途中で一定期間静止したGIF動画を作っていきます。
2. globで画像ファイル名を取得のコードに一定期間静止させたいファイル名と追加する枚数を指定して、その他は同じ手順です。(例では、pillowを使いduaration=1000からduaration=60に変更してます。)

途中で一定期間静止させたGIF動画の例
import glob


filenames = sorted(glob.glob('*.png'))  # ファイル名を読み込みソートする
'''ここから一定期間静止させるために変更したコード'''
filenames = append_filename(filenames, '00.png', 20)  # 00.pngを20枚追加
filenames = append_filename(filenames, '15.png', 5)  # 15.pngを5枚追加
filenames = append_filename(filenames, '30.png', 20)  # 30.pngを20枚追加

出力結果
image_pillow_still.gif
サーボモータを制御しているみたいですね。

loopの最後に一定期間静止させたGIF動画の作成方法

ここで、loopの最後に一定期間静止させた例も紹介します。途中で一定期間静止させたGIF動画の作成方法と同様に、関数のappend_filenameの引数に最後のファイル名を入力してももちろん良いです。ですが、毎回ファイル名を確認して打ち込むと大変なので、リストfilenamesの最後ということで**filenames[-1]を使うと汎用性が高い**です。(例では、pillowを使いduaration=1000からduaration=60に変更してます。)

loopの最後に一定期間静止させたGIF動画の例
import glob


filenames = sorted(glob.glob('*.png'))  # ファイル名を読み込みソートする
'''ここから一定期間静止させるために変更したコード'''
# loopの最後のファイルを20枚追加(ファイル名を入力するよりリストのindexで最後を指定する方が楽)
filenames = append_filename(filenames, filenames[-1], 20)

出力結果
image_pillow_still-2.gif

3種類の方法の比較

3種類(pillow、imageio、imagemagickのconvert)の処理速度とデータサイズを比較します。比較には、サンプルで用意した時計の秒針をイメージした60枚のPNG画像を使います。それぞれオプションなしで比較した結果なので、解像度などのオプションを付けると結果が変わる可能性があります。

処理速度の比較

処理速度の比較は、方法の3. GIF動画を作り保存(pillow、imageio、imagemagickのconvert)の箇所で、それぞれのモジュールをimport後からします。
計測には%%timeitを使い、セルの中身を10回実行して計測します。

pillowの処理速度
%%timeit -r10
images = [PIL.Image.open(filename) for filename in filenames]  # ファイルを開く
out_filename = 'image_pillow.gif'  # GIF動画のファイル名
images[0].save(out_filename, 
               save_all=True, append_images=images[1:], duration=1000, loop=0)
# 結果 : 1 loop, best of 10: 454 ms per loop
imageioの処理速度
%%timeit -r10
images = [imageio.imread(filename) for filename in filenames]  # ファイルを開く
out_filename = 'image_imageio.gif'  # GIF動画のファイル名
imageio.mimsave(out_filename, images, duration=1.00, loop=0)
# 結果 : 1 loop, best of 10: 1.38 s per loop
imagemagickのconvertの処理速度
%%timeit -r10
filenames_str = ' '.join(filenames)  # 00.png 01.png 02.png…60.pngの文字列にする
out_filename = 'image_imagemagick.gif'  # GIF動画のファイル名
!convert -delay 100 -loop 0 $filenames_str $out_filename
# 結果 : 1 loop, best of 10: 9.26 s per loop
```
処理速度はimagemagick遅い< imageio < pillow速い結果となりました
## データサイズの比較
データサイズは[os.path.getsize()](https://docs.python.org/3/library/os.path.html#os.path.getsize)を使いバイト単位で取得します。

```Python:データサイズの比較
import os


print('pillow', os.path.getsize('image_pillow.gif'))
print('imageio', os.path.getsize('image_imageio.gif'))
print('imagemagick', os.path.getsize('image_imagemagick.gif'))
# 出力結果 : pillow 714429
# 出力結果 : imageio 932089
# 出力結果 : imagemagick 220164
```
データサイズはimagemagick小さい< pillow < imageio大きい結果となりました
# 補足
GIF動画の表示間隔の限界速度がブラウザごとに設けられているらしいです詳しくは[あなたは大丈夫高速GIFアニメになってしまう症状](https://at.sachi-web.com/blog-entry-712.html)を参照してください



# まとめ
Google ColabでGIF動画を作りインライン表示させる方法を3種類紹介しましたが比較した結果も見つつ好きなものを使いましょう
2
3
1

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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?