見出し画像

実在しない世界のフレーム:動画フレームの補間を行うPythonスクリプトの作成

今回は、撮影した動画から存在しないフレームを生成し、滑らかな動きを持つ映像を作成します。具体的には、補間フレームを生成する手法を用いて行います。処理は次の手順で進めます:

  1. 元のフレーム間に補間フレームを生成してフレームレートを倍にします。

  2. 元のフレームを削除して、補間フレームだけを残します。

  3. 新しいフレームレート(例:24fps)で動画を保存します。

その結果、新しい動画は元の動画の実在するフレームを含まず、補間フレームだけで構成されます。この手法を使用すると、スムーズに見える新しい動画が得られますが、元の動画のリアルなフレームは含まれません。

以下にこのプロセスの流れをわかりやすく説明します:

  1. 元の動画が24fps(毎秒24フレーム)で撮影されているとします。

  2. 各フレーム間に補間フレームを生成し、フレームレートを48fpsにします。これにより、元のフレームと補間フレームが交互に並ぶ動画ができます。

  3. 補間フレームを間引き、元のフレームを削除します。つまり、偶数番号のフレーム(元のフレーム)を削除し、奇数番号のフレーム(補間フレーム)のみを残します。

  4. 新しい動画は、48fpsの半分の24fpsで保存されますが、その内容は元のフレームではなく、すべて補間フレームで構成されています。

Pythonを使って動画フレームの補間を行うスクリプトを紹介します。penCVを利用して、二つのフレーム間の補間フレームを生成し、動画を新たに保存します。さらに、Tkinterを使用してGUIを作成し、ファイル選択を簡単にします。

必要なライブラリのインストール

まず、必要なライブラリをインストールします。以下のコマンドを実行してください。

pip install opencv-python numpy

スクリプトの説明

インポートと関数の定義

import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog

必要なライブラリをインポートします。`cv2`はOpenCV、`numpy`は数値計算、`tkinter`はGUI作成のために使用します。

フレーム補間関数

def frame_interpolation(_frame1, _frame2):
    _alpha = 0.5
    _beta = 1.0 - _alpha
    return cv2.addWeighted(_frame1, _alpha, _frame2, _beta, 0.0)

二つのフレーム間の補間を行う関数です。単純に二つのフレームを半々の重みで加算しています。

動画処理関数

def process_video(_input_path, _output_path):
    _cap = cv2.VideoCapture(_input_path)
    _fps = int(_cap.get(cv2.CAP_PROP_FPS))
    _width = int(_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    _height = int(_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    _fourcc = cv2.VideoWriter_fourcc(*'mp4v')

    _output_fps = 24
    _out = cv2.VideoWriter(_output_path, _fourcc, _output_fps, (_width, _height))

    _frames = []
    while True:
        _ret, _frame = _cap.read()
        if not _ret:
            break
        _frames.append(_frame)

    _cap.release()

    _interpolated_frames = []
    for _i in range(len(_frames) - 1):
        _interpolated_frames.append(_frames[_i])
        _interpolated_frame = frame_interpolation(_frames[_i], _frames[_i + 1])
        _interpolated_frames.append(_interpolated_frame)
    _interpolated_frames.append(_frames[-1])

    _new_frames = _interpolated_frames[1::2]

    for _frame in _new_frames:
        _out.write(_frame)

    _out.release()
    print(f'Processed video saved to {_output_path}')

動画を読み込み、フレームを取得して補間を行い、新たな動画ファイルとして保存します。補間フレームを生成し、フレームを間引いて新しいフレームリストを作成しています。

ファイル選択関数

def select_file():
    _input_path = filedialog.askopenfilename(filetypes=[("Video files", "*.mp4;*.avi;*.mov")])
    if _input_path:
        _output_filename = filedialog.asksaveasfilename(defaultextension=".mp4", filetypes=[("MP4 files", "*.mp4")])
        if _output_filename:
            process_video(_input_path, _output_filename)

ファイルダイアログを使用して入力ファイルと出力ファイルを選択します。

GUIの設定

# GUI Setup
root = tk.Tk()
root.title("Video Frame Interpolation")
root.geometry("400x200")

label = tk.Label(root, text="Select a video file to process", pady=20)
label.pack()

button = tk.Button(root, text="Select File", command=select_file, padx=20, pady=10)
button.pack()

root.mainloop()

Tkinterを使って簡単なGUIを作成しています。ボタンをクリックするとファイル選択ダイアログが表示され、選択したファイルが処理されます。



GUI

フレーム補間の目的

フレーム補間の目的は、二つの既存のフレーム間に新しいフレームを生成することで、動画のスムーズさを向上させることです。例えば、元のフレームレートが低く、動きがカクカクしている動画を滑らかにするために使われます。

補間フレームの生成方法

補間フレームを生成するために、二つの既存のフレーム間の中間値を計算します。これは以下のように行います:

  1. 線形補間: 二つのフレームをある比率で混ぜ合わせることで新しいフレームを生成します。

  2. 重み付けの計算: 各ピクセルの値を線形に補間します。具体的には、二つのフレームのピクセル値を `0.5:0.5` の割合で合成します。

例えば、以下の関数で行っています:

def frame_interpolation(_frame1, _frame2):
    _alpha = 0.5  # 第一フレームの重み
    _beta = 1.0 - _alpha  # 第二フレームの重み
    return cv2.addWeighted(_frame1, _alpha, _frame2, _beta, 0.0)

この関数は、二つのフレーム `_frame1` と `_frame2` を等しい重み付けで合成し、新しいフレームを生成します。

実在しないフレームの本来の利用例

補間フレームは、元々存在しなかったフレームを計算によって生成したものです。これにより、以下のような効果が得られます:

  • スムーズな動き: 動きが滑らかに見えるようになります。

  • フレームレートの向上: 実際のフレームレートが低くても、高フレームレートのように見せることができます。

実在しないフレームを追加することで、動画全体の視覚的な体験が向上しますが、生成されたフレームは元の動画には存在しなかった情報を含んでいるため、完全に正確な再現ではない点に留意する必要があります。

補間フレームの利用例

例えば、以下のようなシナリオで補間フレームが役立ちます:

  • スポーツ映像の解析: スポーツの映像をスロー再生するときに、動きが滑らかに見えるようにするため。

  • アニメーション制作: フレームレートの低いアニメーションをスムーズにするため。

  • ビデオゲーム: リアルタイムに生成することが難しいフレームを補間して滑らかに表示するため。

このように、フレーム補間は多くのアプリケーションで利用されています。生成されたフレームは仮想的なものであり、元のデータには存在しなかった情報を含んでいることを理解しておくと良いでしょう。

結論

このスクリプトを使えば、簡単に動画フレームの補間が行えます。OpenCVとTkinterを組み合わせることで、動画処理とGUI操作がシームレスに統合され、使いやすいツールとなっています。ぜひ試してみてください。

import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog

def frame_interpolation(_frame1, _frame2):
    _alpha = 0.5
    _beta = 1.0 - _alpha
    return cv2.addWeighted(_frame1, _alpha, _frame2, _beta, 0.0)

def process_video(_input_path, _output_path):
    _cap = cv2.VideoCapture(_input_path)
    _fps = int(_cap.get(cv2.CAP_PROP_FPS))
    _width = int(_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    _height = int(_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    _fourcc = cv2.VideoWriter_fourcc(*'mp4v')

    _output_fps = 60
    _out = cv2.VideoWriter(_output_path, _fourcc, _output_fps, (_width, _height))

    _frames = []
    while True:
        _ret, _frame = _cap.read()
        if not _ret:
            break
        _frames.append(_frame)

    _cap.release()

    _interpolated_frames = []
    for _i in range(len(_frames) - 1):
        _interpolated_frames.append(_frames[_i])
        _interpolated_frame = frame_interpolation(_frames[_i], _frames[_i + 1])
        _interpolated_frames.append(_interpolated_frame)
    _interpolated_frames.append(_frames[-1])

    _new_frames = _interpolated_frames[1::2]

    for _frame in _new_frames:
        _out.write(_frame)

    _out.release()
    print(f'Processed video saved to {_output_path}')

def select_file():
    _input_path = filedialog.askopenfilename(filetypes=[("Video files", "*.mp4;*.avi;*.mov")])
    if _input_path:
        _output_filename = filedialog.asksaveasfilename(defaultextension=".mp4", filetypes=[("MP4 files", "*.mp4")])
        if _output_filename:
            process_video(_input_path, _output_filename)

# GUI Setup
root = tk.Tk()
root.title("Video Frame Interpolation")
root.geometry("400x200")

label = tk.Label(root, text="Select a video file to process", pady=20)
label.pack()

button = tk.Button(root, text="Select File", command=select_file, padx=20, pady=10)
button.pack()

root.mainloop()

この記事が参加している募集