MoviePyで一定間隔でフレームを抽出する
Posted On 2023-11-28
MoviePyを使い、動画から一定間隔でフレームを抽出する方法を見ていきます。iter_framesを使うとできます。動画編集と組み合わせたいときにこの方法は便利です。
目次
はじめに
動画を1秒や0.5秒など一定間隔でフレーム数を切り出す方法としていくつかあります。例えば、
- OpenCVを使う
- 最もよく出てくる方法。コードの量が多いのと、OpenCVの扱いそのものがめんどい
- Decordを使う
- https://blog.shikoan.com/decord/
- VideoLoaderでスキップ数を調整して読み込めますが、長時間動画で安定性が悪くなる可能性があります(絶対おこるというわけではないので、気にならなければOKです。普通に便利なライブラリです)
今回はMoviePyを使って似たようなことを行う方法です。Decordが読み込みに特化したライブラリであるのに対し、MoviePyは編集もできるため、読み込み+編集して書き出しのようなユースケースだとそもそもMoviePyが向いています。今回行うのはMoviePyを読み込み部分で使う方法です。
MoviePy
公式サイトからインストールしましょう
https://zulko.github.io/moviepy/
動画ダウンロード
YouTubeから動画をダウンロードします。たまたま聞いてたやつを例にとります。yt-dlpを使っています。
from yt_dlp import YoutubeDL
def download_youtube(url):
ydl_opts = {
"format": "best",
"paths": {
"home": "video"
}
}
with YoutubeDL(ydl_opts) as ydl:
result = ydl.download([url])
if __name__ == '__main__':
download_youtube('https://www.youtube.com/watch?v=HJ7sjZwy-uY')
フレームを切り出す
MoviePyのiter_framesという関数を使います。抽出するFPSを指定できるので、それで取り出し間隔を調整します。
import moviepy.editor as mpe
import os
from PIL import Image
def extract_frames(file_path, fps=1):
clip = mpe.VideoFileClip(file_path)
out_dir = f"frames/{os.path.splitext(os.path.basename(file_path))[0]}_fps{fps}"
os.makedirs(out_dir, exist_ok=True)
for i, frame in enumerate(clip.iter_frames(fps=fps)):
with Image.fromarray(frame) as img:
img.save(out_dir+f"/frame_{i:04}.jpg", quality=92)
if __name__ == "__main__":
extract_frames("video/北朝鮮 「<画面音楽:新映像>朝鮮人民軍歌 (조선인민군가)」 KCTV 2023⧸02⧸08 日本語字幕付き [HJ7sjZwy-uY].mp4",
fps=1)
結果
FPSを0.5、1、2と変えて抽出した結果を示します。
FPS=0.5の場合
FPS=1の場合
FPS=2の場合
うまくいっているようです。
Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー