エッジデバイスでAIを現場に！　～Jupyter NotebookでDocker上のOpenVINOを試す～

# はじめに

### 概要

この記事では、日立のエッジ製品CE50-10Aを用いたAIアプリケーションの開発の初歩(Hello Worldレベル)の手順をご説明します。
この手順に従うことで、高度な知識が求められる学習モデルの作成や環境セットアップといった作業をスルーして、初歩的なAIアプリケーションの開発を体験することができます。

読者としては、あまりAIに馴染みのない方を想定しています。

### CE50-10Aとは

CE50-10Aは、工場や発電所のような産業・インフラ業界におけるAI活用を念頭に、日立が2021年に販売開始したエッジ製品です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/1c0feb41-13cb-9ae0-2725-9be7c1b9f6ad.png)
引用元：https://www.hitachi.co.jp/products/it/control_sys/platform/cs_edge/index.html

IntelのAI推論フレームワーク **OpenVINO** をプレインストールしており、すぐに開発を始めることができます。
また、粉塵や振動、多湿といった過酷な環境に耐えうる堅牢な構造により、幅広い現場で長期に渡る利用が可能です。


### OpenVINOとは

OpenVINOとは、Intelが提供するフリーのAI推論の開発フレームワークです。
Intel製のCPUやGPUで推論処理を高速に実行することができます。
OpenVINOは、C/C++やPythonから利用可能ですが、CE50-10AではPythonからの利用をサポートしています。


### CE50-10Aが提供する機能

CE50-10Aは、単にOpenVINOをプレインストールしているだけでなく、AIアプリケーションの開発／運用を効率よく・確実に行えるように複数の機能を提供しています。
具体的には、

* 複数の種類のカメラデバイスに対応し、アプリケーション側でその違いに対応する必要を無くす**データ入力機能**
* データをキャッシュして状態を管理する**データ管理機能**
* 現場に配備（=再頒布)する時問題となる、OSSや動画コーデックなどのライセンスに対応した**推論実行機能**
* OpenVINO上のアプリ開発を手軽に体験出来る**推論開発機能**

などです。
本記事では、「推論開発機能」について、実際に操作を体験していきます。

# 推論開発機能を使ってみる

推論開発機能は、Dockerコンテナ上にセットアップされたOpenVINOとJupyter Notebookを提供するものです。
Jupyter Notebookは、PythonなどをWebブラウザ上で記述・実行出来る開発環境です。
入力したプログラムをすぐに実行したり、実行結果を確認できるので非常に便利です。


## この章で出来ること

監視カメラの映像に映っている自動車の位置を検出してみます。


![car-detection.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/4a068a8c-6c1e-bf49-d288-0e9c80a54a9b.gif)

引用元：https://github.com/intel-iot-devkit/sample-videos/blob/master/preview/car-detection.gif

結果がこのように表示されます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/9bc9319d-98ca-7a2b-2a51-af737aa8cc60.png)

これを応用すれば、例えば、

* 通過した自動車の台数をカウントしたい
* 自動車が写っているときだけ録画したい

といったユースケースで使うことができるでしょう。

## 準備するもの

以下のファイルをダウンロードしておきます。

* サンプル動画ファイル  
入力となる動画ファイルです。  
https://github.com/intel-iot-devkit/sample-videos/blob/master/car-detection.mp4
* 学習済みモデルファイル  
自動車を検出できるように、あらかじめ学習させたニューラルネットワークのファイルです。  
IntelはOpen Model Zooというサイトで様々な学習済みモデルファイルを配布しています。  
https://download.01.org/opencv/2020/openvinotoolkit/2020.3/open_model_zoo/models_bin/1/vehicle-detection-adas-0002/FP32/

## 環境のセットアップ

CE50-10Aと操作用のPCを同じネットワークに接続します。
また、Dockerが利用可能なようにセットアップしておきます。セットアップ方法は[ユーザーズガイド](https://www.hitachi.co.jp/products/it/control_sys/platform/cs_edge/dl_files/ce50-10a_cc-5-0179.pdf)の3章を参照してください。

## Dockerコンテナの起動

推論実行機能はDockerコンテナとして実行されます。
以下のコマンドを実行します。

```bash
$ sudo docker run -it --rm -p 8888:8888 ctrl_edge_ai/openvino_devel:1.0
```

以下のようにURLが表示されます。

```
    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://0.0.0.0:8888/?token=66b5d8ca82cac0134b3af02709acb1cf8d521ec17e6016da
```

このURLの`0.0.0.0`を、CE50-10Aに設定したIPアドレスに置き換えて、作業用PCのブラウザで開きます。

以下のようにJupyter Notebookの画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/9b8cbe96-be34-8622-b15e-b60964e1d138.png)




## ファイルのアップロード

「準備するもの」でダウンロードしたファイルをアップロードしていきます。

[Upload]をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/8ad64fdd-2eb4-a5c0-1e3f-05581d909f04.png)

ファイル選択ダイアログが表示されたら、アップロードするファイルを開きます。
以下のようにファイル名が表示されるので、表示されたファイル名の右にある[Upload]をクリックすると、ファイルがアップロードされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/8625eba0-c55a-d8f8-de0a-0e9c06cf6dad.png)



## Notebookの作成

[New]をクリックし、[Python3]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/36639cdc-0f49-414f-bf9a-202c78cf146b.png)

以下のようにNotebookが開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/85653a10-1f3b-6c52-65c3-b49d1ab480ff.png)

## プログラムの入力と実行

いよいよプログラムの入力と実行をしていきます。

### ライブラリのインポート

まずは使用するライブラリをインポートします。

```python
import cv2
import matplotlib.pyplot as plt
from openvino.inference_engine import IECore
import numpy as np
```

上のコードをNotebookにコピー＆ペーストして、Shift+Enterを実行します。すると、入力したプログラムが実行されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/fd4a9ca8-7aed-c2dd-0039-717d757f4be8.png)

以降も同様に
(1)プログラムをコピー&ペースト
(2)Shift+Enterでプログラムを実行
を実施してください。

### 動画ファイルのオープン

動画ファイルを読み込みます。
2行目では、動画の200コマ目から画像を読み出すよう設定しています。自動車が映っているタイミングを選ぶためです。

```python
cap=cv2.VideoCapture("car-detection.mp4")
cap.set(cv2.CAP_PROP_POS_FRAMES,200)
```

### 画像の読み出しとプレビュー

読み込んだ動画の画像をJupyter Notebook上で確認してみます。

```python
ret,img=cap.read()
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
```

以下のようにJupyter Notebook上に画像が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/ce4f000b-5460-c6cb-d1a0-a8b3cc361769.png)



### 学習済みモデルの読み込み

学習済みモデルのファイルを読み込みます。
OpenVINOでニューラルネットワークの推論を行うInference Engineのインスタンスを生成して、
そこにモデルファイルを読み込ませます。
load_network関数のパラメータで、推論を実行するデバイスを指定します。
ここでは"CPU"としていますが、ここを"GPU"にするだけでGPUで実行できます[^1]。

[^1]: 実際にはコンテナ上のプログラムがGPUにアクセスできるようにDockerの設定が必要です。

```python
model_xml="vehicle-detection-adas-0002.xml"
model_bin="vehicle-detection-adas-0002.bin"
ie=IECore()
net=ie.read_network(model=model_xml,weights = model_bin)
exec_net=ie.load_network(network=net,device_name="CPU")
```

### 画像の変換

取得した画像を推論できるように変換します。

```python
n, c, h,w = net.inputs['data'].shape
ih, iw = img.shape[:-1]
input_img = cv2.resize(img,(w,h))
input_img=input_img.transpose((2,0,1))
data={}
data['data']=input_img
```

### 推論を実行

いよいよ推論を実行します。
学習済みモデルを読み込んだInference Engineのインスタンスに、変換した画像データを入力すると、結果が出力されて変数resに格納されます。


```python
res=exec_net.infer(inputs=data)
```

### 推論結果を確認

推論結果を表示させてみます。

```python
res['detection_out'][0][0][0]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/3e2366f1-042a-743b-5194-46259b82688c.png)

この数値が、画像上で自動車が存在する場所の座標などの情報を示しています。


### 推論結果を表示

このままではよくわからないので、推論結果を表示させてみます。
推論結果は、自動車のある位置の左上・右下のそれぞれの位置を画像の幅・高さを1とした割合で示しています。
forにより推論結果から、1件ずつ自動車のある位置を取り出し、OpenCVのrectangle関数で、その座標に白い枠を描画しています[^2]。

[^2]: 実際には、座標に加えて(1)バッチ処理時の画像ID(2)画像のクラス(3)confidence(推論結果の確からしさ）が返されます。プログラムを単純化するための省略していますが、閾値未満のconfidenceの座標は描画しないといった処理の検討が必要です。

```python
for number, proposal in enumerate(res['detection_out'][0][0]):
    xmin = np.int(iw * proposal[3])
    ymin = np.int(ih * proposal[4])
    xmax = np.int(iw * proposal[5])
    ymax = np.int(ih * proposal[6])
    img = cv2.rectangle(img, (xmin,ymin),(xmax,ymax),(255,255,255),2)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2186837/0ef88af4-e13a-b41a-6654-b9b64472f86c.png)

自動車の位置を囲んで表示することができました。


# まとめ

CE50-10Aに搭載された推論開発機能を用いて、Docker上に構築したOpenVINOをJupyter Notebookで操作する手順を示しました。
上記に沿って実施することで、初歩的なAIアプリケーションの開発を体験することができます。

CE50-10Aについての詳しい情報は以下のサイトをご覧ください。
https://www.hitachi.co.jp/products/it/control_sys/platform/cs_edge/index.html


# 免責・商標類

* 本記事に記載した手順は、その動作や結果を保証するものではありません。
* 製品の改良により、予告なく記載されている仕様が変更になる場合があります。
* 本記事に記載の会社名、製品名などは、それぞれの会社の商標もしくは登録商標です。