Django + Dockerで簡単な画像処理apiを作ってみた。

# Django + Dockerで簡単な画像処理apiを作ってみた。

&nbsp;
※ この記事は **[エキサイト Advent Calendar 2017](https://qiita.com/advent-calendar/2017/excite)** 16 日目の記事です ※

あいさつ
---

はじめまして [**エキサイト**](https://www.excite.co.jp/) のニュース技術secでエンジニアをしている永野と申します。
普段はphpをメインに[**エキサイトニュース**](https://www.excite.co.jp/News)と[**ローリエプレス**](https://laurier.press/ )などのサービス運用のサーバーサイドからフロントエンドまで何でもやっています。

[<img src ="http://image.excite.co.jp/jp/corp/logo_excitenews.gif" width=200>](https://www.excite.co.jp/News)   


 [ <img src ="https://s.eximg.jp/laurier/logo/smartnews.png" width=200> ](https://laurier.press/ )



はじめに
---
最近業務で画像処理を使ったapiを作る機会がありまして、その勉強とアルゴリズムの検証ついでに簡単なapiを作りました。また使用するライブラリとしてopencvとdlibのものを比較しました。入力画像から顔認識したところを矩形で囲って表示する簡単なものです。

例えば画像処理でおなじみのレナさんの場合だとこんな感じです。
### 出力画像
![ダウンロード.jpeg](https://qiita-image-store.s3.amazonaws.com/0/152938/0f4b0931-7224-2375-46de-4e22bd59460b.jpeg)

構成と目標
---
今回は以下の構成で開発環境をつくりました。
今回作成したサンプルプログラムは[こちら](https://github.com/glassmonkey/django-image-sample)からダウンロード出来ます。
 
*  python + django
    * 画像処理なのでpythonかc++か迷いましたが、c++は保守が辛そうなので今回は除外
*  docker　+ docker-compose
    *  今後のことを考えてコンテナベースな環境にチャレンジしたかった

apiなのでnginxでリバースプロキシをした方がそれっぽいかなとも思ったのですが、

* 画像処理の環境をdockerを使ってサクッと作る
* アルゴリズムの検証があくまで目的

としたので今回は省略しました。

ちなみに、pythonもdockerも共に歴2週間ぐらいなのでコードなど汚いことがあると思いますがご容赦ください。

環境構築
---
環境構築と言ってもdockerとdocker-composeのインストールのみです。dockerは楽で良いですね(小並感)
プライベートの個人用macと会社のubuntu16.04にインストールして検証したので記載します。ちなみにubuntuはExciteのOpenstack環境でやってみました。

| OS  | cpu | RAM |
|:-:|:-:|:-:|
|  mac(Elcapitan(10.11.6)   |  2.7 GHz Intel Core i5 |  8GB |
|  ubuntu(ubuntu16.04 ) | 仮想CPU8コア  | 8GB  |



* 執筆時のdockerとdocker-composeは以下のバージョンです。
  * Docker `17.09.0-ce, build afdb6d4`
  * docker-compose `1.16.1, build 6d1ac21`

### mac編導入
公式の[ Install Docker for Mac ] ( https://docs.docker.com/docker-for-mac/install/ )から落としてきます。私は安定版としてリリースされている` Stable channel`を選びました。
インストールが完了したらCLIから

```shell
$ docker run hello-world
```

と入力した後に


```shell
Hello from Docker!
This message shows that your installation appears to be working correctly.
…
```

のような表示がされればインストール完了です。

### ubuntu編導入
mac同様に公式のリファレンスを参考にしました。[ Get Docker CE for Ubuntu ] ( https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/#prerequisites/ )
また、ExciteのOpenstack環境はプロキシ環境なのでそれ用の設定を追加してあります。

* プロキシの設定(例）

```shell
$ export http_proxy=http://proxy.example.com:80
$ export https_proxy=https://proxy.example.com:443
```

* パッケージの更新

```shell
$ sudo apt-get update
```

* 必要パッケージのインストール

```shell
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common
```

* GPG鍵の入手

```shell
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-
key add -
```

* Docker本体のインストール

```shell
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
```

* Docker-composeのインストール

```shell
$ sudo curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
```

* Docker用プロキシ設定
httpとhttps用の設定ファイルをそれぞれ作成する。上記で設定したプロキシに対応しているので読み替えてください。


```etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
```

```etc/systemd/system/docker.service.d/https-proxy.conf
[Service]
Environment="HTTPS_PROXY=https://proxy.example.com:443"
```

* mac 同様にインストールが完了したらCLIから

```shell
$ docker run hello-world
```

と入力した後に


```shell
Hello from Docker!
This message shows that your installation appears to be working correctly.
…
```
と表示されたらインストール完了です。


apiの実行
---

* apiの実行にあたって事前に作っておいた [サンプルプログラム](https://github.com/glassmonkey/django-image-sample)をcloneします。

```bash

$ git clone https://github.com/glassmonkey/django-image-sample
```

* プロジェクトディレクトリに移動します。

```shell
$ cd django-image-sample
```

* 実行します。

```shell
$ docker-compose up
```
http://localhost:8000/face?i=画像URLをエンコードしたもの 
にアクセスにアクセスできていれば無事に動いています。

実行時にハマったところ (in proxy環境）
---

docker-comopse-ymlは以下のように記載してますが

```dockerfile

version: '3'
services:
  app:
     build: .
     command: "python /var/www/manage.py runserver 0.0.0.0:8000"
     ports:
       - "8000:8000"
     volumes:
       - ".:/var/www"
    ```
Exciteのプロキシ環境ではデフォルトのネットワークモード（`bredge`)では動かなかったので、

```dockerfile

version: '3'
services:
  app:
     #build: .
     image: app:latest #事前にイメージをbuildしておく
     command: "python /var/www/manage.py runserver 0.0.0.0:8000"
     ports:
       - "8000:8000"
     volumes:
       - ".:/var/www"
     network_mode: "host"```
```

のように記述がapp以下に`net_workmode`で`host`追記する必要がありました。
ちなみにビルドはどうやってもネットワークモードがbredgeのままだったので
proxy環境下では

* dockerコマンドで明示的にnetをhostでそれぞれをビルド
    * `$ docker build --tag app:latest --net host .`
* docker-composeで上記のコマンド等でビルドしたイメージをrunして使うのが良さそうです。　



検証
---
簡単にですが、顔認識でpythonで導入が楽そうなopencvとdlibの2種類のライブラリを検討しました。
上記でapiを実行中の場合は[こちら](http://localhost:8000/face/?i=https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F152938%2F0b5804b5-5521-48c4-420f-0df462648e48.jpeg)を参照してみてください。それぞれのアルゴリズムで認識した領域を示します。ちなみにdjangoのview部分に画像処理コードを記載するようにしてあります。
djangoでの構成に関しては今回は割愛します。

### 画像処理コード一部抜粋
```django-smaple/face/views/Face.py
 def process(self, image):
    img_array = np.asarray(bytearray(image), dtype=np.uint8)
    img = self.decode(img_array)
    img = self.detect(img)
    img = self.cv_detect(img)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    return Image.fromarray(img)
#dlib
  def detect(self, img):
    detector = dlib.get_frontal_face_detector()
    dets, scores, idx = detector.run(img, 0)
    for i, rect in enumerate(dets):
        cv2.rectangle(img, (rect.left(), rect.top()), (rect.right(), rect.bottom()), (0,0,255), thickness=5)
    return img
# OpenCV
  def cv_detect(self, img):
      cascade_fn = "/var/www/haarcascade/haarcascade_frontalface_alt.xml"
      cascade = cv2.CascadeClassifier(cascade_fn)
      facerect = cascade.detectMultiScale(img)
      for (x, y, w, h) in facerect:
          cv2.rectangle(img, (x, y), (x+w, y+h), (255,0,0), thickness=5)
      return img
```

### 入力画像
![IRIS16011412_TP_V (1).jpg](https://qiita-image-store.s3.amazonaws.com/0/152938/0b5804b5-5521-48c4-420f-0df462648e48.jpeg)


### 認識後

* opencv (Haar-like) ( `rgb(0,0,255)`青の矩形)
* dlib (HOG + SVM)（`rgb(255,0,0)`赤の矩形）

![ダウンロード (3).jpeg](https://qiita-image-store.s3.amazonaws.com/0/152938/2a3eadab-59a9-b0a9-3ce1-b6c45b6f94dc.jpeg)

どちらも全ての顔は認識出来ていませんが、精度的にはdlibのものが良さそうです。これは[dlibのアルゴリズム](https://www.pyimagesearch.com/2017/04/03/facial-landmarks-dlib-opencv-python/)が回転量に普遍に作られているからとのことです。この２つだとdlibを使う方針で良さそうです。



謝辞
---
また、プロキシ環境下のdocker導入にあたり相談にのっていただいた @y-takahashi-ex さんには多大なるご指導をいただきました。この場を借りてお礼を申し上げます。


宣伝
---
ローリエプレスは現在注力しているサービスのひとつでして、特に最近は本を出しましたので可愛い女子を知りたいって方は是非購入を検討してみてください!! 
[<img src="https://qiita-image-store.s3.amazonaws.com/0/152938/287adc14-5602-7294-ae8b-cfa31d2329d1.jpeg" width ="350" >] (http://amzn.to/2zEyORj)


さいごに
---
macでdocker導入することはそれなりに簡単でしたが、proxy環境で導入するのは結構ハードルが高くハマるポイントがこれからも多そうでした。わかる方いたら教えて下さい(他力本願）
画像処理環境自体は簡単に作ることができたので引き続きdockerを使って場所を選ばず開発していけたらと思います。
画像処理自体も数年前の大学時代の知識で止まっていたので久しぶり触れて楽しかったです。

**[エキサイト Advent Calendar 2017](https://qiita.com/advent-calendar/2017/excite) はまだまだ続きます.**
明日の記事は 「**Webのアクセシビリティ**」についてです。これからの社会を考えるとサービスの方向性を考える際に重要な概念ですよね。明日から使っていけそうで楽しみです。

参考文献
---
https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81
https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/#install-docker-ce 
https://qiita.com/tora470/items/cd9335f5ce638e11bb97
https://qiita.com/amedama/items/b856b2f30c2f38665701
http://vaaaaaanquish.hatenablog.com/entry/2016/08/15/193636
https://qiita.com/Kazuhito/items/837d03b6551373be332c
https://qiita.com/hitomatagi/items/04b1b26c1bc2e8081427
https://www.pyimagesearch.com