Skip to content

fppcng/nerfstudio-project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Pipeline Nerfstudio per la ricostruzione di oggetti

Questo repository contiene gli script e i file utilizzati per preparare i dataset per Nerfstudio e per eseguire una serie di esperimenti di ricostruzione 3D con Nerfacto, Depth-Nerfacto e Splatfacto.

Il progetto parte da dataset acquisiti tramite scanner 3D, composti da immagini RGB in formato .heif e da corrispondenti immagini di profondità in formato .png. Non erano disponibili file utilizzabili con la posizione e l'orientamento delle camere. Per ovviare a questo è stato usato COLMAP come strumento di preprocessing.

La pipeline parte dai dati RGB e depth grezzi, li converte nel formato dataset di Nerfstudio utilizzando COLMAP, aggiunge opzionalmente annotazioni di profondità e maschere, addestra diversi metodi di Nerfstudio e infine valuta, renderizza ed esporta le ricostruzioni ottenute.

Installazione

Nerfstudio può essere installato seguendo la guida ufficiale.

Dopo l’installazione di Nerfstudio, dovrebbero essere disponibili i seguenti comandi:

ns-process-data
ns-train
ns-eval
ns-render
ns-export

Le dipendenze Python richieste dagli script del progetto sono indicate nel file:

scripts/requirements.txt

e possono essere installate con:

pip install -r scripts/requirements.txt

Pipeline di preparazione dei dataset

1. Conversione delle immagini HEIF in PNG

Le immagini RGB grezze sono state inizialmente convertite dal formato HEIF al formato PNG utilizzando lo script:

1_convert_heif_to_png.sh

Questo passaggio crea la cartella contenente le immagini PNG, che verrà poi utilizzata come input per la creazione del dataset in formato Nerfstudio.

2. Creazione del dataset Nerfstudio

Le immagini PNG vengono successivamente utilizzate per creare il dataset vero e proprio nel formato richiesto da Nerfstudio. Questo passaggio viene eseguito tramite lo script:

2_create_nerfstudio_dataset.sh

Lo script prende in input il nome di un dataset presente nella cartella project_data/processed/ e crea il corrispondente dataset Nerfstudio nella cartella project_data/nerfstudio_dataset/.

Ad esempio:

./2_create_nerfstudio_dataset.sh bauletto_nero

Internamente, lo script esegue il comando:

ns-process-data images \
    --data "$INPUT_DIR" \
    --output-dir "$OUTPUT_DIR"

Questo comando prende in input le immagini PNG e le organizza nel formato atteso da Nerfstudio. Durante questo processo, Nerfstudio utilizza COLMAP come strumento esterno di preprocessing. COLMAP non fa parte direttamente di Nerfstudio, ma viene richiamato per stimare le pose delle camere, cioè la posizione e l’orientamento associati a ciascuna immagine, e per produrre una ricostruzione sparsa iniziale della scena, partendo dalle sole immagini RGB.

Il dataset Nerfstudio risultante ha una struttura simile alla seguente:

.
├── colmap
│   ├── database.db
│   └── sparse
│       └── 0
│           ├── cameras.bin
│           ├── images.bin
│           ├── points3D.bin
│           └── project.ini
├── images
├── images_2
├── images_4
├── images_8
├── sparse_pc.ply
└── transforms.json

La cartella images contiene le immagini PNG originali copiate nel dataset Nerfstudio. Le cartelle images_2, images_4 e images_8 contengono versioni ridotte delle stesse immagini, generate automaticamente per permettere a Nerfstudio di lavorare a diverse risoluzioni durante l’addestramento.

Il file transforms.json contiene i parametri intrinseci della camera e le pose stimate per le immagini del dataset. Questo file è uno degli output principali della fase di preparazione, perché descrive la relazione geometrica tra le diverse viste della scena.

Il file sparse_pc.ply contiene la point cloud sparsa ricostruita da COLMAP.

La cartella colmap contiene i dati intermedi generati da COLMAP durante la stima delle pose e la ricostruzione sparsa. In particolare, database.db contiene i dati relativi all’estrazione delle feature e al matching tra immagini, mentre i file presenti in sparse/0 contengono le informazioni sulle camere ricostruite, sulle immagini registrate e sui punti 3D della ricostruzione sparsa.

3. Aggiunta delle mappe di profondità

Dopo la creazione del dataset Nerfstudio a partire dalle sole immagini RGB, sono state aggiunte le mappe di profondità associate a ciascun frame.

Nel workflow utilizzato, le mappe di profondità non vengono generate né associate automaticamente da ns-process-data come avviene invece per le immagini RGB. Per questo motivo sono state preparate e collegate manualmente al file transforms.json, in modo da poter eseguire esperimenti con metodi che sfruttano anche informazioni di profondità, come Depth-Nerfacto.

Per questa fase sono stati utilizzati tre script principali:

3_prepare_nerfstudio_depths.py
4_create_downscaled_depths.py
5_add_depth_paths_to_transforms.py

Il primo script, prepare_nerfstudio_depths.py, è stato utilizzato per generare la cartella:

depths/

all’interno del dataset Nerfstudio. I dati originali forniti dallo scanner contenevano immagini RGB in formato HEIF e mappe di profondità già salvate come file PNG. Le immagini RGB e le mappe di profondità avevano nomi corrispondenti e lo stesso ordinamento.

Durante la preparazione del dataset, però, le immagini RGB vengono convertite in PNG e poi processate da Nerfstudio e COLMAP. Questo processo produce un dataset in cui le immagini RGB vengono rinominate secondo la convenzione frame_XXXXX.png e, nel nostro caso, risultano anche ruotate rispetto ai dati originali. Le mappe di profondità, invece, non vengono processate automaticamente da COLMAP. Per questo motivo è stato necessario prepararle separatamente.

Lo script prepare_nerfstudio_depths.py esegue quindi le seguenti operazioni:

  • legge le depth maps originali;
  • assume che immagini RGB e depth maps siano nello stesso ordine;
  • rinomina ogni depth map usando il nome del frame RGB corrispondente;
  • ruota le depth maps di 90 gradi in senso orario, per allinearle alle immagini RGB processate;
  • ridimensiona le depth maps alla stessa risoluzione delle immagini RGB;
  • salva le depth maps processate nella cartella depths/ del dataset Nerfstudio.

Ad esempio, dopo questa fase, a un’immagine RGB come:

images/frame_00001.png

corrisponde una mappa di profondità:

depths/frame_00001.png

Successivamente è stato utilizzato lo script create_downscaled_depths.py per creare versioni ridotte delle mappe di profondità. Infatti, durante la creazione del dataset, ns-process-data genera automaticamente le versioni downscaled delle immagini RGB, come images_2, images_4 e images_8, ma non genera automaticamente le cartelle equivalenti per le depth map personalizzate.

Lo script create_downscaled_depths.py prende quindi in input la cartella:

depths/

e crea le corrispondenti cartelle ridotte:

depths_2/
depths_4/
depths_8/

Ad esempio:

python create_downscaled_depths.py \
    nerfstudio_dataset/cestino_medio/depths

Il risultato è una struttura coerente con quella delle immagini RGB:

depths/frame_00001.png
depths_2/frame_00001.png
depths_4/frame_00001.png
depths_8/frame_00001.png

Infine, è stato utilizzato uno script dedicato per generare una versione del file transforms.json contenente anche i riferimenti alle mappe di profondità. Il file transforms.json prodotto dalla pipeline standard contiene già i percorsi delle immagini RGB, i parametri intrinseci della camera e le pose stimate da COLMAP, ma non contiene automaticamente riferimenti alle depth map aggiunte manualmente.

Lo script carica quindi il file transforms.json, associa a ogni frame RGB la depth map con lo stesso nome, verifica che il file esista e aggiunge un campo depth_file_path a ciascun frame. Il file originale non viene modificato direttamente; viene invece generato un nuovo file, ad esempio:

transforms_depth.json

Un frame che inizialmente contiene solo il percorso dell’immagine RGB:

{
    "file_path": "images/frame_00001.png"
}

viene esteso aggiungendo il percorso della depth map corrispondente:

{
    "file_path": "images/frame_00001.png",
    "depth_file_path": "depths/frame_00001.png"
}

Dopo l’aggiunta delle mappe di profondità, il dataset assume quindi una struttura simile alla seguente:

.
├── colmap
│   ├── database.db
│   └── sparse
│       └── 0
│           ├── cameras.bin
│           ├── images.bin
│           ├── points3D.bin
│           └── project.ini
├── images
├── images_2
├── images_4
├── images_8
├── depths
├── depths_2
├── depths_4
├── depths_8
├── sparse_pc.ply
├── transforms.json
└── transforms_depth.json

Il risultato è un dataset Nerfstudio arricchito con supervisione geometrica aggiuntiva, utilizzabile per confrontare esperimenti basati esclusivamente su immagini RGB con esperimenti che utilizzano anche le mappe di profondità.

4. Aggiunta delle maschere di segmentazione

images/frame_00001.png
masks/frame_00001.png

In questo modo, ogni immagine RGB può essere associata alla rispettiva maschera binaria, dove i pixel appartenenti all’oggetto sono rappresentati in bianco e lo sfondo in nero.

Per la generazione delle maschere sono stati utilizzati due script principali: uno script automatico, basato su modelli di object detection e segmentazione, e uno script interattivo, utilizzato per correggere o rigenerare manualmente le maschere di alcuni frame specifici. Questa seconda procedura è stata utile nei casi in cui la segmentazione automatica non risultava sufficientemente precisa.

Dopo la creazione della cartella:

masks/

sono stati utilizzati script analoghi a quelli impiegati per le mappe di profondità, con lo scopo di creare le versioni ridotte delle maschere e di aggiungere i relativi percorsi al file transforms.json.

In particolare, lo script:

create_downscaled_masks.py

è stato utilizzato per generare le cartelle:

masks_2/
masks_4/
masks_8/

a partire dalla cartella originale masks/. Queste cartelle seguono la stessa logica delle directory images_2, images_4, images_8 e depths_2, depths_4, depths_8, mantenendo la coerenza tra immagini RGB, depth map e maschere alle diverse risoluzioni.

Ad esempio:

masks/frame_00001.png
masks_2/frame_00001.png
masks_4/frame_00001.png
masks_8/frame_00001.png

Successivamente, lo script:

add_masks_to_transforms.py

è stato utilizzato per aggiungere a ogni frame del file transforms.json il percorso della maschera corrispondente, tramite il campo mask_path.

Un frame che inizialmente contiene il percorso dell’immagine RGB:

{
    "file_path": "images/frame_00001.png"
}

viene quindi esteso nel seguente modo:

{
    "file_path": "images/frame_00001.png",
    "mask_path": "masks/frame_00001.png"
}

Dopo l’aggiunta delle maschere, il dataset può assumere una struttura simile alla seguente:

.
├── colmap
│   ├── database.db
│   └── sparse
│       └── 0
│           ├── cameras.bin
│           ├── images.bin
│           ├── points3D.bin
│           └── project.ini
├── images
├── images_2
├── images_4
├── images_8
├── depths
├── depths_2
├── depths_4
├── depths_8
├── masks
├── masks_2
├── masks_4
├── masks_8
├── sparse_pc.ply
├── transforms.json
├── transforms_depth.json
└── transforms_mask.json

Il risultato è un dataset Nerfstudio arricchito con maschere di segmentazione, utilizzabile per esperimenti in cui si vuole concentrare l’addestramento sull’oggetto principale e ridurre l’influenza dello sfondo.

5. Varianti dei file transforms

Alcune cartelle dei dataset contengono una directory:

transforms/

con più varianti dei file transform. Dopo la generazione delle diverse versioni, le varianti sono state raccolte in questa cartella separata per mantenere ordinata la root del dataset e conservare i file necessari ai diversi esperimenti.

Queste varianti sono state utilizzate per passare da esperimenti basati solo su RGB a esperimenti con profondità, con maschere o con sottoinsiemi di immagini.

La struttura risultante è simile alla seguente:

project_data/nerfstudio_dataset/bauletto_nero/
├── colmap/
├── depths/
├── depths_2/
├── depths_4/
├── depths_8/
├── images/
├── images_2/
├── images_4/
├── images_8/
├── masks/
├── masks_2/
├── masks_4/
├── masks_8/
├── sparse_pc.ply
├── transforms/
│   ├── transforms_rgb.json
│   ├── transforms_depth.json
│   ├── transforms_mask.json
│   └── transforms_depth_mask.json
└── transforms.json

Quando viene lanciato un addestramento, Nerfstudio prende in considerazione il file chiamato:

transforms.json

presente nella root del dataset. Per questo motivo, prima di avviare un training o una valutazione, è necessario scegliere la variante da utilizzare, copiarla nella root del dataset e rinominarla all’occorrenza con quel preciso nome.

Per esempio:

project_data/nerfstudio_dataset/bauletto_nero/transforms.json

Nerfstudio legge questo file durante il training o la valutazione del modello e lo utilizza per determinare quali immagini, mappe di profondità o maschere devono essere caricate.

6. Addestramento e valutazione dei modelli

Una volta preparati i dataset sono stati eseguiti 27 test di addestramento con Nerfstudio. Gli esperimenti sono stati pensati per confrontare metodi diversi, varianti dei dati in input e durate di training differenti.

I metodi utilizzati sono stati:

nerfacto
depth-nerfacto
splatfacto

Gli output degli addestramenti sono stati salvati nella cartella:

project_data/outputs/

Ogni esperimento contiene una sottocartella con il nome del test, il metodo utilizzato e il timestamp dell’esecuzione. All’interno di questa struttura, il file:

config.yml

contiene la configurazione completa dell’addestramento, inclusi il nome dell’esperimento, il dataset utilizzato, il numero di iterazioni, gli eventuali checkpoint di partenza e i principali parametri del modello.

La struttura di un output è simile alla seguente:

project_data/outputs/
└── bauletto_nero_rgb_100k/
    └── nerfacto/
        └── <data_dell_esperimento>/
            ├── config.yml
            └── nerfstudio_models/

Gli esperimenti su bauletto_nero costituiscono la serie principale di confronto. Su questo oggetto sono stati eseguiti test RGB con Nerfacto, test con Depth-Nerfacto usando le mappe di profondità, test con Depth-Nerfacto usando mappe generate tramite Depth Anything, test con Splatfacto, test con varianti Splatfacto modificate e test con maschere.

Per alcune famiglie di esperimenti sono stati confrontati training a:

100k
200k
300k

iterazioni. In questa documentazione viene usato il totale indicato nel nome dell’esperimento. Ad esempio, un esperimento chiamato:

bauletto_nero_splatfacto_300k

viene considerato come esperimento a 300k iterazioni, anche quando il relativo config.yml descrive una prosecuzione da un checkpoint precedente.

Gli esperimenti su cestino_medio e poltrona_vimini sono stati utilizzati per verificare il comportamento della pipeline su altri oggetti. In questi casi sono stati eseguiti soprattutto test a 100k iterazioni, confrontando Nerfacto, Splatfacto, varianti Splatfacto modificate e Splatfacto con maschere.

Dopo l’addestramento, i modelli sono stati valutati usando le metriche prodotte dalla fase di eval di Nerfstudio e alcune metriche aggiuntive sui render ottenuti. Le metriche considerate includono:

PSNR
SSIM
LPIPS
num_rays_per_sec
FPS
depth_mse
BRISQUE
CLIP-IQA

Per raccogliere le metriche prodotte da ns-eval e calcolare le metriche aggiuntive BRISQUE e CLIP-IQA sui render è stato utilizzato lo script:

scripts/13_evaluation.py

Le metriche PSNR, SSIM e LPIPS sono state utilizzate per confrontare la qualità fotometrica delle viste renderizzate rispetto alle immagini di riferimento. Le metriche num_rays_per_sec e FPS sono state usate per confrontare le prestazioni di rendering.

La metrica depth_mse è stata considerata per gli esperimenti in cui era disponibile una supervisione o una valutazione basata sulla profondità. BRISQUE e CLIP-IQA sono state usate come metriche percettive aggiuntive sui render.

Il riepilogo numerico degli esperimenti e delle metriche di valutazione è contenuto nel file:

project_data/results/results.csv

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors