Estende il motore con stabilizzazione sub-pixel, deflicker per regioni, transizioni giorno-notte, movimento di macchina virtuale, rimappatura non lineare del tempo e accumulo temporale. Tutto in-house, nessuna dipendenza aggiunta: il progetto continua a non contenere un solo PackageReference. Perche' questa forma. Il rendering non percorre piu' la sequenza sorgente ma un piano di fotogrammi d'uscita, ognuno con posizione anche frazionaria e durata propria. Le quattro modalita' temporali producono tutte quella stessa forma, quindi il ciclo di rendering e' uno solo e non ha un ramo per ciascun caso; da li' discende anche la sfocatura, perche' un fotogramma che copre v scatti ha angolo di otturatore diviso v. Gli spostamenti si misurano con la correlazione di fase, che ignora per costruzione le differenze di luminosita' fra scatti - in un time-lapse ci sono sempre - e reagisce alla sola geometria. Il picco intero non basta: la superficie di correlazione viene ricostruita a passo fine valutando la somma di Fourier sulle posizioni intermedie invece di interpolare con una parabola tre campioni di una cresta che parabola non e'. L'errore misurato scende da 0,14 a 0,08 px. I gradini di esposizione non sono rumore da mediare: l'ampiezza si legge esatta nei metadati e viene ridistribuita su una transizione a derivata nulla agli estremi. Il deflicker lavora poi su una serie gia' priva di gradini, invece di trasformare lo scalino in una rampa con due spigoli. La maschera delle regioni nasce dalla mediana temporale di un campione di fotogrammi, che toglie di mezzo proprio le nuvole di passaggio, e la linea d'orizzonte viene agganciata al massimo del gradiente verticale. Sulla scena di prova il terreno passa da 0,062 a 0,026 stop di oscillazione. Ritaglio virtuale e correzione di stabilizzazione sono entrambi affini e vengono composti in una sola trasformazione: due ricampionamenti in fila costerebbero il doppio di nitidezza senza dare nulla in cambio. Sulla memoria: i moduli avanzati hanno rotto l'assunto che bastassero due fotogrammi vivi alla volta, quindi il disco entra ora in gioco - come annotato nel commit precedente, e' questa la porta che si apriva. La finestra attiva resta sempre in memoria perche' la mediana ha bisogno di tutti i suoi fotogrammi insieme; solo la lettura in anticipo viene parcheggiata su disco oltre il tetto, e ripresa una volta sola. Non esiste un caso in cui lo stesso fotogramma vada e torni piu' volte. Il file di parcheggio si cancella da se'. Verifica: da 26 a 51 controlli. Nessuna soglia scelta a posteriori - il tremolio ha un percorso noto, il gradino un'ampiezza dichiarata nei metadati e visibile nei pixel, la nuvola attraversa il solo cielo. Il controllo conclusivo rende una sequenza con tutti i moduli attivi insieme e tetto di memoria volutamente stretto, poi la rilegge con il lettore di sistema. Verificato anche sui DNG GoPro reali: 580 file, render di prova conforme. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Titano
Applicazione desktop per la creazione e l'ottimizzazione di time-lapse di livello professionale, sviluppata interamente in-house.
Il vincolo che definisce il progetto
Zero librerie di terze parti. Il file di progetto non contiene alcun
<PackageReference>: tutto ciò che non è la libreria standard di .NET è scritto qui dentro
oppure ottenuto tramite P/Invoke diretto verso componenti del sistema operativo.
| Ambito | Come è risolto |
|---|---|
| Parsing EXIF / XMP | Parser binario proprietario (Metadata/) |
| Analisi fotometrica e deflicker | Algoritmi proprietari (Analysis/) |
| Campo vettoriale e motion blur | Schema differenziale piramidale proprietario (Motion/) |
| Trasformata di Fourier | Cooley–Tukey a base due, in loco (Motion/Fourier.cs) |
| Segmentazione e stabilizzazione | Otsu, gradiente e correlazione di fase proprietarie |
| Interpolazione di curve | Spline monotona e Bézier proprietarie (Core/Spline.cs) |
| Contenitore MP4 | Multiplexer ISO-BMFF proprietario (Video/Mp4Muxer.cs) |
| Decodifica immagini | WIC, componente di Windows, via COM interop scritto a mano |
| Codifica video | Media Foundation Transform, encoder hardware di sistema |
| Interfaccia grafica | WinForms + GDI+, ogni controllo disegnato a mano (UI/) |
Non viene invocato nessun processo esterno: niente FFmpeg, niente ExifTool.
Principi architetturali
Il video è l'unico file che sopravvive. Il flusso compresso viene prodotto in streaming:
ftyp e l'header mdat all'apertura, i pacchetti dell'encoder direttamente nel file, moov
in chiusura. L'elaborazione può creare un solo altro file — il parcheggio temporaneo dei
fotogrammi descritto più sotto — che nasce con la cancellazione automatica alla chiusura e non
contiene nulla di riutilizzabile.
Impronta di memoria governata, non incidentale. I fotogrammi vivono in array float presi
da un pool (Imaging/ImageBuffer.cs) e passano da una finestra scorrevole
(Pipeline/FrameWindow.cs). L'occupazione dipende dall'ampiezza della finestra — che l'utente
sceglie — e mai dalla lunghezza della sequenza: la verifica automatica lo misura esplicitamente.
Interfaccia reattiva. Ingestion, analisi e render girano su thread di lavoro; il thread
dell'interfaccia riceve solo aggiornamenti di stato immutabili tramite IProgress<T>.
Struttura
Metadata/ parser binario TIFF/Exif, scanner XMP, riconoscimento contenitori
Core/ sequenza, cadenza, shutter angle, spline monotona e Bézier
Imaging/ buffer poolati, spazio colore lineare, decodifica WIC, stadio geometrico
Analysis/ luminanza, deflicker, segmentazione in regioni, transizioni giorno-notte
Motion/ Fourier, correlazione di fase, stabilizzazione, optical flow, blur, stacking
Video/ conversione NV12, encoder Media Foundation, multiplexer MP4
Pipeline/ piano di rendering, finestra scorrevole, orchestrazione, impostazioni
UI/ tema scuro, grafico vettoriale, editor di curve e keyframe, pannelli
Diagnostics/ sequenze sintetiche, verifica end-to-end, ispettore MP4
Il piano di rendering
Il motore non percorre la sequenza sorgente: percorre un elenco di fotogrammi d'uscita, ognuno con la propria posizione — anche frazionaria — e la propria durata. Durata costante, durata proporzionale all'intervallo, cadenza uniformata e rimappatura non lineare producono tutte quella stessa forma, quindi il ciclo di rendering è uno solo e non contiene un ramo per ciascuna modalità.
Una posizione frazionaria significa un fotogramma che non esiste e va sintetizzato dal campo
vettoriale. Da qui discende anche la sfocatura: se un fotogramma d'uscita copre v scatti,
l'angolo di otturatore effettivo è quello di ripresa diviso v, e la scia da sintetizzare si
scala della stessa quantità. Accelerare e rallentare restano quindi coerenti con la fisica
dell'esposizione invece di essere un semplice salto di indici.
Note sugli algoritmi
Deflicker. Per ogni fotogramma si esegue una regressione lineare locale pesata sulla finestra mobile. I pesi combinano una gaussiana sulla distanza temporale e, in seconda passata, un peso di robustezza di Tukey che neutralizza i fotogrammi anomali. La componente lineare segue senza ritardo le rampe reali di luce (alba, tramonto) e rimuove solo la componente ad alta frequenza dovuta alle micro-variazioni del diaframma. La misura è la media logaritmica troncata: invariante alla scala, insensibile a cieli bruciati e ombre chiuse.
Motion blur. Lo shutter angle reale è 360 × posa / intervallo. La scia già incisa nel
fotogramma e quella sintetica si compongono in quadratura, quindi per raggiungere l'apertura
obiettivo serve una scia di √(obiettivo² − reale²) volte lo spostamento: sommare
linearmente produrrebbe un'immagine sistematicamente troppo morbida. Il filtro di
ricostruzione è di tipo gather: un campione contribuisce al pixel centrale solo se la
propria scia lo raggiunge davvero, così lo sfondo fermo non viene trascinato nei soggetti in
movimento.
Optical flow. Schema differenziale piramidale: su ogni nodo di una griglia rada si risolve iterativamente il sistema normale 2×2 costruito dai gradienti spaziali e dalla differenza temporale. Un filtro mediano fra un livello e l'altro elimina i vettori spuri delle zone piatte. L'analisi avviene a risoluzione ridotta e i vettori vengono riportati in scala piena: il costo non dipende dalla dimensione dei file sorgente. Il campo si calcola solo quando serve davvero: se la posa copre già più dell'apertura obiettivo — il caso delle riprese notturne, con pose da trenta secondi su intervalli da trentaquattro — non c'è sfocatura da sintetizzare e la voce di spesa più pesante della pipeline viene saltata.
Moduli avanzati
Stabilizzazione sub-pixel. Lo spostamento fra fotogrammi adiacenti si misura con la correlazione di fase: una traslazione nello spazio è uno sfasamento lineare in frequenza, e normalizzando lo spettro incrociato al modulo unitario resta solo la fase, la cui antitrasformata è un impulso posto sullo spostamento. Il metodo ignora quindi per costruzione le differenze di luminosità fra due scatti — che in un time-lapse ci sono sempre — e reagisce alla sola geometria. Sotto il pixel la superficie di correlazione viene ricostruita a passo fine valutando direttamente la somma di Fourier sulle posizioni intermedie, invece di interpolare con una parabola tre campioni di una cresta che parabola non è: l'errore misurato scende da 0,14 a 0,08 px. Nove riquadri per coppia danno nove misure indipendenti, da cui si stima ai minimi quadrati una similitudine — traslazione, rotazione e scala — scartando i riquadri occupati da nuvole o fronde, che si muovono per conto loro. Il percorso assoluto viene infine lisciato: ciò che resta fra percorso vero e percorso liscio è il tremolio, e la sua inversa è la correzione. Una panoramica voluta sopravvive perché è già liscia.
Deflicker per regioni. Una nuvola densa che attraversa il cielo abbassa la luminanza media del fotogramma; il deflicker legge un calo di luce e schiarisce tutto, paesaggio compreso, che invece non era cambiato. Titano divide il fotogramma in due regioni e dà a ciascuna la propria curva. La maschera nasce una volta sola dalla mediana temporale di un campione di fotogrammi — la mediana toglie di mezzo proprio ciò che passa e non resta — e la linea d'orizzonte viene poi agganciata al massimo del gradiente verticale, che cade sul bordo vero e non dove capita la soglia. Sulla scena di prova il terreno passa da 0,062 a 0,026 stop di oscillazione.
Transizioni giorno-notte. Attraversando il tramonto la macchina deve cambiare tempo, ISO o diaframma, e ogni cambio è quantizzato: nel filmato si vede uno scalino netto. Il deflicker ordinario non aiuta, perché un gradino non è rumore da mediare e la finestra mobile lo trasforma in una rampa con due spigoli. Titano legge invece l'ampiezza esatta nei metadati — il valore di esposizione vale log2(N²/t) − log2(ISO/100) — e la ridistribuisce su una transizione a derivata nulla agli estremi, lunga a piacere. Il deflicker lavora poi sulla serie già priva di gradini. Il bilanciamento del bianco riceve un trattamento parallelo: si liscia il rapporto fra i canali invece della loro luminanza, così sparisce il tremolio del bilanciamento automatico e resta il viaggio verso il caldo del tramonto.
Movimento di macchina virtuale. Un sensore da dodici megapixel contiene un 4K con abbondante margine di ritaglio: da lì si ricava una panoramica che in ripresa avrebbe richiesto una slitta motorizzata, decisa dopo, guardando il materiale. Fra un nodo e il successivo il tempo passa per una curva di Bézier con estremi fissi — senza, il movimento partirebbe e si fermerebbe di colpo, e a velocità di time-lapse lo scatto si vede benissimo. Ritaglio e correzione di stabilizzazione sono entrambi affini e vengono composti in una sola trasformazione: il fotogramma viene interpolato una volta sola, con una cubica di Catmull-Rom.
Rimappatura non lineare del tempo. Una curva di velocità dice quanti scatti vengono consumati per ogni fotogramma d'uscita: sopra 1 la sequenza accelera saltando scatti, sotto 1 rallenta e i mancanti vengono sintetizzati. La velocità del filmato resta fissa. La spline è monotona per costruzione (schema di Fritsch–Carlson): una Catmull-Rom ordinaria sovraelongerebbe fra due nodi molto diversi, e una velocità negativa farebbe tornare indietro la sequenza.
Accumulo temporale. La mediana su una finestra di fotogrammi dà a ogni pixel il valore centrale della propria serie: la scena stabile resta se stessa, chi attraversa l'inquadratura una volta sola sparisce. Una media lascerebbe un fantasma tanto più visibile quanto più l'intruso era contrastato. Il massimo progressivo conserva invece il valore più alto incontrato e trasforma le stelle in archi continui; con una lunghezza di scia finita l'accumulo decade e le scie hanno una coda invece di riempire il cielo. Se la stabilizzazione è attiva i fotogrammi vengono sovrapposti dopo averli raddrizzati, altrimenti la mediana scambierebbe per intruso il tremolio stesso.
Memoria e parcheggio su disco
I moduli avanzati hanno rotto l'assunto su cui la pipeline originale era costruita: che bastassero due fotogrammi vivi alla volta. La mediana ne vuole un'intera finestra simultaneamente, la rimappatura può chiedere lo stesso scatto per molti fotogrammi d'uscita consecutivi.
La regola di residenza è una sola e non produce mai andirivieni: la finestra attiva sta sempre in memoria, perché chi la usa ha bisogno di tutti i suoi fotogrammi insieme; i fotogrammi letti in anticipo — che servono a tenere occupati tutti i processori sulla decodifica dei RAW, la parte più lenta della pipeline — finiscono su disco quando il tetto di memoria è raggiunto, e vengono ripresi una volta sola, quando entrano nella finestra. Non esiste un caso in cui lo stesso fotogramma vada e torni dal disco più volte. Il file di parcheggio nasce con l'opzione di cancellazione alla chiusura: se il programma termina male, lo rimuove il sistema.
Sorgenti RAW e limiti dei codec
I file RAW e DNG non espongono l'immagine principale nella prima directory: quella è una miniatura, e le dimensioni vere vivono in una SubIFD marcata come non ridotta. Titano sceglie la directory dell'immagine principale invece di fidarsi della prima, e legge i metadati con una finestra che si estende su richiesta: di un DNG da 15 MiB ne tocca 128 KiB.
Orientamento. Il tag Exif dice come raddrizzare l'immagine, ma non basta leggerlo: alcuni decodificatori di sistema lo applicano già per conto proprio e altri no. Applicarlo una seconda volta ribalta il fotogramma, non applicarlo mai lo lascia coricato. In modalità automatica Titano confronta le dimensioni che il decodificatore restituisce con quelle che il file dichiara di contenere e decide di conseguenza; la sezione Generale mostra cosa ha dedotto e permette di imporre una trasformazione diversa, con riscontro immediato nell'anteprima.
Qualità. Il profilo di elaborazione governa finezza della griglia del campo vettoriale, livelli della piramide, iterazioni per livello, campioni per pixel della scia e risoluzione della passata fotometrica. Il valore predefinito è "Massima": costa circa tre volte il tempo di "Standard" e si vede nei bordi dei soggetti in movimento.
La risoluzione di lavoro viene infine ricondotta ai limiti del codec. Un encoder hardware accetta volentieri un fotogramma 4000×3000 e dichiara un livello alto: il flusso è formalmente valido ma supera il Livello 5.2 di H.264, e i decodificatori comuni si rifiutano di aprirlo. Il video uscirebbe, peserebbe, e non si riprodurrebbe. Titano riduce quindi il fotogramma al massimo riproducibile conservando le proporzioni, e lo dichiara in interfaccia. Le sorgenti 16:9 fino al 4K UHD non vengono toccate.
Compilazione ed esecuzione
dotnet build -c Release
dotnet run -c Release
Richiede .NET 10 SDK su Windows x64.
Verifica automatica
Titano.exe --selftest [cartella]
Genera sequenze sintetiche dalle proprietà note e le fa attraversare l'intera pipeline, confrontando 51 grandezze misurate con i valori attesi. Nessuna soglia è scelta a posteriori: la scena è costruita perché il valore atteso sia la conseguenza aritmetica di come è stata generata — il tremolio ha un percorso noto, il gradino di esposizione un'ampiezza dichiarata nei metadati e visibile nei pixel, la nuvola attraversa il solo cielo.
Copre campi Exif, cadenza e shutter angle; riduzione dello sfarfallio e conservazione della rampa di luce; invarianza della luminanza alla scala di decodifica; correlazione di fase sotto il pixel; misura e rimozione del tremolio con la panoramica voluta che sopravvive; copertura della maschera delle regioni; riconoscimento e ridistribuzione dei cambi di esposizione; lisciatura cromatica con la deriva del tramonto conservata; monotonia e simmetria delle curve di accelerazione; trasparenza e ritaglio dello stadio geometrico; monotonia della rimappatura temporale; rimozione dell'intruso da parte della mediana e conservazione dei passaggi da parte del massimo; allineamento delle NAL dentro ogni campione del contenitore; e — prova conclusiva — un rendering con tutti i moduli attivi insieme, con tetto di memoria volutamente stretto per esercitare il parcheggio su disco, riletto poi dal lettore di sistema.
Titano.exe --diagnose <cartella> [uscita.mp4] [fotogrammi] [larghezza] [h264|hevc]
Riporta cosa il programma riesce davvero a leggere da una cartella reale: metadati file per file, dimensione dichiarata contro dimensione decodificata, coerenza della luminanza fra risoluzioni diverse, cadenza sull'intera sequenza. Indicando un file di uscita esegue anche un render di prova e ne riverifica il contenitore. È lo strumento con cui si distingue un difetto del motore da un formato che il sistema non sa aprire.
Titano.exe --capture <file.png> [cartella-sequenza] [scheda] [avanzato]
Cattura l'interfaccia in un'immagine, per verificarne la resa in modo riproducibile. Con
avanzato i moduli opzionali vengono accesi prima del caricamento, così l'immagine mostra
curve di regione, marcatori dei cambi di esposizione, percorso della macchina virtuale e
contorno delle regioni con dati veri invece che a riposo.
Titano.exe --make-icon <file.ico>
Ridisegna l'icona dell'applicazione — scie stellari attorno al polo celeste, l'immagine in cui il tempo diventa visibile — e ne scrive il contenitore ICO a mano. I livelli sotto i venti pixel ricevono meno archi e più spessi: alla dimensione della barra delle applicazioni sette scie sottili diventerebbero una macchia.