231 Views
October 31, 26
スライド概要
[Kanagawa.swift #3 - connpass](https://japan-region-swift.connpass.com/event/389036/) での発表資料です。
フリーランスiOSエンジニア 「エンジニアと人生」コミュニティ主宰
最近の深度推定モデルの話 Core ML+ARKit+Metalで「LiDARなし」の3Dスキャンアプリを自作する
堤 修一 フリーランスiOSエンジニア ID: shu223 GitHub Docswell(スライド) Zenn Qiita note はてなブログ YouTube Podcast 著書:商業出版4冊、個人出版多数 X (Twitter)
今日話すこと • 所用で深度推定モデルの最適化の検討をする機会があった • 久々に深度推定モデルを触ると、いろいろ高機能なことに気づいて 勉強になったので共有します
深度推定モデルとは 入力 出力 画素ごとの奥行きを推定 深度推定モデル 画像 深度(デプスマップ)
2019年:初の深度推定モデルが公式Core MLページで公開 FCRN (Fully Convolutional Residual Networks) 二眼カメラの視差情報なしでも、 リアルタイムに深度を推定できる
Depth Anything 3 Small (DA3-Small) FCRN DA3-Small 発表 2016年 2025年 開発元 Laina ほか ByteDance Seed Team 約255 MB(32ビット) 約69 MB(16ビット、公開の1枚入力版) 入力 304×228 504×504 出力 160×128 504×504 Core ML版の大きさ 大きさの差には重みのビット数の差(32ビットと16ビット)も含む
FCRN v.s. DA3-Small 入力画像 FCRN 近い DA3-Small 遠い それぞれの出力の最小〜最大で色を付けた(距離の値は比べられない)
デモ
単眼深度推定とマルチビュー深度推定 単眼:画像を独立に処理 画像A 単眼推定 マルチビュー:画像間の情報も使う 深度A 画像A 画像間のAttention 画像B 単眼推定 深度B 画像B 同時に渡した画像の特徴を参照して、 各視点の深度を推定する 深度A + 深度B
DA3-Smallの入力と出力 こうだと思っていたが…… 入力 画像 モデル DA3-Small 出力 深度
DA3-Smallの入力と出力 マルチビュー深度推定をサポートしている 入力 モデル 出力 画像 1〜S枚 カメラ姿勢 カメラの内部パラメータ DA3-Small 各視点の深度 カメラ姿勢(推定)
マルチビュー深度推定対応のCore MLモデル 公開のCore ML版は1枚入力 → マルチビュー・姿勢入力を使えるよう、coremltoolsで変換し直す 公開のCore ML版 入力:RGB 504 × 504 を1枚 出力:深度 [1, 504, 504] 公式PyTorch 自前の変換 入力:RGB 504 × 504 をS枚 カメラ姿勢 [1, S, 4, 4] カメラの内部パラメータ [1, S, 3, 3] 出力:深度 [1, S, 504, 504] 推定したカメラ姿勢 [1, S, 9] (位置3・回転の四元数4・視野角2) trace → ML Program(FP16) Sは2・3・4で固定し、枚数別のモデルを作る
深度から3D点群への変換 ピンホールカメラモデル 画素 (u, v)、深度 z、カメラの内部パラメータ K x = (u − cx) z / fx y = (v − cy) z / fy カメラ座標の点 = (x, y, z) fx, fy:焦点距離、cx, cy:主点(いずれも画素単位) z:光軸方向の深度 カメラ姿勢でワールド座標へ変換し、観測を重ねる
DA3の深度のメートルへの換算 推定したカメラ位置を、基準となるメートルのカメラ位置と重ねて、 相対深度をメートルに直す倍率を求める Umeyama法:対応する位置の集合を、回転・平行移動・倍率を含む変換で重ねる方法 基準となるカメラ位置(m) x₁ 2視点なら DA3が推定したカメラ位置(姿勢 [1, S, 9] の先頭3つ) x₂ y₁ s = ‖y₂ − y₁‖ / ‖x₂ − x₁‖ メートル深度 z = 相対深度 d / s(位置合わせのうち、倍率sだけを使う) y₂
ARKitから得るカメラ姿勢とカメラの内部パラメータ カメラ姿勢と内部パラメータはARKitから取れる ARKitの座標系から、DA3の入力の座標系に変換して渡す カメラ姿勢の位置は、Umeyama法の基準のメートルのカメラ位置にもなる ARKitから得る情報 画像 S枚 カメラ姿勢 T DA3-Small カメラ姿勢を条件に、 各視点の深度を推定 カメラの内部パラメータ K 入力仕様:T [1,S,4,4]、K [1,S,3,3](Float32) Tはworld→camera、Kは入力画像の画素座標に合わせる
推論の繰り返しと同じ空間への蓄積 モデル:画像の組を更新して推論 A+B 2視点推定 深度B B+C 2視点推定 深度C アプリ:点を同じ空間に積む 深度 3D点群へ変換 カメラ姿勢で座標変換 保持
カメラ姿勢を入れた3視点の深度推定のデモ
重ねたときの同じ場所のずれ 推定深度の画像は それらしく見える 空間に置くと、 床や机が何重にもなる
推論の組ごとに変わる距離の倍率 青と赤は、同じ画像を 別の組で推定した結果 深度と、Umeyama法で求めた 換算倍率が組ごとに変わり、 同じ表面が違う距離に置かれる 横から見た図(縦軸は高さ、m) scale:1 mあたりの相対深度、median:光軸深度の中央値(m)
ARKitの平面を基準にした倍率の補正 Umeyama法の倍率の代わりに ARKitの平面 DA3の深度 z:メートルの深度 d:モデルが返す相対深度 カメラ光線と平面の交点 メートルの深度 同じ画素の相対深度 s = median(d / z) メートル深度 = d / s s:1 mあたりの相対深度、median:対応画素の比の中央値 2枚の間の小さな移動(約5 cm)から倍率を求めないので、組ごとに揺れにくい
倍率の補正で床の高さをそろえる 床の高さの幅(上下5%の外れを除く) 補正前 86.1 cm 補正後 1.7 cm 床を写した32回の推論で、点群から床の高さを求めて比べた 1.7 cmは推論どうしの床のそろい方で、実際の寸法との誤差ではない 補正にARKitの床の平面を使うので、床がそろうのは補正の作り方による
推論ごとに足されるだけの点群 推論のたびに、新しい点群が足される 推論の間で、同じ面を1つにそろえる処理は無い 倍率の補正後に積んだ床の点を横から見たもの(色は推論ごと)
重なった深度をまとめる(TSDF) 空間を細かい格子に分け、格子ごとに表面までの符号つき距離を平均し、 0になるところを面として取り出す TSDF(Truncated Signed Distance Function):切り詰めた符号つき距離関数 カメラ側:δ > 0 表面の奥:δ < 0 δ = z_obs − z_voxel (両方とも光軸方向の深度、m) φ = clip(δ / μ, −1, 1) δ=0 F ← (wF + φ) / (w + 1) z_obs:換算後の観測深度、z_voxel:格子中心の深度、μ:切り詰め幅(m) F:統合値、w:蓄積した重み(δ < −μ の格子は更新しない) 図は1観測の断面の模式図
倍率の補正後に積んだ点とまとめた面 点を積んだもの まとめた面(点で表示)
格子からの三角形の面の生成 Marching Cubes 隣接する格子点の値から F = 0の位置を補間し、 三角形でつなぐ 三角形の集まりを、 面として描画する
倍率補正+TSDF+メッシュ生成のデモ
枚数とともに増える推論時間とメモリ 2枚入力で、1枚の約5倍の時間とメモリ 入力の枚数 推論時間の中央値 メモリ(アプリ側+Neural Engine側) 1枚 83.7 ms 174 MiB 2枚 438.5 ms 868 MiB 3枚 603.4 ms 1,195 MiB 4枚 933.2 ms 1,717 MiB 12 Pro・固定画像・カメラ姿勢を渡さないモデル単体の記録、各75秒(3枚だけ別の日の120秒) メモリは定常の値(3枚だけ記録の中央値)
視点数とともに大きくなる画像間のAttentionの行列 S:視点数 N = (504 / 14)² + 1 = 1,297 tokens / 視点 画像間のAttentionの1つのhead・blockで、score行列は (S × N)² 要素 14×14画素のパッチとカメラトークン1個 4N 2N 2枚入力 4枚入力 要素数は4倍
Qだけの分割と全体を参照するK・V softmax(QKᵀ / √d) V Qを分割して順に計算 分割前 Q1 × K全体 → Vを集約 Q全体 × K全体 Q2 × K全体 → Vを集約 Q3 × K全体 → Vを集約 Q:照合する側、K:照合される側、V:集める特徴、d:Q/Kの1 headの次元(図は模式) coremltoolsのQ分割パスを画像間のAttentionへ適用(実装は16分割)
3枚入力での推論時間とメモリの削減 推論時間(ms) 603.4 メモリ(MiB、アプリとNeural Engineの中央値の合計) 1,195 480.1 624 分割前 分割後 推論時間 約20%減 分割前 分割後 メモリ 約48%減 12 Pro・3枚の固定画像・各120秒の記録の中央値(測定方法は付録)
試した最適化と結果 推論時間には効かなかった 動かなかった・出力が壊れた 重みの量子化・パレット化(ファイルとメモリは減る) 途中の値もINT8にする量子化(W8A8。調査中) CPUに置かれる演算を無くすグラフの書き換え(数%だけ) 一部の量子化(OSの版で壊れる変種が違う) iOS 18のまとめたAttentionの演算 Core AI版のパレット化(4ビットで出力が壊れる) Q分割の数を16より細かく Core AI版(Core ML版より遅い) 機種・枚数で効き方が変わった トレードオフがあった 演算器をNeural Engineに絞る Core AIのbackboneの書き直し(調査中) (12 Proの2枚だけ速く、4枚は遅い) (速くなるがメモリが増え、Core ML版より遅い) 入力解像度を下げる(速くなるが、深度が少し変わる) はっきり効いたのは、画像間のAttentionのQ分割 12 Pro、3枚入力で推論時間 約20%減・メモリ 約48%減
メッシュへのテクスチャの貼り付け • 格子の1マス(3 cm)に1色のメッシュに、 カメラの画像を貼る • 撮影中:推論ごとに、画像・カメラ姿勢・ カメラの内部パラメータ・深度を保存 • 撮影後:三角形ごとに、隠れずに写っている視点のうち、 正面から近くで見ている視点の画像を選んで貼る 17 Proで作ったモデル
デモ(最適化+テクスチャ)
まとめ • 最近の深度推定モデルはマルチビュー深度推定をサポートしていたりと高機能 • 姿勢情報を入力してLiDARなしの3Dスキャンアプリの自作も可能 • 相対深度を実際のメートルに補正するためにARKitの情報を使った • Core ML最適化を色々試した
付録:最近の深度推定モデルの機能の比較 1枚の画像を入力にするモデル 複数の画像を入力にするモデル モデル 発表 大きさ DA2 Metric(Small) 2024 約25M MoGe-2(ViT-S) メートルの 深度 大きさ メートルの カメラ姿勢 カメラ姿勢 深度 の入力 の推定 モデル 発表 ○ DA3-Small 2025 約80M ― ○ ○ 2025 約35M ○ VGGT 2025 約1B ― ― ○ MoGe-3(ViT-L) 2026 約370M ○ VGGT-Ω 2026 約1B ― ― ○ DA3Metric-Large 2025 約350M ○ MapAnything 2025 約1.2B ○ ○ ○ Depth Pro 2024 ― ○ Pi3X 近似 ○ ○ Metric3D v2 2024 ― ○ UniDepthV2 2025 ― ○ 2025 約1.4B 2026年10月31日時点 大きさ:パラメータ数(版が複数あるモデルはいちばん小さい版で、括弧はその版の名前) ―:公式のREADME・モデルカードに記載なし 近似:READMEの「approximate metric scale」
付録:Q分割の比較の推論時間・メモリの測定方法 推論時間 各120秒の記録から、1回目の推論を除いた中央値 3枚の固定画像2組を繰り返し入力 メモリ アプリ側とNeural Engine側の、それぞれの中央値を合算 単位:MiB(1,048,576 bytes) ピーク値やモデルのファイルサイズではない
参考資料:モデルとAppleの実行環境 深度推定モデルとはの図 Core ML Koch et al. (2018) / iBims-1 ↗ Apple / Core ML ↗ マルチビューのイラスト TUM CVG / Multiple View Geometry (SS2025) ↗ FCRN(2019年に試したモデル) Laina et al. (2016) / FCRN-DepthPrediction ↗ Depth Anything 3 公式コード・機能 ↗ DA3-SMALL モデルカード ↗ 論文 / Recovering the Visual Space from Any Views ↗ DA3公式 / 姿勢の入力と正規化 ↗ Apple / PyTorch Conversion Workflow ↗ 利用した公開Core ML版 ↗ ARKit Apple / Understanding World Tracking ↗
参考資料:幾何計算とAttention OpenCV / ピンホールカメラモデルと座標変換 ↗ Umeyama (1991) / 相似変換の最小二乗推定 ↗ Fischler & Bolles (1981) / RANSAC ↗ Curless & Levoy (1996) / 距離場への体積統合 ↗ Lorensen & Cline (1987) / Marching Cubes ↗ Vaswani et al. (2017) / Scaled Dot-Product Attention ↗ Apple / coremltools 9.0のQ分割パス ↗