Python Tips: アニメーション GIF から静止画をまとめて抽出したい
Python でアニメーション GIF ( animated GIF )からフレーム画像を抽出する方法をご紹介します。 早速結論ですが、 Python の画像処理用ライブラリ Pillow を使うのが比較的かんたんです。他にも方法は無数にあるかと思いますが、私は Pillow でやるのがスムーズでした。 Pillow は画像処理ライブラリ PIL のフォークで、 PIL が対応していない Python 3.x に対応しているのがポイントです。 Pillow は pip でインストールするときには名称 Pillow でインストールしますが、スクリプトの中で import するときは PIL と同じ PIL という名前を使用します。
$ pip install Pillow from PIL import Image実際に、 Pillow を使ってアニメーション GIF を分解する方法を見てみましょう。
# coding: utf-8 '''アニメーション GIF のフレーム画像(静止画)を抽出する ''' from pathlib import Path from PIL import Image, ImageSequence # 分割したいアニメーション GIF 画像 IMAGE_PATH = 'target.gif' # 分割した画像の出力先ディレクトリ DESTINATION = 'splitted' # 現在の状況を標準出力に表示するかどうか DEBUG_MODE = True def main(): frames = get_frames(IMAGE_PATH) write_frames(frames, IMAGE_PATH, DESTINATION) def get_frames(path): '''パスで指定されたファイルのフレーム一覧を取得する ''' im = Image.open(path) return (frame.copy() for frame in ImageSequence.Iterator(im)) def write_frames(frames, name_original, destination): '''フレームを別個の画像ファイルとして保存する ''' path = Path(name_original) stem = path.stem extension = path.suffix # 出力先のディレクトリが存在しなければ作成しておく dir_dest = Path(destination) if not dir_dest.is_dir(): dir_dest.mkdir(0o700) if DEBUG_MODE: print('Destionation directory is created: "".'.format(destination)) for i, f in enumerate(frames): name = '/-'.format(destination, stem, i + 1, extension) f.save(name) if DEBUG_MODE: print('A frame is saved as "".'.format(name)) if __name__ == '__main__': main()このスクリプトを実行すると、アニメーション GIF ファイル target.gif の静止画をすべて抽出して splitted というフォルダに書き出してくれます。元の GIF 画像には変更を加えることなく、抽出した画像を、末尾に連番をつけた形でファイルとして切り出します。
ポイントは PIL.ImageSequence.Iterator クラスです。 PIL.Image で開いた画像をこのコンストラクタに渡すと、アニメーション GIF 内の各フレーム(静止画)を返すイテレータオブジェクトを生成してくれます。
逆に、静止画を組み合わせてアニメーション GIF を作る方法については、次の Stack Overflow ページでやりとりされています。興味のある方はこちらもよろしければ。