LoginSignup
5
11

More than 3 years have passed since last update.

【Python】動的に生成されるnumpy.ndarray画像列を動画として保存する(OpenCV + ffmpeg)

Last updated at Posted at 2019-10-10

やりたいこと

Pythonで画像列を作って動画にしたいことはよくある。よく転がっているコードは以下の2つ。

  • OpenCVのVideoWriterを使う → 保存形式を細かく設定する方法がわかりにくい。
  • 一旦ファイルに書き出してffmpegを使う → 先にすべての画像を作っておく必要がある。また,ファイルに書き出してまた読む余分な時間が無駄。

今回は,

  • 動的に生成されるnumpy.ndarray画像列を
  • ffmpegのオプションを使って
  • 余分なファイル入出力無しで
  • 動画ファイルとして保存したい

できたもの(Python 3.5)

ffmpegには入力画像列をパイプで受け取る機能があるので,Popenして順番に渡してやればいい。

def save_images_as_movie(filename, images, fps, ffmpeg_args='-y', ffmpeg_executable='ffmpeg'):
    """画像列を動画ファイルとして保存する.

    Parameters
    ----------
    filename: str
      保存先ファイル名.
    images: iterable of array-like
      動画として保存する画像列.
    fps: int
      動画のフレームレート.
    ffmpeg_args: str or iterable of str
      ffmpegの出力ファイル設定.
      例: PowerPointで再生可能かつ無圧縮にしたいなら '-vcodec rawvideo -pix_fmt yuv420p'
    ffmpeg_executable: str
      ffmpegの実行ファイルパス.

    Return
    ------
    completed_process: subprocess.CompletedProcess
      ffmpegの実行結果.

    Examples
    --------
    >>> import numpy as np
    >>> images = [np.full(shape=(128, 128), fill_value=v, dtype=np.uint8) for v in np.linspace(0, 255, 60)]
    >>> result = save_images_as_movie('example.mp4', images, fps=60)
    >>> result.check_returncode()
    """
    from subprocess import Popen, PIPE, CompletedProcess
    from cv2 import imencode

    if isinstance(ffmpeg_args, str):
        ffmpeg_args = ffmpeg_args.split()
    ffmpeg = Popen(
        args=[
            ffmpeg_executable,
            '-r', str(fps), '-f', 'image2pipe', '-i', '-',
            '-r', str(fps), *ffmpeg_args, filename,
        ],
        stdin=PIPE,
        stdout=PIPE,
        stderr=PIPE,
    )
    try:
        for image in images:
            success, buffer = imencode('.bmp', image)
            if not success:
                raise ValueError('imencode failed')
            ffmpeg.stdin.write(buffer)
    finally:
        out, err = ffmpeg.communicate()
    return CompletedProcess(
        args=ffmpeg.args, 
        returncode=ffmpeg.returncode, 
        stdout=out, 
        stderr=err,
    )
5
11
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
5
11