Diffusers 文档
Marigold 计算机视觉
并获得增强的文档体验
开始使用
Marigold 计算机视觉
Marigold 是一种基于扩散的方法,它包含一系列pipelines,专为稠密的计算机视觉任务设计,包括单目深度预测、表面法线估计和本征图像分解。
本指南将引导您使用 Marigold 为图像和视频生成快速且高质量的预测。
每个 pipeline 都针对特定的计算机视觉任务量身定制,处理输入的 RGB 图像并生成相应的预测。目前,已实现以下计算机视觉任务:
所有原始检查点均在 Hugging Face 上的 PRS-ETH 组织下提供。它们设计用于 diffusers pipelines 和原始代码库,后者也可用于训练新的模型检查点。以下是推荐检查点的总结,所有这些检查点在 1 到 4 步内都能产生可靠的结果。
模型权重 | 模态 | 评论 |
---|---|---|
prs-eth/marigold-depth-v1-1 | 深度 | 仿射不变深度预测为每个像素分配一个介于 0(近平面)和 1(远平面)之间的值,这两个平面均由模型在推理过程中确定。 |
prs-eth/marigold-normals-v0-1 | 法线 | 表面法线预测是屏幕空间相机中的单位长度三维向量,其值在 -1 到 1 的范围内。 |
prs-eth/marigold-iid-appearance-v1-1 | 本征属性 | InteriorVerse 分解由反照率和两种 BRDF 材质属性组成:粗糙度和金属度。 |
prs-eth/marigold-iid-lighting-v1-1 | 本征属性 | HyperSim 图像分解由反照率、漫反射阴影和非漫反射残差:. |
下面的示例主要针对深度预测,但它们可以普遍应用于其他支持的模态。我们使用由 Midjourney 生成的同一张阿尔伯特·爱因斯坦的输入图像来展示预测结果。这使得比较不同模态和检查点的预测可视化变得更加容易。

深度预测
要获得深度预测,请将 `prs-eth/marigold-depth-v1-1` 检查点加载到 MarigoldDepthPipeline 中,将图像通过 pipeline 处理,然后保存预测结果。
import diffusers
import torch
pipe = diffusers.MarigoldDepthPipeline.from_pretrained(
"prs-eth/marigold-depth-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
depth = pipe(image)
vis = pipe.image_processor.visualize_depth(depth.prediction)
vis[0].save("einstein_depth.png")
depth_16bit = pipe.image_processor.export_depth_to_16bit_png(depth.prediction)
depth_16bit[0].save("einstein_depth_16bit.png")
visualize_depth() 函数应用 matplotlib 的颜色映射表 之一(默认为 `Spectral`),将预测的像素值从单通道的 `[0, 1]` 深度范围映射到 RGB 图像中。使用 `Spectral` 颜色映射表时,近深度的像素被涂成红色,远深度的像素被涂成蓝色。16 位 PNG 文件存储了从 `[0, 1]` 范围线性映射到 `[0, 65535]` 的单通道值。以下是原始预测和可视化预测。在可视化中,较暗和较近的区域(例如胡须)更容易区分。


表面法线估计
将 `prs-eth/marigold-normals-v1-1` 检查点加载到 MarigoldNormalsPipeline 中,将图像通过 pipeline 处理,然后保存预测结果。
import diffusers
import torch
pipe = diffusers.MarigoldNormalsPipeline.from_pretrained(
"prs-eth/marigold-normals-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
normals = pipe(image)
vis = pipe.image_processor.visualize_normals(normals.prediction)
vis[0].save("einstein_normals.png")
visualize_normals() 函数将像素值在 `[-1, 1]` 范围内的三维预测映射到 RGB 图像中。该可视化函数支持翻转表面法线坐标轴,以使可视化与其他参考系选择兼容。从概念上讲,每个像素根据参考系中的表面法线向量进行着色,其中 `X` 轴指向右,`Y` 轴指向上,`Z` 轴指向观察者。以下是可视化的预测结果:

在此示例中,鼻尖几乎肯定有一个表面点,其表面法线向量直指观察者,这意味着其坐标为 `[0, 0, 1]`。该向量映射到 RGB `[128, 128, 255]`,对应于紫蓝色。类似地,图像右侧脸颊上的表面法线具有较大的 `X` 分量,这增加了红色调。肩膀上指向上方的点具有较大的 `Y` 分量,这促进了绿色。
本征图像分解
Marigold 为本征图像分解(IID)提供了两个模型:“外观(Appearance)”和“光照(Lighting)”。每个模型都生成反照率图,分别源自 InteriorVerse 和 Hypersim 的标注。
- “外观”模型还估计材质属性:粗糙度和金属度。
- “光照”模型生成漫反射阴影和非漫反射残差。
以下是保存由“外观”模型所做预测的示例代码:
import diffusers
import torch
pipe = diffusers.MarigoldIntrinsicsPipeline.from_pretrained(
"prs-eth/marigold-iid-appearance-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
intrinsics = pipe(image)
vis = pipe.image_processor.visualize_intrinsics(intrinsics.prediction, pipe.target_properties)
vis[0]["albedo"].save("einstein_albedo.png")
vis[0]["roughness"].save("einstein_roughness.png")
vis[0]["metallicity"].save("einstein_metallicity.png")
另一个演示“光照”模型预测的示例:
import diffusers
import torch
pipe = diffusers.MarigoldIntrinsicsPipeline.from_pretrained(
"prs-eth/marigold-iid-lighting-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
intrinsics = pipe(image)
vis = pipe.image_processor.visualize_intrinsics(intrinsics.prediction, pipe.target_properties)
vis[0]["albedo"].save("einstein_albedo.png")
vis[0]["shading"].save("einstein_shading.png")
vis[0]["residual"].save("einstein_residual.png")
两种模型共享相同的 pipeline,同时支持不同的分解类型。确切的分解参数化(例如,sRGB 与线性空间)存储在 `pipe.target_properties` 字典中,该字典被传递到 visualize_intrinsics() 函数中。
以下是一些展示预测分解输出的示例。所有模态都可以在本征图像分解 Space 中进行检查。


加速推理
以上快速入门代码片段已经针对质量和速度进行了优化,加载了检查点,利用了 `fp16` 版本的权重和计算,并执行了默认数量(4)的去噪扩散步骤。加速推理的第一步是以牺牲预测质量为代价,将去噪扩散步骤减少到最小值:
import diffusers
import torch
pipe = diffusers.MarigoldDepthPipeline.from_pretrained(
"prs-eth/marigold-depth-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
- depth = pipe(image)
+ depth = pipe(image, num_inference_steps=1)
通过此更改,在 RTX 3090 GPU 上,`pipe` 调用在 280 毫秒内完成。在内部,输入图像首先使用 Stable Diffusion VAE 编码器进行编码,然后由 U-Net 执行单个去噪步骤。最后,预测的潜变量通过 VAE 解码器解码到像素空间。在这种设置中,三个模块调用中有两个专用于在像素和 LDM 的潜空间之间进行转换。由于 Marigold 的潜空间与 Stable Diffusion 2.0 兼容,通过使用轻量级的 SD VAE 替代品,推理速度可以提高 3 倍以上,将 RTX 3090 上的调用时间减少到 85 毫秒。请注意,使用轻量级 VAE 可能会略微降低预测的视觉质量。
import diffusers
import torch
pipe = diffusers.MarigoldDepthPipeline.from_pretrained(
"prs-eth/marigold-depth-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
+ pipe.vae = diffusers.AutoencoderTiny.from_pretrained(
+ "madebyollin/taesd", torch_dtype=torch.float16
+ ).cuda()
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
depth = pipe(image, num_inference_steps=1)
到目前为止,我们已经优化了扩散步骤和模型组件的数量。自注意力操作占用了相当大一部分的计算量。可以通过使用更高效的注意力处理器来加速它们:
import diffusers
import torch
+ from diffusers.models.attention_processor import AttnProcessor2_0
pipe = diffusers.MarigoldDepthPipeline.from_pretrained(
"prs-eth/marigold-depth-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
+ pipe.vae.set_attn_processor(AttnProcessor2_0())
+ pipe.unet.set_attn_processor(AttnProcessor2_0())
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
depth = pipe(image, num_inference_steps=1)
最后,如优化中所建议,启用 `torch.compile` 可以根据目标硬件进一步提升性能。然而,编译在首次 pipeline 调用时会产生显著的开销,因此只有在重复调用同一 pipeline 实例时(例如在循环中)才会有益。
import diffusers
import torch
from diffusers.models.attention_processor import AttnProcessor2_0
pipe = diffusers.MarigoldDepthPipeline.from_pretrained(
"prs-eth/marigold-depth-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
pipe.vae.set_attn_processor(AttnProcessor2_0())
pipe.unet.set_attn_processor(AttnProcessor2_0())
+ pipe.vae = torch.compile(pipe.vae, mode="reduce-overhead", fullgraph=True)
+ pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True)
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
depth = pipe(image, num_inference_steps=1)
最大化精度和集成
Marigold pipelines 具有内置的集成机制,可以组合来自不同随机潜变量的多个预测。这是一种通过暴力方式提高预测精度的方法,利用了扩散的生成特性。当 `ensemble_size` 参数设置为大于或等于 `3` 时,集成路径会自动激活。当目标是最大化精度时,同时调整 `num_inference_steps` 和 `ensemble_size` 是合理的。推荐值因检查点而异,但主要取决于调度器类型。集成的效果在表面法线上尤其明显:
import diffusers
pipe = diffusers.MarigoldNormalsPipeline.from_pretrained("prs-eth/marigold-normals-v1-1").to("cuda")
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
- depth = pipe(image)
+ depth = pipe(image, num_inference_steps=10, ensemble_size=5)
vis = pipe.image_processor.visualize_normals(depth.prediction)
vis[0].save("einstein_normals.png")


可以看出,所有具有精细结构(如头发)的区域都得到了更保守且平均更准确的预测。这样的结果更适合对精度敏感的下游任务,例如三维重建。
具有时间一致性的逐帧视频处理
由于 Marigold 的生成特性,每个预测都是独一无二的,并由用于潜变量初始化的随机噪声决定。与传统的端到端稠密回归网络相比,这成了一个明显的缺点,如下列视频所示:


为了解决这个问题,可以向 pipelines 传递 `latents` 参数,它定义了扩散的起始点。根据经验,我们发现,将同一个起始点噪声潜变量与对应于前一帧预测的潜变量进行凸组合,可以得到足够平滑的结果,如下面的代码片段所示:
import imageio
import diffusers
import torch
from diffusers.models.attention_processor import AttnProcessor2_0
from PIL import Image
from tqdm import tqdm
device = "cuda"
path_in = "https://huggingface.co/spaces/prs-eth/marigold-lcm/resolve/c7adb5427947d2680944f898cd91d386bf0d4924/files/video/obama.mp4"
path_out = "obama_depth.gif"
pipe = diffusers.MarigoldDepthPipeline.from_pretrained(
"prs-eth/marigold-depth-v1-1", variant="fp16", torch_dtype=torch.float16
).to(device)
pipe.vae = diffusers.AutoencoderTiny.from_pretrained(
"madebyollin/taesd", torch_dtype=torch.float16
).to(device)
pipe.unet.set_attn_processor(AttnProcessor2_0())
pipe.vae = torch.compile(pipe.vae, mode="reduce-overhead", fullgraph=True)
pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True)
pipe.set_progress_bar_config(disable=True)
with imageio.get_reader(path_in) as reader:
size = reader.get_meta_data()['size']
last_frame_latent = None
latent_common = torch.randn(
(1, 4, 768 * size[1] // (8 * max(size)), 768 * size[0] // (8 * max(size)))
).to(device=device, dtype=torch.float16)
out = []
for frame_id, frame in tqdm(enumerate(reader), desc="Processing Video"):
frame = Image.fromarray(frame)
latents = latent_common
if last_frame_latent is not None:
latents = 0.9 * latents + 0.1 * last_frame_latent
depth = pipe(
frame,
num_inference_steps=1,
match_input_resolution=False,
latents=latents,
output_latent=True,
)
last_frame_latent = depth.latent
out.append(pipe.image_processor.visualize_depth(depth.prediction)[0])
diffusers.utils.export_to_gif(out, path_out, fps=reader.get_meta_data()['fps'])
在这里,扩散过程从给定的计算潜变量开始。pipeline 设置 `output_latent=True` 来访问 `out.latent` 并计算其对下一帧潜变量初始化的贡献。现在结果要稳定得多:


Marigold 用于 ControlNet
深度预测与扩散模型的一个非常常见的应用是与 ControlNet 结合使用。深度的清晰度在从 ControlNet 获得高质量结果中起着至关重要的作用。如上与其他方法的比较所示,Marigold 在该任务上表现出色。下面的代码片段演示了如何加载图像、计算深度,并以兼容的格式将其传递给 ControlNet:
import torch
import diffusers
device = "cuda"
generator = torch.Generator(device=device).manual_seed(2024)
image = diffusers.utils.load_image(
"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/controlnet_depth_source.png"
)
pipe = diffusers.MarigoldDepthPipeline.from_pretrained(
"prs-eth/marigold-depth-v1-1", torch_dtype=torch.float16, variant="fp16"
).to(device)
depth_image = pipe(image, generator=generator).prediction
depth_image = pipe.image_processor.visualize_depth(depth_image, color_map="binary")
depth_image[0].save("motorcycle_controlnet_depth.png")
controlnet = diffusers.ControlNetModel.from_pretrained(
"diffusers/controlnet-depth-sdxl-1.0", torch_dtype=torch.float16, variant="fp16"
).to(device)
pipe = diffusers.StableDiffusionXLControlNetPipeline.from_pretrained(
"SG161222/RealVisXL_V4.0", torch_dtype=torch.float16, variant="fp16", controlnet=controlnet
).to(device)
pipe.scheduler = diffusers.DPMSolverMultistepScheduler.from_config(pipe.scheduler.config, use_karras_sigmas=True)
controlnet_out = pipe(
prompt="high quality photo of a sports bike, city",
negative_prompt="",
guidance_scale=6.5,
num_inference_steps=25,
image=depth_image,
controlnet_conditioning_scale=0.7,
control_guidance_end=0.7,
generator=generator,
).images
controlnet_out[0].save("motorcycle_controlnet_out.png")



定量评估
要对 Marigold 在标准排行榜和基准测试(如 NYU、KITTI 和其他数据集)中进行定量评估,请遵循论文中概述的评估协议:加载全精度 fp32 模型,并为 `num_inference_steps` 和 `ensemble_size` 使用适当的值。可选择性地设置随机种子以确保可复现性。最大化 `batch_size` 将实现最大的设备利用率。
import diffusers
import torch
device = "cuda"
seed = 2024
generator = torch.Generator(device=device).manual_seed(seed)
pipe = diffusers.MarigoldDepthPipeline.from_pretrained("prs-eth/marigold-depth-v1-1").to(device)
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
depth = pipe(
image,
num_inference_steps=4, # set according to the evaluation protocol from the paper
ensemble_size=10, # set according to the evaluation protocol from the paper
generator=generator,
)
# evaluate metrics
使用预测不确定性
Marigold pipelines 中内置的集成机制结合了从不同随机潜变量获得的多个预测。作为一个副作用,它可以用来量化认知(模型)不确定性;只需将 `ensemble_size` 指定为大于或等于 3,并设置 `output_uncertainty=True`。结果的不确定性将在输出的 `uncertainty` 字段中提供。它可以如下可视化:
import diffusers
import torch
pipe = diffusers.MarigoldDepthPipeline.from_pretrained(
"prs-eth/marigold-depth-v1-1", variant="fp16", torch_dtype=torch.float16
).to("cuda")
image = diffusers.utils.load_image("https://marigoldmonodepth.github.io/images/einstein.jpg")
depth = pipe(
image,
ensemble_size=10, # any number >= 3
output_uncertainty=True,
)
uncertainty = pipe.image_processor.visualize_uncertainty(depth.uncertainty)
uncertainty[0].save("einstein_depth_uncertainty.png")



不确定性的解释很简单:较高的值(白色)对应于模型难以做出一致预测的像素。
- 深度模型在不连续处表现出最大的不确定性,即物体深度急剧变化的地方。
- 表面法线模型在像头发这样的精细结构和像衣领区域这样的黑暗区域中最不自信。
- 反照率不确定性表示为 RGB 图像,因为它独立地捕捉每个颜色通道的不确定性,与深度和表面法线不同。它在阴影区域和不连续处也较高。
结论
我们希望 Marigold 对您的下游任务有所价值,无论是作为更广泛的生成工作流程的一部分,还是用于基于感知的应用,如三维重建。
< > 在 GitHub 上更新