見出し画像

【Python】imageioを使ってWebカメラ映像をmp4リアルタイムエンコードしたらハマった

【状況】Webカメラの映像をリアルタイムエンコードして保存するだけだから簡単かと思ったら,機材によって挙動が変わるようなので試行錯誤.また,1920x1080にしたいのに,1920x1088に自動的にされてしまう.
【対処】カメラの仕様によりOpenCVで指定したフレームレートに実際になっていなかったので,実際のfpsを取得して動画を設定.また,1920x1080にするため,ffmpegのマクロブロックサイズを1に変更した(非推奨ではありますが).

Webカメラのfps設定

fpsを15にしてエンコードしたはずが,動画を再生するとやたら動きが速い.動画のプロパティを確認すると15fpsとなっているので入力がおかしいことに気づきました.カメラのfps設定後にfpsを確認すると実動作5fps.5fpsしか無いのに15fpsでエンコードしたのが原因なので,実fpsを取得して使うことにしました(15fpsの設定がカメラに無かったことがあとで発覚).

# カメラのフレームレートを設定
camera.set(cv2.CAP_PROP_FPS, 15)

actual_fps = camera.get(cv2.CAP_PROP_FPS)
print(f"実際のカメラのFPS: {actual_fps}")

ffpmegのオプションを設定

実fps,ビットレートなどを指定してエンコード.1920x1080の出力を期待したところ,縦が16の倍数でないので1088で出力されました.警告にその旨が書かれ,非推奨ながらマクロブロックサイズを変えればよい,あります.ffmpegのオプションに追加して指定したところ,1920x1080の出力を確認できました.ブロックサイズによる不具合が生じる場合はmacro_clock_sizeの行は削ります.

# ffmpegのオプションを指定して動画書き込みを設定
ffmpeg_output_params = {
    'codec': 'libx264',
    'fps': actual_fps,
    'bitrate': '4000k',
    'macro_block_size': 1,  # マクロブロックサイズを1に設定(1920x1088ではなく1920x1080にするため)
}

BGRからRGBへ変換

色情報について,pythonの標準はBGR,動画にするときはRGBであることが必要なため変換(基本的なことなんですけど).最初,忘れていたので不気味な映像ができましたw

	# 動画保存するため,BGRからRGBに変換
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

完成版

import imageio
import cv2

# ウェブカメラを初期化
camera = cv2.VideoCapture(0)

# カメラの解像度を設定
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

# カメラのフレームレートを設定
camera.set(cv2.CAP_PROP_FPS, 15)

actual_fps = camera.get(cv2.CAP_PROP_FPS)
print(f"実際のカメラのFPS: {actual_fps}")

# ffmpegのオプションを指定して動画書き込みを設定
ffmpeg_output_params = {
    'codec': 'libx264',
    'fps': actual_fps,
    'bitrate': '4000k',
    'macro_block_size': 1,  # マクロブロックサイズを1に設定(1920x1088ではなく1920x1080にするため)
}

writer = imageio.get_writer('output.mp4', format='FFMPEG', mode='I', **ffmpeg_output_params)

# カメラからフレームを取得して書き込み
while True:
    ret, frame = camera.read()
    if not ret:
        break

    # リアルタイムでのプレビュー
    cv2.imshow('Live view', frame)
    
	# 動画保存するため,BGRからRGBに変換
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # フレームを動画ファイルに書き込む
    writer.append_data(frame_rgb)
    
    # 「q」が押されたら終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# リソースの解放
camera.release()
writer.close()
cv2.destroyAllWindows()

とりあえず動画がちゃんとできたからよし♪

この記事が気に入ったらサポートをしてみませんか?