Come stimare la profondità da una singola immagine |  di Jacob Marks, Ph.D.  |  Gennaio 2024

 | Intelligenza-Artificiale

Esegui e valuta modelli di stima della profondità monoculare con Hugging Face e FiftyOne

Mappe di calore della profondità monoculare generate con Marigold su immagini di profondità v2 della New York University. Immagine per gentile concessione dell’autore.

Gli esseri umani vedono il mondo attraverso due occhi. Uno dei principali vantaggi di questo binoculare la visione è la capacità di percepire profondità – quanto sono vicini o lontani gli oggetti. Il cervello umano deduce la profondità degli oggetti confrontando le immagini catturate contemporaneamente dagli occhi sinistro e destro e interpretando le disparità. Questo processo è noto come stereopsi.

Proprio come la percezione della profondità gioca un ruolo cruciale nella visione e nella navigazione umana, la capacità di stimare la profondità è fondamentale per un’ampia gamma di applicazioni di visione artificiale, dalla guida autonoma alla robotica e persino alla realtà aumentata. Tuttavia, una serie di considerazioni pratiche, dalle limitazioni spaziali ai vincoli di budget, spesso limitano queste applicazioni a una singola telecamera.

Stima della profondità monoculare (MDE) ha il compito di prevedere la profondità di una scena da una singola immagine. Il calcolo della profondità da una singola immagine è intrinsecamente ambiguo, poiché esistono diversi modi per proiettare la stessa scena 3D sul piano 2D di un’immagine. Di conseguenza, l’MDE è un compito impegnativo che richiede (esplicitamente o implicitamente) di prendere in considerazione molti segnali come la dimensione dell’oggetto, l’occlusione e la prospettiva.

In questo post illustreremo come caricare e visualizzare i dati della mappa di profondità, eseguire modelli di stima della profondità monoculare e valutare le previsioni di profondità. Lo faremo utilizzando i dati di SOLE RGB-D set di dati.

In particolare, tratteremo quanto segue:

Utilizzeremo la faccia che abbraccia trasformatori E diffusori librerie per inferenza, Cinquantuno per la gestione e la visualizzazione dei dati e scikit-image per le metriche di valutazione. Tutte queste librerie sono open source e gratuite. Dichiarazione di non responsabilità: lavoro presso Voxel51, i principali manutentori di una di queste librerie (FiftyOne).

Prima di iniziare, assicurati di avere tutte le librerie necessarie installate:

pip install -U torch fiftyone diffusers transformers scikit-image

Quindi importeremo i moduli che utilizzeremo in tutto il post:

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

IL Set di dati SUN RGB-D contiene 10.335 immagini RGB-D, ciascuna delle quali ha un’immagine RGB, un’immagine di profondità e elementi intrinseci della fotocamera corrispondenti. Contiene immagini da Profondità della New York University v2Berkeley B3DOE SOLE3D set di dati. SOLE RGB-D è uno dei più popolari set di dati per la stima della profondità monoculare e attività di segmentazione semantica!

💡Per questa procedura dettagliata utilizzeremo solo le porzioni della profondità v2 della NYU. La profondità della NYU v2 è concesso in licenza permissiva per uso commerciale (MIT), e può esserlo scaricato da Hugging Face direttamente.

Scaricamento dei dati grezzi

Innanzitutto, scarica il set di dati SUN RGB-D da Qui e decomprimilo oppure utilizza il seguente comando per scaricarlo direttamente:

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

E poi decomprimilo:

unzip sunrgbd.zip

Se desideri utilizzare il set di dati per altre attività, puoi convertire completamente le annotazioni e caricarle nel tuo file fiftyone.Dataset. Tuttavia, per questo tutorial utilizzeremo solo le immagini di profondità, quindi utilizzeremo solo le immagini RGB e le immagini di profondità (memorizzate nella cartella depth_bfx sottodirectory).

Creazione del set di dati

Poiché siamo solo interessati a far capire il punto, ci limiteremo ai primi 20 campioni, che provengono tutti dalla porzione del set di dati 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);

Qui stiamo memorizzando le mappe di profondità come mappe di calore. Tutto è rappresentato in termini di normalizzato, parente distanze, dove 255 rappresenta la distanza massima nella scena e 0 rappresenta la distanza minima nella scena. Questo è un modo comune per rappresentare le mappe di profondità, sebbene non sia l’unico modo per farlo. Se fossimo interessati assoluto distanze, potremmo memorizzare parametri a livello di campione per le distanze minima e massima nella scena e usarli per ricostruire le distanze assolute dalle distanze relative.

Visualizzazione dei dati della verità terrestre

Con le mappe di calore memorizzate sui nostri campioni, possiamo visualizzare i dati reali:

session = fo.launch_app(dataset, auto=False)
## then open tab to localhost:5151 in browser
Mappe di profondità della realtà terrestre per campioni dal set di dati SUN RGB-D. Immagine per gentile concessione dell’autore.

Quando si lavora con le mappe di profondità, lo schema dei colori e l’opacità della mappa termica sono importanti. Sono daltonico, quindi trovo che il viridis la mappa colori con l’opacità completamente alzata funziona meglio per me.

Impostazioni di visibilità per le mappe di calore. Immagine per gentile concessione dell’autore.

Realtà di base?

Esaminando queste immagini RGB e mappe di profondità, possiamo vedere che ci sono alcune imprecisioni nelle mappe di profondità della verità a terra. Ad esempio, in questa immagine, la spaccatura scura al centro dell’immagine è in realtà il più lontano parte della scena, ma la mappa di profondità della verità sul terreno la mostra come più vicina parte della scena:

Problema nei dati di profondità della realtà terrestre per il campione del set di dati SUN RGB-D. Immagine per gentile concessione dell’autore.

Questa è una delle sfide principali per le attività MDE: i dati reali sono difficili da ottenere e spesso sono rumorosi! È essenziale esserne consapevoli durante la valutazione dei modelli MDE.

Ora che abbiamo caricato il nostro set di dati, possiamo eseguire modelli di stima della profondità monoculare sulle nostre immagini RGB!

Per molto tempo, i modelli all’avanguardia per la stima della profondità monoculare come DORN E Profondità densa sono stati costruiti con reti neurali convoluzionali. Recentemente, tuttavia, entrambi i modelli basati su trasformatore come DPT E GLPNe modelli basati sulla diffusione come Calendula hanno ottenuto risultati notevoli!

In questa sezione ti mostreremo come generare previsioni della mappa di profondità MDE sia con DPT che con Marigold. In entrambi i casi, puoi facoltativamente eseguire il modello localmente con la rispettiva libreria Hugging Face o eseguirlo in remoto con Replicare.

Per eseguire tramite Replicate, installa il client Python:

pip install replicate

Ed esporta il tuo token API Replica:

export REPLICATE_API_TOKEN=r8_<your_token_here>

💡 Con Replica, potrebbe essere necessario un minuto affinché il modello venga caricato in memoria sul server (problema di avvio a freddo), ma una volta fatto, la previsione dovrebbe richiedere solo pochi secondi. A seconda delle risorse di elaborazione locali, l’esecuzione sul server può offrire enormi velocità rispetto all’esecuzione locale, in particolare per Marigold e altri approcci di stima della profondità basati sulla diffusione.

Stima della profondità monoculare con DPT

Il primo modello che utilizzeremo è un trasformatore di previsione densa (DPT). I modelli DPT hanno trovato utilità sia nella MDE che nella segmentazione semantica, attività che richiedono previsioni “dense” a livello di pixel.

Il checkpoint seguente utilizza MiDaSche restituisce il mappa di profondità inversaquindi dobbiamo invertirlo per ottenere una mappa di profondità comparabile.

Per eseguire localmente con transformersper prima cosa carichiamo il modello e il processore di immagini:

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)

Successivamente, incapsuliamo il codice per l’inferenza su un campione, inclusa la pre e post elaborazione:

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()

Qui, stiamo memorizzando le previsioni in a label_field campo sui nostri campioni, rappresentato con una mappa termica proprio come le etichette di verità sul terreno.

Da notare che nel apply_dpt_model() funzione, tra il passaggio in avanti del modello e la generazione della mappa di calore, notiamo che effettuiamo una chiamata a torch.nn.functional.interpolate(). Questo perché il passaggio in avanti del modello viene eseguito su una versione sottocampionata dell’immagine e vogliamo restituire una mappa termica della stessa dimensione dell’immagine originale.

Perché dobbiamo farlo? Se volessimo solo *guardare* le mappe di calore, questo non avrebbe importanza. Ma se vogliamo confrontare le mappe di profondità della realtà terrestre con le previsioni del modello in base ai pixel, dobbiamo assicurarci che abbiano le stesse dimensioni.

Tutto ciò che resta da fare è scorrere il set di dati:

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

session = fo.launch_app(dataset)

Mappe di profondità relativa previste da un modello ibrido MiDaS DPT su immagini campione SUN RGB-D. Immagine per gentile concessione dell’autore.

Per eseguire con Replicate, è possibile utilizzare Questo modello. Ecco come appare l’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)

Stima della profondità monoculare con calendula

Grazie al loro enorme successo nei contesti text-to-image, i modelli di diffusione vengono applicati a una gamma sempre più ampia di problemi. Calendula “ripropone” modelli di generazione di immagini basati sulla diffusione per la stima della profondità monoculare.

Per eseguire Marigold localmente, dovrai clonare il repository git:

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

Questo repository introduce una nuova pipeline di diffusori, MarigoldPipelineche rende facile applicare la calendula:

## 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')

È quindi necessaria la post-elaborazione dell’immagine della profondità di output.

Per eseguire invece tramite Replicate, possiamo creare un file apply_marigold_model() funziona in analogia con il caso DPT sopra e itera sui campioni nel nostro set di dati:

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)

Mappe di profondità relativa previste con l’endpoint Marigold sulle immagini campione SUN RGB-D. Immagine per gentile concessione dell’autore.

Ora che abbiamo previsioni da più modelli, valutiamoli! Faremo leva scikit-image applicare tre semplici parametri comunemente utilizzati per la stima della profondità monoculare: errore radice quadrata media (RMSE), rapporto segnale/rumore di picco (PSNR) e indice di somiglianza strutturale (SÌ).

💡Punteggi PSNR e SSIM più alti indicano previsioni migliori, mentre punteggi RMSE più bassi indicano previsioni migliori.

Tieni presente che i valori specifici a cui arrivo sono una conseguenza delle specifiche fasi di pre e post elaborazione che ho eseguito lungo il percorso. Ciò che conta è la prestazione relativa!

Definiremo la routine di valutazione:

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()

Quindi applicare la valutazione alle previsioni di entrambi i modelli:

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

Calcolare le prestazioni medie per un determinato modello/metrica è semplice come richiamare il set di dati mean()metodo su quel campo:

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

Tutti i parametri sembrano concordare sul fatto che DPT supera Marigold. Tuttavia, è importante notare che questi parametri non sono perfetti. Ad esempio, RMSE è molto sensibile ai valori anomali e SSIM non è molto sensibile ai piccoli errori. Per una valutazione più approfondita, possiamo filtrare in base a questi parametri nell’app per visualizzare cosa sta facendo bene il modello e cosa sta facendo male o dove i parametri non riescono a catturare le prestazioni del modello.

Infine, attivare e disattivare le maschere è un ottimo modo per visualizzare le differenze tra la verità fondamentale e le previsioni del modello:

Confronto visivo delle mappe di calore previste dai due modelli MDE e della verità fondamentale. Immagine per gentile concessione dell’autore.

Ricapitolando, abbiamo imparato come eseguire modelli di stima della profondità monoculare sui nostri dati, come valutare le previsioni utilizzando metriche comuni e come visualizzare i risultati. Abbiamo anche appreso che la stima della profondità monoculare è un compito notoriamente difficile.

La qualità e la quantità dei dati sono fattori fortemente limitanti; i modelli spesso faticano a generalizzare a nuovi ambienti; e le metriche non sono sempre buoni indicatori delle prestazioni del modello. I valori numerici specifici che quantificano le prestazioni del modello possono dipendere in larga misura dalla pipeline di elaborazione. E anche la valutazione qualitativa delle mappe di profondità previste può essere fortemente influenzata dagli schemi di colori e dalle scale di opacità.

Se c’è una cosa che impari da questo post, spero che sia questa: è fondamentale che tu guardi le mappe di profondità stesse e non solo le metriche!

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *