はじめに
こんにちは、プログラミングを始めて約3年のエンジニアのkeitaMaxです。
今回はポイントクラウドのサンプルアプリと、公式を見ながらARKitとかMetalとか、ついでにSwift UIとかをふんわりと理解していこうと思います。
このサンプルアプリで色々学びたいと思います。
ダウンロードから実機にいれる
まずサイトに行ってダウンロードします。
ダウンロードが完了したら、xcodeで開いて、チームを自分のものに設定します。
これでビルドボタンを押せば、実機に入れられます。
すると、カメラが起動して自分の周りのものが点群としてスキャンされています。
なんかぐあんぐあん動いています。
表示するところを見てみる
MetalViewSample.swift
どうやらここで表示する処理をしていそうです。
MetalDepthView
この中のbody
が起動した時に表示されるところっぽいです。
じっさいに点群を表示しているところはMetalPointCloud
ここのようです。
デバイスが対応している機種かどうか
デバイスがシーンの深度にアクセスするには、LiDAR スキャナーが必要です。深度視覚化ビューのbody定義では、アプリはデバイスがシーンの深度をサポートしているかどうかを確認することで、サポートされていない構成の実行を防ぎます。
if !ARWorldTrackingConfiguration.supportsFrameSemantics([.sceneDepth, .smoothedSceneDepth]) {
Text("Unsupported Device: This app requires the LiDAR Scanner to access the scene's depth.")
}
そして、上記の部分で点群をスキャンできるデバイスかどうかを判定していて、ダメな時は文言を表示する、大丈夫だったら点群スキャンの画面を表示する処理をしています。
ARProviderを見てみる
データの取得と表示を分離するために、サンプル アプリは ARKit 呼び出しをそのARProviderクラスにラップします。
var arProvider: ARProvider = ARProvider()
ここでARKitの呼び出しをしていて、ARProvider
の中で実際に点群データを取得しているらしいです。
ARProvider
関数の中を未定見ると、arReceiver
をの制御をしていました。
init()
ここでMetalを使用して、GPU側に処理を渡しているっぽいです。
まだ詳しくは調べていないので、今後ゆっくり調べて理解していきたいです。
ARReceiverを見てみる
ここは実際にARSessionを開始して点群を取得している処理が書かれていました。
override init() {
super.init()
arSession.delegate = self
start()
}
初期化処理の時にstart()関数を読み込んでいます。
func start() {
guard ARWorldTrackingConfiguration.supportsFrameSemantics([.sceneDepth, .smoothedSceneDepth]) else { return }
// Enable both the `sceneDepth` and `smoothedSceneDepth` frame semantics.
let config = ARWorldTrackingConfiguration()
config.frameSemantics = [.sceneDepth, .smoothedSceneDepth]
arSession.run(config)
}
ここでARSessionをrunしています。
[.sceneDepth, .smoothedSceneDepth]
これってなんだ?と思ったので調べてみました。
公式には以下のように書かれていました。
.sceneDepth
AR エクスペリエンスにおけるデバイスの背面カメラと現実世界のオブジェクトの間の距離に関するデータ。
.smoothedSceneDepth
AR エクスペリエンスでよりスムーズなビジュアルを作成する、デバイスの背面カメラと現実世界のオブジェクト間の距離測定の平均。
.sceneDepth
は撮影した物体とスマホとの距離だということはわかったのですが、
.smoothedSceneDepth
は平均化しているため、.sceneDepth
よりも精度が良いもの?というように捉えておけば良いような気がしました。
func session(_ session: ARSession, didUpdate frame: ARFrame) {
if(frame.sceneDepth != nil) && (frame.smoothedSceneDepth != nil) {
arData.depthImage = frame.sceneDepth?.depthMap
arData.depthSmoothImage = frame.smoothedSceneDepth?.depthMap
arData.confidenceImage = frame.sceneDepth?.confidenceMap
arData.confidenceSmoothImage = frame.smoothedSceneDepth?.confidenceMap
arData.colorImage = frame.capturedImage
arData.cameraIntrinsics = frame.camera.intrinsics
arData.cameraResolution = frame.camera.imageResolution
delegate?.onNewARData(arData: arData)
}
}
新しくキャプチャされたカメラ画像と付随する AR 情報をデリゲートに提供します。
didUpdate
はどうやら点群が新しく取得できた時に呼ばれるもののようです。
frame
というものに、カメラの情報や点群の情報が記載されていそうです。
そして、arData
に点群情報をまとめて入れているようです、
そしてまとめて入れたものを、
delegate?.onNewARData(arData: arData)
この処理でARProvider
に渡しています。
おわりに
今回は疲れたのでここまでにします。
ARの処理は予想以上に難しくてなかなか理解が進みません。。。
初心者にはすこしレベルが高かったかなあと思うところもありますが、諦めずにみていこうと思います。
この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。(公式みて自分なりに理解しているだけなので、少しでも「ん?」と思うことがあったら教えていただけると助かります。)
最後まで読んでいただきありがとうございました!
参考文献