Core ML＋Visionを用いた物体認識の最小実装

iOS 11 より追加される Core ML を使うと、Keras や Caffe や scikit-learn で[学習させたモデルをコマンドラインツールで変換](https://developer.apple.com/documentation/coreml/converting_trained_models_to_core_ml)でき、それを**Xcodeにドラッグ＆ドロップすれば必要なコードが自動生成される**、とのこと。

- [開発者ドキュメントから見る iOS 11 の新機能 #WWDC17 - Qiita](http://qiita.com/shu223/items/e4d55d0b312b94772ebf)

"Vision Framework: Building on Core ML" というWWDC17のセッションに付随する公式サンプルで、Core MLでつくったカスタムモデル＋Visionフレームワークで画像の識別（MNISTの手書き数字認識）をやる実装があるのですが、その実装を参考に**Core ML+Visionでリアルタイム物体認識**を実装してみました。

![](https://qiita-image-store.s3.amazonaws.com/0/3180/0c788c23-4559-f966-281b-aa7f75442962.gif)

このキャプチャはiOS 11のもの**ではなく**、[iOS-10-Sampler](http://qiita.com/shu223/items/f371655c2b101f1b0218)のものです。NDAに配慮してこちらを載せましたが、**同じ Inception v3 モデルなので、動作としては同じ**です。


##実装手順

###1. AVFoundationを用いて、リアルタイムカメラ入力処理を実装する

毎フレームの `CMSampleBuffer` もしくは `CVPixelBuffer` を取得するようにするための実装。ここは新APIではないので、詳細は省略しますが、私はiOS-10-Sampler等でもやっている実装をずっと使いまわしています。

- https://github.com/shu223/iOS-10-Sampler

私はこういうカメラ入力を使ったサンプルをよくつくるので、自作ラッパー [^1] ＋コードスニペットを利用して、すぐに実装できるようにしてます。

[^1]: 完全個人用なので実装は非常に雑です。

###2. `.mlmodel` ファイルをプロジェクトにドラッグ＆ドロップ

プロジェクトのツリーからは見えませんが、**モデル名と同様のSwiftクラスが自動生成**されます。

今回はAppleが配布している Inceptionv3.mlmodel を利用したので、Inceptionv3.swift が自動生成されました。

###3. `VNCoreMLRequest` を作成

`VNCoreMLRequest` オブジェクトを保持するプロパティを用意しておいて、

```swift
private var coremlRequest: VNCoreMLRequest!
```

`VNCoreMLMode` オブジェクトと、CNNの計算処理が完了した後に実行される `VNRequestCompletionHandler`型のハンドラを渡します。

```swift
let model = try! VNCoreMLModel(for: Inceptionv3().model)
coremlRequest = VNCoreMLRequest(model: model, completionHandler: { (request, error) in
    guard let observations = request.results as? [VNClassificationObservation] else { fatalError() }
    guard let best = observations.first else { fatalError() }
    
    DispatchQueue.main.async {
        self.predictLabel.text = "Classification: \"\(best.identifier)\" Confidence: \(best.confidence)"
    }
})
```

上記のハンドラ内では、引数に入ってきた `VNCoreMLRequest` の `results` プロパティから `VNClassificationObservation`型の結果のリストを取り出し、第１位の候補をUIに表示するようにしています。（エラーハンドリング系はコードをシンプルにするために省略）

ちなみにAppleのサンプルでは、`VNCoreMLRequest` を以下のように　`lazy` オプションで初期化してましたが、

```swift
lazy var classificationRequest: VNCoreMLRequest = {
```

これだといざ認識を実行するときになってモデルの読み込み等諸々の準備処理が走ることになるので、良くないんじゃ...と思い上のようにしました。


##所感

Core MLを導入することで、[Metal Performance Shadersのレイヤ](http://qiita.com/shu223/items/bdd537f38872a5af4e4c)にまったく触ることなく、[ポインタがどうのこうの](http://qiita.com/shu223/items/c735ab888a83f214f89a#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)とかをやることもなく、非常に簡単に、GPU Acceleratedなリアルタイム物体認識ができてしまいました。簡単すぎてショックです。

今回やってみて「次はこのあたりを見てみよう」と思ったのが以下の２点

- 自動生成されたコードの中身
- Visionを使わずCore MLだけで書くとどうなるのか？（Visionは何をやってくれてるのか？）
  - 今回のケースでは入出力の橋渡し（`CIImage` -> `Inceptionv3Input` / `Inceptionv3Output` -> `[VNClassificationObservation]`）をしてくれてるだけ？
- Core MLのモデルを使わない、Visionのビルトイン機能を一通り試す

近いうちにまた記事を書こうと思います。


##iOS 11関連記事

- [ARKitについてWWDCのラボで聞いてきたことのメモ - Over&Out その後](http://d.hatena.ne.jp/shu223/20170607/1496918711)
- [開発者ドキュメントから見る iOS 11 の新機能 #WWDC17 - Qiita](http://qiita.com/shu223/items/e4d55d0b312b94772ebf)