Depth Anything V2 ONNX推論ガイド:相対深度・実距離(Metric)モデルの使い方
深度推定モデルをONNXで動かそうとすると、「公式リポジトリにONNX変換スクリプトが無い」「入力テンソル名がモデルによってl_x_だったりimageだったりして分からない」「相対深度と実距離(Metric)の違いが分からず、出力値をそのままメートルとして扱ってしまう」といったつまずきが多く発生します。
1. DepthAnythingV2(相対深度)の利用
概要
Depth Anything V2 の相対深度 ONNX モデルを用いた深度推定の使い方をまとめます。 出力値は単位を持たない相対的な遠近関係であり、絶対距離(メートル等)ではない点に注意してください。
2. モデルの入手
公式リポジトリには ONNX 変換スクリプトが用意されていないため、コミュニティ提供の変換済み ONNX モデルを利用します。
- 変換ツール: https://github.com/fabio-sim/Depth-Anything-ONNX
- 変換済みモデル配布: https://github.com/fabio-sim/Depth-Anything-ONNX/releases
使用するモデル(静的入力・相対深度)
| ファイル名 | サイズ | 入力形状 | 入力テンソル名 |
|---|---|---|---|
depth_anything_v2_vits.onnx |
約95MB | (1,3,518,518) | l_x_ |
depth_anything_v2_vitb.onnx |
約371MB | (1,3,518,518) | l_x_ |
depth_anything_v2_vitl.onnx |
約1.25GB | (1,3,518,518) | l_x_ |
TorchDynamo でエクスポートされたモデルのため、入力テンソル名は l_x_ になります。
実際に使用するモデルの入力名は以下で必ず確認してください。
import onnxruntime as ort
sess = ort.InferenceSession("depth_anything_v2_vits.onnx")
print([i.name for i in sess.get_inputs()])
入力名の確認を省略してしまうと、モデルごとに名前が異なることに気づかず「推論実行時にInvalidArgumentエラーになる」というトラブルにつながりやすいため、必ず事前チェックを習慣にすることをおすすめします。
3. シンプルな推論サンプル
classes.depth_anything_v2 などの共通クラスを使わず、ONNXRuntime のみで直接推論する例です。
import cv2
import numpy as np
import onnxruntime as ort
MODEL_PATH = "depth_anything_v2_vits.onnx"
IMAGE_PATH = "input.jpg"
INPUT_SIZE = 518
INPUT_NAME = "l_x_" # モデルの実際の入力名に合わせて変更する
# ImageNet 統計値(DINOv2 事前学習時の正規化パラメータ)
MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
# 画像読み込み
image = cv2.imread(IMAGE_PATH)
orig_h, orig_w = image.shape[:2]
# 前処理: BGR->RGB, リサイズ, 正規化, NCHW化
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image = cv2.resize(image, (INPUT_SIZE, INPUT_SIZE), interpolation=cv2.INTER_CUBIC)
image = image.astype(np.float32) / 255.0
image = (image - MEAN) / STD
input_tensor = np.transpose(image[None], (0, 3, 1, 2))
# 推論
session = ort.InferenceSession(MODEL_PATH, providers=["CUDAExecutionProvider", "CPUExecutionProvider"])
depth = session.run(None, {INPUT_NAME: input_tensor})[0][0]
# 後処理: 元画像サイズへリサイズ
depth = cv2.resize(depth, (orig_w, orig_h))
# 可視化(相対深度を0-255に正規化してカラーマップ適用)
depth_norm = (depth - depth.min()) / (depth.max() - depth.min()) * 255.0
depth_color = cv2.applyColorMap(depth_norm.astype(np.uint8), cv2.COLORMAP_INFERNO)
cv2.imwrite("depth_color.png", depth_color)
実行結果は以下のとおりです。入力画像に対して、近い場所ほど明るく、遠い場所ほど暗く(カラーマップ上は色相が変化する形で)表示され、シーン内の相対的な奥行き関係が可視化されます。

入力画像

深度推定結果(相対深度)
4. 注意点
- 出力は相対深度であり、実距離(メートル)ではない点に注意
- 絶対距離が必要な場合はMetric(Indoor/Outdoor)モデルを利用(入力名やモデルによって前処理が異なる場合に注意)
_dynamicが付くモデル(可変形状)は入力テンソル名がimageになる場合があるため、事前のget_inputs()による確認が必要- 固定形状モデルの場合、入力サイズはモデルの
input_shapeへの一致が必要(本サンプルは518x518固定)
2. DepthAnythingV2(実距離/Metric)の利用
概要
depth_anything_v2_vits_outdoor_dynamic.onnx を用いた実距離(メートル単位)の深度推定の使い方をまとめます。
屋外シーン向けにファインチューニングされたMetricモデルで、出力値がそのまま推定距離[m]となります。
「相対深度モデルの出力をそのまま距離として扱ってしまい、実際の測距結果とズレが生じる」というのはよくある失敗パターンです。実距離が必要な用途では、必ず本セクションで紹介するMetricモデルを利用してください。
1. モデルの入手
- 配布元: https://github.com/fabio-sim/Depth-Anything-ONNX/releases
- ダウンロードURL: https://github.com/fabio-sim/Depth-Anything-ONNX/releases/download/v2.0.0/depth_anything_v2_vits_outdoor_dynamic.onnx
| 項目 | 内容 |
|---|---|
| モデル | Depth-Anything-V2-Small (vits) |
| 用途 | 屋外(Outdoor)向け、最大距離目安 約80m |
| 入力形状 | (B, 3, H, W)、H・Wは可変(各14の倍数) |
| 入力テンソル名 | image |
TorchScript ベースでエクスポートされているモデルのため、静的モデル(l_x_)とは入力名が異なります。
実際に使用するモデルの入力名は以下で必ず確認してください。
import onnxruntime as ort
sess = ort.InferenceSession("depth_anything_v2_vits_outdoor_dynamic.onnx")
print([i.name for i in sess.get_inputs()]) # -> ['image']
print(sess.get_inputs()[0].shape) # -> [batch, 3, height, width] (可変)
2. シンプルな推論サンプル
共通クラスを使わず、ONNXRuntime のみで直接推論する例です。
import cv2
import numpy as np
import onnxruntime as ort
MODEL_PATH = "depth_anything_v2_vits_outdoor_dynamic.onnx"
IMAGE_PATH = "input.jpg"
INPUT_NAME = "image" # モデルの実際の入力名に合わせて変更する
INPUT_SIZE = 518 # 短辺の目標サイズ
INPUT_MULTIPLE = 14 # 動的モデルの入力辺は14の倍数である必要がある
# ImageNet 統計値(DINOv2 事前学習時の正規化パラメータ)
MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
# 画像読み込み
image = cv2.imread(IMAGE_PATH)
orig_h, orig_w = image.shape[:2]
# 短辺をINPUT_SIZEに合わせつつ、各辺を14の倍数に丸める(可変形状モデル向け)
scale = INPUT_SIZE / min(orig_h, orig_w)
resized_w = max(INPUT_MULTIPLE, round(orig_w * scale / INPUT_MULTIPLE) * INPUT_MULTIPLE)
resized_h = max(INPUT_MULTIPLE, round(orig_h * scale / INPUT_MULTIPLE) * INPUT_MULTIPLE)
# 前処理: BGR->RGB, リサイズ, 正規化, NCHW化
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image = cv2.resize(image, (resized_w, resized_h), interpolation=cv2.INTER_CUBIC)
image = image.astype(np.float32) / 255.0
image = (image - MEAN) / STD
input_tensor = np.transpose(image[None], (0, 3, 1, 2))
# 推論
session = ort.InferenceSession(MODEL_PATH, providers=["CUDAExecutionProvider", "CPUExecutionProvider"])
depth_m = session.run(None, {INPUT_NAME: input_tensor})[0][0]
# 後処理: 元画像サイズへリサイズ(各画素値は推定距離[m])
depth_m = cv2.resize(depth_m, (orig_w, orig_h), interpolation=cv2.INTER_CUBIC)
# 距離の利用例
print(f"中心画素の推定距離: {depth_m[orig_h // 2, orig_w // 2]:.2f} m")
# 可視化(0-255に正規化してカラーマップ適用。値そのものはmではなくなる点に注意)
depth_norm = (depth_m - depth_m.min()) / (depth_m.max() - depth_m.min()) * 255.0
depth_color = cv2.applyColorMap(depth_norm.astype(np.uint8), cv2.COLORMAP_TURBO)
cv2.imwrite("depth_color_metric.png", depth_color)
実行結果は以下のとおりです。中心画素などの各画素値がそのまま推定距離[m]として得られ、可視化画像では近い場所ほど暗く、遠い場所ほど明るく(TURBOカラーマップ上で色相が変化する形で)表示されます。

入力画像
出力
中心画素の推定距離: 39.85 m

深度推定結果(実距離/Metric)
3. 注意点
- 出力値は実距離[m](屋外シーンで学習されたモデルの推定値)であり、
depth.min()/max()による正規化は可視化専用(距離値そのものには不使用) - 屋内シーンで使う場合は
depth_anything_v2_vits_indoor_dynamic.onnxを使用(最大距離目安は異なる点に注意) - 入力サイズは14の倍数に丸める必要があり、固定518x518への単純リサイズは精度低下のおそれ
- モデル精度はカメラの画角・設置条件・実際のシーンとの乖離により変動するため、実運用前に既知距離での検証を推奨
まとめ
Depth Anything V2 のONNXモデルを利用する際は、次の3点を押さえておくことでトラブルを避けられます。
- 推論前に
get_inputs()で入力テンソル名と入力形状を必ず確認 - 相対深度モデルとMetricモデルの違いを理解し、用途に応じて使い分け
- Metricモデルを実運用に使う場合は、既知距離でのキャリブレーション・検証を実施
これらのポイントを踏まえておけば、モデル選定や前処理の実装でつまずくことなく、スムーズに深度推定機能を組み込むことができます。