誰でも出来る！DeepLab v3+でGPUを使って自作データセットで学習・推論する

# はじめに

DeepLab v3+はセマンティックセグメンテーションのための最先端のモデルです。
この記事では、DeepLab v3+の[github](https://github.com/tensorflow/models/tree/master/research/deeplab)を使って、公開されたデータセットまたは自分で用意したデータセットで学習・推論までをおこなう方法を紹介します。  

DeepLabv3+をやってみたという記事は検索すれば多く見つかります。
しかし、どれもこれも、結局どうやってデータ準備したの？とか、手順を省略しすぎて分からん！というものが多かった・・・。
今回の記事では、全ての手順を丁寧に説明しながら誰でもできるように紹介していきたいと思います。
※誰でもと書きましたが、tensorflowとGPUが絡む部分は各自で気を付けて頂かなければ失敗します。

# 環境構築
OSは **Windows10** が対象です。
とはいえLinuxでもコマンドの書き方などを変えれば動くと思います。

学習にはGPUを使います。
tensorflowでGPUを使用するときに気をつけなければならないことは、
python, CUDA, cuDNN, tensorflowのバージョンをそれぞれ対応するものに合わせなければならないことです。
これらのバージョンは思った以上にセンシティブなので、ちょっとくらい違うバージョンでもいいだろ、と思ってると面倒なことになります。
Anaconda, CUDA, cuDNN, tensorflowのインストールは検索すれば多くの記事があるので、ここでは割愛します。

## tensorflow+GPUで成功した環境
|  項目  |  バージョン等  |
| ---- | ---- |
|  OS  |  Windows10  |
|  GPU  |  RTX2060  |
|  GPUのドライバー  |  436.48  |
|  python  |  Python 3.6.9 :: Anaconda, Inc.  |
|  CUDA  |  CUDA Toolkit 9.0  |
|  cuDNN  |  v7.4.1.5  |
|  tensorflow_gpu  |  v1.13.1  |
|  keras  |  2.3.1  |

似たような記事を色々調べた結果、
- python=3.6
- CUDA=9.0
- cuDNN=7.4
- tensorflow=1.13

が一番安定してる？気がします (2019年10月20日時点)。

## pythonの環境構築

環境構築方法は「python Anaconda」で検索してください。

```
conda create -n deeplab python=3.6
conda activate deeplab
pip install tensorflow-gpu==1.13.1
pip install pillow
pip install matplotlib
pip install opencv-python
```

## CUDAの環境確認方法

環境構築方法は「Windows CUDA 環境構築」で検索してください。

```
$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2017 NVIDIA Corporation
Built on Fri_Sep__1_21:08:32_Central_Daylight_Time_2017
Cuda compilation tools, release 9.0, V9.0.176
```

## cuDNNの環境確認方法

環境構築方法は「Windows cuDNN 環境構築」で検索してください。

![cuDNN.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96785/f8ad971a-85ba-0079-dc66-affca0260680.png)

```
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\include\cudnn.h

#define CUDNN_MAJOR 7
#define CUDNN_MINOR 4
#define CUDNN_PATCHLEVEL 1

```



# 学習データセットの準備
今回は以下の2通りの方法を紹介します。

- `(A) Pascal VOCデータセットで学習する場合`
- `(B) 自作データセットで学習する場合`

Pascal VOCデータセットは一般に公開されているsemantic segmentationの有名なデータセットです。

## (A) Pascal VOCデータセットで学習する場合
Pascal VOCデータセットを[こちら](http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar)からダウンロードする。
tar形式であるため、7-Zip等をインストールして解凍してください。

展開すると以下のようなディレクトリ構成となっています。

```
VOCdevkit
  └ VOC2012
      ├ ImageSets
      ├ JPEGImages
      ├ SegmentationClass
      └ SegmentationObject
```

- ImageSets: 学習/検証用データのリストが格納されている
- JPEGImages: 学習データがjpg形式で格納されている
- SegmentationClass: インデックスカラーでsemantic segmentationされたデータ
- SegmentationObject: インデックスカラーでinstance segmentationされたデータ(今回は使いません)

完了。

## (B) 自作データセットで学習する場合
### 画像データの収集
まずは元となる画像を収集します。
例えば、犬や猫の画像をネット上から収集してきましょう。

icrawlerを使うとキーワードで自動収集可能です。
https://github.com/hellock/icrawler

### labelmeの概要
今回はアノテーションツール「**labelme**」を使ってアノテーション情報を作成します。
githubは[こちら](https://github.com/wkentaro/labelme)
labelmeはセマンティックセグメンテーションのアノテーションツールです。
labelmeを使ってデータをアノテーションし、labelmeの形式変換機能を使ってPascalVOC形式に変換します。
![annotation.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96785/c5327a42-5103-bf4f-5886-d1efea3eba3e.jpeg)


### labelmeのインストール
Windows10でAnaconda環境の方は以下を実行してください。

```bash
conda install pillow=4.0.0
pip install labelme
```
それ以外の方はgithubのインストール方法に従ってください。
あとは、コマンドプロンプトで以下を実行すればlabelmeが起動します。

```bash
labelme.exe
```

### データのアノテーション
labelmeの使い方は[こちら](https://qiita.com/kouki_outstand/items/7b7597f271beca0051e5)を参考にしてください。
画像データのアノテーションを行うと、例えば以下のような`dataset`ディレクトリ構成になります。

```
dataset/
　├ 001.jpg
　├ 001.json
　├ 002.jpg
　├ 002.json
　├ 003.jpg
　└ 003.json
```
jpg画像に対してアノテーション情報jsonが付与されています。

次に、ラベル情報を書いたファイル`labels.txt`を作成します。
例えば、[dog、cat、hourse]の3種類をsegmentationする場合、以下のようなファイルになります。

```:labels.txt
__ignore__
_background_
dog
cat
hourse
```
以下の2つは必ず入れてください。

- \_\_ignore\_\_: 無視ラベル
- \_background\_: 背景ラベル

### PascalVOCデータセットの形式に変換
git cloneでlabelmeのソースを入手します。

```bash
git clone https://github.com/wkentaro/labelme
```

次に、`labelme\examples\semantic_segmentation\labelme2voc.py`を使ってアノテーション済データセットをPascalVOC形式に変換します。
以下を実行する。

```bash
python labelme2voc.py "dataset" "VOC2012" --labels labels.txt
```

labelme2voc.py [input_dir] [output_dir] --labels [labels]

- input_dir: labelmeでアノテーションした画像とjsonセットのディレクトリへのパス
- output_dir: 出力ディレクトリへのパス
- labels: ラベル情報テキストへのパス

実行後、以下のような以下のようなディレクトリが作成される。

```
VOC2012/
　├ JPEGImages
　├ SegmentationClass
　├ SegmentationClassPNG
　├ SegmentationClassVisualization
　└ class_names.txt
```

実行後のディレクトリ名はPascalVOCとは異なるため、修正する。
以下のようにフォルダ名変更する。

```
SegmentationClass => SegmentationClass_npy(使わないので適当な名前でよい)
SegmentationClassPNG => SegmentationClass
```

### 学習/検証データリストの作成

VOC2012ディレクトリ直下に以下のようにディレクトリ・ファイル作成

```
mkdir VOC2012/ImageSets/Segmentation

VOC2012/ImageSets/Segmentation/train.txt
VOC2012/ImageSets/Segmentation/trainval.txt
VOC2012/ImageSets/Segmentation/val.txt
```

ここで、学習データ数、検証データ数を記述します。
例えば、001.jpg, 002.jpg, 003.jpgがあった時、
学習データ=001.jpg, 002.jpg
検証データ=003.jpg
とする場合は以下のように記述する。

train.txtには学習データのリストを記述する。
※ファイル名のみ記述

```:train.txt
001
002
```

val.txtには検証データのリストを記述する。

```:val.txt
003
```

trainval.txtには学習+検証データのリストを記述する。

```:trainval.txt
001
002
003
```

最終的に、以下のようなディレクトリ構成になり、自作データセットの準備は完了。

```
VOC2012/
├─class_names.txt
│
├─ImageSets
│  └─Segmentation
│          train.txt
│          trainval.txt
│          val.txt
│
├─JPEGImages
│      001.jpg
│      002.jpg
│      003.jpg
│
├─SegmentationClass
│      001.png
│      002.png
│      003.png
│
├─SegmentationClassVisualization
│      001.jpg
│      002.jpg
│      003.jpg
│
└─SegmentationClass_npy
        001.npy
        002.npy
        003.npy
```

## Pascal VOC形式のデータセットをTFRecordに変換
これまで、以下の2通りのデータセットの準備方法を説明しましたが、

- (A) Pascal VOCデータセットで学習する場合
- (B) 自作データセットで学習する場合

どちらの方法でも最終的に`VOC2012`ディレクトリが作成されたと思います。
以下では、この`VOC2012`を`TFRecord`に変換します。

### deeplabのソース入手とディレクトリ構築
以下を実行

```bash

git clone https://github.com/tensorflow/models
cd models/research/deeplab/datasets
mkdir pascal_voc_seg/VOCdevkit
```

pascal_voc_seg/VOCdevkitの下に`VOC2012`を配置

```
pascal_voc_seg/
    └ VOCdevkit/
        └ VOC2012/
```

### SegmentationClassRawを生成
以下を実行

```bash
python ./remove_gt_colormap.py --original_gt_folder="./pascal_voc_seg/VOCdevkit/VOC2012/SegmentationClass" --output_dir="./pascal_voc_seg/VOCdevkit/VOC2012/SegmentationClassRaw"
```

実行後、特に表示はありませんが「SegmentationClassRaw」ディレクトリが作成されます。

余談
「SegmentationClass」にある画像は、インデックスカラーでセグメンテーションされています。
「SegmentationClassRaw」にある画像は「SegmentationClass」をグレースケール画像（RGBそれぞれに同じインデックス値を設定）に変換したものです。
 `(B) 自作データセットで学習する場合`では「SegmentationClassRaw」は真っ黒な画像が生成されるかもしれませんが、よく見るとセグメンテーション部分が見えます。
SegmentationClassRawにbackground=0, dog=1, cat=2, ...のようにグレイスケール値が割当てられると思います。

### TFRecordを生成
以下を実行

```bash
mkdir pascal_voc_seg/tfrecord
python ./build_voc2012_data.py --image_folder="./pascal_voc_seg/VOCdevkit/VOC2012/JPEGImages" --semantic_segmentation_folder="./pascal_voc_seg/VOCdevkit/VOC2012/SegmentationClassRaw" --list_folder="./pascal_voc_seg/VOCdevkit/VOC2012/ImageSets/Segmentation" --image_format="jpg" --output_dir="./pascal_voc_seg/tfrecord/"
```

実行後、tfrecordディレクトリにTFRecordデータセットが出力される。

# 学習部
## ディレクトリ構築
以下を実行してdeeplab公式が推奨しているディレクトリ構築をおこないます。

```bash
cd models/research/deeplab
mkdir ./datasets/pascal_voc_seg/init_models
```
**※以下ではこのディレクトリ`models/research/deeplab`をカレントディレクトリとして説明しています。**

## 事前学習済モデルの入手
いわゆる転移学習(fine tuning)というやつです。
既に学習済みのモデルを使うことで、少ないデータ数で短時間でも高い精度を得られます。
事前学習済みモデルデータを以下からダウンロード
http://download.tensorflow.org/models/deeplabv3_pascal_train_aug_2018_01_04.tar.gz
解凍して出来た`deeplabv3_pascal_train_aug`を以下に置く

```
models/research/deeplab/datasets/pascal_voc_seg/init_models/deeplabv3_pascal_train_aug/
```

## 学習/検証データ設定の変更

### ***注意(2019/10/22) segmentation_dataset.pyは非推奨になりました***
~~`models/research/deeplab/deprecated/segmentation_dataset.py`を開く~~
多くの関連記事で、segmentation_dataset.pyを編集するように紹介されていますが、これは非推奨になっています。
最新版のdeeplabv3ではそもそもsegmentation_dataset.pyがimportされていないので、値を書き換えても何の意味もないです。

正しくは、
`models/research/deeplab/datasets/data_generator.py`
を開きます。


data_generator.pyには以下のようにPascalVOCデータセットの設定が記述されている。

```python:data_generator.py
_PASCAL_VOC_SEG_INFORMATION = DatasetDescriptor(
    splits_to_sizes={
        'train': 1464,
        'train_aug': 10582,
        'trainval': 2913,
        'val': 1449,
    },
    num_classes=21,
    ignore_label=255,
)
```

`(A) Pascal VOCデータセットで学習する場合`では、特に書き換えは必要ない。
`(B) 自作データセットで学習する場合`では、下記を参考に`train`, `trainval`, `val`を書き換える必要がある。

|  項目  |  内容  |
| ---- | ---- |
|  'train'  |  学習データの数 <br>(VOC2012/ImageSets/Segmentation/train.txtの数)  |
|  'trainval'  |  学習+検証データの数 <br>(VOC2012/ImageSets/Segmentation/trainval.txtの数)  |
|  'val'  |  検証データの数 <br>(VOC2012/ImageSets/Segmentation/val.txtの数)   |
|  'train_aug'  |  image augumentation用データの数、論文ではこれを使ってたのでその名残？無視して良い  |
|  num_classes  |  分類クラス数  <br>background(1) + 分類クラス数。 <br> しかし、今回は転移学習を使うので、`(B) 自作データセットで学習する場合`であっても**21クラスに固定してください**|
|  ignore_label  |  無視ラベルの画素値 <br> この画素値のデータは学習時に一切考慮されなくなります。 <br> `(A) Pascal VOCデータセットで学習する場合`ではSegmentationClassRawに白枠線が見えますが、255というのはこの白枠を指しています。<br> `(B) 自作データセットで学習する場合`では白値はありませんが、255で問題ありません。 |

結果として、`(B) 自作データセットで学習する場合`で[dog, cat, hourse]の3クラスのsegmentationを行う場合は、例えば以下のように記述します。
**注意：本来は[_background_, dog, cat, hourse]の4クラス分類になりますが、転移学習を使うので21クラスのままでOKです**
元のモデルの分類のindex=1のaeroplaneをdogで上書き学習、index=2のbicycleをcatで上書き学習するというイメージです。

```python:data_generator.py
_PASCAL_VOC_SEG_INFORMATION = DatasetDescriptor(
    splits_to_sizes={
        'train': 200,
        'trainval': 300,
        'val': 100,
    },
    num_classes=21, # fix 21 class
    ignore_label=255,
)
```

## パスを通す
### パスの追加
「deeplab」と「slim」のモジュールを使用するため、PATHを設定する必要がある。
[システムの詳細設定]->[環境変数]->[システム環境変数]の[PYTHONPATH]に以下を追加する。

```
C:\Users\xxxx\Desktop\models\research
C:\Users\xxxx\Desktop\models\research\slim
```
- PYTHONPATHが無ければ新しく作る
- フルパスで追加

設定後、**PCを再起動**すると適用される。


### デバッグ
ここで以下のエラー発生

```
ModuleNotFoundError: No module named 'nets'
```
models\research\slimをパスに追加していれば問題ないと思ったのだが
仕方がないので、
`models\research\slim\nets`
を
`models\research\deeplab`
の直下にコピーする

### PYTHONPATHの確認
以下を実行すると、正しくPATHの設定が出来ているかどうかを確認できる。

```
python model_test.py
```


* 2020/12/21更新
    * -vコマンドを付けるとエラーが出るようです。


実行後、以下のように表示されれば、問題ない。

```
Ran 5 tests in 12.709s

OK (skipped=1)
```

## 学習部
### 学習の実行
以下を実行

```
python train.py --logtostderr --training_number_of_steps=300 --train_split="train" --model_variant="xception_65" --atrous_rates=6 --atrous_rates=12 --atrous_rates=18 --output_stride=16 --decoder_output_stride=4 --train_crop_size="513,513" --train_batch_size=1 --dataset="pascal_voc_seg" --tf_initial_checkpoint="./datasets/pascal_voc_seg/init_models/deeplabv3_pascal_train_aug/model.ckpt" --train_logdir="./datasets/pascal_voc_seg/exp/train_on_trainval_set/train" --dataset_dir="./datasets/pascal_voc_seg/tfrecord" --fine_tune_batch_norm=false --initialize_last_layer=true --last_layers_contain_logits_only=false
```

|  引数名  |  意味  | 値(例) |
| ---- | ---- | ---- |
|  --logtostderr  |  ログを標準エラー出力へ出力  | － |
|  --training_number_of_steps  |  学習回数  | 300 <br> ※お試し程度で300にしていますが、実際はもっと増やしてください |
|  --train_split  |  使用データ  | [train, val, trailval] |
|  --model_variant  |  識別モデル種類  | [xception_65, mobilenet_v2] |
|  --atrous_rates  |  Atrous畳み込みの比率<br>※複数回設定可能  | 6, 12, 18 |
|  --output_stride  |  出力ストライド（atrous_rateとの組み合わせ）  | 16 |
|  --decoder_output_stride  |  入出力の空間解像度の比率  | 4 |
|  --train_crop_size  |  画像の切り出しサイズ  | "513,513" (width, height)<br>※「train_crop_size」は「output_stride*k+1 (k>=1)」の値を指定してください。 |
|  --train_batch_size  |  ミニバッチサイズ	  | 1 |
|  --dataset  |  データセット名  | [cityscapes, pascal_voc_seg, ade20k] |
| --tf_initial_checkpoint | 学習済みモデルへのパス | "./datasets/pascal_voc_seg/init_models/deeplabv3_pascal_train_aug/model.ckpt" <br> ※ファイルへのパスではないので、該当ファイルが無くても問題ない|
| --train_logdir | 学習ログ出力フォルダへのパス	 | "./datasets/pascal_voc_seg/exp/train_on_trainval_set/train" |
| --dataset_dir | tfrecordデータセットフォルダへのパス | "./datasets/pascal_voc_seg/tfrecord" |
| --fine_tune_batch_norm | Batch Normalizationの実行 | [true, false] <br>※GPUで学習するときはfalse |
| --initialize_last_layer | 最後のレイヤーの初期化 | true |

実際にはもっと多くの引数があります。
`train.py`のコードを参照してください。

### デバッグ
#### GPUメモリエラー
以下のようなエラーが出た場合、GPUメモリが足りていません。

```
ResourceExhaustedError (see above for traceback): OOM when allocating tensor wit
h shape[1,64,257,257] and type float on /job:localhost/replica:0/task:0/device:G
PU:0 by allocator GPU_0_bfc
```
`train_batch_size`、`train_crop_size`を小さくしてみてください。

### 学習ログの出力
以下に設定したディレクトリに学習結果ログが出力される。
`--train_logdir="./datasets/pascal_voc_seg/exp/train_on_trainval_set/train"`
TensorFlow saveメソッドは以下の4種類のファイルを保存します。
- checkpoint
- model.ckpt-16272.data-00000-of-00001
- model.ckpt-16272.index
- model.ckpt-16272.meta

|  ファイル  |  意味  |
| ---- | ---- |
| checkpoint | 学習の途中記録 |
| .data-????-of-???? | モデルの重みの断片(複数) |
| .index | どの重みがどの断片に保存されているかを示すインデックスファイル |
| .meta | 保存されたグラフ構造が記述されている |

### tensorboardで学習進捗の確認
以下を実行してtensorboardの起動

```
pip install tensorboard
tensorboard --logdir="./datasets/pascal_voc_seg/exp/train_on_trainval_set"
```
ブラウザで`http://localhost:6006`にアクセス

## モデルのエクスポート
学習済みのモデルをProtocol Buffer(.pbファイル)で出力します。
.pbファイルはAndroid, iOS等で使う場合に必要になるようです。

以下を実行します。

```
python export_model.py --checkpoint_path="./datasets/pascal_voc_seg/exp/train_on_trainval_set/train/model.ckpt-300" --export_path="./datasets/pascal_voc_seg/exp/train_on_trainval_set/export/frozen_inference_graph.pb" --num_classes=21 --model_variant="xception_65" --atrous_rates=6 --atrous_rates=12 --atrous_rates=18 --output_stride=16 --decoder_output_stride=4
```

--checkpoint_pathは任意の学習回数のモデルを指定してください。
--num_classes=21について、***学習時のクラス数とは関係なしに「21」に固定しておいてください。***
理由としては、事前学習モデルとして学習部において`--tf_initial_checkpoint="./datasets/pascal_voc_seg/init_models/deeplabv3_pascal_train_aug/model.ckpt"`を使っており、このモデルは21クラス分類であるためです。
学習時のクラス数と初期モデルのクラス数が違っても問題ないので、安心してください。

実行後、--export_pathで指定したフォルダに`frozen_inference_graph.pb`が出力されると思います。

## エクスポートしたモデルで推論
### 概要
エクスポートした`frozen_inference_graph.pb`を使って推論を行います。
ここでは、使いやすいように画像とモデルのパスを指定して推論を行うプログラムを紹介します。
基本的には下記を参照して作っています。
https://github.com/tensorflow/models/blob/master/research/deeplab/deeplab_demo.ipynb

### 画像とモデルを指定して推論と結果表示を行う
以下のプログラムを使用してください。
img_pathに推論する画像を、model_pathに`frozen_inference_graph.pb`を指定します。

```python:prediction.py
# coding: utf-8

import os
from io import BytesIO
import tarfile
import tempfile
from six.moves import urllib

from matplotlib import gridspec
from matplotlib import pyplot as plt
import numpy as np
from PIL import Image

import tensorflow as tf


class DeepLabModel(object):
    INPUT_TENSOR_NAME = 'ImageTensor:0'
    OUTPUT_TENSOR_NAME = 'SemanticPredictions:0'
    INPUT_SIZE = 513
    FROZEN_GRAPH_NAME = 'frozen_inference_graph'

    def __init__(self, frozen_path):
        self.graph = tf.Graph()
        graph_def = None
        with open(frozen_path, 'rb') as f:
            graph_def = tf.GraphDef()
            graph_def.ParseFromString(f.read())
        if graph_def is None:
            raise RuntimeError('Cannot find inference graph in tar archive.')
        with self.graph.as_default():
            tf.import_graph_def(graph_def, name='')
        self.sess = tf.Session(graph=self.graph)

    def run(self, image):
        width, height = image.size
        resize_ratio = 1.0 * self.INPUT_SIZE / max(width, height)
        target_size = (int(resize_ratio * width), int(resize_ratio * height))
        resized_image = image.convert('RGB').resize(target_size, Image.ANTIALIAS)
        batch_seg_map = self.sess.run(
            self.OUTPUT_TENSOR_NAME,
            feed_dict={self.INPUT_TENSOR_NAME: [np.asarray(resized_image)]})
        seg_map = batch_seg_map[0]
        return resized_image, seg_map

    def returnSize(self,image):
        width, height = image.size
        resize_ratio = 1.0 * self.INPUT_SIZE / max(width, height)
        target_size = (int(resize_ratio * width), int(resize_ratio * height))
        return target_size


def create_pascal_label_colormap():
    """Creates a label colormap used in PASCAL VOC segmentation benchmark.

    Returns:
      A Colormap for visualizing segmentation results.
    """
    colormap = np.zeros((256, 3), dtype=int)
    ind = np.arange(256, dtype=int)

    for shift in reversed(range(8)):
        for channel in range(3):
            colormap[:, channel] |= ((ind >> channel) & 1) << shift
        ind >>= 3

    return colormap


def label_to_color_image(label):
    """Adds color defined by the dataset colormap to the label.

    Args:
      label: A 2D array with integer type, storing the segmentation label.

    Returns:
      result: A 2D array with floating type. The element of the array
        is the color indexed by the corresponding element in the input label
        to the PASCAL color map.

    Raises:
      ValueError: If label is not of rank 2 or its value is larger than color
        map maximum entry.
    """
    if label.ndim != 2:
        raise ValueError('Expect 2-D input label')

    colormap = create_pascal_label_colormap()

    if np.max(label) >= len(colormap):
        raise ValueError('label value too large.')

    return colormap[label]


def vis_segmentation(image, seg_map):
    """Visualizes input image, segmentation map and overlay view."""
    plt.figure(figsize=(15, 5))
    grid_spec = gridspec.GridSpec(1, 4, width_ratios=[6, 6, 6, 1])

    plt.subplot(grid_spec[0])
    plt.imshow(image)
    plt.axis('off')
    plt.title('input image')

    plt.subplot(grid_spec[1])
    seg_image = label_to_color_image(seg_map).astype(np.uint8)
    plt.imshow(seg_image)
    plt.axis('off')
    plt.title('segmentation map')

    plt.subplot(grid_spec[2])
    plt.imshow(image)
    plt.imshow(seg_image, alpha=0.7)
    plt.axis('off')
    plt.title('segmentation overlay')

    unique_labels = np.unique(seg_map)
    ax = plt.subplot(grid_spec[3])
    plt.imshow(
        FULL_COLOR_MAP[unique_labels].astype(np.uint8), interpolation='nearest')
    ax.yaxis.tick_right()
    plt.yticks(range(len(unique_labels)), LABEL_NAMES[unique_labels])
    plt.xticks([], [])
    ax.tick_params(width=0.0)
    plt.grid('off')
    plt.show()


# label setting
LABEL_NAMES = np.asarray([
    'background', 'dog', 'cat', 'hourse'
])

FULL_LABEL_MAP = np.arange(len(LABEL_NAMES)).reshape(len(LABEL_NAMES), 1)
FULL_COLOR_MAP = label_to_color_image(FULL_LABEL_MAP)


def main():
    img_path = "img_001.jpg"
    model_path = "./datasets/pascal_voc_seg/exp/train_on_trainval_set/export/frozen_inference_graph.pb"

    # load model
    model = DeepLabModel(model_path)

    # read image
    original_im = Image.open(img_path)

    # inferences DeepLab model
    resized_im, seg_map = model.run(original_im)

    # show inference result
    vis_segmentation(resized_im, seg_map)


if __name__ == '__main__':
    main()

```

# 参考資料

- [公式github](https://github.com/tensorflow/models)
- [DeepLab v3+（意訳）](https://qiita.com/mine820/items/14e7c556b358dbc4ee9a)
- [DeepLab v3+でオリジナルデータを学習してセグメンテーションできるようにする](https://qiita.com/mine820/items/7a08fad3847f2981cb01)
- [AndroidでTensorFlowを使ってアプリ作りたいけど何すりゃいいの(後編・DeepLab用独自データの作成と学習)](https://qiita.com/j_catfish/items/3b16913ea00af9b5e16b)
- [DeepLabv3+ on your own dataset](http://hellodfan.com/2018/07/06/DeepLabv3-with-own-dataset/)
- [DeepLab v3+でオリジナルデータを学習してセマンティックセグメンテーションする](https://qiita.com/otakoma/items/2f40e583980013acb2f7)
- [deeplab doesn't predict correctly the segmentation masks](https://github.com/tensorflow/models/issues/3739)



# まとめ
いかがでしたでしょうか。
誰でも出来るとは書きましたが、どこか1つでも間違ったり、GPUでエラーが出たり、日が経てばバージョン変更で仕様が変わったりするとやはりうまくいかない可能性もあります。
しかし、出来るだけ分かりやすく？書いたつもりなので、誰かの助けになると幸いです。