VisionFrameworkとCoreMLを使用した画像解析

##はじめに
CoreMLを用いた画像解析について、遅まきながら試してみました。

##検証環境
以下の環境を使用しています。

* macOS Sierra Version 10.12.4
* Xcode Version 9.0.0 β6
* iOS11.0β6

##今回行いたいこと
coreMLを用いて、バンドル内に含んだ画像の解析結果をラベルに吐き出すということを行います。簡単な動作確認のみのため、端末内部からの画像取得や、画像の通信取得は行わず、プロジェクト内に画像を直接格納します。

##modelの導入
以下の手順で学習済みモデルをプロジェクト内に含ませます。
今回はAppleが提供しているResNet50を使用します。
※モデルは以下のHPで入手可能です。
https://developer.apple.com/machine-learning/

* モデルをプロジェクトファイルに入れる
    * ドラッグ&ドロップで簡単に入れることができます。
* モデルのターゲットにプロジェクトを追加する
    * Utility AreaのTargetMembershipからプロジェクトを追加します
    * この時点でXcodeによってモデルに対応したクラスが自動生成されます

<img width="260" alt="スクリーンショット 2017-08-21 21.32.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/154099/713876d0-03a7-f84e-b564-66ad7e1474f3.png">

* そのようにするとmodelクラスの画面中央部のModelClassの部分に （->）マークが表示されますので、この部分をタップすることでクラスの内容を確認することができます。

<img width="716" alt="スクリーンショット 2017-08-21 20.52.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/154099/2e4e5d8f-3d73-38b1-2cee-8f8ecd97eb16.png">

クラス内には、初期化用のメソッド、解析用の画像を代入するプロパティ、解析メソッドなどがあります。ここで注目されるのは画像がUIImageではなく、`CVPixelBuffer`で定義されていることです。


```Swift:Resnet50.swift
    /// 画像解析対象のイメージ(224 * 224 のピクセルバッファで定義する必要がある）
    /// Input image of scene to be classified as color (kCVPixelFormatType_32BGRA) image buffer, 224 pixels wide by 224 pixels high
    var image: CVPixelBuffer

    /// 解析用のメソッド
    func prediction(image: CVPixelBuffer) throws -> Resnet50Output
```

##実装
以下の手順で実装します。

1. 画像のURL取得
2. 学習済みModelのインスタンス生成
3. VNCoreRequest用のモデルクラス生成
4. VisionFrameworkのリクエスト用ハンドラの生成
5. VNCoreMLRequestの生成
    * completionHandlerに結果取得後の処理を記載
6. requestの実行

```Swift:
import UIKit
import CoreML
import Vision

class ViewController: UIViewController {
    // 結果出力用のラベル
    @IBOutlet weak var resultLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 画像ファイルのURL取得
        let path = Bundle.main.path(forResource: "sample", ofType: "jpg")
        let imageURL = NSURL.fileURL(withPath: path!)
        
        // 学習済みModelのインスタンス生成
        let modelFile = Resnet50()
        // Vision FrameworkのVNCoreMLRequest用のクラス生成
        let model = try! VNCoreMLModel(for: modelFile.model)
        // VNRequestのhandler作成
        let handler = VNImageRequestHandler(url: imageURL)
        // リクエストの生成
        let request = VNCoreMLRequest(model: model) { (request, error) in
            // 結果取得
            guard let results = request.results as? [VNClassificationObservation] else {
                fatalError("ML Vision Requestの結果取得失敗")
            }
            // 予測結果と信頼度の定義
            var bestPrediction = ""
            var bestConfidence: VNConfidence = 0.0
            
            // 最高信頼度の予測結果と信頼度数値を取得
            for classification in results {
                if(classification.confidence > bestConfidence) {
                    bestConfidence = classification.confidence
                    bestPrediction = classification.identifier
                }
            }
            // 予測結果と信頼度の出力
            print("予測: \(bestPrediction) 信頼度：\(bestConfidence)" )
            self.resultLabel.text = bestPrediction
        }
        // requestの実行
        try! handler.perform([request])
    }
}
```

##所感
CoreML単体ではCVPixelBufferを使用しなければいけない点、modelによって画像サイズのリサイズが必要であったり、予測メソッドの引数やアウトプットの形式がモデルにより微妙に異なることにより学習済みのモデルの切り替えに手間がかかるなど少々ハードルが高く感じました。
その点、VisionFrameworkを使用することでその部分がラップされ、敷居が低くなっているように思います。使用するモデルの変更もVNCoreMLModelの引数のmodelクラスを変更後のモデルにするのみでよく簡易的であると感じました。

##参考文献
[Apple公式ドキュメント - Machine Learning](https://developer.apple.com/machine-learning/)