Pythonで動画ファイルからGIFアニメーションを作成
はじめに
Scratchで作った作品の解説のために、youtubeで動画を公開し記事にリンクを貼っていましたが、再生ボタンを押すという一手間、と、再生した後におすすめ動画一覧が表示されてしまう点、を改善したかったため、今回、GIFアニメーションに置き換えました。その際、色々な調整を効率化するためPythonのスクリプトで動画を変換してみました。
GIFアニメーションとは
GIFは画像ファイルフォーマットの一つで、静止画だけでなくアニメーションも表示できます(詳しくはこちら)。音声はファイルに保存できない、256色までしか表示できない、等の短所がありますが、古くあるフォーマットなので多くのソフトが対応しています。
zennでGIFアニメーションを表示する際の注意点
zennではアップロードできるファイルのサイズは最大3MBになっています。GIFアニメーションを作る際は3MBを上回らないようにする調整する必要があります。今回はmacで画面キャプチャした動画(movファイル)をGIFアニメーションに変換しますが、その際に動画の画像の大きさや尺の長さ、1秒あたりのコマ数を調整して、3MBを上回らないように調整しています。
GIFアニメーションの作成方法
動画ファイルからGIFアニメーションの作成は、
- 無料の変換サイトを利用する
- 変換ソフトを使う
- プログラムを組んで変換する
上記の方法が考えられます。今回は、色々試行錯誤で調整が必要なのと、多数の動画を一括変換する必要があることから、機械的に実行可能な「プログラムを組んで変換する」を選択しました。幸いPythonのmoviepyで動画編集が簡単にできますので、このライブラリを活用しています。
環境構築
最新Python環境の構築
moviepyはPython 3.4以上で動くようですが、念のため、最新のPython環境をcondaで構築しました。Pythonの環境はryeを用いて構築しました。
% brew install rye
以下のコマンで環境を構築して、アクティベートしています。
rye init # ryeの初期化
rye pin 3.12 # Python 3.12を指定
rye sync # 3.12環境を構築
moviepyとffmpegのインストール
moviepyはPythonの仮想環境を作ってそちらにインストールしました。また、moviepyが内部的にffmpegを利用しており、そちらのインストールも必要でした。
brew install ffmpeg # moviepyが内部で使用するffmpegはbrewで必要に応じてインストール
rye add moviepy # moviepyの指定
rye add opencv-python # opencv-pythonの指定
rye sync # moviepyのインストール
これで環境構築は完了です。後はコードを書くだけです。
Pythonのコード
moviepyがよくできていますのでPythonのコードは至ってシンプルです。記事一つに対してPythonのスクリプトを1つ作成するようにしました。以下のコードでは、
- scratch-telenger-0070-1-fighting.mov, scratch-telenger-0070-2-fighting.movの2つのファイルをGIFアニメーション化
- 両方ともFHD(1920px x 1080px)の全画面で録画しているため、Scratchの画面部分をcropで切り抜き
- 切り抜いても画面サイズが大きいので、resizeで幅を480px, 高さはアスペクト比が固定されているので360pxに縮小
- scratch-telenger-0070-1-fighting.movの方は、1秒〜3秒までと18.8秒〜21秒までをsubclipで切り取り、concatenate_videoclipsで結合
- scratch-telenger-0070-2-fighting.movの方は、7秒〜11秒までをsubclipで切り取り
上記の一連の処理を実施しています。
from moviepy.editor import VideoFileClip, concatenate_videoclips
# movファイルを読み込んでトリミング(動画その1)
name = "scratch-telenger-0070-1-fighting"
clip = VideoFileClip("../zenn-assets/videos/" + name + ".mov").crop(x1=300,y1=60,x2=1620,y2=1050)
# サイズを変更(横幅を480pxにして縦はアスペクト比固定で自動的に設定)
clip = clip.resize(width=480)
clip1 = clip.subclip(1, 3) # 1秒〜3秒までの動画を切り抜き
clip2 = clip.subclip(18.8, 21) # 18.8秒〜21秒までの動画を切り抜き
clip = concatenate_videoclips([clip1, clip2]) # 上記の動画を繋げ合わせる
# GIFファイルの出力(fps = frames per second, 1秒あたりのコマ数)
clip.write_gif("images/scratch-telenger-0070/" + name + ".gif", fps=15)
# movファイルを読み込んでトリミング(動画その2)
name = "scratch-telenger-0070-2-fighting"
clip = VideoFileClip("../zenn-assets/videos/" + name + ".mov").crop(x1=300,y1=60,x2=1620,y2=1050)
# サイズを変更(横幅を480pxにして縦はアスペクト比固定で自動的に設定)、7秒〜11秒までの動画を切り抜き
clip = clip.resize(width=480).subclip(7, 11)
# GIFファイルの出力(fps = frames per second, 1秒あたりのコマ数)
clip.write_gif("images/scratch-telenger-0070/" + name + ".gif", fps=15)
実行は、
rye run python src/python/上記のソースコード.py
GIFアニメーションへの変換結果
上記のコードで変換したオリジナル動画(YouTube)、と変換後(GIFアニメーション)は以下になります。GIFアニメーションはzennのアップロード3MB制約に収めるため、尺は数秒が限界ですがすぐに表示されるのでわかりやすいですね。
YouTube(その1)
GIFアニメーション(その1)
YouTube(その2)
GIFアニメーション(その2)
おわりに
動画をPythonで簡単に編集できることがわかりました。今後は機械的な編集はPythonを積極的に活用しようと思います!
Discussion