Help us understand the problem. What is going on with this article?

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

やりたいこと

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,
    )
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away