Creiamo automagicamente un fantastico video partendo da un file 3D con Python

I file 3D hanno rivoluzionato il mondo, ma non sono ancora totalmente accessibili. Vi spiego come renderli più fruibili trasformandoli in video tramite Python.

author avatar
Francesco Strazzante 1/29/2023

Cosa si intende per "creare video da file 3D"?

Ho dovuto addensare tutti i concetti in un titolo, ora cerco di approfondire, punto per punto. Per prima cosa, video e oggetto 3D, sul piano grafico, sono in due dimensioni totalmente diverse: il video è statico e l'oggetto 3D è interattivo. Il video è un insieme di immagini che si susseguono in un determinato ordine mentre l'oggetto 3D si può ruotare, traslare, scalare etc.
Ottaedro Ottaedro
Con questo articolo volevo cercare una soluzione per poter condividere facilmente i sofisticati oggetti 3D. Immaginiamo che vogliamo inviare il nostro oggetto 3D all'amico in Whatsapp o al collega per email. Possiamo inviare il file .obj, ma obbligheremmo il destinatario a scaricarsi un'app per la lettura del file. Forse preferiscono ricevere un video che mostra tutte le facce dell'oggetto 3D?

Perchè perdere le caratteristiche di un oggetto 3D per fare un banale video?

Per necessità! O meglio, non ho una risposta univoca a questa domanda. Sto scrivendo questo articolo basandomi sul problema che il file 3D è poco accessibile: non puoi inviarlo facilmente in Whatsapp, non puoi condividerlo in Instagram, etc. Per questo articolo posso dire che mi sta bene perdere tutti i vantaggi di un oggetto 3d per avere (solo) un video, ma per il prossimo articolo potrei cambiare idea. Quindi se ti serve un modo per condividere facilmente un oggetto 3d, trasformandolo in un video, goditi questo articolo.

Cosa è un file .obj?

Un file cha ha estensione obj è un file che contiene delle informazioni per poter sviluppare un modello 3D. Al suo interno contiene principalmente posizioni di vertici, coordinate per texture e la scala. Ci sono tante altre estensioni di file 3D, ma per questo articolo ci concentreremo sul file .obj. Obj è un formato di file aperto.

Cosa deve fare l'algoritmo in Python per creare il video da un oggetto 3D?

L'idea è molto semplice, l'algoritmo si divide principalmente in due parti: rendering e creazione video. Nella prima parte, rendering andremo a caricare il file 3D in Python, nella seconda parte invece creeremo un video da quello "che stiamo vedendo" (il file 3D).

Rendering

Non ci metteremo a sviluppare un algoritmo che fa il rendering di oggetti 3D, questo perchè infrangeremmo la prima regola dell'informatica: reusability - se c'è una libreria che lo fa già, usa quella libreria. Quindi, per partire, ho cercato in rete un pacchetto che fa rendering di oggetti 3D. Ho trovato vtk, matplotlib e vedo. Ho scelto vedo perchè mi sembra il più intuitivo. (vedo è un progetto straordinario, in questo articolo utilizzeremo solo 1% delle sue funzionalità, spero di poterlo approfondire ulteriormente nei prossimi post. Date un'occhiata alla citazioni del progetto e vi renderete conto dell'importanza di questo progetto.)

Premessa: assicuratevi di avere Python installato. Per questo articolo sto usando la versione 3.10.5. Potete controllarlo anche voi da terminale:
python3 --version
Python 3.10.5

Ora si può partire con l’installazione di vedo. Si legge dalla documentazione ufficiale

pip3 install vedo

Ok ora possiamo usarlo all’interno del nostro codice: partiamo dall’import del file .obj.


Creiamo un nuovo file python e posizionate il file .obj nella stessa cartella (trovate qualche file .obj alla fine di questo articolo). Apriamo il file .py ed inseriamo queste righe di codice.
python
import matplotlib.pyplot as plt
from vedo import *
# creo una nuova finestra con sfondo bianco grande 400x400 e facciamo vedere gli assi
plt = Plotter(bg='white', size=(400, 400), axes=True)
# aggiungo il file obj e lo coloriamo di blu
plt += Mesh("shuttle.obj", c="blue")
plt += __doc__
# fissiamo la finestra
plt.show()

Eseguiamo ed otterremo questo:

Foto di Bergamo con mappa con mura con prettymaps
Molto semplice vero? Grazie vedo! Proseguiamo con la parte di video.

Creazione video

Devo dire che mi sono soffermato molto su questa fase; mi sono chiesto come fare un video con del codice ma non mi venivano soluzioni fattibili. Poi ho ho cancellato tutti le impossibili idee e sono ripartito. Ho trovato una soluzione usando un po' di amica semplicità e sorella matematica. Scherzi a parte, partiamo dalle basi: cosa è un video? Un'insieme di immagini ordinate. Bene, partendo da questo concetto realizzeremo una serie di immagini (immagine 1, ruoto il file, immagine 2, ruoto il file,...) e una dopo l'altra le aggiungeremo al video. Per non creare ulteriore entropia, diciamo che vogliamo ruotare attorno ad un solo asse. Quindi immaginatevi di camminare in cerchio attorno ad un oggetto e ad ogni passo fare una fotografia. Se visionate velocemente le foto, vedrete le varie facce dell'oggetto 3D.

Facciamolo fare al codice! Per prima cosa, per riuscire a spostare la "camera", vedo, ci delizia con l'attributo... "camera". A questo attributo possiamo passare un vtkCamera (si, perchè sotto al cofano di vedo c'è anche vtk) oppure un dizionario di proprietà (che la libreria poi trasformerà in un vtkcamera). Se, con vscode, premete CTRL e cliccate contemporaneamente sul nome della funzione, avrete accesso al codice sorgente. Per questa libreria è ben commentato e possiamo vedere cosa possiamo settare:
  • pos, (list), the position of the camera in world coordinates
  • focal_point (list), the focal point of the camera in world coordinates
  • viewup (list), the view up direction for the camera

Ho dovuto fare diverse prove e ricerche per capire cosa modificassero questi attributi, a quanto pare sono le basi del mondo vtk (ma anche del mondo delle riprese). Fortunatamente ho trovato diversi articoli e presentazioni dove spiegavano in dettaglio la funzione di tutti i possibili attributi. Ve le risparmio e vi sintetizzo il tutto in questa immagine.

Rappresentazione delle proprietà di vtkcamera pos, focal_point e viewup Quindi,
  • pos in rosso: la posizione della camera in coordinate XYZ. Dove 0,0,0 è l’origine degli assi di riferimento dell’oggetto.
  • focal_point in blu: la coordinata verso la quale è puntata la fotocamera. Sempre in coordinate XYZ e 0,0,0 è ancora l’origine degli assi di riferimento dell’oggetto.
  • viewup in verde: l’asse della triade XYZ che punta verso l’alto, solitamente è Z.

Queste sono le principali proprietà che ci serviranno per questo articolo, ma ce ne sono un’infinità. Quindi se volete cimentarvi nel fare l’inquadratura all’americana, sappiate che potete farlo!

Torniamo al nostro shuttle! Decidiamo l’inquadratura di partenza e siamo pronti per “girare” il video. Io ho immaginato di posizionare la camera al punto rosso che vedete qui sotto e con valore z=0.

Immaginiamo l'inquadratura di partenza Facendo due proporzioni con i valori degli assi dell'oggetto, il punto rosso dovrebbe trovarsi a circa x=-30, y=0, z=0. Settiamo quindi questi valori nel codice inserendo il dizionario alla funzione plt.show()
python
...
plt.show(camera={'pos': (-30, 0, 0), 'viewup': (0, 0, 1)})
...

Eseguendo il codice troviamo il nostro shuttle pronto ad essere immortalato da tutti gli angoli!

Settaggio dell'inquadratura di partenza per creare un video da un file 3D con Python

L’idea è quindi di spostarsi lungo una circonferenza e scattare un’altra foto, spostarsi, scattare, spostarsi, scattare etc. Magari in punti equidistanti sulla circonferenza. Possiamo facilmente suddividere la circonferenza utilizzando gli angoli. Iniziamo con 8 punti di scatto e considerando che la circonferenza ha 360°, possiamo facilmente trovare ogni quanti gradi fermarci e scattare: ogni 45°! (360° / 8 = 45°).

Suddivisione circonferenza per fare scatti equidistanti
Purtroppo la camera nel codice però accetta coordinate xy(z), non possiamo utilizzare i gradi (magari si, ma diciamo che vogliamo risolverlo con le coordinate xy(z) e non con gli angoli).
Ora abbiamo bisogno di un po' di matematica per far spostare la telecamera sulla circonferenza: dobbiamo trasformare le 8 posizioni in coordinate (solo xy, in questo articolo, della z possiamo anche dimenticarcene perchè terremo sempre il valore fisso). Le funzioni sin e cos capitano a pennello:
Coordinate da angolo tramite le funzioni sen e cos
Posizione Angolo x=cos() y=cos()
0 1 0
1 45° 0.707 0.707
2 90° 0 1
3 135° -0.707 0.707
4 180° -1 0
5 225° -0.707 -0.707
6 270° 0 -1
7 315° 0.707 -0.707

Perfetto, ora inseriamo le coordinate nel codice:

python
import math
import matplotlib.pyplot as plt
from vedo import *
# creo una nuova finestra con sfondo bianco grande 400x400 e facciamo vedere gli assi
plt = Plotter(bg='white', size=(400, 400), axes=True,
interactive=None, offscreen=True)
# aggiungo il file obj e lo coloriamo di blu
plt += Mesh("shuttle.obj", c="blue")
plt += __doc__
# modifichiamo la posizione
n = 8
for i in range(n):
distance = -30
degrees = 360/n*i
radians = degrees * math.pi / 180
y = -distance * sin(radians)
x = distance * cos(radians)
plt.show(camera={'pos': (x, y, 0), 'viewup': (0, 0, 1)})
plt.screenshot("step-"+str(i) + "-di-8.png")

eseguiamo e ci ritroveremo 8 png:

Facce dello shuttle Facce dello shuttle Facce dello shuttle Facce dello shuttle
Facce dello shuttle Facce dello shuttle Facce dello shuttle Facce dello shuttle

Il più è fatto! Ora dobbiamo solo ordinare i png in un video. Lo possiamo fare, sempre in Python, così:

python
...
# inizializziamo il video
video = Video("shuttle.mp4", backend='cv')
n = 8
for i in range(n):
distance = -30
degrees = 360/n*i
radians = degrees * math.pi / 180
y = -distance * sin(radians)
x = distance * cos(radians)
plt.show(camera={'pos': (x, y, 0), 'viewup': (0, 0, 1)})
# aggiungiamo i frame al video
video.add_frame()
video.close()

Troverete quindi nella stessa cartella anche il video shuttle.mp4. Aprendolo però vi accorgerete di un problema: i frame si susseguono così velocemente che nemmeno noterete i dettagli delle varie facce dell’oggetto 3D. Questo perchè abbiamo utilizzato solo 8 frame e il componente Video (che è sempre del pacchetto vedo) ha un valore di default frame per sec (fps) di 24; quindi la durata del video è meno di 1 secondo (8 frame / 24 frame/sec = 0,333333sec). 24 fps è il valore di default e noi ci accontentiamo perchè i nostri occhi-cervello hanno una frequenza di aggiornamento che va dai 30 ai 60 frame per secondo; 24 fps va più che bene, ma volendo, possiamo modificarlo.



Quindi, siamo riusciti nell'intento ma il video è troppo corto! Dura meno di 1 secondo! Cambiamo approcio! Invece che utilizzare come unità di misura del video, gli 8 frame, utilizziamo i secondi! Partiamo dai secondi di durata del video (esempio 5 secondi), sappiamo che il video mostra 24 frame in un secondo e sappiamo di certo che la circonferenza totale ha 360°. Quindi in totale dobbiamo avere 24*5=120 frame. Vuol dire che ci dovremo fermare 120 volte, quindi ogni 360°/120=3°! Detto, fatto! Ecco la versione finale del codice:
python
import math
import matplotlib.pyplot as plt
from vedo import *
# creo una nuova finestra con sfondo bianco grande 400x400 e facciamo vedere gli assi
plt = Plotter(bg='white', size=(400, 400),
interactive=None, offscreen=True)
# aggiungo il file obj e lo coloriamo di blu
plt += Mesh("shuttle.obj", c="blue")
plt += __doc__
# inizializziamo il video
video = Video("shuttle.mp4", backend='cv')
duration=5
n = int(24*duration)
for i in range(n):
distance = -30
degrees = 360/n*i
radians = degrees * math.pi / 180
y = -distance * sin(radians)
x = distance * cos(radians)
plt.show(camera={'pos': (x, y, 0), 'viewup': (0, 0, 1)})
# aggiungiamo i frame al video
video.add_frame()
video.close()

E.. il risultato:

Video dello shuttle in 3D

Vuoi altre idee per sviluppare in Python?

Se come me sei curioso e conosci poco Python, ti renderai conto che questo linguaggio di programmazione ha alte potenzialità. Per me è divertente: se ti concentri, puoi risolvere problemi non banali con una sola riga di codice. Comunque, ritornando alla domanda; pochi giorni fa ho pubblicato un post dove spiego come usare prettymaps, una libreria per creare mappe.. con Python! Foto di Bergamo con mappa con mura con prettymaps

File .obj di prova

Per comodità riporto questo elenco di file 3D Florida State University, il mio preferito è forse questo semplice ottaedro. Ottaedro

Spero che questo articolo vi sia piaciuto. Vi ho spiegato come creare un video da un file 3D .obj con Python e un po’ di matematica. Il video creato è ora pronto per essere condiviso con i tuoi amici! Non dimenticarti di sbizzarrirti con i colori, durate, inquadrature e texture!



Al prossimo post!

Richiedi una Consulenza Personalizzata Gratuita

Prenota subito una consulenza gratuita per scoprire come posso aiutarti a realizzare il tuo progetto di sviluppo web. Lascia che la mia esperienza e le mie competenze contribuiscano al successo della tua idea.

Contattami oggi per una valutazione dettagliata delle tue esigenze e ricevi un preventivo su misura per te!

Logo logicALG
Driven by quality and inspired by efficency

logicALG di Francesco Strazzante - Sede Legale: via Radini Tedeschi 24/G 24124 Bergamo - P.IVA 04359420165 - logicalg@pec.it - Codice Destinatario: N92GLON
Copyright © 2024. Made with ♥ by logicALG.