dockerでウェブカメラの映像を表示する

ウェブカメラから映像を取得しPC画面上に表示するというプログラムをdockerから行えるようにした際のメモです。
（物体検知を用いた検討を行うための環境ですが、本メモでは物体検知は行っていません。）

# 行ったこと
1.  Dockerfileを使ってpullしたimageをカスタマイズ
2.  ウェブカメラの映像を表示でする。
3.  Jupyter notebookをみられるようにする。(追加)

### ホストの環境
```souece:OS
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
```
### dockerの環境
[こちらで構築した環境](https://qiita.com/iwankoTG/items/05b39c9c42ad278917a0)を使っています。

# 1.  Dockerfileを使ってpullしたimageをカスタマイズ
nvidia-dockerからDeepLearningのフレームワークとしてpytorchのimageをpullします。そのdocker imageをベースに、open-cvなど映像を扱うために必要なモジュールのインストールを行います。

```souece:
$ docker pull nvcr.io/nvidia/pytorch:20.03-py3
  // imageのpullに小一時間程度かかります

$ docker images
REPOSITORY                    TAG                       IMAGE ID            CREATED            SIZE
nvcr.io/nvidia/pytorch     20.03-py3                  16c4987611fa        5 weeks ago         9.39GB
```
open-cvのインストールには、libsm6 libxext6 libxrender-devが必要だったため、Dockerfileではそれらのインストールも行います。
次に、opencvの出力を画面に表示できるようにします。~/.bashrcに QT_X11_NO_MITSHM=1 を追記する必要があるので、そちらもDockerfile以下のように記載しました。

```source:Dockerfile
FROM nvcr.io/nvidia/pytorch:20.03-py3

ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -y python3-tk && \
    pip install --upgrade pip && \
    apt-get install -y libsm6 libxext6 libxrender-dev && \
    pip install opencv-python && \
    pip install pytest && \
    pip install nose

RUN echo 'export QT_X11_NO_MITSHM=1' >> ~/.bashrc && \
    source ~/.bashrc
```
上記Dockerfileを用いて、docker imageを作成します。

```:
$cd **/work/  [Dockerfileのあるフォルダ]
$docker build -t [repository名]:[tag名] . 
例）$docker build -t test:1 . 
```

# 2.  ウェブカメラの映像を表示でする。
次に、PCに接続したウェブカメラの映像を取得できるようにします。
USBカメラをPCにつないで、ホスト側でデバイスの確認を行います。

```:
$sudo apt install v4l-utils
$v4l2-ctl --list-device

UVC Camera :
	/dev/video0
```
と表示され、/dev/video0で指定できることがわかります。
そこで、以下のようにデバイスを指定してdockerをrunします。

```:
$xhost +
$docker run --gpus all -it --rm \
           --device /dev/video0:/dev/video0:mwr \
           -v /tmp/.X11-unix:/tmp/.X11-unix \
           -e DISPLAY=$DISPLAY test:1

=============
== PyTorch ==
=============

NVIDIA Release 20.03 (build 11122848)
PyTorch Version 1.5.0a0+8f84ded

Container image Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
（省略）....
```
Dockerfileで設定した内容が反映されているかを確認します。

```:
#cat ~/.bashrc
.....
export QT_X11_NO_MITSHM=1
```
```:
#python
>>>import cv2
>>>print(cv2.getBuildInformation())

.....
  Video I/O:
    DC1394:                      NO
    FFMPEG:                      YES
      avcodec:                   YES (ver 58.19.100)
      avformat:                  YES (ver 58.13.100)
      avutil:                    YES (ver 56.18.100)
      swscale:                   YES (ver 5.2.100)
      avresample:                NO
    GStreamer:                   NO
    libv4l/libv4l2:              NO
    v4l/v4l2:                    linux/videodev.h linux/videodev2.h
    gPhoto2:                     NO
.....
```
以下のようなコードを実行するとUSBカメラから取得した映像が表示できます。

```souece:test.py
import cv2
camera_width = 1280
camera_height = 960
vidfps = 30

cam = cv2.VideoCapture(0)
cam.set(cv2.CAP_PROP_FPS, vidfps)
cam.set(cv2.CAP_PROP_FRAME_WIDTH, camera_width)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_height)
cv2.namedWindow("USB Camera", cv2.WINDOW_AUTOSIZE)

while True:
    ret, color_image = cam.read()
    if not ret:
        continue
    cv2.imshow('USB Camera', color_image)
    if cv2.waitKey(1)&0xFF == ord('q'):
        break
```
# 3. jupyter notebookを見られるようにする.
提供されるコードがjyupyter notebookになってきたので、こちらも見られるようにしました。
今回はベースとしているpytorchのdocker(nvcr.io/nvidia/pytorch 20.03-py3)にjupyter notebookが
インストールされていたので、Dockerfileは同じままでOKでしたが、入っていない場合には、
Dockerfileに追記します。

```source:Dockerfile
FROM nvcr.io/nvidia/pytorch:20.03-py3

ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -y python3-tk && \
    pip install --upgrade pip && \
    apt-get install -y libsm6 libxext6 libxrender-dev && \
    pip install opencv-python && \
    pip install jupyter && \
    pip install pytest && \
    pip install nose

RUN echo 'export QT_X11_NO_MITSHM=1' >> ~/.bashrc && \
    source ~/.bashrc
```

そうしてイメージを作成したのち、dockerのcontainerを立ち上げます。

```:
$xhost +
$docker run --gpus all -it --rm \
           --device /dev/video0:/dev/video0:mwr \
           -p 10000:8888 \
           -v /[working directory path]/:/work \
           -v /tmp/.X11-unix:/tmp/.X11-unix \
           -e DISPLAY=$DISPLAY test:1
```
コンテナが起動したら、jupyter notebookを立ち上げます。

```:
$cd /work
$jupyter notebook &

    To access the notebook, open this file in a browser:
        file:///root/.local/share/jupyter/runtime/nbserver-346-open.html
    Or copy and paste one of these URLs:
        http://hostname:8888/?token=b4fd4738d4173fc924b86107cba9330d24419df8cd818c0b
```
ホスト側でブラウザを立ち上げ, URLとして http://localhost:10000 を入力します。
tokenを聞く画面が表示されたら、上記のtoken=以下の文字列をコピーして入力すると、ブラウザ上に、
/work以下のフォルダが表示されます。


参考文献
1. [dockerからopencvの画像を表示する。](https://qiita.com/oreyutarover/items/cca3511012b6ad97a1ce)
2. [dockerでデバイスカメラを共有する。](https://kinacon.hatenablog.com/entry/2018/11/16/223249)