こしあん
2023-11-28

MoviePyで一定間隔でフレームを抽出する


950{icon} {views}


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の中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です