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 のドキュメントサイトはこちらです。
実際に、 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 ページでやりとりされています。興味のある方はこちらもよろしければ。