如何从单个图像估计深度

2024年01月26日 由 daydream 发表 395 0

人类通过两只眼睛来观察世界。这种双眼视觉的一个主要好处是能够感知深度——物体的远近程度。人脑通过同时比较左右眼捕捉到的图像并解读差异,推断出物体的深度。这个过程被称为立体视。


正如深度感知在人类视觉和导航中起着至关重要的作用一样,对于从自动驾驶到机器人技术,甚至增强现实等广泛的计算机视觉应用来说,估计深度的能力也至关重要。然而,从空间限制到预算约束等一系列实际考虑因素,往往将这些应用限制在单个摄像头上。


单目深度估计(MDE)是从单张图像预测场景深度的任务。从单张图像计算深度本质上是模糊的,因为同一个3D场景可以有多种方式投影到2D图像平面上。因此,MDE是一项具有挑战性的任务,需要(无论是明确还是隐含地)考虑许多线索,如对象大小、遮挡和透视。


微信截图_20240126111642

使用 Marigold 在纽约大学深度 v2 图像上生成的单目深度热图


在这篇文章中,我们将演示如何加载和可视化深度图数据,运行单目深度估计模型,并评估深度预测。我们将使用来自SUN RGB-D数据集的数据来完成这些任务。


具体来说,我们将涵盖以下内容:


  • 加载和可视化SUN-RGBD地面真实深度图
  • 使用Marigold和DPT进行推理
  • 评估相对深度预测 


我们将使用Hugging Face transformers和diffusers库进行推理,FiftyOne进行数据管理和可视化,以及scikit-image进行评估指标。所有这些库都是开源的,可以免费使用。


在开始之前,请确保已经安装了所有必要的库:

pip install -U torch fiftyone diffusers transformers scikit-image


然后我们将导入在本文中将要使用的模块:

from glob import glob
import numpy as np
from PIL import Image
import torch

import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone.brain as fob
from fiftyone import ViewField as F


加载和可视化SUN-RGBD深度数据


SUN RGB-D数据集包含了10,335张RGB-D图像,每张图像都有相应的RGB图像、深度图像和相机内参。它包含了来自NYU depth v2、Berkeley B3DO和SUN3D数据集的图像。SUN RGB-D是单目深度估计和语义分割任务最受欢迎的数据集之一!


对于这个教程,我们只会使用NYU depth v2部分。NYU depth v2允许商业使用(MIT许可),可以直接从Hugging Face下载。


下载原始数据


首先,下载SUN RGB-D数据集并解压,或者使用以下命令直接下载:

curl -o sunrgbd.zip https://rgbd.cs.princeton.edu/data/SUNRGBD.zip


然后解压它:

unzip sunrgbd.zip


如果想将数据集用于其他任务,可以完全转换注释并将它们加载到.fiftyone.Datasetdepth_bfx中。但是,在本教程中,我们只使用深度图像,所以我们只会使用RGB图像和深度图像(存储在子目录中)。


创建数据集


因为我们只是想把观点讲清楚,我们将限制自己使用前20个样本,这些样本都来自数据集的NYU Depth v2部分:

## create, name, and persist the dataset
dataset = fo.Dataset(name="SUNRGBD-20", persistent=True)

## pick out first 20 scenes
scene_dirs = glob("SUNRGBD/kv1/NYUdata/*")[:20]

samples = []

for scene_dir in scene_dirs:
## Get image file path from scene directory
image_path = glob(f"{scene_dir}/image/*")[0]

## Get depth map file path from scene directory
depth_path = glob(f"{scene_dir}/depth_bfx/*")[0]

depth_map = np.array(Image.open(depth_path))
depth_map = (depth_map * 255 / np.max(depth_map)).astype("uint8")

## Create sample
sample = fo.Sample(
filepath=image_path,
gt_depth=fo.Heatmap(map=depth_map),
)

samples.append(sample)

## Add samples to dataset
dataset.add_samples(samples);

在这里,我们将深度图存储为热力图。一切都以归一化的相对距离表示,其中255代表场景中的最大距离,0代表场景中的最小距离。这是表示深度图的一种常见方法,尽管远非唯一方法。如果我们对绝对距离感兴趣,我们可以为每个样本存储最小和最大距离的参数,并使用这些参数从相对距离重建绝对距离。


可视化地面真实数据


在样本上存储了热力图后,我们可以可视化地面真实数据:

session = fo.launch_app(dataset, auto=False)
## then open tab to localhost:5151 in browser

微信截图_20240126111903

SUN RGB-D 数据集中样本的地面实况深度图


在处理深度图时,热图的配色方案和不透明度很重要。我觉得将不透明度调至最大的viridis配色方案效果最好。


1__YoCd12vrkLIGMGq_GMokQ

热图的可见性设置


什么是地面真实数据?


检查这些RGB图像和深度图,我们可以看到地面真实深度图中存在一些不准确之处。例如,在这张图像中,穿过图像中心的深色裂缝实际上是场景中最远的部分,但地面真实深度图却显示它为场景中最近的部分:


微信截图_20240126111928

SUN RGB-D 数据集中样本的地面实况深度数据存在问题


这是MDE任务的关键挑战之一:地面真实数据难以获得,而且通常含有噪声!在评估您的MDE模型时,意识到这一点至关重要。


运行单目深度估计模型


现在我们已经加载了数据集,我们可以在RGB图像上运行单目深度估计模型了!


长期以来,单目深度估计的最先进的模型,如DORN和DenseDepth,都是使用卷积神经网络构建的。然而,最近,基于变换器的模型(如DPT和GLPN)以及基于扩散的模型(如Marigold)都取得了显著的成果!


在这一部分中,我们将展示如何使用DPT和Marigold生成MDE深度图预测。在这两种情况下,你可以选择使用相应的Hugging Face库在本地运行模型,或者使用Replicate远程运行。


要通过Replicate运行,请安装Python客户端:

pip install replicate


并导出Replicate API令牌:

export REPLICATE_API_TOKEN=r8_<your_token_here>

 

使用Replicate时,模型可能需要几分钟时间才能加载到服务器内存中(冷启动问题),但一旦加载完成,预测应该只需要几秒钟。根据你的本地计算资源,与本地运行相比,在服务器上运行可能会为Marigold等其他基于扩散的深度估计方法带来巨大的速度提升。


使用DPT进行单目深度估计


我们将要运行的第一个模型是密集预测变换器(DPT)。DPT模型在MDE和语义分割中都有应用——这些任务需要“密集的”、像素级别的预测。


下面的检查点使用了MiDaS,它返回的是逆深度图,所以我们需要将其反转以获得可比较的深度图。


要在本地运行,我们首先加载模型和图像处理器:transformers

from transformers import AutoImageProcessor, AutoModelForDepthEstimation

## swap for "Intel/dpt-large" if you'd like
pretrained = "Intel/dpt-hybrid-midas"

image_processor = AutoImageProcessor.from_pretrained(pretrained)
dpt_model = AutoModelForDepthEstimation.from_pretrained(pretrained)


接下来,我们将封装对样本进行推理的代码,包括预处理和后处理:

def apply_dpt_model(sample, model, label_field):
image = Image.open(sample.filepath)
inputs = image_processor(images=image, return_tensors="pt")

with torch.no_grad():
outputs = model(**inputs)
predicted_depth = outputs.predicted_depth

prediction = torch.nn.functional.interpolate(
predicted_depth.unsqueeze(1),
size=image.size[::-1],
mode="bicubic",
align_corners=False,
)

output = prediction.squeeze().cpu().numpy()
## flip b/c MiDaS returns inverse depth
formatted = (255 - output * 255 / np.max(output)).astype("uint8")

sample[label_field] = fo.Heatmap(map=formatted)
sample.save()


在这里,我们将预测存储在样本的字段中,表示为与地面真实标签相似的热图。label_field


注意,在函数中,在模型的前向传播和热图生成之间,我们调用了.apply_dpt_model()torch.nn.functional.interpolate()。这是因为模型的前向传播是在缩小版的图像上进行的,而我们希望返回一个与原始图像大小相同的热图。


为什么我们需要这样做?如果我们只是想看看热图,这并不重要。但是,如果我们想要在每个像素的基础上将地面真实深度图与模型的预测进行比较,我们需要确保它们的大小相同。


剩下的就是遍历数据集:

for sample in dataset.iter_samples(autosave=True, progress=True):
apply_dpt_model(sample, dpt_model, "dpt")

session = fo.launch_app(dataset)

微信截图_20240126112011

混合 MiDaS DPT 模型在 SUN RGB-D 样本图像上预测的相对深度图


要在Replicate上运行,你可以使用这个模型。以下是API的样子:

import replicate

## example application to first sample
rgb_fp = dataset.first().filepath

output = replicate.run(
"cjwbw/midas:a6ba5798f04f80d3b314de0f0a62277f21ab3503c60c84d4817de83c5edfdae0",
input={
"model_type": "dpt_beit_large_512",
"image":open(rgb_fp, "rb")
}
)
print(output)

使用Marigold进行单目深度估计


源于它们在文本到图像上下文中的极大成功,扩散模型正在被应用于越来越广泛的问题。Marigold“重新利用”基于扩散的图像生成模型进行单目深度估计。


要在本地运行Marigold,你需要克隆git存储库:

git clone https://github.com/prs-eth/Marigold.git


这个存储库引入了一个新的diffusers管道,这使得应用Marigold变得简单:

MarigoldPipeline

## load model
from Marigold.marigold import MarigoldPipeline
pipe = MarigoldPipeline.from_pretrained("Bingxin/Marigold")

## apply to first sample, as example
rgb_image = Image.open(dataset.first().filepath)
output = pipe(rgb_image)
depth_image = output['depth_colored']


然后需要对输出的深度图像进行后处理。


要通过Replicate运行,我们可以创建一个与上述DPT情况类似的函数,并在我们的数据集中迭代样本:

apply_marigold_model()

import replicate
import requests
import io

def marigold_model(rgb_image):
output = replicate.run(
"adirik/marigold:1a363593bc4882684fc58042d19db5e13a810e44e02f8d4c32afd1eb30464818",
input={
"image":rgb_image
}
)
## get the black and white depth map
response = requests.get(output[1]).content
return response

def apply_marigold_model(sample, model, label_field):
rgb_image = open(sample.filepath, "rb")
response = model(rgb_image)
depth_image = np.array(Image.open(io.BytesIO(response)))[:, :, 0] ## all channels are the same
formatted = (255 - depth_image).astype("uint8")
sample[label_field] = fo.Heatmap(map=formatted)
sample.save()

for sample in dataset.iter_samples(autosave=True, progress=True):
apply_marigold_model(sample, marigold_model, "marigold")

session = fo.launch_app(dataset)


评估单目深度估计模型


现在我们已经有了多个模型的预测结果,让我们来评估它们!我们将利用scikit-image来应用三个常用的单目深度估计简单指标:均方根误差(RMSE)、峰值信噪比(PSNR)和结构相似性指数(SSIM)。


更高的PSNR和SSIM分数表示更好的预测,而更低的RMSE分数表示更好的预测。


注意,我得到的具体值是由于我沿途执行的特定预处理和后处理步骤的结果。重要的是相对性能!


我们将定义评估程序:

from skimage.metrics import peak_signal_noise_ratio, mean_squared_error, structural_similarity

def rmse(gt, pred):
"""Compute root mean squared error between ground truth and prediction"""
return np.sqrt(mean_squared_error(gt, pred))

def evaluate_depth(dataset, prediction_field, gt_field):
"""Run 3 evaluation metrics for all samples for `prediction_field`
with respect to `gt_field`"""

for sample in dataset.iter_samples(autosave=True, progress=True):
gt_map = sample[gt_field].map
pred = sample[prediction_field]
pred_map = pred.map
pred["rmse"] = rmse(gt_map, pred_map)
pred["psnr"] = peak_signal_noise_ratio(gt_map, pred_map)
pred["ssim"] = structural_similarity(gt_map, pred_map)
sample[prediction_field] = pred

## add dynamic fields to dataset so we can view them in the App
dataset.add_dynamic_sample_fields()


然后将评估应用于两个模型的预测:

evaluate_depth(dataset, "dpt", "gt_depth")
evaluate_depth(dataset, "marigold", "gt_depth"
)


计算某个模型/指标的平均性能就像调用数据集的方法一样简单:

mean()

print("Mean Error Metrics")
for model in ["dpt", "marigold"]:
print("-"*50)
for metric in ["rmse", "psnr", "ssim"]:
mean_metric_value = dataset.mean(f"{model}.{metric}")
print(f"Mean {metric} for {model}: {mean_metric_value}"
)
Mean Error Metrics
--------------------------------------------------
Mean rmse for dpt: 49.8915828817003
Mean psnr for dpt: 14.805904629602551
Mean ssim for dpt: 0.8398022368184576
--------------------------------------------------
Mean rmse for marigold: 104.0061165272178
Mean psnr for marigold: 7.93015537185192
Mean ssim for marigold: 0.42766803372861134


所有指标似乎都表明DPT的表现优于Marigold。然而,重要的是要注意这些指标并不完美。例如,RMSE对异常值非常敏感,而SSIM对小误差不太敏感。为了进行更彻底的评估,我们可以在应用程序中通过这些指标进行过滤,以便可视化模型表现良好和表现不佳的地方——或者指标未能捕捉到模型性能的地方。


最后,打开和关闭掩码是可视化地面真实情况与模型预测之间的差异的好方法:


1_8hJkafTQL3t-b2xkWczL6g


结论


我们学习了如何在我们的数据上运行单目深度估计模型,如何使用常见的指标评估预测结果,以及如何可视化结果。我们还了解到,单目深度估计是一个众所周知的难题。


数据质量和数量是严重的限制因素;模型通常难以泛化到新环境;指标并不总是模型性能的良好指标。量化模型性能的特定数值可能极大地取决于你的处理流程。甚至你对预测深度图的定性评估也可能受到你的颜色方案和不透明度刻度的严重影响。


文章来源:https://towardsdatascience.com/how-to-estimate-depth-from-a-single-image-7f421d86b22d
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消