【OpenCV】動画データを画像データに変換してみる

【OpenCV】動画データを画像データに変換してみる

    タイトル通り、動画データを画像データに変換してみようと思います。これができるようになると、画像解析ができる人は動画の解析もできるようになるんです。動画から画像に変換するためにOpenCVというライブラリを用いますが、簡単なのでOpenCVの入門としては最適かもしれません。
 この記事では、PythonのOpenCVライブラリを用いて動画を画像に変換する具体的な方法をご紹介します。

動作環境
macOS Catalina(10.15.6), python3.7.6, Atomエディタ1.44.0

変換可能な動画と画像の種類

 変換可能な動画と画像データの種類は何かなと思って、OpenCVの公式ドキュメントを見てみましたが載ってなかったので自分で試してみて使えるものを洗い出しました。
 動画に関しては、「.mov」「.mp4」が使用可能で
 画像に関しては、「.jpg」「.png」「.tiff」に変換可能でした。

MEMO
動画は主に1秒あたり60枚のフレーム(画像)を変化させることで動きを表現しています(30枚のものもある)。パラパラ漫画のイメージです。
ですから、ここで使っている「変換」という言葉の意味は、正確には「動画データからフレームを切り出して、そのフレームを画像データとして保存する」という意味です。

動画から画像に変換する具体的な手順(サンプルコードあり)

OpenCVをインストールする

pipコマンドを用いれば簡単にOpenCVのライブラリがインストールできます。
次のようにターミナルにコマンドを入力して、returnキーを押してください。

pip install opencv-python

最終的にSuccessfullyというワードが書かれていれば、無事にインストール完了です。
すでにインストールされている場合は、already satisfiedというワードが表示されると思います。

動画を用意する

今回は以下の.mp4の拡張子の動画からjpg形式の画像に変換しようと思います。

蛍の発光の様子

FireFry.mp4という名前でDesktop/Dr.code/python/data-analysis/convert-movie-to-imageに置きます。

プログラムを実行する

import cv2

movie_name = 'FireFly'
movie = movie_name + '.mp4'
cap = cv2.VideoCapture(movie)

# 幅
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
print('width : {:.0f} px'.format(width))
# 高さ
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print('height : {:.0f} px'.format(height))
# 総フレーム数
num_frame = cap.get(cv2.CAP_PROP_FRAME_COUNT)
print('number of frames : {:.0f}'.format(num_frame))
# fps
fps = cap.get(cv2.CAP_PROP_FPS)
print('fps : {:f}'.format(fps))

count = 0
while True:
    ret, frame = cap.read()
    if ret == True:
        count += 1
        # 画像を生成
        cv2.imwrite('{:s}_{:06d}.jpg'.format(movie_name, count), frame)
    else:
        break

コードが書けたらこのファイルをconvert.pyという名前でDesktop/Dr.code/python/data-analysis/convert-movie-to-imageに保存しましょう。
ターミナル上でcwdをDesktop/Dr.code/python/data-analysis/convert-movie-to-imageに変更し、python convert.pyとコマンド入力し、プログラムを実行しましょう。

プログラムが正常に動作すると、1290枚のjpgファイルが生成されると思います!

コードの解説

このプログラムで重要なのは、5行目の

cap = cv2.VideoCapture(movie)

と26行目の

cv2.imwrite('{:s}_{:06d}.tif'.format(movie_name, count), frame)

の部分です。
前者が動画ファイルの情報をOpenCVのVideoCapture()を使って読み込んでおり、
後者が動画のあるフレームをimwrite()を使って画像として書き込んでいます。フレームに番号を振っておくとあとで解析がしやすいので、画像の名前にフレーム番号を書き込んでいます。

# 幅
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
print('width : {:.0f} px'.format(width))
# 高さ
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print('height : {:.0f} px'.format(height))
# 総フレーム数
num_frame = cap.get(cv2.CAP_PROP_FRAME_COUNT)
print('number of frames : {:.0f}'.format(num_frame))
# fps
fps = cap.get(cv2.CAP_PROP_FPS)
print('fps : {:f}'.format(fps))

7~18行目は、動画から画像に変換するにはあまり意味をなさないコードです。
これは、cap.get()メソッドを使って、動画のフレームに関する情報を取得しています。フレームの横幅と高さのピクセル数、フレームの総数、1秒あたり何フレームか(fps)を取得しています。
capという変数にはcv2.VideoCapture(movie)で動画の情報が格納されているから、cap.get()で情報が取得できるということに注意してください。

ret, frame = cap.read()

ループを繰り返すとretframeが更新されていきます。
フレームがあるときはretTrueですが、フレームがなくなるとFlaseになります。
Flaseになるとループ終了です。