深入理解与实战:Transformers 中的 ZoeDepth 单图度量深度估计
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
ZoeDepth 是 Transformers 中新增的深度估计(depth estimation)模型,它将相对深度估计(相对远近)与度量深度估计(真实尺度下的精确距离)统一到同一个网络之中,并能在推理时通过潜变量分类器自动为每张输入图片选择最合适度量头(metric head)。本文以 ZoeDepth 官方文档 为主线,结合仓库内 模型实现、配置类 与 图像处理器 源码,系统讲解其原理、Pipeline/AutoModel推理、后处理细节与全部配置参数,读完后你将能直接用Intel/zoedepth-nyu-kitti等预训练权重完成单张 RGB 图片的度量深度估计。
模型概述:相对深度与度量深度的融合
ZoeDepth 论文于 2023-02-23 发表于 HF Papers,其官方实现于 2024-07-08 合入 Hugging Face Transformers。该模型的核心思想是:从单张图片中同时获得相对深度估计(物体之间的远近关系)与度量深度估计(在度量尺度上的精确深度值)。
- 预训练数据:使用相对深度在 12 个数据集上预训练;使用NYU Depth v2与KITTI两个数据集获得度量精度。
- 头部设计:为每个域(domain)配备一个轻量级头部,内含一个metric bin module(度量分箱模块)。
- 推理路由:推理时由一个**潜变量分类器(latent classifier)**自动为每张输入图片选择最合适的度量头部。
原始 ZoeDepth 的全部检查点位于 Intel 组织下(例如Intel/zoedepth-nyu、Intel/zoedepth-nyu-kitti),均可直接通过 Transformers 加载。
在仓库中,ZoeDepth 的完整实现集中在src/transformers/models/zoedepth/目录,共 6 个文件:
| 文件 | 职责 |
|---|---|
| configuration_zoedepth.py | ZoeDepthConfig配置类 |
| modeling_zoedepth.py | ZoeDepthForDepthEstimation模型与前向逻辑 |
| image_processing_zoedepth.py | 基于 torchvision 后端的ZoeDepthImageProcessor |
| image_processing_pil_zoedepth.py | 基于 PIL 后端的ZoeDepthImageProcessorPil |
| convert_zoedepth_to_hf.py | 权重转换脚本 |
__init__.py | 模块导出入口 |
从源码看架构:骨干网络 + 颈部 + 相对头 + 度量头
从ZoeDepthForDepthEstimation的__init__(见 modeling_zoedepth.py)可以看出,整个模型按四段式组装:
self.backbone = load_backbone(config) # 骨干网络(默认 BEiT) self.neck = ZoeDepthNeck(config) # 颈部:reassemble + fusion self.relative_head = ZoeDepthRelativeDepthEstimationHead(config) # 相对深度头 self.metric_head = ( ZoeDepthMultipleMetricDepthEstimationHeads(config) # 多度量头(如 NYU+KITTI) if len(config.bin_configurations) > 1 else ZoeDepthMetricDepthEstimationHead(config) # 单度量头 )骨干网络(Backbone)
ZoeDepth 使用load_backbone(config)加载骨干。默认配置(见 configuration_zoedepth.py)是BEiT,image_size=384、24 层、hidden_size=1024、intermediate_size=4096、16 注意力头,并启用相对位置偏置,输出["stage6", "stage12", "stage18", "stage24"]四组特征。骨干必须提供hidden_size与patch_size属性,否则模型会直接抛出ValueError(见 modeling_zoedepth.py)。
颈部(Neck):重排与特征融合
ZoeDepthNeck包含两个阶段(与 DPT 同源,代码注释标明大量结构 Copied from DPT):
ZoeDepthReassembleStage:把骨干输出的序列化 token 重排成图像状特征图,依次处理[CLS]readout token(按readout_type为ignore/add/project)、按neck_hidden_sizes投影通道数、按reassemble_factors做上/下采样。ZoeDepthFeatureFusionStage:自顶向下融合多尺度特征,内部使用预激活残差卷积单元(ZoeDepthPreActResidualLayer,ReLU → Conv → ReLU → Conv 后再加回残差)。
注意,从源码看,若骨干是层级式骨干(如swinv2),则reassemble_stage会被跳过(modeling_zoedepth.py)。
相对深度头(Relative Head)
ZoeDepthRelativeDepthEstimationHead由 3 个卷积层构成:首层把通道减半并上采样 2 倍,随后输出num_relative_features(默认 32)个特征,最后 1×1 卷积得到单通道相对深度。其输出还会作为后续度量头的“相对深度条件”参与计算(见ZoeDepthMetricDepthEstimationHead.forward中对relative_depth的拼接)。
度量头(Metric Head)与度量分箱
度量头是 ZoeDepth 的精髓,由一组在源码中被称为“attractor”(吸引子)的机制驱动:
ZoeDepthSeedBinRegressor:先回归一组“种子 bin 中心”,bin_centers_type="normed"时通过线性归一化把 bin 中心约束在(min_depth, max_depth)区间,"softplus"时无界。ZoeDepthAttractorLayer/ZoeDepthAttractorLayerUnnormed:迭代精化 bin 中心。每个阶段先由一个 MLP 预测吸引子点,再通过inv_attractor公式dc = dx / (1 + alpha * dx^gamma)计算 bin 中心偏移。源码注释特别指出,为兼容原始权重,实现中保留了原仓库的一个 bug(modeling_zoedepth.py)。ZoeDepthConditionalLogBinomialSoftmax:用逐像素 MLP 输出“概率 + 温度”,温度被线性缩放到(min_temp, max_temp)(默认 0.0212~50.0),最终以 Log-Binomial softmax 给出每个像素在各 bin 上的分布;深度值由sum(p * bin_centers)得到。- 多度量头路由:
ZoeDepthMultipleMetricDepthEstimationHeads内置一个 patch transformer(ZoeDepthPatchTransformerEncoder)与ZoeDepthMLPClassifier(输出 2 类 logits,对应 NYU / KITTI 两个域)。前向时对 batch 的domain_logits求和做 softmax 取 argmax,从而自动选择该批次最合适的度量头(modeling_zoedepth.py)。
输出结构
模型返回ZoeDepthDepthEstimatorOutput(继承自DepthEstimatorOutput),在标准字段之外额外包含domain_logits,即“每个域(如 NYU 与 KITTI)的 logits”(见 modeling_zoedepth.py)。当使用多个度量头时,predicted_depth与domain_logits同时非空;单度量头时domain_logits为None。
快速开始:用 Pipeline 一行代码估计深度
官方文档给出的最简用法是直接使用Pipeline。在src/transformers/models/zoedepth对应的测试中,pipeline_model_mapping = {"depth-estimation": ZoeDepthForDepthEstimation}(见 test_modeling_zoedepth.py),因此深度估计任务会自动路由到 ZoeDepth:
import requests from PIL import Image from transformers import pipeline url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" image = Image.open(requests.get(url, stream=True).raw) pipeline = pipeline( task="depth-estimation", model="Intel/zoedepth-nyu-kitti", device=0 ) results = pipeline(image) results["depth"]要点说明:
task="depth-estimation"会依据pipeline_model_mapping自动实例化ZoeDepthForDepthEstimation及配套图像处理器;device=0指定 GPU;无 GPU 时可省略该参数或置为-1;- 返回的
results["depth"]是处理后的深度图(PIL 图像),可直接保存或可视化。
进阶用法:AutoModel 手动控制预处理与后处理
当需要精细控制预处理/后处理(例如做翻转增强、指定输出尺寸)时,官方文档推荐使用AutoImageProcessor+AutoModelForDepthEstimation:
import requests import torch from PIL import Image from transformers import AutoImageProcessor, AutoModelForDepthEstimation image_processor = AutoImageProcessor.from_pretrained( "Intel/zoedepth-nyu-kitti" ) model = AutoModelForDepthEstimation.from_pretrained( "Intel/zoedepth-nyu-kitti", device_map="auto" ) url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" image = Image.open(requests.get(url, stream=True).raw) inputs = image_processor(image, return_tensors="pt").to(model.device) with torch.no_grad(): outputs = model(inputs) # interpolate to original size and visualize the prediction ## ZoeDepth dynamically pads the input image, so pass the original image size as argument ## to `post_process_depth_estimation` to remove the padding and resize to original dimensions. post_processed_output = image_processor.post_process_depth_estimation( outputs, source_sizes=[(image.height, image.width)], ) predicted_depth = post_processed_output[0]["predicted_depth"] depth = (predicted_depth - predicted_depth.min()) / (predicted_depth.max() - predicted_depth.min()) depth = depth.detach().cpu().numpy() * 255 Image.fromarray(depth.astype("uint8"))这段代码体现了 ZoeDepth 使用中最关键的一个约定:ZoeDepth 会对输入图片做动态 padding,因此必须把原图尺寸(height, width)以source_sizes传给post_process_depth_estimation,函数内部会移除 padding 并把预测插值回原始尺寸。这是与 DPT、GLPN 等模型在使用上的显著差异,务必留意。
单/批量形状约定
- 预处理输出:
pixel_values,形状(batch_size, 3, H, W); - 模型输出:
predicted_depth形状(batch_size, H, W),即不带通道维; - 后处理输出:
list[dict],每个元素含predicted_depth,形状(H, W)(见 image_processing_zoedepth.py)。
上述约定有测试佐证:在 test_modeling_zoedepth.py 中,result.predicted_depth.shape == (batch_size, image_size, image_size);集成测试也验证了Intel/zoedepth-nyu与Intel/zoedepth-nyu-kitti在 384×512 输入下输出形状为(1, 384, 512)(test_modeling_zoedepth.py)。
后处理细节:动态 padding 与翻转增强
padding 的来源与去除
ZoeDepthImageProcessor在预处理时默认启用do_pad=True:先按pad_height = int(sqrt(height/2) * 3)、pad_width = int(sqrt(width/2) * 3)对图像做 reflect(镜像)padding(见 image_processing_zoedepth.py)。其目的是修复深度图边界伪影(boundary artifacts)。相应地,post_process_depth_estimation会用同样的公式反向裁剪掉 padding,再插值回原始尺寸。
因此调用后处理时有两条约束(源码中会直接抛ValueError):
- 若不传
source_sizes,则必须显式传do_remove_padding=False; - 传入的
source_sizes数量必须等于 batch 大小。
此外还可用target_sizes指定最终输出分辨率(双三次插值),例如在 test_modeling_zoedepth.py 中把输出放大 2 倍做验证。
翻转增强(flip augmentation)
原版 ZoeDepth 实现会对原始图与水平翻转图各推理一次并平均结果(官方文档注释指向原仓库depth_model.py的对应实现)。Transformers 的post_process_depth_estimation通过可选的outputs_flipped参数支持这一策略:
with torch.no_grad(): outputs = model(pixel_values) outputs_flipped = model(pixel_values=torch.flip(inputs.pixel_values, dims=[3])) post_processed_output = image_processor.post_process_depth_estimation( outputs, source_sizes=[(image.height, image.width)], outputs_flipped=outputs_flipped, )实现中,若传入outputs_flipped,则对两个输出做(pred + flip(pred_flipped)) / 2的平均(见 image_processing_zoedepth.py)。同时要求两个输出的predicted_depth形状一致。测试中针对 “pad 与 flip” 的四种组合(pad/flip 各开/关)均做了数值校验(test_modeling_zoedepth.py),说明四种策略都是受支持的合法用法。
ZoeDepthConfig 全部配置参数详解
ZoeDepthConfig继承自PreTrainedConfig,model_type="zoedepth",且通过sub_configs = {"backbone_config": AutoConfig}支持内嵌骨干配置。以下参数与默认值均来自 configuration_zoedepth.py:
骨干与颈部相关
| 参数 | 默认值 | 说明 |
|---|---|---|
backbone_config | None | 骨干配置(dict 或PreTrainedConfig);不传时默认构建 BEiT 骨干 |
hidden_act | "gelu" | 激活函数,readout 投影等处使用 |
initializer_range | 0.02 | 参数初始化范围 |
batch_norm_eps | 1e-05 | 融合残差块中 BatchNorm 的 epsilon |
readout_type | "project" | 处理骨干中间层[CLS]readout token 的方式:"ignore"(忽略)、"add"(加到所有 token 上)、"project"(拼接后经线性层+GELU 投影回原维度 D) |
reassemble_factors | [4, 2, 1, 0.5] | 重排层的上/下采样因子 |
neck_hidden_sizes | [96, 192, 384, 768] | 骨干各阶段特征图投影到的通道数 |
fusion_hidden_size | 256 | 融合前的通道数(融合块内部工作通道) |
head_in_index | -1 | 相对头使用的特征索引(-1 即最后一组) |
use_batch_norm_in_fusion_residual | False | 融合残差块是否使用 BatchNorm |
use_bias_in_fusion_residual | None | 融合残差卷积是否使用 bias;为None时取“非 BatchNorm”作为默认 |
相对头与度量头相关
| 参数 | 默认值 | 说明 |
|---|---|---|
num_relative_features | 32 | 相对深度头输出的特征数 |
add_projection | False | 是否在深度头前加投影层 |
bottleneck_features | 256 | 瓶颈层特征数 |
num_attractors | [16, 8, 4, 1] | 每个阶段的吸引子数量 |
bin_embedding_dim | 128 | bin 嵌入维度 |
attractor_alpha | 1000 | 吸引子强度:alpha 越小吸引越强 |
attractor_gamma | 2 | 吸引子指数:gamma 越小影响范围越远 |
attractor_kind | "mean" | 吸引子聚合方式:"mean"或"sum" |
min_temp | 0.0212 | 条件 Log-Binomial 的最小温度 |
max_temp | 50.0 | 最大温度 |
bin_centers_type | "softplus" | bin 中心激活:"normed"(线性归一化,有界)或"softplus"(无界) |
bin_configurations | [{"n_bins": 64, "min_depth": 0.001, "max_depth": 10.0}] | 每个 bin 头的配置;传多个配置时启用多度量头路由 |
多度量头专属参数(仅多 bin 配置时生效)
| 参数 | 默认值 | 说明 |
|---|---|---|
num_patch_transformer_layers | None | patch transformer 层数(用于域分类) |
patch_transformer_hidden_size | None | patch transformer 隐藏维度 |
patch_transformer_intermediate_size | None | patch transformer 前馈中间维度 |
patch_transformer_num_attention_heads | None | patch transformer 注意力头数 |
关于bin_configurations的使用约定
bin_configurations的每个字典项需包含n_bins(bin 数量)、min_depth(最小深度)、max_depth(最大深度);只有在传入多个配置时才需要name字段。从源码逻辑可以推断:
- 仅一个配置 → 使用
ZoeDepthMetricDepthEstimationHead(单度量头),且该头部会把相对深度拼接到最后一层特征上作为条件; - 多个配置 → 使用
ZoeDepthMultipleMetricDepthEstimationHeads,此时需要name(如"nyu"、"kitti")用于构建ModuleDict并做域路由。
Intel/zoedepth-nyu-kitti之所以能在室内(NYU)与室外(KITTI)场景下都输出合理尺度,正是因为在推理时由 patch transformer + MLP 分类器在"nyu"与"kitti"两个度量头之间自动抉择。
图像处理器:torchvision 与 PIL 两种后端
ZoeDepth 提供两个图像处理器,它们的预处理/后处理行为一致,仅底层后端不同:
ZoeDepthImageProcessor(image_processing_zoedepth.py):基于TorchvisionBackend,适合 GPU 上批量张量处理;ZoeDepthImageProcessorPil(image_processing_pil_zoedepth.py):基于PilBackend,适合 CPU / PIL 管线,集成测试即使用该后端。
两者的默认预处理参数完全一致(类属性直接可查):
| 属性 | 默认值 |
|---|---|
do_resize | True |
size | {"height": 384, "width": 512} |
resample | PILImageResampling.BILINEAR |
keep_aspect_ratio | True |
ensure_multiple_of | 1 / 32 |
do_pad | True |
do_rescale | True |
do_normalize | True |
image_mean/image_std | ImageNet 标准均值/标准差 |
两个关键缩放参数:
keep_aspect_ratio=True:按高、宽缩放因子中“更接近 1 的那一个”(即尽量少缩放)统一缩放两个维度,保持宽高比;ensure_multiple_of=1/32:把高宽向下取整到该值的整数倍,保证缩放后尺寸能被骨干的 patch 尺寸整除。
在_preprocess中,torchvision 后端还利用group_images_by_shape对同尺寸图片做批量 resize,随后用reorder_images恢复原始顺序,从而兼顾 batch 效率与形状正确性(image_processing_zoedepth.py)。
测试与验证:如何确认实现正确性
仓库在 tests/models/zoedepth/test_modeling_zoedepth.py 中提供了完整的单元测试与集成测试:
ZoeDepthModelTest基于ModelTesterMixin与PipelineTesterMixin,覆盖配置序列化、前向形状(predicted_depth形状等于(batch, H, W))、pipeline 路由等;- 由于 ZoeDepth 没有 base model /
input_embeddings,测试明确跳过test_inputs_embeds、test_model_get_set_embeddings等用例,并跳过训练相关用例(见 test_modeling_zoedepth.py); ZoeDepthModelIntegrationTest使用@slow标记,加载真实权重Intel/zoedepth-nyu与Intel/zoedepth-nyu-kitti,在 COCO 样例图片上校验输出数值切片,并覆盖 pad/flip 四种后处理组合。
如果你在本地复现,可仿照集成测试的写法:加载图像处理器与模型后,用with torch.no_grad()前向一次,再调用post_process_depth_estimation并与预期切片对比。
使用限制与注意事项
- 暂不支持训练:
ZoeDepthForDepthEstimation.forward中一旦传入labels会直接抛出NotImplementedError("Training is not implemented yet")(modeling_zoedepth.py),本仓库中的 ZoeDepth 定位为推理用途; - 输入为单张 RGB 图片:
input_modalities = ("image",),不涉及文本或音频输入; - 动态 padding 约定:任何自定义预处理都必须与
post_process_depth_estimation的source_sizes/do_remove_padding参数配合,否则要么报错、要么产生边界伪影; - 设备与显存:默认骨干为 BEiT-Large 规格(1024 隐藏维度),显存有限时可使用
device_map="auto"(如官方 AutoModel 示例)让 accelerate 自动切分; - 检查点来源:原始权重托管在 Intel 组织下,本文示例使用的
Intel/zoedepth-nyu-kitti(室内+室外双域路由)与Intel/zoedepth-nyu(单域)均可直接加载。
综上,ZoeDepth 在 Transformers 中的实现完整保留了原论文的“相对+度量双头、度量分箱、潜变量域路由”三大设计,配合统一的AutoImageProcessor/Pipeline接口,使单图度量深度估计的开箱即用变得非常简单。如需进一步了解推理示例,官方文档还推荐参考 Transformers-Tutorials 仓库中的 ZoeDepth 笔记本;在本文档库内,ZoeDepth 文档 与上述源码、测试文件可作为继续深入的第一手资料。
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考