scikit-videoを使うと、手軽に動画ファイルの読み込み・書き込みを行うことができる。内部的にはFFmpegが利用されていることもあり、自由度も高い。
scikit-videoのインストール
!pip install scikit-video
Google colaboratory上でscikit-videoを利用するには、pipで手動でライブラリをインストールする必要がある。
Requirement already satisfied: scipy in /usr/local/lib/python3.6/dist-packages (from scikit-video) (1.3.2)
Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from scikit-video) (1.17.4)
Requirement already satisfied: pillow in /usr/local/lib/python3.6/dist-packages (from scikit-video) (4.3.0)
Requirement already satisfied: olefile in /usr/local/lib/python3.6/dist-packages (from pillow->scikit-video) (0.46)
Installing collected packages: scikit-video
Successfully installed scikit-video-1.1.11
2019年11月現在の最新版、1.1.11
がインストールされた。
Google Driveのマウント(オプション)
gdrive_path = "/content/gdrive" # マウント先のパス
from google.colab import drive
drive.mount(gdrive_path)
ファイルの読み書きを実験するに当たって、動作確認の手軽さから今回はGoogle Driveを使う。/content/gdrive
にGoogle Driveがマウントされるようにする。
動画ファイルの読み込み
参考: Reading and Writing Videos
skvideo.ioモジュールを使うと、動画の読み書きをできる。内部的にはFFmpeg
/LibAV
が使われている。
一括でメモリーに読み込む
import skvideo.io
import os
path = os.path.join(gdrive_path, "My Drive", "sample.mp4") # マイドライブにあるsample.mp4を読み込む
videodata = skvideo.io.vread(path)
# 最初のフレームを取得する
first_frame = videodata[0]
vreadメソッドを使うと、動画ファイル全体を一度にメモリー上に読み込むことができる。読み込まれたデータは(フレーム数, 縦px, 横px, チャンネル数)の構造をもつndarray
になっている。手軽である反面、容量の大きい動画ファイルの場合は大量のメモリーを使用することになってしまうので、基本的には後述のvreader
を使い1フレームずつ読み込む方法を使った方が良さそうだ。
1フレームずつ読み込む
import skvideo.io
import os
path = os.path.join(gdrive_path, "My Drive", "sample.mp4") # マイドライブにあるsample.mp4を読み込む
videogen = skvideo.io.vreader(path)
for frame in videogen:
# display_image(frame)...
vreaderを使った読み込みでは、動画ファイルを1フレームずつ読み込んでいくことができる。公式サイトの説明によると、こちらの方法の方がactually faster than loading a video as 1 ndarray
とのことなので、理由がなければvreaderを使う。
videogen = skvideo.io.vreader(path, outputdict={"-r": "3"})
inputdict
とoutputdict
という引数を使うと、FFmpegに対してパラメータを与えることができる。上の例は3FPSで読み込む場合。
動画のメタ情報の取得
metadata = skvideo.io.ffprobe(path)
num_frames = metadata["video"]["@nb_frames"] # マトリョーシカになっている動画ではN/A
skvideo.io.ffprobeを使うと、FFmpegのツールであるffprobeを使った動画のメタ情報の取得ができる。
動画ファイルの書き込み
一括で書き込む
import skvideo.io
import os
path = os.path.join(gdrive_path, "My Drive", "skvideo_out.mp4") # マイドライブにskvideo_out.mp4というファイルを出力する
skvideo.io.vwrite(path, videodata) # videodataは映像の全フレームを(フレーム数, 縦px, 横px, チャンネル数)のndarray形式で指定
読み込み同様、メモリ上のデータを一括でファイルに出力する方法も用意されているが、基本的には1フレームずつ処理して書き込んでいった方がパフォーマンスが良さそうだ。
1フレームずつ書き込む
import skvideo.io
import os
path = os.path.join(gdrive_path, "My Drive", "skvideo_out.mp4") # マイドライブにskvideo_out.mp4というファイルを出力する
writer = skvideo.io.FFmpegWriter(path)
# 1フレームずつ書き込む
writer.writeFrame(frame1)
writer.writeFrame(frame2)
writer.writeFrame(frame3)
...
writer.close()
FFmpegWriterを使うと、1フレームずつ映像を書き込んでいくことができる。FFmpegWriterはその名の通りFFmpegを使って動画の出力を行うので、フレームレートなど出力のためのFFmpeg用のパラメータをそのまま渡して適用することができる。例えば、FPS=1で出力したいような場合は下記のように指定できる。
writer = skvideo.io.FFmpegWriter(path, inputdict={"-r": "1"}, outputdict={"-r": "1"})
サンプル
以下のコードは、元動画から1フレームずつ画像を読み込み、10フレーム毎に間引いて10倍速にして出力する。
!pip install scikit-video
import os
import skvideo.io
import skvideo.datasets
from tqdm import tqdm
from google.colab import drive
gdrive_path = "/content/gdrive" # マウント先のパス
drive.mount(gdrive_path)
src_path = os.path.join(gdrive_path, "My Drive", "source.mp4") # 元となるファイルはマイドライブのsource.mp4
out_path = os.path.join(gdrive_path, "My Drive", "skvideo_10x.mp4") # マイドライブにskvideo_10x.mp4というファイルを出力する
writer = skvideo.io.FFmpegWriter(out_path, inputdict={"-r": "1"}, outputdict={"-r": "1"})
videogen = skvideo.io.vreader(src_path)
for i, frame in enumerate(tqdm(videogen)):
if i % 10 == 0:
writer.writeFrame(frame)
writer.close()