PySceneDetect：シーン単位で分割できるPythonライブラリ

# はじめに

- 映画の予告編のカット割り数を研究したい
- 本記事では、pythonを使用して動画ファイルのカット変更タイミングの検出を行います

# PySceneDetectとは？

PySceneDetectは、動画からシーンの変更点を検出して、それに基づいて動画をシーン単位で分割することができるPythonライブラリです。このツールは、動画編集や分析、コンテンツ管理などに応用されています。

https://www.scenedetect.com/

## 例

ライブラリをインストールします。

```python
!pip install scenedetect
```

該当の動画ファイル(`input_video.mp4`)を置き、処理を実行します。

```python

# 必要なライブラリをインポート
import cv2
from scenedetect import VideoManager
from scenedetect import SceneManager
from scenedetect.detectors import ContentDetector
from IPython.core.display import display, HTML

# シーン検出と一覧出力の関数
def find_scenes(video_path):
    # VideoManager オブジェクトを作成
    video_manager = VideoManager([video_path])
    scene_manager = SceneManager()
    
    # ビデオのフレームレートを取得
    framerate = video_manager.get_framerate()

    # ContentDetectorを閾値、最小シーン長、フレームスキップと共に追加
    scene_manager.add_detector(ContentDetector(threshold=30, min_scene_len=15 * framerate, frame_skip=2))

    # ビデオとシーンマネージャを関連付け
    video_manager.set_downscale_factor()
    video_manager.start()
    scene_manager.detect_scenes(frame_source=video_manager)

    # 検出されたシーンのリストを取得
    scene_list = scene_manager.get_scene_list(video_manager.get_base_timecode())

    # HTMLの初期設定
    html_content = "<html><body><h1>シーン一覧</h1><table border='1'><tr><th>シーン番号</th><th>開始タイムコード</th><th>終了タイムコード</th><th>カットの秒数</th><th>画像</th></tr>"

    # 各シーンの最初のフレームを画像として保存し、シーンの秒数を計算
    cap = cv2.VideoCapture(video_path)
    for i, scene in enumerate(scene_list):
        start_frame = scene[0].get_frames()
        end_frame = scene[1].get_frames()
        scene_duration = (end_frame - start_frame) / framerate
        cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
        ret, frame = cap.read()
        if ret:
            img_path = f'scene_{i+1}.png'
            cv2.imwrite(img_path, frame)
            html_content += f"<tr><td>{i+1}</td><td>{scene[0].get_timecode()}</td><td>{scene[1].get_timecode()}</td><td>{scene_duration:.2f}</td><td><img src='{img_path}' width='200'></td></tr>"

    # HTMLの終了設定
    html_content += "</table></body></html>"

    # HTMLファイルに保存
    with open("scene_list.html", "w") as f:
        f.write(html_content)

    # HTMLを表示
    display(HTML(html_content))

    print("シーンリストと画像がHTMLで生成されました。")

# 入力となる動画のパス（例: "example.mp4"）
video_path = "input_video.mp4"

# シーン検出関数を呼び出し
find_scenes(video_path)


```

実行完了すると、`scene_list.html`と、キャプチャのサムネ画像が一覧出力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348593/5f4357a1-d28e-be16-bab7-a99ec343eaf3.png)


`scene_list.html`を開くと、こんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/348593/5889f64a-9e11-ba95-6262-ef0f9109d997.png)


# 用語まとめ

| カテゴリ | 用語 | 説明 | 
|---------|------|------|
| ライブラリ | [PySceneDetect](https://www.scenedetect.com/) | 動画からシーンの変更点を検出して、シーン単位で分割できるPythonライブラリ |
| クラス | VideoManager | 動画ファイルの読み込みと管理を行うクラス |
| クラス | SceneManager | シーン検出の設定と実行を管理するクラス |  
| クラス | ContentDetector| 動画コンテンツの変化に基づいてシーン変更を検出するクラス |
| パラメータ | Threshold | シーン変更を検出するための閾値。閾値以下の変化は無視される |
| パラメータ | Min Scene Length | 検出されるシーンの最小の長さ。この時間以下のシーンは無視される | 
| パラメータ | Frame Skip| 処理速度向上のためにスキップするフレーム数。高い値だと処理が早くなるが精度が低下 |

# おわりに

PySceneDetectを使用して、動画からシーンの変更点を検出して、それに基づいて動画をシーン単位で分割しました。