OpenCV – VideoCapture でカメラ映像の取得、動画ファイルの読み込み

目次

概要

OpenCV の VideoCapture の使い方について解説します。VideoCapture では、以下のことが行えます。

  • Web カメラから映像を取得する
  • 動画ファイルからフレームを取得する
  • 連番の画像ファイルから順番に画像を取得する

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

引数に映像を取得するカメラデバイスの ID を指定します。 カメラが 1 台しか接続されていない場合は、0 を指定します。 カメラが開けた場合、ret, frame = cap.read() で 1 フレーム分取得できます。

cv2.VideoCapture(index)
引数
名前 デフォルト値
index int
デバイス ID (0, 1, …)

Logicool の Logitech C270 HD Webcam を PC に接続して、確認しました。 フレームの大きさや FPS といった情報は VideoCapture.get() で取得できます。

  • 動画の幅: cap.get(cv2.CAP_PROP_FRAME_WIDTH)
  • 動画の高さ: cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
  • 動画の fps: cap.get(cv2.CAP_PROP_FPS)
In [ ]:
import cv2

# VideoCapture を作成する。
device_id = 0
cap = cv2.VideoCapture(device_id)
if not cap.isOpened():
    print(f"Could not open video device {device_id}.")

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))
print(f"size: ({width}, {height}), fps: {fps}")

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()

動画ファイルからフレームを取得する

引数に動画ファイルを指定した場合、動画ファイルを開きます。 ret, frame = cap.read() で 1 フレーム分取得できます。 末尾に到達して、読み込むフレームが無くなった場合、retFalse になります。

cv2.VideoCapture(filename)
引数
名前 デフォルト値
filename str
動画ファイル名
In [ ]:
import cv2

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

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))
print(f"size: ({width}, {height}), fps: {fps}")

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

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

cap.release()
cv2.destroyAllWindows()

sleep を入れない場合、PC の処理能力で可能な速度で映像が取得されます。 動画の実際の FPS で再生したい場合は、適当に sleep を入れる必要があります。

FPS は 1 秒あたりのフレーム数のため、1 フレームあたり $\frac{1}{\text{FPS}}$ 秒でループを回せばよいことになります。 フレームの取得自体にも時間がかかるので、それを除いた以下の時間 sleep すればよいです。

$$ \text{sleep する時間 (secs)} = \frac{1}{\text{FPS}} – \text{フレーム取得にかかった時間 (secs)} $$
In [ ]:
import cv2
import time

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

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))
print(f"size: ({width}, {height}), fps: {fps}")

while True:
    start = time.time()
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break  # 取得に失敗した場合
    elapsed_secs = time.time() - start  # 映像取得にかかった時間 (secs)

    sleep_secs = max(1, int((1 / fps - elapsed_secs) * 1000))  # sleep する時間 (secs)
    cv2.imshow("Frame", frame)
    if cv2.waitKey(sleep_secs) & 0xFF == ord("q"):
        break  # q キーを押したら終了する。

cap.release()

連番の画像ファイルから順番に画像を取得する

以下のようにあるディレクトリ以下に連番で画像ファイルがある場合、VideoCapture で順番に読み込むことができます。filename には、frame\_%03d.png のようにフレーム番号の部分を書式指定子で指定します。VideoWriter を使えば、連番画像を動画化できます。

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
In [ ]:
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)
引数
名前 デフォルト値
propId int
プロパティID。VideoCaptureProperties を参照する。
返り値
名前 説明
retval 指定したプロパティの値。返り値は float になっている。

cv2.VideoCapture.get() では、以下の情報を取得できます。

  • フレームの幅: cap.get(cv2.CAP_PROP_FRAME_WIDTH)
  • フレームの高さ: cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
  • fps: cap.get(cv2.CAP_PROP_FPS)
  • FOURCC: cap.get(cv2.CAP_PROP_FOURCC)
  • フレーム数の合計: cap.get(cv2.CAP_PROP_FRAME_COUNT)
In [ ]:
import cv2

video_path = "sample.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}"
)

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}")

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

cap.release()
cv2.destroyAllWindows()

コメント

コメントする

目次