【初心者】YOLOv5を使ってみる #2 (RTMP中継サーバ経由でのリアルタイムでの映像物体検出)

# 1. 目的

- 前回の記事「[【初心者】YOLOv5を使ってみる #1 (AWS上でのインストール、物体検出確認)](https://qiita.com/mksamba/items/3ab9ad80a11d7f5fdde5)」で、まずはYOLOv5を動かしてみた。
- 今回は自分の手元のPCのカメラの映像を、AWS上のYOLOv5に送信し、リアルタイムに物体検出できるようにする。

# 2. やったこと
- AWS上のYOLOv5サーバの隣に、中継サーバを構築する。
- PC(OBS Studio) -> RTMP -> 中継サーバ -> HLS -> YOLOv5 の流れで、手元のPCからの映像をYOLOv5にて分析させる。

# 3. 構成図

![構成図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/93082a47-27ab-bba0-f357-646e7d0cbf88.png)

# 4. 実施手順

## 4.1 中継サーバの構築
- PCの映像をそのままYOLOv5に送信することはできない。今回はnginxと、nginxでrtmpが扱えるようにする追加モジュールを使用し、中継サーバを構築する。PCからはrtmpで中継サーバに映像を送信し、中継サーバからブラウザやYOLOv5へはHLSで映像を配信する。
- 手順は以下を参照する。
    - Nginx社ブログ「[Enabling Video Streaming for Remote Learning with NGINX and NGINX Plus](https://www.nginx.com/blog/video-streaming-for-remote-learning-with-nginx/)」
    - Transonic社ブログ「[[CentOS7.6][nginx][HLS]ライブストリーミングサーバ構築](https://trans-it.net/centos7-ngin-livestreaming/)」
- OSは今回はCentOS7を使用。

- 必要なパッケージをインストールする。

```
[centos@ip-10-0-0-124 ~]$ sudo yum update
[centos@ip-10-0-0-124 ~]$ sudo yum groupinstall "Development Tools"
[centos@ip-10-0-0-124 ~]$ sudo yum install git
[centos@ip-10-0-0-124 ~]$ sudo yum install pcre-devel zlib-devel openssl-devel
```
・nginx 及び nginx用のrtmpモジュールをインストールする。

```
[centos@ip-10-0-0-124 ~]$ git clone https://github.com/arut/nginx-rtmp-module.git
[centos@ip-10-0-0-124 ~]$ git clone https://github.com/nginx/nginx.git
[centos@ip-10-0-0-124 ~]$ cd nginx
[centos@ip-10-0-0-124 ~]$ ./auto/configure --add-module=../nginx-rtmp-module
[centos@ip-10-0-0-124 ~]$ make
[centos@ip-10-0-0-124 ~]$ sudo make install
```

- nginx.conf を編集し、rtmpの受信、及びHLSの配信を行うように設定する。今回は「[[CentOS7.6][nginx][HLS]ライブストリーミングサーバ構築](https://trans-it.net/centos7-ngin-livestreaming/)」の記事内のnginx.confをベースとして使わせて頂いた。
- 同記事内では、"hls_playlist_length"の値が1sだったが、本環境ではそれだと安定しなかったため30sに変更した。
- 各パラメータの解説は公式のGitHubレポジトリの「[Directives](https://github.com/arut/nginx-rtmp-module/wiki/Directives)」にあるが、内容の深堀りはできていない。

``` /usr/local/nginx/conf/nginx.conf
#user  nobody;
worker_processes  auto;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

rtmp_auto_push on;
rtmp {
    server {
          listen 1935;
          access_log logs/rtmp_access.log ;
          chunk_size 4096;

          application live1 {
                 live on;
                  wait_video on;
                  hls on;
                  hls_cleanup on;
                  hls_path /usr/local/nginx/html/hls;

                  hls_fragment 500ms;
                  hls_playlist_length 30s;
                  hls_type live;
                  hls_nested on;
          }
    }

}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        location /hls {
            types {
                   application/vnd.apple.mpegurl m3u8;
            }
            root   html;
            index  index.html index.htm;
        }

        location /dash {
            types {
                   application/vnd.apple.mpegurl mpd;
            }
            root   html;
            index  index.html index.htm;
        }
    }
}
```

- nginx をバックグラウンドで起動する。

```
sudo /usr/local/nginx/sbin/nginx &
```


## 4.2 映像送信(OBS Studio)
- 上記で構築した中継サーバに向けてrtmpで映像の送信を行う。
- OBS Studioの「設定」- 「配信」で、配信先を以下のように設定する。
    - サーバー:  rtmp://[中継サーバのグローバルIP]:1935/live1
    - ストリームキー： stream
- 「live1」の値は自由に設定可能だが、nginx.conf の 「application」で設定した値と一致させる必要がある。
- 「stream」の値は自由に設定可能。この値は後で受信を行う際に必要になる(URLの一部になるため)。

![yolo04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/c139d8af-d24c-ae6b-7830-99aa82259f98.png)

- OBS Studio から映像を送信すると、中継サーバ内に映像のファイルが生成される。

```
[root@ip-10-0-0-124 stream]# pwd
/usr/local/nginx/html/hls/stream
[root@ip-10-0-0-124 stream]# ls
0.ts  1.ts  2.ts  3.ts  4.ts  5.ts  6.ts  index.m3u8
```

- また、送信完了後にはログも出力される。

```
[root@ip-10-0-0-124 logs]# pwd
/usr/local/nginx/logs
[root@ip-10-0-0-124 logs]# more rtmp_access.log
x.x.x.x [25/Aug/2021:07:10:50 +0000] PUBLISH "live1" "stream" "" - 66766223 529 "" "FMLE/3.0 (compatible; FMSc/1.0)" (3m 21s)
```

## 4.3 映像受信(IE)
- 中継サーバと同一Subnetに存在するインスタンス(Windows Server 2019)で、HLSで映像を受信する。
- ブラウザ(IE) で再生できるよう、中継サーバ側にhtmlファイルを用意する。今回は「[[CentOS7.6][nginx][HLS]ライブストリーミングサーバ構築](https://trans-it.net/centos7-ngin-livestreaming/)」の記事内のhtmlファイルをベースとして使わせて頂いた。
- 「player.setSrc」のところのIPアドレスは、今回は同一Subnet内でのアクセスになるため、中継サーバの実IP(プライベートIP)を設定する。

``` /usr/local/nginx/html/play.html
<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <title>MediaElement</title>
 <!-- MediaElement style -->
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mediaelemen
t/4.2.9/mediaelementplayer.css" />
</head>
<body>
 <!-- MediaElement -->
 <script src="https://cdnjs.cloudflare.com/ajax/libs/mediaelement/4.2.9/mediaele
ment-and-player.js"></script>
 <video id="player" />


</body>
<script type="text/javascript">
     var player = new MediaElementPlayer('player', {
       success: function(mediaElement, originalNode) {
         console.log("Player initialised");
       }
     });
     player.setSrc("http://10.0.0.124/hls/stream/index.m3u8");
</script>
</html>
```
- ブラウザ(IE)で、上記のURL(http://10.0.0.124/play.html) を開くことで、映像を再生することが可能。

## 4.4 映像受信/物体検出(YOLOv5)
- 中継サーバと同一Subnetに存在するインスタンス(Windows Server 2019)にインストール済のYOLOv5で、中継サーバからHLSで映像を受信し物体検出を行う。

```
C:\Users\Administrator\yolov5>python detect.py --source "http://10.0.0.124/hls/stream/index.m3u8"
```

- リアルタイムではないが、映像を継続的に処理し物体検出を行うことが可能。

## 4.5 簡易的な遅延の確認
- OBS Studio では、カメラ(自分の顔)と、デジタル時計を並べて配信する。職場のインターネット(wifi)経由で、AWS東京リージョンの中継サーバへ映像を送信する。
- 並行して、Windows Server 2019のインスタンスにRDPし、IEで映像を中継サーバ経由でそのまま受信した画面と、YOLOv5で物体検出を行っている画面を並べて表示する。YOLOv5ではきちんと自分の顔が「person」として検知される。
- 配信しているリアルの時刻が16:41:10、IEでそのまま受信/再生している時刻が16:40:42、YOLOv5が物体検出した結果を表示している時刻が16:40:27となり、IEは約28秒遅れ、YOLOv5は約43秒遅れとなっている。中継サーバをチューニングしたり、YOLOv5のモデルを変えたりしたら速くなるのかもしれない。

![yolo05a.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/60492a85-ddbe-a655-3cf2-710d2576b6b1.jpeg)


# 5. 所感
- 手元の映像を使用して物体検出を行うことができた。引き続き様々なトピック（他のモデルの利用など）にチャレンジしてみたい。