TensorFlow-Object-Detection(物体認識)を使って学習モデルの作成からiOSでビルドするまでの道のり【学習準備】

TensorFlowはGoogleが開発している機械学習のためのオープンソースライブラリで、例えば1枚の画像が猫なのか犬なのか、認識の正確さと共に推論できます。画像以外の推論にももちろん使えますが、この技術をどのように使いこなすかが焦点となり、実際のところいい案が出てきません（自分の場合）。そんな中、TensorFlowから`Object Detection API`という機能がリリースされました。これは、「1枚の画像の中のどこどこの座標にこれくらいの正確さで物体が映っています」といったことを可能にしてくれる機能です。
![kites_detections_output.jpg](https://qiita-image-store.s3.amazonaws.com/0/117549/25d5e132-9b45-e8c3-b9cd-35fc106dad8e.jpeg)


1枚画像での認識ではなく、検出した場所まで取得できるので応用が効きそうです。今回は、このObject Detection APIを使って、オリジナルスタンプを学習させて、iOS上で検出できるデモを作成したので、完成まで長かった道のりを備忘録として記します。ちなみに私は機械学習についてはまったくの素人ですので、間違った情報が含まれているかもしれません。

## 環境環境
##### version
|  | |
| :--- | :--- |
| **Python** | 3.6.0 |
| **TensorFlow** | 1.9.0 |
##### machine spec
|||
|:-----------|:------------|
|**PC**|MacBook Pro(Retina, 15-inch, Mid 2015)|
|**processor**|2.2 GHz Intel Core i7|
|**memory**|16 GB|
|**graphics**|Intel Iris Pro 1536 MB|

##### 学習後の主なフォルダ構成
```
|-labelImg
|-models(本家のtensorflow/models)
|-tensorflow-stamp-model
    |-anotations
        |-label_map.pbtxt
        |-trainval.txt
        |-xmls
            |-IMG_2703.xml
            |-IMG_2704.xml
                ...
    |-bin
        |-protoc
    |-cocoapi
    |-images
        |-IMG_2703.jpg
        |-IMG_2704.jpg
    |-model    
    |-pycocotools    
    |-model.ckpt.data-00000-of-00001
    |-model.ckpt.index
    |-model.ckpt.meta
    |-pycocotools    
    |-slim
    |-object_detection
        |-model_main.py
    |-train.record
    |-val.record
    |-create_tf_record.py
```

##### おおまかな流れ
1. **学習準備**
 1. **TensorFlowのセットアップ**
 * **対象画像を集める**
 * **画像ごとにラベリング**
 * **TFRecordを作成**
2. 学習
 1. モデル学習用のconfigファイルを用意
 * 学習
 * 確認と学習モデルの書き出し
3. iOS

## TensorFlowのセットアップ
まず、[本家のリポジトリ](https://github.com/tensorflow/models)をクローンします。
そして、その中にある[installの手順](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md)に沿って、objectde_detectionを使えるようにします。[こちらの方](https://qiita.com/YudaiSadakuni/items/530ee4150cb3ec020010#_reference-fdda7169c9b19ee7028b)の記事も大変参考になりました。

このセットアップを終えたら、メイン作業フォルダ`tensorflow-stamp-model`を用意し、本家の`models/research/`フォルダにある`slim`と`object_detection`フォルダだけ、メイン作業フォルダにコピーします。

メイン作業フォルダで以下のファイルを実行すると、環境が整っている確認ができます。

```bash:/tensorflow-stamp-model
python object_detection/builders/model_builder_test.py

.....
----------------------------------------------------------------------
Ran 22 tests in 0.085s

OK
```
ここでつまづいた時は、パスがとっているかもう一度確認します。

```bash
export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
```

## 対象画像を集める
集めるといっても、一つのスタンプ画像を様々な大きさや角度から撮影する必要があり、テストといっても少なくとも1つの画像につき100枚は必要のようです。また、認識精度にも影響してきます。私は紙に様々な角度スタンプを押して、それをいろんな角度で100枚撮影しました。

![IMG_2703.jpg](https://qiita-image-store.s3.amazonaws.com/0/117549/4d7a2caa-4602-d732-f751-871fbf7b3c58.jpeg)

撮りためた画像は`tensorflow-stamp-mode/images/`に保存します。この際、600x600など適度な画像サイズにすると学習時間が減らせます。

## 画像ごとにラベリング
次に、TensorFlowを使って学習させるために、撮影した画像のどこの座標に何（物体）が映っているかをラベリングを行います。このラベリングデータはxml形式で保存し、後の工程でTFRecordというtensorflowで扱うデータ形式に変換します。[labelImg](https://github.com/tzutalin/labelImg)というソフトウェアを使うと、変換に適したxmlデータを簡単に作ることができます。ただし、Windowsではそのままバイナリが配布されているのでダウンロードしてすぐに使えますが、Macの場合は自分でソースコードをダウロードして自前でビルドする必要があります。

<img width="500" alt="スクリーンショット 2019-01-22 15.24.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/117549/65886c9c-392f-4a17-2efb-99adfbd67374.png">

出来上がったxmlファイルは`tensorflow-stamp-model/annotations/xmls`フォルダを作成し、その中に移動させます。
ここで注意する点は、出来上がったxmlデータの中でfilenameノードのファイル名に必ず「IMG_2712.jpg」と拡張子までつける必要がある点です。私の環境だと、後の工程でTFRecordに変換する際にエラーが発生しました。大量のファイルからxmlノードの一部を変換する場合、面倒なので自動化することをお勧めします。[こちらの記事](https://qiita.com/mitsuyacider/items/8238e3a05ce5fb1154ca)を参考にしてください。

## TFRecordを作成
先ほど作成したxmlファイルからTFRecordというTensorFlowが扱える形式に変換します。変換コードを実行するためにはさらに以下の2つのデータがあります。

* trainval.txt
 - xmlファイルパス（ファイル名まで）の一覧

```bash:trainval.txt
IMG_2703
IMG_2704
...
```
ディレクトリ内のファイルからファイル名だけを抜き出して、テキストファイルに保存するの自動化スクリプトを書いたので、[こちらの記事](https://qiita.com/mitsuyacider/items/19a839e4c47c80fc1705)を参考にしてください。

* label_map.pbtxt
 - 識別する物体のリスト(ラベリングした時の名前）

```json
item {
  id: 1
  name: 'tent'
}

item {
  id: 2
  name: 'build'
}

item {
  id: 3
  name: 'house'
}
```

この2つを`TensorFlow/annotations/`フォルダに保存します。

次に、変換用のコード`create_tf_record.py`を`tensorflow-stamp-model/`に保存します。この変換コードは本家の`models/research/object_detection/dataset_tools`にあるコードでも良いですが、私は[こちらのソース](https://github.com/bourdakos1/Custom-Object-Detection/blob/master/object_detection/create_tf_record.py)をベースにフォルダのパスだけ変更しました。

```bash:/tensorflow-stamp-model
python create_tf_record.py
```

実行すると`train.record`と`val.record`が生成されます。

---
動作保証はしませんが、今回のTensorFlowのプロジェクトは[GitHub](https://github.com/mitsuyacider/tensorflow-stamp-model)に置いてあります。
また、TensorFlowの学習モデルをiOSに組み込んだプロジェクトは[こちら](https://github.com/mitsuyacider/tensorflow-ios-build)です。ご自由にお使いください。