
01. 为什么 Depth Pro 值得关注
Depth Pro 是 Apple 发布的单目度量深度估计模型,对应论文 Depth Pro: Sharp Monocular Metric Depth in Less Than a Second。从项目 README 看,它主打三个关键词:
– Sharp:输出边界锐利、细节丰富的深度图。
– Metric:预测带有绝对尺度的米制深度,而不仅是相对深度。
– Fast:README 中描述可在标准 GPU 上快速生成 2.25MP 深度图。
传统单目深度估计常见问题是:深度边界容易糊、尺度不稳定、依赖相机内参或数据域适配。Depth Pro 的前沿性在于,它将 高分辨率细节保持、多尺度 ViT 特征融合、单图焦距/FOV 估计、米制深度恢复 放进一个相对清晰的推理框架中。
简单说,它不是只回答“哪里近、哪里远”,而是在尝试回答:
这张普通 RGB 图片里的物体,距离相机大约多少米?它们的边界在哪里?📏
02. 项目整体架构
本地项目核心文件如下:
| 模块 | 作用 | 典型文件 |
|---|---|---|
| 模型创建与推理 | 组装编码器、解码器、深度头、FOV 头 | src/depth_pro/depth_pro.py |
| 多尺度编码器 | 图像金字塔、重叠 patch、ViT 特征提取 | src/depth_pro/network/encoder.py |
| 多分辨率解码器 | DPT 风格特征融合与上采样 | src/depth_pro/network/decoder.py |
| FOV / 焦距估计 | 从单图估计视场角,辅助恢复米制深度 | src/depth_pro/network/fov.py |
| 图像加载 | EXIF 读取、HEIC 支持、焦距像素换算 | src/depth_pro/utils.py |
| 命令行推理 | 单图/目录推理,保存 .npz 与可视化深度图 |
src/depth_pro/cli/run.py |
| 边界评估 | Scale-Invariant Boundary F1 / Recall | src/depth_pro/eval/boundary_metrics.py |
整体流程可以概括为:

03. 核心技术一:多尺度图像金字塔保留局部细节
Depth Pro 的关键设计之一,是它没有把整张大图简单缩小后直接预测深度,而是构建三层图像金字塔,并在高分辨率层面用重叠 patch 处理细节。
在 encoder.py 中,图像金字塔创建逻辑如下:
def _create_pyramid(
self, x: torch.Tensor
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
"""Create a 3-level image pyramid."""
# Original resolution: 1536 by default.
x0 = x
# Middle resolution: 768 by default.
x1 = F.interpolate(
x, size=None, scale_factor=0.5, mode="bilinear", align_corners=False
)
# Low resolution: 384 by default, corresponding to the backbone resolution.
x2 = F.interpolate(
x, size=None, scale_factor=0.25, mode="bilinear", align_corners=False
)
return x0, x1, x2
这意味着模型同时观察:
- 原始高分辨率图像:用于捕获边界、细线条、小物体。
- 中分辨率图像:用于理解局部结构。
- 低分辨率图像:用于获得全局布局和语义上下文。
这种设计非常适合深度估计,因为深度任务既需要全局空间布局,也需要边缘位置足够准确。只看低分辨率,边界容易糊;只看局部 patch,则可能失去场景整体尺度感。
04. 核心技术二:重叠 patch 编码,让高分辨率输入可计算
ViT 直接处理 1536×1536 图像会带来很高的计算成本。Depth Pro 的做法是把高分辨率图像切成 384×384 的重叠 patch,再批量送入 ViT 编码器。
关键代码如下:
def split(self, x: torch.Tensor, overlap_ratio: float = 0.25) -> torch.Tensor:
"""Split the input into small patches with sliding window."""
patch_size = 384
patch_stride = int(patch_size * (1 - overlap_ratio))
image_size = x.shape[-1]
steps = int(math.ceil((image_size - patch_size) / patch_stride)) + 1
x_patch_list = []
for j in range(steps):
j0 = j * patch_stride
j1 = j0 + patch_size
for i in range(steps):
i0 = i * patch_stride
i1 = i0 + patch_size
x_patch_list.append(x[..., j0:j1, i0:i1])
return torch.cat(x_patch_list, dim=0)
对应的合并逻辑如下:
def merge(self, x: torch.Tensor, batch_size: int, padding: int = 3) -> torch.Tensor:
"""Merge the patched input into a image with sliding window."""
steps = int(math.sqrt(x.shape[0] // batch_size))
idx = 0
output_list = []
for j in range(steps):
output_row_list = []
for i in range(steps):
output = x[batch_size * idx : batch_size * (idx + 1)]
if j != 0:
output = output[..., padding:, :]
if i != 0:
output = output[..., :, padding:]
if j != steps - 1:
output = output[..., :-padding, :]
if i != steps - 1:
output = output[..., :, :-padding]
output_row_list.append(output)
idx += 1
output_row = torch.cat(output_row_list, dim=-1)
output_list.append(output_row)
output = torch.cat(output_list, dim=-2)
return output
这里的 padding 裁剪很重要。重叠 patch 的边缘往往更容易出现不稳定特征,合并时裁掉边缘区域,可以减少拼接缝对深度图的影响。
工程上可以把它理解为:用局部窗口降低 ViT 计算压力,用重叠和裁剪保证空间连续性。
05. 核心技术三:DINOv2 ViT 作为视觉基础特征
项目默认使用 dinov2l16_384 预设。vit_factory.py 中的配置如下:
VIT_CONFIG_DICT = {
"dinov2l16_384": ViTConfig(
in_chans=3,
embed_dim=1024,
encoder_feature_layer_ids=[5, 11, 17, 23],
encoder_feature_dims=[256, 512, 1024, 1024],
img_size=384,
patch_size=16,
timm_preset="vit_large_patch14_dinov2",
timm_img_size=518,
timm_patch_size=14,
),
}
这段配置说明 Depth Pro 不是从零构建视觉表征,而是站在 DINOv2 这类视觉基础模型之上。它从多个 Transformer block 层抽取特征,并把这些特征交给后续密集预测解码器。
这样做的好处是:
- 低层特征更关注纹理和边缘。
- 中层特征更关注局部结构。
- 高层特征更关注物体与场景语义。
深度估计正需要这种多层信息融合:既要知道“杯子的边界在哪里”,也要知道“桌子、墙面、地面之间的空间关系”。
06. 核心技术四:多分辨率卷积解码器融合 ViT 特征
Depth Pro 的解码器位于 decoder.py,整体接近 Dense Prediction Transformer 的思想:把不同层级、不同分辨率的特征投影到统一通道数,再从低分辨率向高分辨率逐级融合。
class MultiresConvDecoder(nn.Module):
"""Decoder for multi-resolution encodings."""
def forward(self, encodings: torch.Tensor) -> torch.Tensor:
"""Decode the multi-resolution encodings."""
num_levels = len(encodings)
# Project features of different encoder dims to the same decoder dim.
# Fuse features from the lowest resolution (num_levels-1)
# to the highest (0).
features = self.convs[-1](encodings[-1])
lowres_features = features
features = self.fusions[-1](features)
for i in range(num_levels - 2, -1, -1):
features_i = self.convs[i](encodings[i])
features = self.fusions[i](features, features_i)
return features, lowres_features
其中 FeatureFusionBlock2d 负责残差融合和上采样:
def forward(self, x0: torch.Tensor, x1: torch.Tensor | None = None) -> torch.Tensor:
"""Process and fuse input features."""
x = x0
if x1 is not None:
res = self.resnet1(x1)
x = self.skip_add.add(x, res)
x = self.resnet2(x)
if self.use_deconv:
x = self.deconv(x)
x = self.out_conv(x)
return x
这类融合结构对高质量深度图很关键。深度边缘不是简单的图像边缘复制,而是语义、几何和纹理共同决定的结果。多层特征融合可以让模型在“边界锐利”和“空间连续”之间取得更好的平衡。
07. 核心技术五:从 canonical inverse depth 到米制深度
在 depth_pro.py 中,模型输出的是 canonical_inverse_depth,随后根据焦距进行尺度恢复:
canonical_inverse_depth, fov_deg = self.forward(x)
if f_px is None:
f_px = 0.5 * W / torch.tan(0.5 * torch.deg2rad(fov_deg.to(torch.float)))
inverse_depth = canonical_inverse_depth * (W / f_px)
f_px = f_px.squeeze()
if resize:
inverse_depth = nn.functional.interpolate(
inverse_depth, size=(H, W), mode=interpolation_mode, align_corners=False
)
depth = 1.0 / torch.clamp(inverse_depth, min=1e-4, max=1e4)
这里有一个非常重要的工程含义:
- 如果输入图像有 EXIF 焦距,模型可以使用真实焦距来恢复深度尺度。
- 如果没有焦距,模型会通过 FOV head 估计视场角,再推算像素焦距。
- 最终输出
depth的单位是米。
这正是 Depth Pro 与普通相对深度模型的关键区别。许多单目深度模型只能输出相对深度,需要后处理或标定才能变成可用的米制距离;Depth Pro 则把焦距估计纳入模型推理链路,降低了应用门槛。
08. 核心技术六:单图 FOV / 焦距估计
fov.py 中定义了 FOVNetwork:
class FOVNetwork(nn.Module):
"""Field of View estimation network."""
def __init__(
self,
num_features: int,
fov_encoder: Optional[nn.Module] = None,
):
super().__init__()
fov_head0 = [
nn.Conv2d(
num_features, num_features // 2, kernel_size=3, stride=2, padding=1
),
nn.ReLU(True),
]
fov_head = [
nn.Conv2d(
num_features // 2, num_features // 4, kernel_size=3, stride=2, padding=1
),
nn.ReLU(True),
nn.Conv2d(
num_features // 4, num_features // 8, kernel_size=3, stride=2, padding=1
),
nn.ReLU(True),
nn.Conv2d(num_features // 8, 1, kernel_size=6, stride=1, padding=0),
]
FOV 估计的输入包括低分辨率特征,也可以引入独立的 fov_encoder:
def forward(self, x: torch.Tensor, lowres_feature: torch.Tensor) -> torch.Tensor:
"""Forward the fov network."""
if hasattr(self, "encoder"):
x = F.interpolate(
x,
size=None,
scale_factor=0.25,
mode="bilinear",
align_corners=False,
)
x = self.encoder(x)[:, 1:].permute(0, 2, 1)
lowres_feature = self.downsample(lowres_feature)
x = x.reshape_as(lowres_feature) + lowres_feature
else:
x = lowres_feature
return self.head(x)
对应用方来说,这个模块的价值非常明确:许多业务图片来自手机、网页、监控、历史资料或用户上传,不一定保留可靠 EXIF。FOV head 为“无内参输入”提供了一条可行路径。
不过需要温和提醒:单图焦距估计仍然存在不确定性。在高精度测量、机器人执行或安全相关场景中,若能提供真实相机内参,仍应优先使用真实内参。
09. 输入图像处理:EXIF、HEIC 与焦距像素换算
utils.py 中的 load_rgb 对工程部署很实用。它支持 HEIC,并会从 EXIF 中读取 35mm 等效焦距,再换算成像素焦距:
def fpx_from_f35(width: float, height: float, f_mm: float = 50) -> float:
"""Convert a focal length given in mm (35mm film equivalent) to pixels."""
return f_mm * np.sqrt(width**2.0 + height**2.0) / np.sqrt(36**2 + 24**2)
图像读取逻辑也处理了方向、灰度图和 alpha 通道:
def load_rgb(
path: Union[Path, str], auto_rotate: bool = True, remove_alpha: bool = True
) -> Tuple[np.ndarray, List[bytes], float]:
...
if auto_rotate:
exif_orientation = img_exif.get("Orientation", 1)
if exif_orientation == 3:
img_pil = img_pil.transpose(Image.ROTATE_180)
elif exif_orientation == 6:
img_pil = img_pil.transpose(Image.ROTATE_270)
elif exif_orientation == 8:
img_pil = img_pil.transpose(Image.ROTATE_90)
img = np.array(img_pil)
if img.ndim < 3 or img.shape[2] == 1:
img = np.dstack((img, img, img))
if remove_alpha:
img = img[:, :, :3]
这说明 Depth Pro 项目并不只关注模型本身,也考虑了真实图片输入中常见的格式问题。对于部署而言,这些细节往往决定系统是否稳定。
10. CLI 推理与产物保存
项目提供了命令行入口:
depth-pro-run -i ./data/example.jpg
cli/run.py 中会自动选择设备:
def get_torch_device() -> torch.device:
"""Get the Torch device."""
device = torch.device("cpu")
if torch.cuda.is_available():
device = torch.device("cuda:0")
elif torch.backends.mps.is_available():
device = torch.device("mps")
return device
推理时默认使用半精度:
model, transform = create_model_and_transforms(
device=get_torch_device(),
precision=torch.half,
)
model.eval()
并保存两类结果:
np.savez_compressed(output_file, depth=depth)
cmap = plt.get_cmap("turbo")
color_depth = (cmap(inverse_depth_normalized)[..., :3] * 255).astype(
np.uint8
)
PIL.Image.fromarray(color_depth).save(
color_map_output_file, format="JPEG", quality=90
)
因此,Depth Pro 很适合快速封装成批处理工具:输入图片目录,输出可供算法使用的 .npz 深度矩阵,以及可供人工查看的彩色深度图。
11. 边界质量评估:为什么 Sharp 不只是视觉效果

Depth Pro 项目提供了专门的边界评估指标,位于 eval/boundary_metrics.py。例如:
def SI_boundary_F1(
predicted_depth: np.ndarray,
target_depth: np.ndarray,
t_min: float = 1.05,
t_max: float = 1.25,
N: int = 10,
) -> float:
"""Calculate Scale-Invariant Boundary F1 Score for depth-based ground-truth."""
assert predicted_depth.ndim == target_depth.ndim == 2
thresholds, weights = get_thresholds_and_weights(t_min, t_max, N)
f1_scores = np.array(
[
boundary_f1(invert_depth(predicted_depth), invert_depth(target_depth), t)
for t in thresholds
]
)
return np.sum(f1_scores * weights)
它关注的不是普通 RMSE 或 AbsRel,而是深度边界是否准确跟随真实物体边缘。
这对很多应用非常重要:
- AR 遮挡需要准确边界,否则虚拟物体会“穿帮”。
- 图像编辑需要前景边界清晰,否则抠图与景深效果会糊。
- 机器人抓取需要物体轮廓稳定,否则会误判可抓区域。
- 3D 重建与点云生成需要边界干净,否则会出现“拉丝”和悬浮噪声。
12. 技术应用领域与前景 🌍

1. AR / VR 与空间计算
Depth Pro 的米制深度和锐利边界,非常适合 AR 遮挡、虚拟物体放置、空间测距和场景理解。对于移动端空间计算,单目 RGB 输入的低门槛尤其重要。
2. 移动影像与计算摄影
手机照片的背景虚化、重打光、图像分层编辑、主体增强、景深合成,都依赖稳定的深度图。Depth Pro 的边界表现使它适合成为图像编辑链路中的深度基础模块。
3. 机器人与智能硬件
在没有深度相机或 ToF 传感器的设备上,Depth Pro 可以提供单目距离估计能力。它可用于导航避障、桌面物体理解、抓取候选区域估计等任务。
4. 电商、文旅与三维内容生产
单张商品图或场景图的深度估计,可以辅助生成视差动效、伪 3D 展示、前后景分层、简单点云或 mesh 初始化。它不会直接替代完整 3D 扫描,但能降低内容生产门槛。
5. 自动驾驶与智能交通
单目深度可以辅助道路结构理解、障碍物距离估计和数据标注。但自动驾驶属于高风险场景,Depth Pro 更适合作为辅助感知或离线标注工具,不能单独承担安全决策。
13. 部署应用建议
1. 环境准备
项目建议 Python 3.9,并可通过 editable install 安装:
conda create -n depth-pro -y python=3.9
conda activate depth-pro
pip install -e .
pyproject.toml 中的核心依赖包括:
torch
torchvision
timm
numpy<2
pillow_heif
matplotlib
下载预训练权重:
source get_pretrained_models.sh
脚本会把权重下载到 checkpoints/depth_pro.pt:
mkdir -p checkpoints
wget https://ml-site.cdn-apple.com/models/depth-pro/depth_pro.pt -P checkpoints
2. 推理服务封装建议
建议将 Depth Pro 服务拆成以下模块:
API 层:上传图片、指定是否返回可视化图、是否传入焦距
预处理层:格式转换、EXIF 读取、方向修正、RGB 标准化
推理层:transform -> model.infer -> depth/focallength_px
后处理层:保存 npz、生成伪彩色深度图、输出统计指标
质量控制层:检查异常深度、极端焦距、图像分辨率和模糊度
如果用于批量处理图片目录,可以直接参考 CLI 的路径遍历和输出保存方式。
3. 焦距与内参使用建议
Depth Pro 支持两种尺度恢复路径:
| 输入条件 | 推荐做法 | 稳定性 |
|---|---|---|
| 有真实相机内参 | 传入真实 f_px |
最稳定 |
| 有 EXIF 35mm 等效焦距 | 使用 load_rgb 自动换算 |
较稳定 |
| 无 EXIF / 无内参 | 使用 FOV head 自动估计 | 方便,但需质量检查 |
实际产品中,如果能从设备 SDK、相机标定文件或拍摄元数据中获得焦距,应优先传给 model.infer(image, f_px=f_px)。
4. 性能优化建议
- GPU 推理建议使用
torch.half,项目 CLI 已采用半精度。
- 服务端可在启动时加载模型,避免每次请求重复加载权重。
- 对超大图像应设置上传尺寸上限,避免预处理和内存压力不可控。
- 批量任务建议使用队列,记录任务 ID、输入路径、输出路径、耗时和设备信息。
- 对实时应用,可考虑缓存 transform 后的张量或对视频做关键帧抽取。
- 若部署到 Apple Silicon,可利用 MPS,但仍需根据实际算子支持与速度做测试。
5. 输出格式建议
建议至少保存三类产物:
| 产物 | 用途 |
|---|---|
.npz depth 矩阵 |
算法后处理、测距、3D 转换 |
彩色深度图 .jpg/.png |
人工检查、报告展示、前端预览 |
| JSON 元数据 | 输入尺寸、焦距来源、估计焦距、模型版本、耗时 |
对于需要 Web 展示的系统,可以进一步把 depth 转为:
- WebGL 视差效果图。
- 简单点云。
- mesh 初始化结果。
- 前后景分层 mask。
14. 风险边界与质量控制
Depth Pro 已经显著提高了单目深度可用性,但单目深度估计仍然不是测量仪器。部署时建议关注以下问题:
| 风险 | 说明 | 建议 |
|---|---|---|
| 焦距估计误差 | 无 EXIF 时依赖模型估计 FOV | 高精度场景优先使用真实内参 |
| 透明/反光物体 | 玻璃、镜子、水面深度不稳定 | 标记低可信场景,必要时人工复核 |
| 纹理缺失区域 | 白墙、天空、大面积平面可能偏差大 | 结合语义分割或规则过滤 |
| 动态场景 | 运动模糊、滚动快门会影响结果 | 上传前做图像质量筛查 |
| 高风险决策 | 自动驾驶、机械臂执行不能只依赖单目深度 | 与多传感器、规则约束和安全策略融合 |
温和但重要的一点是:Depth Pro 输出的是“模型估计的米制深度”,不是经过物理标定和现场验收的工程测距结果。用于生产系统时,应为不同场景建立误差评估集。
15. 与传统深度方案的关系
| 方案 | 优点 | 局限 |
|---|---|---|
| 双目深度 | 几何约束明确,可测距 | 需要双目硬件与标定 |
| ToF / LiDAR | 直接获得物理距离 | 成本、功耗、分辨率或反光问题 |
| 普通相对深度模型 | 输入简单,泛化较好 | 缺少绝对尺度 |
| Depth Pro | 单目输入、米制尺度、边界锐利 | 对焦距估计、数据域和高风险场景仍需控制 |
未来更现实的方向不是让 Depth Pro 单独替代所有传感器,而是作为视觉深度基础模型,与相机内参、IMU、SLAM、语义分割、3D 重建和多传感器融合共同工作。
16. 小结:Depth Pro 的真正意义 ✨
Depth Pro 的价值不只是“从单张图片估计深度”,而是把单目深度推向了更接近真实应用的三个方向:
- 更锐利的边界,让深度图能服务于编辑、AR 和机器人感知。
- 更明确的米制尺度,让输出更接近工程可用的距离信息。
- 更低的输入门槛,让没有完整相机内参的普通图片也能进入空间理解流程。
如果说过去的单目深度更多是视觉效果和相对结构理解,那么 Depth Pro 代表的是一种更务实的趋势:让普通 RGB 图像具备可计算、可度量、可部署的空间感。
它还不是万能的测距设备,但已经是单目空间智能向前迈出的一大步。对于 AR、计算摄影、机器人、内容生产和数据标注等方向,Depth Pro 都值得作为重点技术储备来评估和跟进。