ColabでOpenCVで動画を再生しようとしたら画像が出力され続けてしまう

# はじめに

最近画像処理をしっかりと勉強していこうと思い、まずはOpenCVを用いた画像処理を勉強しています。

そこで、OpenCVの本を利用してどのような前処理があるのかや専門用語などの理解を進めています。
画像処理用の開発環境は特に用意していないので、簡単に使える`Google Colablatory`を利用して学習を進めていますが、OpecCVとの親和性があまり良くないためか？書籍のコードではColab上では動かないことが多いです。

今回もColab上では想定通りに動かなかったことがあったため記事にしました。

# 問題

動画を読み込んで再生する処理を行います。
書籍をColabで動かせるように修正を行い以下のコードを実行しました。

```
import sys
import cv2

cap = cv2.VideoCapture('data/campus.mp4')
if not cap.isOpened():
  print('Can not open video file')
  sys.exit()

while True:
  ret, img = cap.read()

  if not ret:
    break
  cv2_imshow(img)

  if cv2.waitKey(30) == 27:
    break

cap.release()
```

すると以下のように1フレームずつが、出力されてしまいます。
本来であれば、1つのウィンドウ内でフレームが出力されなければ動画にはなりません。

![1627611547.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/7fe77f9f-a5fc-b652-494d-6816c6b44b4a.png)

# 解決方法

[How to Capture and Play Video in Google Colab?](https://androidkt.com/how-to-capture-and-play-video-in-google-colab/)こちらの記事に解決方法がありました。

```
from IPython.display import HTML
from base64 import b64encode

def show_video(video_path, video_width = 600):

  video_file = open(video_path, "r+b").read()

  video_url = f"data:video/mp4;base64,{b64encode(video_file).decode()}"
  return HTML(f"""<video width={video_width} controls><source src="{video_url}"></video>""")

show_video(video_path)
```

このコードを実行することでColab上でも動画を再生することができます。

また、フレームの途中で前処理(2値化)して、動画を保存する処理を行ったところ、このコードでは再生できず、以下の記事を参考にし動かすことができました。

[Google Colaboratory 上の cv2 で作成した動画を表示する](https://walkingmask.hatenablog.com/entry/2019/07/22/141729)

```make_video.ipynb
import cv2

# ビデオファイルを開く
cap = cv2.VideoCapture('./data/sample2.mp4')
# ビデオファイルの保存設定
rec = cv2.VideoWriter('./data/video_dst.mp4',
                      cv2.VideoWriter_fourcc(*"mp4v"),
                      30, (640, 480))

# ループ開始
while True:
    # 1フレーム読み込み
    ret, src = cap.read()
    if not ret:
        break
    # グレースケールに変換
    dst = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    # 2値化
    ret, dst = cv2.threshold(dst, 128, 255, cv2.THRESH_BINARY)
    # 3チャンネルに変換
    dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
    # cv2_imshow(dst)
    # 1フレーム書き込み
    rec.write(dst)
    # キー入力待機（30 ms）。Escキーで中断。
    if cv2.waitKey(30) == 27:
        break
rec.release()
```

```start_movie.ipynb

!ffmpeg -i ./data/video_dst.mp4 -vcodec vp9 ./out.webm

from IPython.display import HTML
import base64
import io

def play(file_path):
    video = io.open(file_path, 'r+b').read()
    encoded = base64.b64encode(video)
    return(HTML(data='''<video width="320" height="240" controls><source src="data:video/mp4;base64,{0}" type="video/mp4" /></video>'''.format(encoded.decode('ascii'))))

play('./out.webm')
```

また、フレームごとの前処理で

```
# ビデオファイルの保存設定
rec = cv2.VideoWriter('data/video_dst.mp4',
                      cv2.VideoWriter_fourcc(*'H264'),
                      30, (640, 480))
```

としていましたが、`H264`だと動画の保存ができなかったため、`mp4v`にしています。

# おわりに

OpenCVを本格的に利用していくとなるとColabでは厳しいのかなとすこし思い始めました。画像処理を勉強したらそれ用の環境をつくろうかと思います。

Dockerでも書籍のコードは動かなそうと思うと大変そうです。
Colabの相性でつまずくことが多そうなので、またつまずいたら記事にまとめていきます。

# 参考文献
- [How to Capture and Play Video in Google Colab?](https://androidkt.com/how-to-capture-and-play-video-in-google-colab/)
- [Google Colaboratory 上の cv2 で作成した動画を表示する](https://walkingmask.hatenablog.com/entry/2019/07/22/141729)