4.15. Framebuffer¶
Una volta inizializzato il sensore della camera, esso emette frame in modo continuo al proprio frame rate – un nuovo frame a ogni periodo di frame, indipendentemente dal fatto che l’applicazione sia pronta o meno a riceverlo. Ogni frame ha bisogno di un posto in RAM dove atterrare, altrimenti viene perso. Il pool di frame buffer è il luogo in cui questi frame risiedono tra il momento in cui lasciano il DMA e quello in cui vengono elaborati dal codice utente, e il numero di frame buffer che la camera mantiene in quel pool determina come il DMA e l’applicazione li condividono. La scelta è esposta tramite framebuffers(), e sono disponibili quattro modalità, selezionate in base al numero di buffer.
4.15.1. Buffer singolo (count = 1)¶
Un solo frame buffer in RAM. Il DMA lo riempie; l’applicazione vi legge; la chiamata successiva a snapshot() non può iniziare finché l’applicazione non ha rilasciato il buffer, poiché lo stesso buffer serve a entrambi.
La camera e l’applicazione procedono in lock-step. Il DMA deve attendere che l’applicazione finisca, e l’applicazione deve attendere che il DMA finisca, il che significa che il frame rate ottenibile è nella migliore delle ipotesi la metà del frame rate del sensore – un frame sì e uno no tra quelli emessi dal sensore arriva mentre il buffer è occupato e viene perso.
Questa modalità è la più contenuta in termini di RAM e la più lenta in termini di throughput. Usala solo quando la RAM è troppo limitata per allocare un secondo buffer.
4.15.2. Buffer doppio (count = 2)¶
Due frame buffer in RAM: un buffer back che il DMA riempie e un buffer front da cui l’applicazione legge. Quando l’applicazione termina con il buffer front, i due ruoli si scambiano, e il DMA inizia a riempire il buffer appena rilasciato mentre l’applicazione legge da quello appena riempito.
Finché l’applicazione elabora ogni frame in meno di un periodo di frame della camera, l’applicazione vede il frame rate pieno del sensore – il frame successivo del DMA è già in attesa nel buffer back quando l’applicazione chiama nuovamente snapshot(). Nel momento in cui però il tempo di elaborazione supera un periodo di frame, il rate si dimezza: la camera produrrà due frame nel tempo in cui l’applicazione ne elabora uno, e solo il secondo dei due verrà consegnato.
Oltre quel punto il rate degrada in modo graduale con il tempo di elaborazione. Ogni volta che il DMA termina un nuovo frame nel buffer back mentre l’applicazione sta ancora lavorando sul buffer front, il nuovo frame sovrascrive in loco la cattura precedente anziché essere scartato. L’applicazione ottiene sempre il frame più recente prodotto dalla camera alla sua successiva snapshot(), e il rate ottenibile dall’applicazione diventa l’inverso del suo tempo di elaborazione.
4.15.3. Buffer triplo (count = 3)¶
Tre frame buffer in RAM: due buffer back che il DMA percorre ciclicamente e un buffer front su cui l’applicazione sta attualmente lavorando. Questa è la modalità predefinita che la OpenMV Cam sceglie quando c’è RAM sufficiente disponibile, con ripiego automatico su buffer doppio o singolo quando non ce n’è.
Il terzo buffer disaccoppia completamente il frame rate della camera dal frame rate dell’applicazione. Il DMA ha sempre un buffer in cui scrivere; l’applicazione ha sempre un buffer da cui leggere; a ogni snapshot() il buffer back pronto più recente diventa il nuovo buffer front e il precedente buffer front viene liberato per il DMA. Il frame rate dell’applicazione coincide con il tempo che effettivamente impiega per elaborare ogni frame – senza il dimezzamento in cui il buffer doppio incappa quando il tempo di elaborazione supera di poco un periodo di frame.
4.15.4. Video FIFO (count = 4 o più)¶
Quattro o più frame buffer in RAM, disposti come un anello di frame catturati uno di seguito all’altro. Ogni frame consegnato dalla camera viene accodato nella FIFO, e snapshot() restituisce il frame accodato più vecchio anziché quello più recente. L’applicazione percorre i frame catturati nell’ordine di cattura, nel tempo che effettivamente ha a disposizione per ciascuno.
Questa modalità è la scelta giusta quando ogni frame conta e sono attese brevi pause di elaborazione: scrittura di video su una scheda SD il cui stack di storage può bloccarsi per decine di millisecondi durante una cancellazione, streaming via USB verso un host che smette brevemente di leggere, o buffering di un breve burst di un evento rapido per l’ispezione nel codice.
Due politiche gestiscono il caso in cui la FIFO si riempie prima che l’applicazione l’abbia svuotata.
Scarta i frame vecchi (predefinito). Quando la FIFO si riempie, tutti i frame accodati tranne quello attivo vengono scartati, in modo che la successiva
snapshot()restituisca un frame recente anziché uno obsoleto. Il DMA continua a catturare per tutto il tempo, così l’applicazione vede sempre dati freschi dopo una pausa. Questa è la politica giusta quando l’obiettivo è mantenere aggiornato lo stream catturato – registrazione video, streaming dal vivo.Interrompi la cattura in caso di overflow. Passa
fflush=Falseal costruttore diCSIe il DMA smetterà di riempire la FIFO quando è piena, lasciando intatti i frame accodati.snapshot()continua a restituire i frame nell’ordine di cattura finché l’applicazione non li ha svuotati, dopodiché il DMA riprende. Questa è la politica giusta quando l’obiettivo è preservare ogni frame di un breve burst – catturare un movimento rapido per ispezionarlo frame per frame nel codice in seguito.
Vedi csi.CSI.framebuffers() per l’API completa.
4.15.5. Modalità triggered¶
Un’alternativa alle modalità sempre in esecuzione sopra descritte è la cattura triggered, in cui il sensore emette un frame solo quando snapshot() ne richiede uno. La camera resta inattiva tra uno snapshot e l’altro e avvia una nuova esposizione ogni volta che l’applicazione la interpella.
Il costo è il throughput: una cattura triggered non può sovrapporsi a quella precedente, quindi il frame rate massimo ottenibile è la metà del rate normale del sensore. Il vantaggio è il timing dell’esposizione. Lo snapshot controlla esattamente quando inizia l’esposizione, che è ciò che un’applicazione desidera quando l’esposizione deve allinearsi a un evento esterno – un flash a strobo, un sensore di posizione del nastro trasportatore, un impulso su una linea GPIO – anziché capitare ovunque si trovi il frame in rolling del sensore a corsa libera nel momento in cui l’applicazione è pronta a leggerlo.
La modalità triggered è specifica del sensore. Sui sensori supportati si abilita chiamando csi0.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True) e si disabilita passando False.