《深度估计》Apple Depth Pro | 单目度量深度估计 | 秒级

Depth Pro 是一个单目深度估计模型,它仅通过单张RGB图像就能恢复场景的三维深度信息

它一款面向零样本场景的单目度量深度估计模型,不仅实现了秒级推理,还能输出高精度、高清晰度的度量深度图。

论文地址:Depth Pro: Sharp Monocular Metric Depth in Less Than a Second

开源地址:https://github.com/apple/ml-depth-pro

本文将深度解析Depth Pro的核心特性、使用方法与技术架构。并提供实践指导和推理代码。

一、Depth Pro核心特性

相较于传统单目深度估计方法,Depth Pro的核心优势体现在以下几点:

  1. 零样本度量深度估计:作为基础模型无需针对特定数据集微调,可直接输出以米为单位的度量级深度值,解决传统方法"相对深度无物理意义"的问题。
  2. 高效推理:优化编码器-解码器架构,结合CUDA/MPS硬件加速,单张图像推理耗时小于1秒,满足实时应用需求。
  3. 高精度Sharp深度图:通过多分辨率特征融合和边界优化,输出的深度图细节丰富、边缘锐利,物体边界处精度显著提升。
  4. 易用的工具链:提供CLI命令行工具和Python API,支持单张/批量图像推理,内置SI_boundary_F1等边界指标评估深度图质量。
  5. 跨平台兼容:支持CPU、CUDA、Apple MPS(Metal Performance Shaders),可适配不同硬件环境。

二、环境搭建与预训练模型下载

1. 环境依赖

Depth Pro基于Python 3.9开发,核心依赖包括PyTorch、torchvision、timm等,建议通过虚拟环境隔离依赖:

bash 复制代码
# 创建并激活conda虚拟环境
conda create -n depth-pro -y python=3.9
conda activate depth-pro

# 安装Depth Pro包(本地开发模式)
git clone https://github.com/apple/ml-depth-pro.git
cd ml-depth-pro
pip install -e .

2. 下载预训练模型

Apple提供了预训练权重,通过脚本一键下载(自动保存到checkpoints目录):

bash 复制代码
source get_pretrained_models.sh

也可以在浏览器手动下载:

https://ml-site.cdn-apple.com/models/depth-pro/depth_pro.pt

下载后的权重depth_pro.pt,大约1.9G左右。

三、快速上手:两种推理方式

Depth Pro提供了两种便捷的推理方式,满足不同开发场景需求。

1. 命令行(CLI)快速推理

适用于快速验证单张/批量图像,无需编写代码:

bash 复制代码
# 单张图像推理(默认输出深度图可视化结果)
depth-pro-run -i ./data/example.jpg

# 批量处理文件夹下所有图像,并保存深度图(npz格式+彩色可视化图)
depth-pro-run -i ./data/input_images -o ./data/output_depth --skip-display

关键参数说明:

  • -i/--image-path:输入图像/文件夹路径;
  • -o/--output-path:输出深度图保存路径(npz格式存储原始深度值,jpg格式存储彩色可视化结果);
  • --skip-display:跳过Matplotlib实时可视化;
  • -v/--verbose:打印详细日志(如焦距估计值)。

2. Python API自定义推理

适用于集成到业务代码中,支持更灵活的定制化:

python 复制代码
from PIL import Image
import depth_pro
import torch

# 1. 加载模型和预处理变换(自动适配硬件:CUDA/MPS/CPU)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model, transform = depth_pro.create_model_and_transforms(
    device=device,
    precision=torch.half  # 半精度推理,提升速度
)
model.eval()  # 推理模式

# 2. 加载并预处理图像(自动读取EXIF中的焦距信息)
image_path = "./data/example.jpg"
image, _, f_px = depth_pro.load_rgb(image_path)  # f_px:像素级焦距
image_tensor = transform(image).unsqueeze(0)  # 添加batch维度

# 3. 推理:输出度量深度(米)和焦距(像素)
with torch.no_grad():  # 禁用梯度计算,提升速度
    prediction = model.infer(image_tensor, f_px=f_px)

# 4. 解析结果
depth_map = prediction["depth"].cpu().numpy()  # 深度图(H×W,单位:米)
focal_length_px = prediction["focallength_px"]  # 焦距(像素)

print(f"深度图尺寸:{depth_map.shape}")
print(f"平均深度值:{depth_map.mean():.2f}米")
print(f"估计焦距:{focal_length_px:.2f}像素")

四、核心技术架构解析

Depth Pro的高性能源于其精心设计的模型架构和推理流程,核心分为编码器解码器FOV头深度计算四部分。

1. 模型整体架构

Depth Pro采用"双ViT编码器 + 多分辨率卷积解码器"架构:

  • 编码器 :基于DINOv2 ViT(dinov2l16_384)构建双编码器(Patch Encoder + Image Encoder),提取多尺度图像特征;
  • 解码器:MultiresConvDecoder融合多分辨率特征,恢复高分辨率深度图;
  • FOV头:FOVNetwork估计图像的视场角(FOV),进而计算像素级焦距,实现度量深度的转换;
  • 推理后处理:自动将模型输出的"规范逆深度"转换为物理意义的度量深度,并适配输入图像分辨率。

2. 关键模块解析

(1)编码器:双ViT特征提取

Depth Pro使用两个预训练的ViT编码器分别提取"Patch级特征"和"全局图像特征",并通过Hook机制获取多尺度特征图,为解码器提供丰富的语义和细节信息:

python 复制代码
# 核心代码片段(depth_pro.py)
patch_encoder, patch_encoder_config = create_backbone_model(preset="dinov2l16_384")
image_encoder, _ = create_backbone_model(preset="dinov2l16_384")
encoder = DepthProEncoder(
    dims_encoder=patch_encoder_config.encoder_feature_dims,
    patch_encoder=patch_encoder,
    image_encoder=image_encoder,
    hook_block_ids=patch_encoder_config.encoder_feature_layer_ids,
    decoder_features=256,
)
(2)解码器:多分辨率特征融合

MultiresConvDecoder将编码器输出的多尺度特征投影到统一维度,再通过FeatureFusionBlock2d从低分辨率到高分辨率逐步融合,恢复Sharp的深度图:

python 复制代码
# 核心代码片段(decoder.py)
decoder = MultiresConvDecoder(
    dims_encoder=[256] + list(encoder.dims_encoder),
    dim_decoder=256,
)
features, _ = decoder(encodings)  # 融合多尺度特征
canonical_inverse_depth = self.head(features)  # 输出规范逆深度
(3)FOV头与度量深度计算

Depth Pro的核心创新之一是通过FOV头估计视场角,结合图像宽度计算焦距,最终将"规范逆深度"转换为度量深度(米):

python 复制代码
# 核心代码片段(depth_pro.py)
if f_px is None:
    # 无EXIF焦距时,通过FOV估计焦距
    f_px = 0.5 * W / torch.tan(0.5 * torch.deg2rad(fov_deg.to(torch.float)))
inverse_depth = canonical_inverse_depth * (W / f_px)
depth = 1.0 / torch.clamp(inverse_depth, min=1e-4, max=1e4)  # 转换为度量深度

3. 边界指标评估

Depth Pro内置了针对深度图边界精度的评估指标(SI_boundary_F1/SI_boundary_Recall),用于量化深度图在物体边缘的精度:

python 复制代码
from depth_pro.eval.boundary_metrics import SI_boundary_F1, SI_boundary_Recall
import numpy as np

# 示例:计算深度图的SI-boundary-F1
predicted_depth = np.load("./output/depth.npz")["depth"]
target_depth = np.load("./gt/depth.npz")["depth"]
f1_score = SI_boundary_F1(predicted_depth, target_depth)
print(f"边界F1分数:{f1_score:.4f}")

# 示例:计算掩码场景下的边界召回率
target_mask = np.load("./gt/mask.npy")
recall_score = SI_boundary_Recall(predicted_depth, target_mask)
print(f"边界召回率:{recall_score:.4f}")

模型性能指标对比:

五、实践应用

5.1、单张图片推理

  1. 基础功能 :基于Apple开源的DepthPro模型实现单张RGB图像的度量级深度估计(深度值单位为米),完成从模型加载、图像预处理到深度推理的全流程,同时支持GPU自动检测与加速,大幅提升推理效率。
  2. 可视化与结果保存:将推理得到的深度张量转换为NumPy数组,通过matplotlib绘制带色彩映射的深度可视化图(jet配色),支持中文/英文标签自适应显示,同时保存可视化图片和原始深度值的.npy文件,便于后续分析使用。
  3. 全流程耗时统计:精准统计模型加载、图像预处理、深度推理、可视化保存四个核心阶段的耗时及总耗时,以格式化方式输出(保留4位小数),便于分析性能瓶颈和优化方向。

代码如下所示:

python 复制代码
# 导入必要的库
from PIL import Image
import depth_pro  # DepthPro核心库,用于深度估计
import torch       # 深度学习框架,支撑模型推理
import matplotlib.pyplot as plt  # 可视化库,用于绘制深度图
import numpy as np  # 数值计算库,处理深度数据
import time        # 时间统计核心库
import matplotlib  # 用于配置matplotlib字体

# ===================== 解决matplotlib中文显示警告 =====================
# 方案1:Linux系统配置支持中文的字体(优先推荐)
matplotlib.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei', 'DejaVu Sans']  # 优先文泉驿,无则用默认
matplotlib.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 方案2(备选):若系统无中文字体,直接用英文(注释掉方案1,打开下面注释)
# matplotlib.rcParams['font.sans-serif'] = ['DejaVu Sans']

# ===================== 初始化计时变量(总耗时) =====================
total_start_time = time.time()

# ===================== 1. 加载模型和预处理工具(计时) =====================
model_load_start = time.time()
# 创建预训练的DepthPro模型和图像预处理变换
model, transform = depth_pro.create_model_and_transforms()
# 将模型设置为评估模式,关闭训练相关层
model.eval()

# 【可选优化】若有GPU,将模型移到GPU(大幅提速)
if torch.cuda.is_available():
    model = model.cuda()
    print("✅ 检测到GPU,已将模型移至GPU运行(推理速度会大幅提升)")
else:
    print("⚠️ 未检测到GPU,使用CPU运行(推理速度较慢)")

model_load_end = time.time()
model_load_time = model_load_end - model_load_start  # 模型加载耗时

# ===================== 2. 加载并预处理图像(计时) =====================
image_process_start = time.time()
# 待预测的图像路径(请替换为你的图片路径)
image_path = 'data/example.jpg'
# 加载RGB图像:返回图像、元数据、像素焦距f_px
image, _, f_px = depth_pro.load_rgb(image_path)
# 对图像进行预处理
image = transform(image)

# 【可选优化】若有GPU,将图像移到GPU
if torch.cuda.is_available():
    image = image.cuda()

image_process_end = time.time()
image_process_time = image_process_end - image_process_start  # 图像预处理耗时

# ===================== 3. 执行深度预测推理(计时) =====================
depth_infer_start = time.time()
# 执行推理:传入预处理后的图像和像素焦距
prediction = model.infer(image, f_px=f_px)
# 提取深度值(单位:米)
depth = prediction["depth"]
# 提取预测的像素焦距
focallength_px = prediction["focallength_px"]
depth_infer_end = time.time()
depth_infer_time = depth_infer_end - depth_infer_start  # 深度预测耗时

# ===================== 4. 深度图可视化与保存(计时) =====================
visualization_start = time.time()
# 将深度张量移回CPU并转换为numpy数组(便于可视化)
depth_np = depth.squeeze().cpu().numpy()

# 设置可视化样式
plt.figure(figsize=(10, 8))
plt.imshow(depth_np, cmap='jet')
# 颜色条标签(中文/英文可选)
plt.colorbar(label='深度值 (米)' if 'WenQuanYi' in matplotlib.rcParams['font.sans-serif'][0] else 'Depth Value (m)')
plt.axis('off')
# 标题(中文/英文可选)
plt.title('DepthPro 深度预测结果' if 'WenQuanYi' in matplotlib.rcParams['font.sans-serif'][0] else 'DepthPro Depth Prediction Result')
# 保存可视化结果
plt.savefig('data/depth_visualization.png', bbox_inches='tight', dpi=150)
plt.show()

# 保存深度值为numpy文件
np.save('data/depth_values.npy', depth_np)
visualization_end = time.time()
visualization_time = visualization_end - visualization_start  # 可视化+保存耗时

# ===================== 5. 输出时间统计结果 =====================
total_end_time = time.time()
total_time = total_end_time - total_start_time  # 总耗时

# 格式化输出(保留4位小数,单位:秒)
print("\n" + "="*50)
print("【DepthPro 各阶段耗时统计】")
print(f"1. 模型加载耗时:{model_load_time:.4f} 秒")
print(f"2. 图像预处理耗时:{image_process_time:.4f} 秒")
print(f"3. 深度预测推理耗时:{depth_infer_time:.4f} 秒")
print(f"4. 可视化+保存耗时:{visualization_time:.4f} 秒")
print(f"→ 总耗时:{total_time:.4f} 秒")
print("="*50)

运行效果:

5.2、多张张图片推理

  1. 批量深度估计处理:基于Apple DepthPro模型实现指定目录下多格式图片(jpg/jpeg/png/bmp/tiff等)的批量度量级深度估计(深度值单位为米),模型仅初始化一次,支持GPU/CPU自动检测与加速,大幅提升批量处理效率。
  2. 自动化目录与格式适配:自动创建输入/输出目录,兼容大小写图片格式,批量收集指定目录下所有支持格式的图片路径,无有效图片时友好提示并终止程序。
  3. 结果保存与可视化:对每张图片的深度推理结果,分别保存原始深度值的.npy文件和带色彩映射的深度可视化图片(jet配色),可视化时适配中文/英文标签显示,且及时关闭画布释放内存。

运行代码如下所示:

python 复制代码
# 导入必要的库
from PIL import Image
import depth_pro  # DepthPro核心库
import torch       # 深度学习框架
import matplotlib.pyplot as plt  # 可视化库
import numpy as np  # 数值计算库
import time        # 时间统计
import matplotlib  # 配置字体
import os          # 目录/文件操作
import glob        # 批量文件匹配

# ===================== 1. 基础配置(可根据需求修改) =====================
# 输入图片目录(请替换为你的图片目录)
INPUT_IMAGE_DIR = "data/input_images"
# 输出目录(自动创建:保存npy深度文件和可视化图片)
OUTPUT_NPY_DIR = "data/output_depth_npy"
OUTPUT_VIS_DIR = "data/output_depth_vis"
# 支持的图片格式(大小写都兼容)
SUPPORTED_FORMATS = ("*.jpg", "*.jpeg", "*.png", "*.bmp", "*.tiff", "*.JPG", "*.PNG")

# ===================== 2. 初始化配置(修复中文显示+创建目录) =====================
# 修复matplotlib中文显示
matplotlib.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei', 'DejaVu Sans']
matplotlib.rcParams['axes.unicode_minus'] = False

# 自动创建输出目录(不存在则创建)
os.makedirs(INPUT_IMAGE_DIR, exist_ok=True)
os.makedirs(OUTPUT_NPY_DIR, exist_ok=True)
os.makedirs(OUTPUT_VIS_DIR, exist_ok=True)

# ===================== 3. 加载模型(仅加载一次,提升效率) =====================
print("="*60)
print("【初始化DepthPro模型】")
model_load_start = time.time()

# 创建模型和预处理工具
model, transform = depth_pro.create_model_and_transforms()
model.eval()  # 评估模式

# GPU加速(自动检测,有则使用)
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(DEVICE)
print(f"✅ 模型加载完成,使用设备:{DEVICE}")

model_load_time = time.time() - model_load_start
print(f"模型加载耗时:{model_load_time:.4f} 秒")
print("="*60)

# ===================== 4. 批量处理图片 =====================
# 收集所有支持格式的图片路径
image_paths = []
for fmt in SUPPORTED_FORMATS:
    image_paths.extend(glob.glob(os.path.join(INPUT_IMAGE_DIR, fmt)))

if not image_paths:
    print(f"⚠️ 在目录 {INPUT_IMAGE_DIR} 中未找到支持的图片文件(格式:{SUPPORTED_FORMATS})")
    exit()

# 初始化统计变量
total_images = len(image_paths)
success_count = 0
fail_count = 0
total_infer_time = 0.0
total_vis_time = 0.0

print(f"\n【开始批量处理】共发现 {total_images} 张图片")
batch_start_time = time.time()

# 遍历每张图片处理
for idx, image_path in enumerate(image_paths, 1):
    try:
        print(f"\n[{idx}/{total_images}] 处理图片:{os.path.basename(image_path)}")
        
        # -------------------- 4.1 加载并预处理图片 --------------------
        img_start_time = time.time()
        # 加载RGB图片
        image, _, f_px = depth_pro.load_rgb(image_path)
        # 预处理
        image = transform(image).to(DEVICE)  # 移到指定设备(GPU/CPU)
        
        # -------------------- 4.2 深度预测推理 --------------------
        infer_start = time.time()
        prediction = model.infer(image, f_px=f_px)
        depth = prediction["depth"]  # 深度值(米)
        infer_time = time.time() - infer_start
        total_infer_time += infer_time
        
        # -------------------- 4.3 保存.npy深度文件 --------------------
        # 获取文件名(不含后缀)
        img_name = os.path.splitext(os.path.basename(image_path))[0]
        npy_save_path = os.path.join(OUTPUT_NPY_DIR, f"{img_name}_depth.npy")
        # 张量转numpy并保存
        depth_np = depth.squeeze().cpu().numpy()
        np.save(npy_save_path, depth_np)
        print(f"  ✅ 深度文件已保存:{npy_save_path}")
        
        # -------------------- 4.4 生成并保存可视化图片 --------------------
        vis_start = time.time()
        # 绘制深度图
        plt.figure(figsize=(10, 8))
        plt.imshow(depth_np, cmap='jet')
        plt.colorbar(label='深度值 (米)' if 'WenQuanYi' in matplotlib.rcParams['font.sans-serif'][0] else 'Depth Value (m)')
        plt.axis('off')
        plt.title(f'DepthPro - {img_name}')
        
        # 保存可视化图片
        vis_save_path = os.path.join(OUTPUT_VIS_DIR, f"{img_name}_depth.png")
        plt.savefig(vis_save_path, bbox_inches='tight', dpi=150)
        plt.close()  # 关闭画布,释放内存
        vis_time = time.time() - vis_start
        total_vis_time += vis_time
        print(f"  ✅ 可视化图片已保存:{vis_save_path}")
        
        # -------------------- 4.5 统计单张耗时 --------------------
        img_total_time = time.time() - img_start_time
        print(f"  ⏱️ 单张耗时:推理 {infer_time:.4f}s | 可视化 {vis_time:.4f}s | 总计 {img_total_time:.4f}s")
        
        success_count += 1

    except Exception as e:
        # 捕获异常,继续处理下一张
        fail_count += 1
        print(f"  ❌ 处理失败:{str(e)}")
        continue

# ===================== 5. 批量处理统计 =====================
batch_total_time = time.time() - batch_start_time
print("\n" + "="*60)
print("【批量处理完成 - 统计结果】")
print(f"总图片数:{total_images} | 成功:{success_count} | 失败:{fail_count}")
print(f"模型加载耗时:{model_load_time:.4f} 秒")
print(f"批量处理总耗时:{batch_total_time:.4f} 秒")
if success_count > 0:
    print(f"平均单张耗时:{batch_total_time/success_count:.4f} 秒")
    print(f"平均推理耗时:{total_infer_time/success_count:.4f} 秒/张")
    print(f"平均可视化耗时:{total_vis_time/success_count:.4f} 秒/张")
print(f"\n📁 输出目录:")
print(f"  - 深度npy文件:{OUTPUT_NPY_DIR}")
print(f"  - 可视化图片:{OUTPUT_VIS_DIR}")
print("="*60)

运行效果:

分享完成~

相关推荐
zh路西法18 天前
【双目相机 深度估计】PixelXYZ 3D 双目相机标定与实时深度估计全流程
python·opencv·相机标定·深度估计·双目相机·sgbm
这张生成的图像能检测吗8 个月前
(论文速读)LCT:用于RGB-D突出物体检测的轻型跨模态变压器
图像处理·目标检测·计算机视觉·深度估计·轻量化模型·跨模态融合·rgb-d
bryant_meng8 个月前
【Depth Estimation】learning notes
人工智能·深度学习·计算机视觉·深度估计·depth anything
这张生成的图像能检测吗1 年前
(论文速读)Prompt Depth Anything:让深度估计进入“提示时代“
深度学习·计算机视觉·深度估计
人类发明了工具1 年前
【机器人-深度估计】双目深度估计原理解析
数码相机·机器人·深度估计·双目深度估计
略知121 年前
SLAM十四讲【四】相机与图像
数码相机·3d·单目·三维重建·3d视觉·双目
点云SLAM2 年前
CVPR 2024 3D方向总汇包含(3DGS、三维重建、深度补全、深度估计、全景定位、表面重建和特征匹配等)
计算机视觉·特征匹配·三维重建·深度估计·3dgs·深度补全·三维匹配
Struart_R2 年前
DepthLab: From Partial to Complete 论文解读
人工智能·深度学习·计算机视觉·3d·深度估计·场景生成
阿_旭2 年前
Meta Sapiens太强了!人物姿态、部位分割、深度估计、表面法线预测一网打尽【附步骤源码】
深度学习·ai·姿态估计·深度估计·sapiens