OpenCVでスプラトゥーン2の動画を試合ごとに切り出す

# はじめに

最近スプラトゥーンの動画を撮る環境を整えたんですが、動画を撮って後から見るときに、頭から見返していくのが大変だなと感じていました。そこで、一本の長い動画から、一試合毎にファイルに切り出せば後から見返すときに便利かなと思い、OpenCV の画像認識を利用した動画の切り出しをやってみました。

# 考え方

スプラトゥーンでは試合の開始時のマッチング画面と、試合後のリザルト画面が毎回はいるので、画像認識をうまく使えば試合の開始と終了を認識できるのではないかと考えました。

OpenCV は初めてだったので、いろいろ触りながら試してみましたが、以下のテンプレートを使ったテンプレートマッチングで試合の頭とお尻を認識させるのが一番うまくいったので、最終的にそれで開始と終了時間を取得→ffmpegで動画を切り出しという作りにしました。
（以下のテンプレート画像はガチマッチのものなので、他のパターンでは利用できないかもしれません。）

 - 試合の開始（マッチング画面）
  <img src="https://qiita-image-store.s3.amazonaws.com/0/131166/8498ff21-80c1-87c8-c5a6-ea3b8721e35a.png" width=50%>

 - 試合の終了（リザルト画面）
  <img src="https://qiita-image-store.s3.amazonaws.com/0/131166/0adb2139-440e-224d-1e08-209f1003f2c3.png" width=30%>


# 実装

先程のテンプレート画像 (STARTIMG, ENDIMG) をグレースケールで読み込みます。

```python
    ss = cv2.cvtColor(cv2.imread(STARTIMG), cv2.COLOR_BGR2GRAY)
    es = cv2.cvtColor(cv2.imread(ENDIMG), cv2.COLOR_BGR2GRAY)
```

元となる動画を読み込んで、FPS, トータルフレーム数、トータル時間を取得します。

```python
    cap = cv2.VideoCapture(sys.argv[1])
    fps = cap.get(cv2.CAP_PROP_FPS)
    all_fps = cap.get(cv2.CAP_PROP_FRAME_COUNT)
    total_time = all_fps / fps
    print fps, all_fps, total_time
```

動画の頭から5秒毎に画像を取得して、グレースケールにします。

```python
    for i in range(int(total_time / SKIPS)):
        cap.set(cv2.CAP_PROP_POS_MSEC, i * 1000 * SKIPS)
        r, f = cap.read()
        aa = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
```

テンプレート画像（マッチング画面）に一致するかチェックし、一致したらその時点の時刻（フレーム）を取得します。（一致したかどうかのしきい値は 0.9 としました。）
→それを試合の開始時刻に

```python
            r = cv2.matchTemplate(aa, ss, cv2.TM_CCOEFF_NORMED)
            min_val, smax_val, min_loc, max_loc = cv2.minMaxLoc(r)
            if smax_val > 0.9:
                start_fps = cap.get(cv2.CAP_PROP_POS_FRAMES)
```

同様に、テンプレート画像（リザルト画面）に一致するかチェックし、一致したらその時点のフレームを取得します。
→それを試合の終了時刻に

```python
            r = cv2.matchTemplate(aa, es, cv2.TM_CCOEFF_NORMED)
            min_val, emax_val, min_loc, max_loc = cv2.minMaxLoc(r)
            if emax_val > 0.9:
                end_fps = cap.get(cv2.CAP_PROP_POS_FRAMES)
```

開始時刻から試合時間分を ffmpeg で切り出します。
余計なエンコードをしないよう、 "-vcodec copy -acodec copy" としています。
ついでにリザルト画面の画像をファイルに記録しておきます。

```python
                start = start_fps / fps
                duration = (end_fps - start_fps) / fps + OFFSET   
                cmd = "ffmpeg -ss %d -i %s -t %d -vcodec copy -acodec copy %s.mp4" \
                      % (start, sys.argv[1], duration, fname)
                os.system(cmd)
                cv2.imwrite("%s/%s_%04d.png" % (outd, fname, start), f)
```

# スクリプト全文

以下に貼っておきました。
（雑なスクリプトですが。。。）

[splacut.py](https://gist.github.com/nigaky/7b09fb107eb2d0cc66ad00b754192fe9)

以下のように実行すると、一つの動画ファイルから一試合毎に切り出して指定のフォルダ配下に出力します。合わせてリザルト画面の画像も png で保存します。

```bash
./splacut.py ./Rec/2018071919474521.mp4
    :
ls -l ./Rec/out/
合計 9268760
-rw-rw-r-- 1 user user 874024504  7月 30 00:21 2018-07-29_2005_0400.mp4
-rw-rw-r-- 1 user user   1788096  7月 30 00:21 2018-07-29_2005_0400.png
-rw-rw-r-- 1 user user 924826280  7月 30 00:21 2018-07-29_2005_0770.mp4
-rw-rw-r-- 1 user user   2023764  7月 30 00:21 2018-07-29_2005_0770.png
-rw-rw-r-- 1 user user 908500746  7月 30 00:22 2018-07-29_2005_1155.mp4
        :
```

以下のようなリザルト画面と動画ファイルをペアで切り出すことができました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/131166/c1141b3b-5f59-2dee-f7ed-b848c4b3a4c7.png" width=50%>


# まとめ

OpenCV を使うと簡単に画像認識を行うことができました。
うまく応用すれば、キルした場面を自動的に収集したりといった他のことにも活用できそうです。