1
0

動画を画像・画像を動画に変換 | Python

Posted at

はじめに

研究で動画をイベントデータに変更したいという場面がありました。
その際、直接動画をイベントデータに変更できるものもあれば、画像からイベントデータに変更するものもありました。
そこで、動画と画像を自由に変換できるプログラムを作成しました。
また、pythonに慣れたいというモチベーションもあったので、今後も使う機会のありそうなツールをpythonで作成しようと思いました。

Video2Frame

作成したプログラムが以下になります。
最初は動画をフレーム画像に変換する目的だったため、Video2Frameと題しましたが、後からFrame2Video機能も実装しました。。

機能

主に以下の機能があります

  • video2frame
    動画をフレームごとに切り出して、画像として保存します。
  • frame2video
    1つのディレクトリにある複数の画像から動画を作成します。画像は名前順で使用されます。
  • cut
    開始時間と終了時間を指定して、その間のみを動画として切り出します。
  • split
    分割したい数を指定して、動画を分割します。
  • info
    動画のパス, width, height, フレーム数, フレームレート, 秒数などの情報を出力します

使用方法はREADME.mdをご確認ください。

実装

環境の設定が面倒くさいという方は以下のコードなどを参考に関数のみご活用ください
基本はPython3, opencv, moviepy, ffmpegが入っていれば動くと思います

import
import cv2
import os
import datetime
import time
from moviepy.editor import VideoFileClip, ImageSequenceClip
video2frame
def video_to_image_all(input_path, output_path='../data/output', basename='frame', extension='jpg'):
    """Save all frames of a video to images.

    Args:
        input_path (str): input_path of the video.
        output_path (str, optional): output_path of the images. Defaults to '../data/output'.
        basename (str, optional): basename for the saved images. Defaults to 'frame'.
        extension (str, optional): extension for the saved images. Defaults to 'jpg'.
    """
    cap = cv2.VideoCapture(input_path)

    if not cap.isOpened():
        return

    timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
    output_path = os.path.join(output_path, timestamp)
    os.makedirs(output_path, exist_ok=True)
    base_path = os.path.join(output_path, basename)

    digit = len(str(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))))

    n = 0

    while True:
        ret, frame = cap.read()
        if ret:
            cv2.imwrite('{}_{}.{}'.format(base_path, str(n).zfill(digit), extension), frame)
            n += 1
        else:
            break
frame2video
def frames_to_video(input_path, output_path='../data/output', basename='video', extension='mp4', fps=30):
    """Convert frames to a video.

    Args:
        input_path (str): input_path of the frames.
        output_path (str, optional): output_path of the video. Defaults to '../data/output'.
        fps (int, optional): frame rate of the video. Defaults to 30.
    """
    os.makedirs(output_path, exist_ok=True)
    output_path = os.path.join(output_path, '{}.{}'.format(basename, extension))
    if os.path.isdir(input_path):
        frames_dir = input_path
        frame_paths = sorted([os.path.join(frames_dir, f) for f in os.listdir(frames_dir) if f.endswith(('.png', '.jpg'))])
        clip = ImageSequenceClip(frame_paths, fps=fps)
        clip.write_videofile(output_path, codec='libx264', audio_codec='aac')
    else:
        raise ValueError("input_path must be a directory. images in the directory will be used to create the video.")
cut
def video_cut(input_path, output_path='../data/output', basename='video', extension='mp4', start=0, end=10, swap_aspect=False):
    """Cut a video.

    Args:
        input_path (str): input_path of the video.
        output_path (str, optional): output_path of the video. Defaults to '../data/output'.
        basename (str, optional): basename for the saved video. Defaults to 'video_'.
        extension (str, optional): extension for the saved video. Defaults to 'mp4'.
        start (int, optional): start time to cut the video. Defaults to 0.
        end (int, optional): end time to cut the video. Defaults to 10.
        swap_aspect (bool, optional): swap aspect ratio of the video. Defaults to False.
    """
    output_filename = f"{basename}_{start}s_{end}s.{extension}"
    output_file_path = os.path.join(output_path, output_filename)

    os.makedirs(output_path, exist_ok=True)

    clip = VideoFileClip(input_path)

    width, height = clip.size

    if swap_aspect:
        cut_clip = clip.subclip(start, end).resize((height, width))
    else:
        cut_clip = clip.subclip(start, end)

    cut_clip.write_videofile(output_file_path, codec='libx264', audio_codec='aac')

    clip.close()
    cut_clip.close()
split
def video_split(input_path, output_path='../data/output', basename='video', extension='mp4', split_num=2, swap_aspect=False):
    """Split a video into multiple videos.

    Args:
        input_path (str): input_path of the video.
        output_path (str, optional): output_path of the video. Defaults to '../data/output'.
        basename (str, optional): basename for the saved video. Defaults to 'video_'.
        extension (str, optional): extension for the saved video. Defaults to 'mp4'.
        split_num (int, optional): number of split videos. Defaults to 2.
        swap_aspect (bool, optional): swap aspect ratio of the video. Defaults to False.
    """
    os.makedirs(output_path, exist_ok=True)

    clip = VideoFileClip(input_path)
    total_duration = clip.duration
    width, height = clip.size

    clip_duration = total_duration / split_num
    clip.close()

    for i in range(split_num):
        clip = VideoFileClip(input_path)
        start_time = i * clip_duration
        end_time = min((i + 1) * clip_duration, total_duration)

        output_filename = f"{basename}_{i+1}.{extension}"
        output_file_path = os.path.join(output_path, output_filename)

        if swap_aspect:
            cut_clip = clip.subclip(start_time, end_time).resize((height, width))
        else:
            cut_clip = clip.subclip(start_time, end_time)

        cut_clip.write_videofile(output_file_path, codec='libx264', audio_codec='aac')

        clip.close()
        cut_clip.close()
        time.sleep(2)

info
def video_info(input_path):
    """Print information of a video.

    Args:
        input_path (str): input_path of the video.
    """
    cap = cv2.VideoCapture(input_path)

    if not cap.isOpened():
        return

    print ('File:', input_path)
    print('Frame width:', int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
    print('Frame height:', int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
    print('Frame count:', int(cap.get(cv2.CAP_PROP_FRAME_COUNT)))
    print('Frame rate:', cap.get(cv2.CAP_PROP_FPS))
    print('Duration (s):', int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) / cap.get(cv2.CAP_PROP_FPS))

最後に

簡単な動作確認しか行っていないので、不完全部分もあるかもしれませんがご容赦ください。
何か問題があればgithubのissueまたはコメントをお願いします。
せっかく作成したので、誰かの役に立てば嬉しいです。最後まで読んでいただきありがとうございました。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0