Apple公式のデプス推定Core MLモデル「FCRN-DepthPrediction」
WWDC19のタイミングで、Appleが公式に配布するCore MLモデル[1]に「FCRN-DepthPrediction」と呼ばれるデプス推定モデルが追加されました。
名前の通り、デプスをディープラーニングで推定するモデルです。これを用いることでデプスをもたない画像からもデプスデータ [2] を取得できるようになります。
複眼カメラやLiDARを搭載したスマホもずいぶん普及しましたが、世の中に出回っている大半の静止画や動画にはデプスは含まれていません。そういった画像に対してデプスデータが必要な場合に本モデルが役立ちます。
FCRN-DepthPredictionモデル
「FCRN-DepthPrediction」が、今回追加されたデプス推定のCore MLモデルの名称です。2D画像を入力すると、推定デプスマップを出力します。FCRNは"Fully Convolutional Residual Networks"の略で、本モデルのソースコードはこちらにあります。
実際のデプスと本モデルで推定したデプスの比較(FCRNのREADMEより)
Appleの公式ページで配布されている.mlmodel
ファイルは32ビット浮動小数点数でパラメータ(重み)を保持するFCRN.mlmodel
と、16ビット浮動小数点数版のFCRNFP16.mlmodel
の2種類があります。
デプス推定モデルを使用する
実際にFCRN.mlmodel
を使用して、カメラから得られる画像からリアルタイムにデプス推定を行ってみましょう。
基本実装(推論処理実行まで)
FCRN.mlmodel
に入力画像を渡し、推論処理を行うところまではこのモデル特有の実装はほとんどなく[3]、「いつもの」Visionを使ったCore MLモデルの推論処理の実装です。簡単に流れを紹介します。
1. VNCoreMLModelの初期化
.mlmodel
から自動生成されるFCRN
クラスはMLModel
型のmodel
プロパティを持つので、それをイニシャライザに渡してVNCoreMLModel
オブジェクトを生成します。
let depthModel = try! VNCoreMLModel(for: FCRN().model)
2. VNCoreMLRequestの初期化、設定
VNCoreMLRequest
のイニシャライザに手順1で生成したVNCoreMLModel
オブジェクトと、推論処理完了後の処理ブロックVNRequestCompletionHandler
を渡し、初期化します。
let request = VNCoreMLRequest(
model: depthModel, completionHandler: { request, error in
// 推論完了後の処理
...
})
request.preferBackgroundProcessing = true
request.imageCropAndScaleOption = .centerCrop
3. VNImageRequestHandlerを初期化し、処理を開始する
カメラから毎フレーム取得できるCVPixelBuffer
オブジェクトをイニシャライザに渡してVNImageRequestHandler
オブジェクトを生成し、perform(_:)
メソッドにVNRequest
オブジェクト(ここでは手順2で生成したVNCoreMLRequest
オブジェクト)を渡して推論処理を開始します。
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
try! handler.perform([request])
以上で推論処理が実行できます。
推論結果(MLMultiArray)の処理
上述の手順で推論処理は実行されますが、完了後の処理をまだ実装していません。Core MLの推論結果は、その出力の形式に応じて処理方法が変わってきます。
1. モデルの入出力仕様を確認する
FCRN.mlmodel
をプロジェクトに追加し、Xcodeで見てみると、次のように入出力仕様を確認できます。
入力として304 x 228
のカラー画像(RGB画像)を受け取り、シェイプが(1 x 12 x 160)
、値をDouble
で持つMLMultiArray
型のデプスマップを出力することがわかります。
2. 推論結果のMLMultiArrayオブジェクトを取得する
VNCoreMLRequest
のイニシャライザには処理完了後に実行するVNRequestCompletionHandler
ブロックを渡すと書きました。この第1引数にはVNRequest
オブジェクトが入ってきます。そして、そのresults
プロパティからは、(出力の方がMLMultiArray
の場合は)VNCoreMLFeatureValueObservation
の配列が得られます。
let request = VNCoreMLRequest(
model: depthModel, completionHandler: { request, error in
let results = request.results as! [VNCoreMLFeatureValueObservation]
そして、このVNCoreMLFeatureValueObservation
はfeatureValue
というMLFeatureValue
型のプロパティを持っており、
var featureValue: MLFeatureValue { get }
このMLFeatureValue
のmultiArrayValue
プロパティより、MLMultiArray
オブジェクトを取り出せます。
var multiArrayValue: MLMultiArray? { get }
したがって、推論処理完了時に渡されてくるVNRequest
オブジェクトから推論結果であるMLMultiArray
オブジェクトを取り出すまでの一連の処理は、次のようになります。
guard let result =
request.results?.first as? VNCoreMLFeatureValueObservation
else { return }
guard result.featureValue.type == .multiArray else { fatalError() }
let multiArray = result.featureValue.multiArrayValue!
3. MLMultiArrayをCGImageに変換する
MLMultiArray
という型は多次元を扱う型で、機械学習の文脈では便利なのですが、ここに格納されたピクセル情報をCGImage
のようなiOSで一般的に用いる画像の型に変換するのは少々複雑な実装が必要になります。
そこで、ここでは「CoreMLHelpers」[4]というオープンソースのヘルパーライブラリを使用することにします。このライブラリはMLMultiArray
を画像に変換するextension等、Core MLを使う際に便利な実装が揃っています。
このヘルパーライブラリを利用して、次のようにMLMultiArray
をCGImage
に変換できます。
let cgImage = multiArray.cgImage(min: min, max: max)!
あとはこれをCIImage
にしてマスクとして背景合成に使用するなり、そのままMetalで描画するなりできます。
FCRN.mlmodelを用いてリアルタイムにデプス推定を行い可視化
モデルの情報・更新履歴
DeeplabV3のApple公式のCore MLモデルは2019年が初出ですが、以降もたびたび(ひっそりと)更新されています [5]。
-
FCRN.mlmodel
- 254.7MB
-
FCRNFP16.mlmodel
- 127.3MB
モデルの種類
種類・ファイルサイズはバージョンに関わらず共通。
-
モデルバージョン: 1.1
- coremltoolsバージョン: 4.0a6
-
モデルバージョン: 1.0
モデルのバージョン
-
iOSにおけるデプスについては拙著「Depth in Depth - iOSデプス詳解」をご参照ください。 ↩︎
-
実際は入力仕様に合わせて画像をリサイズしたりクロップしたりといったことも必要なのですが、Visionを使用する場合はそのあたりをよしなにやってくれるので、基本的には入力仕様を意識する必要がありません。 ↩︎
-
AppleのCore MLモデル配布サイトを見ても更新情報はまったく書かれていないので、毎年WWDC期間中やcoremltoolsのメジャーアップデートがリリースされるタイミングでダウンロードして確認しています。 ↩︎
Discussion