🍃
GIFアニメーションをPythonで処理する方法
GIFアニメーションファイルには、複数の画像とそれぞれの画像の表示時間などのメタデータが含まれる。GIFアニメーションを編集するためには、そのファイルに含まれるすべての画像を適切に処理する必要がある。PILでの処理方法を以下に紹介する。
対象ファイルがGIFアニメーションか判別する
def is_animated_gif(path: str) -> bool:
try:
with Image.open(path) as img:
if img.format != 'GIF':
return False
return img.is_animated
except UnidentifiedImageError:
return False
リサイズ
def resize_gif(input_path: str, output_path: str, width: int, height: int) -> None:
with Image.open(input_path) as image:
frames: list[Image.Image] = []
disposal_methods: list[int] = []
try:
while True:
resized_frame = image.resize((width, height))
disposal_methods.append(image.disposal_method) # type: ignore
frames.append(resized_frame)
image.seek(image.tell() + 1)
except EOFError:
pass
frames[0].save(
output_path,
save_all=True,
append_images=frames[1:],
disposal=disposal_methods,
)
ポイントはdisposal_method
を配列でsave
メソッドに渡すこと。これを省くと残像が残ったりして正しくGIFファイルがリサイズされないことがある。disposal_method
とは、アニメーションGIF内の各フレームが表示された後にどのように扱われるかを定義するもの。
矩形切り取り
def crop_gif(
input_path: str, output_path: str, left: int, upper: int, right: int, lower: int
) -> None:
box = (left, upper, right, lower)
with Image.open(input_path) as image:
frames: list[Image.Image] = []
disposal_methods: list[int] = []
try:
while True:
cropped_frame = image.crop(box)
frames.append(cropped_frame)
disposal_methods.append(image.disposal_method) # type: ignore
image.seek(image.tell() + 1)
except EOFError:
pass
frames[0].save(
output_path,
save_all=True,
append_images=frames[1:],
disposal=disposal_methods,
)
上下反転、左右反転
def flip_gif(input_path: str, output_path: str, horizontal: bool) -> None:
with Image.open(input_path) as image:
frames: list[Image.Image] = []
disposal_methods: list[int] = []
try:
while True:
flipped = image.transpose(
Image.FLIP_LEFT_RIGHT if horizontal else Image.FLIP_TOP_BOTTOM
)
frames.append(flipped)
disposal_methods.append(image.disposal_method) # type: ignore
image.seek(image.tell() + 1)
except EOFError:
pass
frames[0].save(
output_path,
save_all=True,
append_images=frames[1:],
disposal=disposal_methods,
)
Discussion