OpenVINO Face Detection with Tello

# はじめに

エッジでのディープラーニングの推論は、インターネット（クラウド）接続がない環境でのリアルタイム処理において真価を発揮するものと思いますが、デバイスの計算リソースに依存してしまいます。

でもエッジデバイスに取り付けたカメラの Video Streaming を解析すれば、推論処理自体は処理能力が高いコンピューターに分離できます。

ここでは OpenVINO の顔検出/分析モデルとおもちゃドローン(Tello)を使って、リアルタイムで Video Streaming を解析する例を紹介します。


# ドローンを使って顔検出・分析

## 使ったもの

#### 1. エッジデバイス（ドローン: Tello）

[Tello は](https://www.ryzerobotics.com/jp/tello)   わずか 80g のおもちゃドローンです。プログラミング（Python）で制御できます。ホバーリングの安定感が抜群です。今回は「動くカメラ」として Video Streaming を PC に送信します。

#### 2. 計算デイバス
PC ：Windows 10 (Intel Core i7-6500U) / Python 3.6.5
推論エンジン: Intel OpenVINO Toolkit R5


## ドローン（Tello） による顔検出・分析デモ

[前回作った WebUI の仕組み](https://qiita.com/kodamap/items/3517f1a14621e88f1642)をベースに Tello からの Video Streaming を PC で受信して顔検出/顔分析をします。

ブラウザで Tello を操作します。左上の映像は、Telloが飛んでいる様子を重ねたものです。※ 画像は YouTube Link になってます。<a href="https://www.youtube.com/watch?v=I6-YKfPHo_g">
<img src="https://qiita-image-store.s3.amazonaws.com/0/118309/23658392-1e07-ee3f-d51c-f34c7617bb7f.gif" alt="OpenVINO_face_detection_with_Tello" width="auto" height="auto" ></a>

GitHub: https://github.com/kodamap/tellooo


## Tello を Python で動かすには

Tello の公式サイトにある  [Tello SDK Documentation (v1.3)] (https://dl-cdn.ryzerobotics.com/downloads/Tello/20180910/Tello%20SDK%20Documentation%20EN_1.3.pdf) のドキュメントに使用できるコマンドが記載されています。 

Tello SDK では Tello と Wi-Fi で接続し UDP Socketで 通信します。アプリケーションは テキストコマンドを送信することで Tello をコントロールできるようになります。

## アプリケーションの構成

WebUI は Flask を使っています。

#### 基本的な流れ

* 各ボタンをクリックすると所定の URI に POST リクエストを送信します。
* リクエストを受け取った Flask は、UDP Socket 経由で Tello にコマンドを送信してコントロールします。
* Tello からの Video Streaming を受ける camera モジュールは、フレームを検出モジュール(interactive_detection)に渡します。
* 検出モジュールは、各顔分析（detectors)の結果を反映したフレームを Flask 経由でブラウザに返します。
* データ転送は、Tello 自体が Wi-Fi の AP になるので、プログラムを実行するデバイスを直接接続してプライベートネットワーク内で通信します。![telloapp.png](https://qiita-image-store.s3.amazonaws.com/0/118309/b47e94e8-34fe-8757-c6a8-b8f02838849f.png)

#### その他工夫したところ

* Tello からのストリーミング処理を安定させるために、入力フレームを 480 * 320 にリサイズしています。画面はやや小さいですがフレームの遅れが少なくちょうどいいと思います。
* 顔検出や分析をフレーム毎に処理するとノイズが激しく耐えられない映像になります。threading モジュールで 検出インターバルを設けて Thread が生きている間は推論処理をスキップすることで安定しました。（ベストな方法ではないと思いますが。）
* ブラウザからリアルタイムで推論モデルを切り替えることができます。


## Tello からの Video Stream 受信

ポイントとなる Tello からの Video Stream 受信について Tello SDK のドキュメントには以下のようにあります。

>Receive Tello Video Stream Tello IP: 192.168.10.1 ->> PC/Mac/Mobile UDP Server: 0.0.0.0 UDP PORT:11111 Remark4: Set up a UDP server on PC, Mac or Mobile device and listen the message from IP 0.0.0.0 via UDP PORT 11111.

Telloから Video Stream を受信するには "streamon" コマンドを送信した後、 UDP port 11111 で ストリーミングを Listen する必要があります。

OpenCV で Tello からの Video Streaming を受信するコードの例です。"cv2.VideoCapture()" に Streaming URL を指定します。"cv2.VideoCapture('udp://127.0.0.1:11111')" が実行されるタイミングで、 PC は UDP 0.0.0.0 :11111 で Listen します。

```py:camera.py
class VideoCamera(object):
    def __init__(self, socket, algorithm, target_color, is_stream, is_test,
                 speed, detections):

        # Receive Tello's video streaming.
        # Tello sends video stream to your pc using udp port 11111
        self.cap = cv2.VideoCapture('udp://127.0.0.1:11111')
```

UDP Socketを作って bind する必要があると思ったのですが、明示的な bind の記述は必要なく以下のコードで動作します。実はよく分かっていない部分です。OpenCVのドキュメントを見ても この記述で 0.0.0.0 (All local addresses)  で bind する仕組みは見つけられませんでした。

※ Streaming が受信できない場合は、クライアントファイアウォールを確認してみてください。



# まとめ

私のデモのやり方が悪く推論の結果は満足のいくものではありませんでしたが、エッジデバイス（ドローン）とは別の計算リソースでリアルタイム推論を行うことができました。非力な Raspberry Pi Zero W でも カメラの映像を Video Streaming で PC に送れば 同じように推論処理ができます。

また、構成としては Web べースのアプリケーションにすることで、計算デバイスは Web/AP サーバーとなり、他の PC やタブレット、スマートフォンからも接続することが可能です。エッジデバイスの計算リソースが足りない場合の代替の手段となり得るのではないでしょうか。