【Python】動画からGIFをつくる

普段はWebサービスを使っていますが、時間がかかりすぎたり失敗したりするんで自分で作ってみる。


普段使ってるやつ

GIFにテキスト埋め込んだりできるので好き。

GIF Maker - Video to GIF Creator Tools | GIPHY


使うライブラリ


  • Pillow

  • OpenCV


コード


make_gif.py

import math

import cv2
from PIL import Image

def get_fps_n_count(video_path):
"""動画のfpsとフレーム数を返す"""
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return (None, None)

count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = round(cap.get(cv2.CAP_PROP_FPS))

cap.release()
cv2.destroyAllWindows()
return (fps, count)

def aspect_ratio(width, height):
"""アスペクト比を返す"""
gcd = math.gcd(width, height)
ratio_w = width // gcd
ratio_h = height // gcd
return (ratio_w, ratio_h)

def resize_based_on_aspect_ratio(aspect_ratio, base_width, max_width=400):
"""アスペクト比を元にリサイズ後のwidth, heightを求める"""
if base_width < max_width:
return None

base = max_width / aspect_ratio[0]
new_w = int(base * aspect_ratio[0])
new_h = int(base * aspect_ratio[1])
return (new_w, new_h)

def get_frame_range(video_path, start_frame, stop_frame, step_frame):
"""指定された範囲の画像をPillowのimage objectのリストにする"""
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return None

width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
asp = aspect_ratio(width, height)
# でかすぎてもあれなので最大幅を400にしとく
width_height = resize_based_on_aspect_ratio(asp, width, max_width=400)

im_list = []
for n in range(start_frame, stop_frame, step_frame):
cap.set(cv2.CAP_PROP_POS_FRAMES, n)
ret, frame = cap.read()
if ret:
if width_height is not None:
frame = cv2.resize(frame, dsize=width_height)
# BGRをRGBにする
img_array = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# numpyのarrayからPillowのimage objectを作る
im = Image.fromarray(img_array)
im_list.append(im)

cap.release()
cv2.destroyAllWindows()
return im_list

def make_gif(filename, im_list):
"""gifを作る"""
im_list[0].save(filename, save_all=True, append_images=im_list[1:], loop=0)

def main():
"""メイン処理"""
video_file = "Wildlife.wmv"

fps, count = get_fps_n_count(video_file)
if fps is None:
print("動画ファイルを開けませんでした")
return

# gifにしたい範囲を指定
start_sec = 0
stop_sec = 8

start_frame = int(start_sec * fps)
stop_frame = int(stop_sec * fps)
# 適当(fpsに応じてうまいことやれるようにしたい)
step_frame = 3

print("開始(けっこう時間がかかる)")
im_list = get_frame_range(video_file, start_frame, stop_frame, step_frame)
if im_list is None:
print("動画ファイルを開けませんでした")
return

make_gif('どうぶつ.gif', im_list)
print("終了")

if __name__ == "__main__":
main()



BGRとRGB問題


上のコードから抜粋

# BGRをRGBにする

img_array = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

動画から画像を読むのにOpenCV、GIFをつくるのにPillowを使っているわけですが、この処理がないと色が変な感じになります。


調べたところcv2.read()ではBGRで読み込まれるようで、これが色がおかしくなる原因だった。


出来たGIF

u2egKgw.gif


参考

 

おわり