Pynote

Python、機械学習、画像処理について

OpenCV - VideoCapture / VideoWriter で動画の読み込み/書き込みを行う。

API リファレンス

Web カメラから映像を取得する。

VideoCapture オブジェクトを作成する。

cv2.VideoCapture(index)
  • 引数
    • index: デバイスID (0, 1, ...)
import cv2

device_id = 0
cap = cv2.VideoCapture(device_id)

while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break  # 映像取得に失敗

    cv2.imshow("Frame", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break  # q キーを押したら終了する。

cap.release()
cv2.destroyAllWindows()

動画を読み込む。

VideoCapture オブジェクトを作成する。

cv2.VideoCapture(filename)
  • 引数
    • filename: ファイル名

サンプルとして こちら の動画を使用する。

import cv2

filepath = "vtest.avi"
cap = cv2.VideoCapture(filepath)

while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break  # フレームの取得に失敗または動画の末尾に到達した場合

    cv2.imshow("Frame", frame)
    cv2.waitKey(1)

cap.release()
cv2.destroyAllWindows()

動画を書き込む。

VideoWriter オブジェクトを作成する。

cv2.VideoWriter(filename, fourcc, fps, frameSize[, isColor])
  • 引数
    • filename: 出力ファイル名
    • fourcc: FourCC を表す4つ文字のリスト (例: ['H', '2', '6', '4'])
    • fps: FPS
    • frameSize: フレームの大きさ
    • isColor: 書き込むフレームがカラーかどうか (このフラグは Windows のみサポート)
import cv2

# VideoCapture を作成する。
cap = cv2.VideoCapture("vtest.avi")
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)

# VideoWriter を作成する。
fourcc = cv2.VideoWriter_fourcc(*"DIVX")
writer = cv2.VideoWriter("output.avi", fourcc, fps, (width, height))

while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break  # フレームの取得に失敗または動画の末尾に到達した場合

    writer.write(frame)  # フレームを書き込む。

writer.release()
cap.release()

出力した動画が開けない場合は、フレームの書き込みに失敗している可能性が高い。
VideoWriter のコンストラクタで設定した大きさ (width, height) と実際に writer.write(frame) で出力しようとしたフレームの大きさが一致していることを確認すること。

各フレームを画像として、保存する。

from pathlib import Path

import cv2

# 保存するディレクトリ
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)

# VideoCapture を作成する。
cap = cv2.VideoCapture("vtest.avi")

n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  # フレーム数を取得する。
n_digits = len(str(n_frames))  # フレーム数の桁数を取得する。

while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break  # フレームの取得に失敗または動画の末尾に到達した場合

    # フレームを画像として保存する。
    frame_no = int(cap.get(cv2.CAP_PROP_POS_FRAMES))

    save_path = output_dir / f"frame_{frame_no:0{n_digits}d}.png"
    cv2.imwrite(str(save_path), frame)

cap.release()
output
├── frame_001.png
├── frame_002.png
├── frame_003.png
├── frame_004.png
├── frame_005.png
├── frame_006.png
├── frame_007.png
├── frame_008.png
├── frame_009.png
├── frame_010.png
├── frame_011.png
├── frame_012.png
...

画像から動画を作成する。

今 output ディレクトリ以下に連番で画像ファイルがあるとする。

output
├── frame_001.png
├── frame_002.png
├── frame_003.png
├── frame_004.png
├── frame_005.png
├── frame_006.png
├── frame_007.png
├── frame_008.png
├── frame_009.png
├── frame_010.png
├── frame_011.png
├── frame_012.png
...

このとき、VideoCapture() に 'frame_%03d.png' のようにフレーム番号の部分だけフォーマットで指定することで、順番に読み込む機能がある。これを利用して、連番の画像ファイルから動画を作成する。

from pathlib import Path

import cv2

# 画像ファイルのパス
img_paths = Path("output") / "frame_%03d.png"

# VideoCapture を作成する。
cap = cv2.VideoCapture(str(img_paths))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = 15
print(f"width: {width}, height: {height}, fps: {fps}")

# VideoWriter を作成する。
fourcc = cv2.VideoWriter_fourcc(*"DIVX")
writer = cv2.VideoWriter("output.avi", fourcc, fps, (width, height))

while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break  # フレームの取得に失敗または動画の末尾に到達した場合

    writer.write(frame)  # フレームを書き込む。

writer.release()
cap.release()

動画の各種プロパティを取得する。

VideoCapture.get() で幅、高さ、FPS といった動画のプロパティを取得できる。

retval = cv2.VideoCapture.get(propId)
  • 引数
  • 返り値
    • retval: 指定したプロパティの値。float 型で返すため、必要に応じて、int 型にすればよい。
import cv2

video_path = "vtest.avi"
cap = cv2.VideoCapture(video_path)

# 各種プロパティを取得する。
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # フレームの幅

height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # フレームの高さ

fps = int(cap.get(cv2.CAP_PROP_FPS))  # FPS

fourcc = int(cap.get(cv2.CAP_PROP_FOURCC))  # FOURCC
fourcc = fourcc.to_bytes(4, "little").decode("utf-8")  # int を4バイトずつ解釈する。

n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  # フレーム数の合計

print(
    f"width: {width}, height: {height}, fps: {fps}, forcc: {fourcc}, total frames: {n_frames}"
)
# width: 768, height: 576, fps: 10, forcc: div3, total frames: 795

while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break  # 映像取得に失敗

    curr_pos = int(cap.get(cv2.CAP_PROP_POS_MSEC))  # 動画の最初から何 ms のフレームか
    curr_frame_no = int(cap.get(cv2.CAP_PROP_POS_FRAMES))  # 現在のフレーム番号
    print(f"pos: {curr_pos} ms, frame no: {curr_frame_no}/{n_frames}")
    # pos: 100 ms, frame no: 1/795
    # pos: 200 ms, frame no: 2/795
    # ...

    cv2.imshow("Frame", frame)
    cv2.waitKey(1)

cap.release()
cv2.destroyAllWindows()

動画の再生 FPS を計算する。

動画自体の FPS は VideoCapture.get(cv2.CAP_PROP_FPS) で取得できるが、動画を実際に再生した際の FPS (frames per second) は、フレーム数 / 再生開始時からの計算時間で計算できる。

import time

import cv2

video_path = "vtest.avi"
cap = cv2.VideoCapture(video_path)

n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  # フレーム数

start = time.time()
while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break  # フレームの取得に失敗または動画の末尾に到達した場合

    frame_no = int(cap.get(cv2.CAP_PROP_POS_FRAMES))  # 現在のフレーム番号
    fps = frame_no / (time.time() - start)  # fps = フレーム数 / 経過時間 (s)
    print(f"fps: {fps:.2f}, frame no: {frame_no}/{n_frames}", end="\r")

    cv2.imshow("Frame", frame)
    cv2.waitKey(1)

cap.release()
cv2.destroyAllWindows()