OpenCVで動画を読み込み・書き出す方法
Pythonでは、OpenCVライブラリを使用して動画ファイルの操作を行うことができます。「動画を1枚ずつの画像に分けたい」「Pythonで動画を作りたい」といった場面で役立ちます。この記事では、インストールから動画の読み取り・書き出しまでを、動かせるコードで順に説明します。
この記事で分かることは次のとおりです。
- OpenCVのインストールと、動画を扱える環境かどうかの確認方法
- 動画をフレーム(静止画)に分けてJPEG保存する方法
- FPS・フレームサイズ・総フレーム数の取得方法
- 生成したフレームを動画として書き出す方法
インストール
OpenCVはpipでインストールします。Pythonで読み込むときのモジュール名はcv2です。
pip install opencv-python
動画を扱える環境かを確認する
動画の読み書きは、OpenCVの内部でFFMPEGなどのバックエンド(実際の処理を担当するライブラリ)が行います。getBuildInformation()を使うと、手元のOpenCVで利用できるバックエンドを確認できます。
import cv2
print(cv2.getBuildInformation())
ずらずらと表示されますが、Video I/O:の項目を確認します。以下は表示例で、内容は環境によって異なります。
Video I/O:
DC1394: NO
FFMPEG: YES
avcodec: YES (59.37.100)
avformat: YES (59.27.100)
avutil: YES (57.28.100)
swscale: YES (6.7.100)
avresample: NO
GStreamer: NO
v4l/v4l2: YES (linux/videodev2.h)
FFMPEG: YESなら、動画の読み書きの土台は使えます。
ただし、FFMPEGがYESでもH.264やH.265(HEVCとも呼ばれます)で書き出せるとは限りません。ビルドやエンコーダ(動画を圧縮する部品)の有無によって使えないことがあり、環境によって異なります。私がOpenCV 4.13で試した環境では、FFMPEG: YESでもavc1・H264(H.264)とhev1(H.265)の書き出しは失敗し、成功したのはmp4vだけでした。本記事の書き出し例はmp4vを使います。
動画ファイルの読み取り
動画をフレーム(動画を構成する1枚1枚の静止画)に分けて、JPEG画像として保存します。
動画の読み取りの例では、入力動画(./video/input.mp4)と画像の保存先フォルダ(./images)を事前に用意してください。無いとエラー(FileNotFoundError など)になります。入力動画が無い場合はisOpened()が False になってFile open errorと表示され、保存先フォルダが無い場合は FileNotFoundError になります。
動画からフレームを取得する
import cv2
import codecs
import numpy as np
input_video_path = './video/input.mp4'
video_capture = cv2.VideoCapture(input_video_path)
if not video_capture.isOpened():
print('File open error')
exit()
cnt = 1
while True:
# フレームを読み込む
ret, frame = video_capture.read()
if not ret or frame is None:
break
ret, encoded = cv2.imencode('.jpg', frame)
if not ret:
continue
# フレームをjpeg形式で保存
jpeg_bytes = encoded.tobytes()
with codecs.open(f'./images/{cnt}.jpg', 'wb') as f_out:
f_out.write(jpeg_bytes)
cnt += 1
#;
# リソースを解放
video_capture.release()
コードの流れは次のとおりです。
VideoCaptureで動画を開き、isOpened()で開けたかを確認read()で1フレームずつ読み込み、読み込めなくなったらループを終了imencode()でJPEGのバイト列に変換し、1.jpg、2.jpgと連番で保存- 最後に
release()で動画ファイルを解放
cv2.imwriteは、環境によって日本語を含むパスに保存できない場合があります。この例ではバイト列にしてから、Pythonのファイル書き込みで保存しています。
動画のフレームレート(FPS)を取得する
以降の3つのコードは、上の例のvideo_captureを使う前提です。isOpened()の確認後、release()の前に実行してください。
FPS(Frames Per Second)は、1秒間に表示するフレーム数です。30であれば、1秒間に30枚の画像を切り替えて動画にします。
fps = video_capture.get(cv2.CAP_PROP_FPS)
動画のフレームサイズを取得する
フレームサイズは、画像の幅と高さ(ピクセル数)です。get()は小数(float)で値を返すため、int()で整数にします。
frame_width = int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
動画の総フレーム数を取得する
総フレーム数を FPS で割ると、動画の長さ(秒)の目安になります。処理の進捗表示にも使えます。
frame_count = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
動画ファイルの書き出し
NumPyで作ったフレームを、1本の動画にまとめます。次の例では、黒い画像に「Frame 0」のような文字を描いた100フレームを、30FPSの動画にします。出力先は./video/output.mp4なので、./videoフォルダを用意してください。
import cv2
import numpy as np
# 動画の設定
frame_width = 640
frame_height = 480
fps = 30
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video_writer = cv2.VideoWriter('./video/output.mp4', fourcc, fps, (frame_width, frame_height))
# フレームを生成して動画に書き込む
for i in range(100): # 100フレーム作成
# フレームを生成(ここでは単色のフレームを生成)
frame = np.zeros((frame_height, frame_width, 3), dtype=np.uint8)
cv2.putText(frame, f"Frame {i}", (50, 240), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
# フレームを書き込む
video_writer.write(frame)
#;
# リソースを解放
video_writer.release()
ポイントは次のとおりです。
fourccは動画の圧縮形式(コーデック)を表す4文字のコードVideoWriterの引数は、出力パス・fourcc・FPS・フレームサイズ- フレームサイズは
(幅, 高さ)の順で、NumPy配列の(高さ, 幅, 3)とは順番が逆 - 書き込むフレームは、指定したサイズと同じ大きさにそろえることが必要
write()で1フレームずつ追加し、最後にrelease()でファイルを閉じる処理
動画ファイルが作られない場合は、video_writer.isOpened()で開けているかを確認してください。False であれば、fourcc が環境で使えない可能性があります。まずはmp4vで試します。
まとめ
動画の読み書きは、VideoCaptureで開いてread()でフレームを取り出し、VideoWriterのwrite()で書き出す流れです。つまずきやすいのは、環境ごとに使えるコーデックが違う点です。
