Google Colaboratoryでscikit-videoを使って動画の読み書きをする

[scikit-video](http://www.scikit-video.org/stable/index.html)を使うと、手軽に動画ファイルの読み込み・書き込みを行うことができる。内部的にはFFmpegが利用されていることもあり、自由度も高い。

# scikit-videoのインストール
```python
!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のマウント(オプション)
```python 
gdrive_path = "/content/gdrive" # マウント先のパス

from google.colab import drive
drive.mount(gdrive_path)
```

ファイルの読み書きを実験するに当たって、動作確認の手軽さから今回はGoogle Driveを使う。`/content/gdrive`にGoogle Driveがマウントされるようにする。

# 動画ファイルの読み込み
参考: [Reading and Writing Videos](http://www.scikit-video.org/stable/io.html)

skvideo.ioモジュールを使うと、動画の読み書きをできる。内部的には`FFmpeg`/`LibAV`が使われている。

## 一括でメモリーに読み込む
```python 
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](http://www.scikit-video.org/stable/modules/generated/skvideo.io.vread.html#skvideo.io.vread)メソッドを使うと、動画ファイル全体を一度にメモリー上に読み込むことができる。読み込まれたデータは(フレーム数, 縦px, 横px, チャンネル数)の構造をもつ`ndarray`になっている。手軽である反面、容量の大きい動画ファイルの場合は大量のメモリーを使用することになってしまうので、基本的には後述の`vreader`を使い1フレームずつ読み込む方法を使った方が良さそうだ。

## 1フレームずつ読み込む
```python 
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](http://www.scikit-video.org/stable/modules/generated/skvideo.io.vreader.html#skvideo.io.vreader)を使った読み込みでは、動画ファイルを1フレームずつ読み込んでいくことができる。[公式サイトの説明](http://www.scikit-video.org/stable/io.html)によると、こちらの方法の方が`actually faster than loading a video as 1 ndarray`とのことなので、理由がなければvreaderを使う。

```python 
videogen = skvideo.io.vreader(path, outputdict={"-r": "3"})
```

`inputdict`と`outputdict`という引数を使うと、FFmpegに対してパラメータを与えることができる。上の例は3FPSで読み込む場合。

## 動画のメタ情報の取得
```python 
metadata = skvideo.io.ffprobe(path)
num_frames = metadata["video"]["@nb_frames"] # マトリョーシカになっている動画ではN/A
```

[skvideo.io.ffprobe](http://www.scikit-video.org/stable/modules/generated/skvideo.io.ffprobe.html#skvideo.io.ffprobe)を使うと、FFmpegのツールである[ffprobe](https://ffmpeg.org/ffprobe.html)を使った動画のメタ情報の取得ができる。

# 動画ファイルの書き込み
## 一括で書き込む
```python 
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フレームずつ書き込む
```py
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](http://www.scikit-video.org/stable/modules/generated/skvideo.io.FFmpegWriter.html#skvideo.io.FFmpegWriter)を使うと、1フレームずつ映像を書き込んでいくことができる。FFmpegWriterはその名の通りFFmpegを使って動画の出力を行うので、フレームレートなど出力のためのFFmpeg用のパラメータをそのまま渡して適用することができる。例えば、FPS=1で出力したいような場合は下記のように指定できる。

```python 
writer = skvideo.io.FFmpegWriter(path, inputdict={"-r": "1"}, outputdict={"-r": "1"})
```

# サンプル
以下のコードは、元動画から1フレームずつ画像を読み込み、10フレーム毎に間引いて10倍速にして出力する。

```python 
!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()
```