Ubiregi Advent Calendar 2019 17日目です。
今回はiOS13のVision.frameworkで新たに使えるようになったVNRecognizeAnimalsRequest
を使って動物(🐶と🐱)を識別するアプリのサンプルを紹介します。
Vision.frameworkとは
参照:https://developer.apple.com/documentation/coreml
- iOS11から追加された画像や映像から人や文字など様々なものを識別するAPIを提供するApple標準フレームワーク
- 機械学習フレームワークの
Core ML
を抽象化したもの - iOS13から新たに🐶と🐱の識別子が追加された🎉🎉🎉
やってみた
私の家には可愛いがいて被写体としてちょうどいいんですが残念ながら🐶を検出できないため、
今回は止む無くネットから拾ってきた画像を使いカメラロールを経由して動物画像をVision
に解析してもらいCollectionViewController
に表示するプログラムを作りました。
遷移先の画面で識別結果を表示しています。confidence
は信頼を返す値です。
二匹以上いる場合や、🐶と🐱が混じっている場合もそれぞれ認識されていますし、何気なく突っ込んだサングラス🐶や🐱もちゃんと識別されていることに驚きました。
カメラロールには、シュミレータに入っているデフォルトの風景画像以外に、猫に近い豹や虎などを含めてみましたが精度はとても高く結局画像が荒いものは精度は落ちました。
ライオンキングはネコでした。癒されているので問題ありません。
どうやっているのか
private func searchAnimals(with datas: [Data], completion: @escaping () -> (Void)) {
let group = DispatchGroup()
let lock = NSLock()
var searchIndex = 0
let animalRecognitionRequest = VNRecognizeAnimalsRequest { [weak self] (request, error) in
DispatchQueue.global(qos: .userInitiated).async {
defer { lock.unlock() }
guard let results = request.results as? [VNRecognizedObjectObservation], results.count > 0 else {
return
}
var detectionString = ""
var animalCount = 0
for result in results {
let animals = result.labels
for animal in animals {
animalCount = animalCount + 1
let animalType = animal.identifier == VNAnimalIdentifier.cat.rawValue ? "😸" : "🐶"
let string = "#\(animalCount) \(animal.identifier) \(animalType) confidence is \(animal.confidence)\n"
detectionString = detectionString + string
}
}
guard let self = self else { return }
self.thumbnails.append(ThumbnailEntity(data: datas[searchIndex], detection: detectionString))
self.output.insertItem(at: self.thumbnails.count - 1)
}
}
let queue = DispatchQueue.global(qos: .userInitiated)
for (i, data) in datas.enumerated() {
group.enter()
queue.async(group: group) {
defer { group.leave() }
lock.lock()
guard let image = UIImage(data: data), let cgImage = image.cgImage else { return }
searchIndex = i
let requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
try? requestHandler.perform([animalRecognitionRequest])
}
}
group.notify(queue: .main) {
completion()
}
}
説明
VNImageRequestHandler
に対してCGImage
をセットしてVNRecognizeAnimalsRequest
配列を渡して実行します。
するとVNRecognizedObjectObservation
配列として結果が返ってきます。
この時に、一枚の画像の中にたくさんの🐶や🐱がいる場合もあるので配列で受け取ります。とても尊い配列です。
あとは🐶か🐱を判定するために、VNAnimalIdentifier.doc
やVNAnimalIdentifier.cat
を使えば判定が可能です。
rawValue
はDog
とCat
文字列です。
このサンプルコードでは、今後AppleのアップデートでVNAnimalIdentifier
がたくさん増えると🐱以外は全て🐶になります。逆にしたら良かった。(そうじゃない)
今回画像データをループでまわして見つかり次第CollectionViewController
にinsert
しているので排他制御を行うためのコードが入っています。普段、UIの更新はメインスレッドでやれば、くらいしかちゃんと気にしていなかったので勉強になりました。
サンプルコード
今回作ったサンプルはこちらです。ツッコミ所はいろいろあって例えば今回は画像が何百枚もあることを考えていません。
VIPER
というUIアーキテクチャを採用して作ってみたのでその感想あたりもいろいろ書きたいんですが枠が残っていれば書こうかと思います。
最後に
Vision系のML技術はAppleのVision.framework以外にもたくさんあります。
例えば、
・Cloud Vision APIとCloud AutoML Vision
https://cloud.google.com/solutions/image-search-app-with-cloud-vision?hl=ja
・FirebaseのML Kitも画像のラベル付け機能をベータ版リリースしています。
https://firebase.google.com/docs/ml-kit/label-images
これらはGoogleの提供する機能ですが、iPhoneとandroidを比較すると画像に対する機能はiPhoneより一歩進んでいる印象です。
これらを使えばiOSでもandroidでも共通したAPIを利用できる利点がありますが、Appleのframeworkを使えば、サードパーティに頼らずサーバーレスでアプリ内で完結して使えるのも一つの強みだと思います。