39
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Visionなしで、Core ML単体で使う

Last updated at Posted at 2018-03-09

Appleのサンプルを含め、Web上で見つかる記事のほとんどがCore MLのモデルを利用する際、Visionを併用した実装となっています。純正フレームワークとはいえ、不要な依存は少ないに越したことはありません。Visionを使わず、Core MLだけで実装するとしたらどんな感じになるんだろう、Visionはどのあたりを楽にしてくれているのか?ということに興味があり、やってみました。

比較として、以下の記事に書いたVisionを用いた実装と併記してみます。使用モデルはInception v3です。

モデル読み込み

Visionあり
private let model = try! VNCoreMLModel(for: Inceptionv3().model)
Visionなし
private let model = Inceptionv3()

Visionありの場合は.mlmodelから自動生成されたクラスからMLModelオブジェクトを取り出し、VNCoreMLModelを生成しましたが、Core ML単体のときは自動生成クラスをそのまま使います。(そのクラスに自動で生やされたメソッドを使いたいので)

画像入力

Visionあり
let handler = VNImageRequestHandler(cgImage: cgImage)
Visionなし
let input = Inceptionv3Input(image: pixelBuffer)

Visionありの場合は、リクエストハンドラを生成する際に入力画像を渡します。引数に渡せる入力画像の型はCVPixelBuffer, CGImage, CIImage, URL, Data等。

Visionなしの場合は、自動生成された入力型を生成します。初期化時に引数に渡せる入力画像の型はCVPixelBufferのみ。

これだけ見ると大して違いはなさそうですが、実は、Visionを使わない場合には自分でモデルの入力に合わせてリサイズを行う必要があります。

たとえば、Inceptionv3.mlmodelから自動生成された入力型Inceptionv3Inputには次のようなコメントがあります。

Inceptionv3.swift
/// Input image to be classified as color (kCVPixelFormatType_32BGRA) image buffer, 299 pixels wide by 299 pixels high

つまり、UIImageなりCGImageなりの手元にある画像データを、299x299にリサイズし、CVPixelBufferに格納する必要があります。(このあたりからVisionのありがたみがわかってきます)

このあたりの面倒さを助けてくれるヘルパークラスの集合として、

というものを見つけました。

こんな感じでUIImageのリサイズ 〜 CVPixelBufferへの変換を簡単に書けるようになります。

guard let pixelBuffer = image.pixelBuffer(width: 299, height: 299) else {fatalError()}

ちなみにこのヘルパーライブラリのリサイズ機能、いまのところは単純にスケールされてしまうので、アスペクトを維持したい場合には多少自分でコードを書く必要があります。

一方VisionのVNCoreMLRequestを使う場合はimageCropAndScaleOptionという便利オプションがあります。

BGRRGB といった color_layout の処理

モデルによってはRGBではなくBGR画像を入力として想定しているものもあります。

そういった色チャンネルの並び順はCore MLモデルフォーマットでは color_layout として指定できるようになっています。

Vision の VNImageRequestHandler を通してCore MLモデルに画像を渡す場合は、この color_layout を踏まえてよしなにやってくれます 1

推論処理の実行

Visionあり
try! handler.perform([request])
Visionなし
let output = try! model.prediction(input: input)

こんな感じで、これだけ見るとあまり変わらなそうですが、Visionを使う場合は、

  • VNCoreMLRequestにはpreferBackgroundProcessingでバックグラウンド処理を指定することもできる
  • perform()は複数のVNCoreMLRequestを引数に取ることができる

等々、並列処理を楽にさせてくれる機能があります。

結果の取得

Visionあり
guard let results = request.results as? [VNClassificationObservation] else {return}

results.forEach({ (result) in
    print("\(result.identifier) \(result.confidence * 100)")
})
Visionなし
print(output.classLabel)      // 1位候補のラベル
print(output.classLabelProbs) // 各クラスのラベルと確率

Visionなしの方のプロパティは、モデルから自動生成される出力型Inceptionv3Outputに生えているものなので、モデル依存です。

さらに、classLabelProbsの方は、結果がソートされていません。

Visionを使うメリット

というわけで、Core MLモデルを使った推論を行う際に、Core ML単体での実装と、Visionを併用する場合の実装の比較を行ってみました。

ここから見えてきたVisionを使うことのメリットとしては、

  • モデルに(比較的)依存しない処理が書ける
  • 入力画像のサイズやチャンネル数やチャンネル並び順(BGR等)を気にしなくてよい
  • 並列処理とかが簡単にできる

といったあたりでしょうか。

追記

MLModelオブジェクトを利用すれば、Visionを使わない場合でもモデルに依存せず処理をかけます。というわけで上述のいくつかの部分は修正が必要なので、時間ができたときにアップデートします。

  1. ただし、画像が CVPixelBuffer のようにピクセルフォーマットの情報を持つ場合。UIImageおよびCGImageは、アルファチャンネルの位置やエンディアンの情報は保持できるものの、BGRかRGBかの情報は型自体には保持していないので、明示的に入れ替えたりする必要がありそう。要確認。

39
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?