这篇文章介绍了如何在本地运行 Depth-Anything V2,因为我使用的无人机是Tello,其本身仅提供了一个单目视觉相机,在众多单目视觉转 Depth 的方案中我选择了 Depth-Anything V2,这个库的强大在于其基于深度学习模型将单目视觉以较低的代价转换成 RGBD 图像,可以用来无人机避障与SLAM。
Step1. 拉取 Depth-Anything V2 源码与模型下载
官方仓库 提供了两种方式调用Depth-Anything,这里先介绍第一种即直接源码调用,后面会补充他们说的第二种使用 HuggingFace
的 transformers
库的调用方式。
- 直接clone仓库:
$ git clone git@github.com:DepthAnything/Depth-Anything-V2.git
- 安装必要的库:
$ pip install -r requirements.txt
- 下载官方提供的预训练模型:
这里建议新建一个文件夹checkpoints
用来保存模型,这样在运行示例的时候就不需要对源码进行修改:
$ cd Depth-Anything-V2
$ mkdir checkpoints
在官方仓库中的 README.md
文件的 Pre-trained Models
章节提供了三个预训练好的模型,我将其下载后统计了一下实际模型大小:
Model | Params | Checkpoint | Size |
---|---|---|---|
Depth-Anything-V2-Small | 24.8M | Download | 99.2 MB |
Depth-Anything-V2-Base | 97.5M | Download | 390 MB |
Depth-Anything-V2-Large | 335.3M | Download | 1.34 GB |
Depth-Anything-V2-Giant | 1.3B | Coming soon |
此时你的文件结构应该如下:
(LLM) ~/Desktop/Depth-Anything-V2 $ tree -L 2
.
├── DA-2K.md
├── LICENSE
├── README.md
├── app.py
├── assets
│ ...
├── depth_anything_v2
│ ...
├── metric_depth
│ ...
├── checkpoints # 上一步创建的存放模型的文件夹
│ ├── depth_anything_v2_vitb.pth # 390 MB
│ ├── depth_anything_v2_vitl.pth # 1.34 GB
│ └── depth_anything_v2_vits.pth # 99.2 MB
├── requirements.txt
├── run.py
└── run_video.py
Step2. 【可选】准备示例图像与视频
为了能测试效果,我这里提供了一个自己拍摄的餐桌图像 dining-table.jpg
与风景区视频 landsacpe.mp4
放在网盘上,这两个文件不涉及任何版权问题,可以随意下载。
链接: https://pan.baidu.com/s/1i3COEYRJgzsCSG4O3doekg?pwd=7dba 提取码: 7dba
Step3. 运行单个图像示例
将测试数据下载好后就可以运行官方提供的示例 run.py
,这里假设使用的是我提供的图像 dining-table.jpg
并放在当前目录下:
此处还需要根据实际情况修改源码处的一个位置,大概在第19行
,这里是让你选择用于计算的模型,其默认是使用最大的即效果最好的模型,如果你只下载了上面的一个模型就需要对下面的代码中的 default='vitl'
进行修改,修改依据就是下方代码中的 model_configs
内容:
parser.add_argument('--encoder', type=str, default='vitl', choices=['vits', 'vitb', 'vitl', 'vitg'])
...
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]}
}
完成修改后就可以运行demo了,会在当前目录下生成一个 vis_depth
文件夹:
$ python run.py --img-path ./dining-table.jpg
Step4. 运行单个视频示例
与上一步运行单个图像示例一致,同样需要在第19行
处根据自己实际情况修改要加载的模型,然后执行命令:
$ python run_video.py --video-path ./landscape.mp4
执行后会在当前路径下生成一个vis_video_depth
文件夹:
Step5. 使用opencv调用本地摄像头
成功运行完上面两个demo后我在网盘中也提供了一个 python 脚本 local-camera.py
功能是使用opencv调用本地摄像头实时转换单目图像到深度图上:
【注意】:在我的 MacBook Pro M2 笔记本上运行后会非常卡顿并且有较大的延迟,处理一帧图像大概需要250ms左右,后续我会写一篇文章来展示如何将计算部署到远程GPU Server上并将计算结果回传到本地。
import argparse
import cv2
import matplotlib
import numpy as np
import torch
import time
from depth_anything_v2.dpt import DepthAnythingV2
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Depth Anything V2')
parser.add_argument('--input-size', type=int, default=518)
parser.add_argument('--encoder', type=str, default='vits', choices=['vits', 'vitb', 'vitl', 'vitg'])
parser.add_argument('--pred-only', dest='pred_only', action='store_true', help='only display the prediction')
parser.add_argument('--grayscale', dest='grayscale', action='store_true', help='do not apply colorful palette')
args = parser.parse_args()
DEVICE = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
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]}
}
depth_anything = DepthAnythingV2(**model_configs[args.encoder])
depth_anything.load_state_dict(torch.load(f'./models/depth_anything_v2_{args.encoder}.pth', map_location='cpu'))
depth_anything = depth_anything.to(DEVICE).eval()
margin_width = 50
cmap = matplotlib.colormaps.get_cmap('Spectral_r')
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Could not open camera.")
exit()
print("Successed: Camera opened.")
while True:
ret, raw_frame = cap.read()
if not ret:
print("Error: Failed to capture image.")
continue
start_time = time.time()
depth = depth_anything.infer_image(raw_frame, args.input_size)
depth = (depth - depth.min()) / (depth.max() - depth.min()) * 255.0
depth = depth.astype(np.uint8)
print(f"Calculate cost time: {time.time() - start_time}")
if args.grayscale:
depth = np.repeat(depth[..., np.newaxis], 3, axis=-1)
else:
depth = (cmap(depth)[:, :, :3] * 255)[:, :, ::-1].astype(np.uint8)
if args.pred_only:
cv2.imshow('Depth Prediction', depth)
else:
split_region = np.ones((raw_frame.shape[0], margin_width, 3), dtype=np.uint8) * 255 # 分割线
combined_frame = cv2.hconcat([raw_frame, split_region, depth]) # 将原始图像和深度图进行拼接
cv2.imshow('Raw Frame and Depth Prediction', combined_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
print(f"[{args.encoder}] Single frame cost time {time.time() - start_time}")
print(f" origin frame shape={raw_frame.shape}, processed shape={depth.shape}")
cap.release()
cv2.destroyAllWindows()
运行:
$ python local-camera.py
总体而言 Depth-Anything V2 在单目项目转深度方面效果是目前开源的几个方案中比较好且稳定的,特别是对于精度要求不高的情况下,虽然没有 RealSence 这种从硬件层面进行转换的快,但其最大的优势在于可以让Tello这种无人机在不进行任何改装的前提下就能够获取深度信息,这可以极大降低无人机单体的硬件成本,你需要的仅仅是一台能够运行模型的3060服务器,这个服务器在未来还可以肩负LLM Agent的功能。