📸

単眼深度推定のアルゴリズム Depth-Anything を試す

2024/07/26に公開

こんにちは! HACARUS でインターンをしている長野です。
本記事では深度推定の最新モデルの一つである Depth-Anything と、その簡単な実行方法を紹介します。

深度推定とは

カメラから被写体までの距離のことを深度と言い、画像から深度を推測することを深度推定と呼びます。以下の画像は深度推定を行った例です。右の画像ではカメラから近い物体ほど明るい色で表現されていることがわかります。
example
元画像: https://github.com/LiheYoung/Depth-Anything/blob/main/assets/examples/demo3.png

人間は過去の経験から物体ごとのおおよそのサイズ感や遠近感などをわかっているため、画像を一目見て大体の距離感が掴めますが、コンピュータにはその判断が非常に難しいです。そのため、以前はステレオカメラなど複数の視点の画像をもとに深度の推定を行ったり、LiDAR などを用いて深度を測定することが主流でした。

しかし、近年はAI技術の発達に伴い、一枚の画像だけから深度を推定する技術(単眼深度推定)の研究開発が進んでいます。今回紹介する Depth-Anything は、2024年に発表された最新の単眼深度推定のモデルです。

Depth-Anything とは

従来の単眼深度推定のモデルである MiDaS などでは、RGBの画像とそれに対応する深度の正解ラベルを学習データとして準備する必要がありました。Depth-Anything では、そのような正解ラベルがついているデータに加えて、多くのラベルなし画像も活用することで、より高性能のモデルを学習することを可能にしました。これによって、Depth-Anything では、学習データに含まれていない未知の画像に対しても優れた深度推定が実現できています。

ここでいうラベル付き、ラベルなしとは画像データに正解となる深度情報を付与したかどうかです。ラベル付き画像であれば、まずモデルに推論を行わせ、その結果を正解ラベルと比較し微調整を繰り返すことでモデルの学習を進めることができます。ただし大量のデータに深度情報を付与するには膨大なコストがかかり、データの数を増やすのが難しいです。その点、ラベルなし画像は入手自体は容易です。

そこで、Depth-Anything では、以下のようにラベルなし画像を学習に活用しています。

  1. 150万枚のラベル付き画像(図の manual label ) を用いて MiDaS をベースとした初期のモデルを学習
  2. 1で学習したモデル(teacher model)を使用して6200万枚のラベルなし画像に深度情報を付与(擬似ラベルの生成)
  3. ラベル付き画像と擬似ラベルをつけたラベルなし画像で自己学習を行う
  4. ラベルなし画像に色や空間的な歪みの摂動を加えることでより多様な画像に対応できるように学習

explanation
Depth Anything の論文より引用

モデルを実際に動かす

それでは、実際に Depth-Anything を使って深度推定を行ってみましょう。今回使用したコードは Google Colab で公開しています。非常に簡単に動かせるので興味のある方はぜひ試してみてください。

まず Depth-Anything のコードを git clone します。

git clone https://github.com/LiheYoung/Depth-Anything.git
cd ./Depth-Anything/

次に、トレーニング済みのモデルを読み込みます。エンコーダのモデルは、vitl > vitb > vits の順に大きいため、適切なサイズのモデルを選択します。ここでは、一番小さい vits を選びます。

encoder = 'vits' # choose encoder from [vitl, vitb, vits] (model size: vitl>vitb>vits)

# Check if GPU is available; otherwise use the CPU
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
print(f'device: {DEVICE}')

# Load a pre-trained DepthAnything model, move it to the selected device (GPU or CPU),
# and set it to evaluation mode
depth_anything = DepthAnything.from_pretrained('LiheYoung/depth_anything_{}14'.format(encoder)).to(DEVICE).eval()

深度推定を行う画像のパスを指定し、実際に推論します。

filename = './assets/examples/demo10.png'
raw_image = cv2.imread(filename)
raw_image = cv2.cvtColor(raw_image, cv2.COLOR_BGR2RGB) / 255.0

h, w = raw_image.shape[:2]
image = transform({'image': raw_image})['image']
image = torch.from_numpy(image).unsqueeze(0).to(DEVICE)

with torch.no_grad():
    depth = depth_anything(image) # Obtain depth map from the model

# Resize the depth map to the original image dimensions
depth = F.interpolate(depth[None], (h, w), mode='bilinear', align_corners=False)[0, 0]
depth = (depth - depth.min()) / (depth.max() - depth.min()).cpu().numpy()

得られた 深度推定の結果を描画すると、以下のようになりました。人間が見ても特に違和感はなく、うまく深度が推定できていることがわかります。

result

まとめ

本記事では単眼深度推定の最新のモデルである Depth-Anything について紹介し、実際にいくつかの画像で深度推定を試しました。任意の画像を一枚入力するだけで単眼深度推定が手軽にできることが確認できたかと思います。単眼深度推定は、動画にも活用可能で、将来的には自動運転や拡張現実 (AR)、仮想現実 (VR) などのさまざまな分野での活用が期待されます。

(追記) Depth-Anything-V2

2024-06-14に Depth-Anything-V2 が発表されました。従来と比較して、より詳細でロバストな推論が高速で実行できるようです。
Depth-Anything-V2 の使い方も簡単に紹介します。まずはじめに、リポジトリをクローンして、モデルの重みのダウンロードを行います。

git clone https://github.com/DepthAnything/Depth-Anything-V2
%cd ./Depth-Anything-V2/
wget https://huggingface.co/depth-anything/Depth-Anything-V2-Small/resolve/main/depth_anything_v2_vits.pth

次にモデルの読み込みと推論を行います。
従来の Depth-Anything と異なり、前処理部分や後処理部分がモデルのinfer_imageの内部で実行されるようになり、実行コードが簡潔になりました。

from depth_anything_v2.dpt import DepthAnythingV2

DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
print(f'device: {DEVICE}')

model_configs = {
    'vits': {'encoder': 'vits', 'features': 64, 'out_channels': [48, 96, 192, 384]},
    'vitb': {'encoder': 'vitb', 'features': 128, 'out_channels': [96, 192, 384, 768]},
    'vitl': {'encoder': 'vitl', 'features': 256, 'out_channels': [256, 512, 1024, 1024]},
    'vitg': {'encoder': 'vitg', 'features': 384, 'out_channels': [1536, 1536, 1536, 1536]}
}

encoder = 'vits' # or 'vits', 'vitb', 'vitg'
depth_anything_v2 = DepthAnythingV2(**model_configs[encoder])
depth_anything_v2.load_state_dict(torch.load(f'depth_anything_v2_{encoder}.pth', map_location='cpu'))
depth_anything_v2 = depth_anything_v2.to(DEVICE).eval()

filename = "./assets/examples/demo10.png"
raw_image = cv2.imread(filename)
with torch.no_grad():
    depth_2 = depth_anything_v2.infer_image(raw_image)

元画像(左)に対して、従来の Depth-Anythingの結果(中)と Depth-Anything-V2 の結果(右)を比較すると、以下のように Depth-Anything-V2 の方が細部がはっきりとしており、より正確な深度が推定できていることがわかります。

result_2

参考文献

HACARUS Tech Blog

Discussion