iOS11 カメラとCoreML(Vision)で画像検出

カメラ(ビデオ)で取得した画像データから、`iOS11`で導入された`CoreML`とその上位レイヤーの`Vision`を使用して画像検出するサンプルです。
`Xcode9`と、カメラを使用しますので`iOS11`をインストールした実機が必要です。
先日の`WWDC2017`のセッションの[こんな雰囲気](https://youtu.be/7VGMGtDqgwI?t=683)のものです。ちなみにアメリカのドラマ シリコンバレーに登場した`ホットドック🌭 かそうでないか`を判定するアプリがギャグで流行ったため、ここでもバナナを写し `not 🌭` でウケています。

元ネタは`Brian`さんのこちらの[YouTubeビデオ](https://www.youtube.com/watch?v=p6GA8ODlnX0)です。(彼に了承済み)

短いのでソースコードをざっと載せます。(ストーリボードは未使用)

```swift:ViewController.swift

import UIKit
import AVKit
import Vision

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
    
        let captureSession = AVCaptureSession()
        captureSession.sessionPreset = .photo
        
        guard let captureDevice = AVCaptureDevice.default(for: .video) else { return }
        guard let input = try? AVCaptureDeviceInput(device: captureDevice) else { return }
        
        captureSession.addInput(input)
        captureSession.startRunning()
        
        let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        view.layer.addSublayer(previewLayer)
        previewLayer.frame = view.frame
        
        let dataOutput = AVCaptureVideoDataOutput()
        dataOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
        captureSession.addOutput(dataOutput)
    }
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

        guard let model = try? VNCoreMLModel(for: Inceptionv3().model) else { return }

        let request = VNCoreMLRequest(model: model) { (finishedReq, err) in
            guard let results = finishedReq.results as? [VNClassificationObservation] else { return }
            guard let firstObservation = results.first else { return }
            print(firstObservation.identifier, firstObservation.confidence)
        }
        
        try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request])
    }
}
```

その他に、CoreMLの学習モデルファイル(Inceptionv3.mlmodel)とカメラの使用許可設定(Info.plist)が必要です。

![HelloSwift4_xcodeproj.png](https://qiita-image-store.s3.amazonaws.com/0/75231/a62b3a87-a080-da4d-dfde-37ece8573b04.png)

CoreMLの[モデルファイル](https://developer.apple.com/machine-learning/)をダウンロードします。`Inception v3`を選んでください。
![12Machine_Learning_-_Apple_Developer.png](https://qiita-image-store.s3.amazonaws.com/0/75231/694da308-9711-25df-5698-f54bf780208c.png)

ダウンロードしたモデルファイルをプロジェクトにコピーします。
モデルファイルを選択し、`File Inspector > Target Membership` にチェックを入れます。すると、自動的にクラスファイルが生成されます。(数秒かかります)
![Inceptionv3.png](https://qiita-image-store.s3.amazonaws.com/0/75231/e50e19ce-395a-573f-d2e6-1da59c39e0c5.png)


実機を繋いで実行します。[こんな感じになります。](https://youtu.be/p6GA8ODlnX0?t=1488)

### 学習モデルを変えたい場合

`CoreML`に対応した出来合えの学習モデルファイルは[こちらから](https://github.com/likedan/Awesome-CoreML-Models)もダウンロードできます。
MNIST、ランドマークから緯度経度を返したり、車の車種、果物、花に特化した学習モデルなどもあります。
基本的には、以下の`VNCoreMLModel`の引数を書き換えるだけで動きます。
![89ViewController_swift.png](https://qiita-image-store.s3.amazonaws.com/0/75231/5085db25-b85c-b8bc-a986-e4b30772ff47.png)

CoreML自体は推論機能だけなので、KerasやCaffeなどで学習モデルを作成し、[コンバージョンツール](https://developer.apple.com/documentation/coreml/converting_trained_models_to_core_ml)で`CoreML`の学習モデルファイルに変換する必要があります。