Camera multispettrale a eventi¶
Il modulo Multispectral Event Camera abbina il sensore a eventi GENX320 a un sensore di colore a global-shutter PAG7936 da 1 MP su un unico modulo, ovvero una pipeline sincronizzata eventi + colore per il tracciamento di oggetti ad alta velocità, il tracciamento di LED, il flusso di fluidi e altre scene dinamiche.
Per il datasheet completo, le foto e l’acquisto consulta la pagina prodotto Multispectral Event Camera.
Nota
Supportato solo su OpenMV N6.
Punti salienti¶
Sensore a eventi 320x320, gamma dinamica >140 dB, istogrammi a 375 Hz+
Colore PAG7936: 1280x800 @ 120 FPS, 640x400 @ 240 FPS
Timestamp degli eventi sincronizzati con trigger di esposizione condiviso
Vede al di sotto di 5 lux senza esposizione automatica
Il consumo parte da circa 3 mW per lo streaming degli eventi
Pensato per il tracciamento ad alta velocità, il tracciamento di LED e il flusso di fluidi/particelle
Utilizzo¶
Il sensore di colore e il sensore a eventi GENX320 ottengono ciascuno la propria istanza csi.CSI. La prima chiamata usa per impostazione predefinita il sensore primario (il PAG7936); la seconda si associa al GENX320 passando cid= csi.GENX320. Esegui un hard-reset del sensore di colore con csi.CSI.reset (hard=True) per attivare l’alimentazione, e configura il GENX320 con hard=False in modo che il suo driver riprogrammi solo il chip senza ripristinare nuovamente il reset.
Il GENX320 produce 320x320 in modalità istogramma; il PAG7936 a csi.QVGA produce 320x200. L’overlay di base qui sotto ritaglia le 120 righe inferiori del frame del GENX320. Usa la trasformazione di omografia (più avanti) per un overlay adattato o per una framesize PAG7936 più grande.
Due buffer di lavoro restano costanti durante il loop dei frame: una palette alpha 256x1 memorizzata come image.Image in modo che i pixel dell’istogramma alla linea di base del grigio medio (128) diventino trasparenti mentre sia gli highlight degli eventi ON sia le ombre degli eventi OFF diventino opachi, e un frame buffer GENX320 pre-allocato con image.Image in modo che csi.CSI.snapshot (blocking=False, image=...) possa riempirlo in loco a ogni iterazione senza riallocare:
import time
import csi
import image
import math
# V-shaped alpha: pixels far from the baseline 128 become opaque.
alpha_pal = image.Image(256, 1, image.GRAYSCALE)
for i in range(256):
alpha_pal[i] = int(math.pow(abs(i - 128) / 128.0, 2) * 255)
# Setup the color camera sensor.
csi0 = csi.CSI()
csi0.reset(hard=True) # force hardware reset.
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
csi1 = csi.CSI(cid=csi.GENX320)
csi1.reset(hard=False) # no hardware reset - just configure GENX320
csi1.pixformat(csi.GRAYSCALE)
csi1.framesize((320, 320))
csi1.brightness(128) # histogram baseline (default)
csi1.contrast(64) # per-event step
clock = time.clock()
img1 = image.Image(csi1.width(), csi1.height(), csi1.pixformat())
while True:
clock.tick()
img0 = csi0.snapshot()
csi1.snapshot(blocking=False, image=img1)
img0.draw_image(img1, 0, 0, color_palette=image.PALETTE_EVT_LIGHT,
alpha_palette=alpha_pal,
hint=image.BILINEAR)
print(clock.fps())
Ogni iterazione acquisisce uno snapshot a colori bloccante e uno snapshot GENX320 non bloccante. Image.draw_image quindi compone i due: color_palette= image.PALETTE_EVT_LIGHT (oppure image.PALETTE_EVT_DARK per uno sfondo scuro) mappa l’istogramma in scala di grigi del GENX320 in una rampa di colori, alpha_palette= fonde ogni pixel usando la mappa alpha a forma di v in modo che le regioni quiete della scena lascino passare l’immagine a colori, e hint= image.BILINEAR ammorbidisce l’upscaling quando il sensore di colore funziona a una risoluzione superiore rispetto al GENX320.
I preset di bias del GENX320, il filtro AFK, la calibrazione dei pixel difettosi e gli ioctl del filtro STC funzionano tutti allo stesso modo in questa configurazione a doppia camera: chiamali su csi1 dopo csi.CSI.reset. Vedi le sezioni seguenti per i dettagli.
Allineamento accelerato da GPU¶
Image.draw_image accetta un argomento transform= — una matrice di omografia 3x3 come array ulab.numpy 2-D. Sull’OpenMV N6 la GPU esegue la proiezione per-pixel durante lo stesso disegno, quindi il frame del GENX320 può essere riallineato rispetto alla prospettiva della camera a colori senza un passaggio di warp separato — utile quando i due sensori hanno ottiche o campi visivi leggermente diversi, o quando la camera a colori funziona a una risoluzione superiore. Calibra la matrice per ogni camera con lo strumento GenX320 Overlay Calibration, che mostra una scacchiera lampeggiante in modo che il sensore a eventi produca eventi sugli angoli senza alcun movimento fisico:
import time
import csi
import image
from ulab import numpy as np
import math
# Calibration matrix from the GenX320 Overlay Calibration tool.
m = np.array([
[2.000000, 0.000000, 0.000000],
[0.000000, 2.000000, 80.000000],
[0.000000, 0.000000, 1.000000],
])
alpha_pal = image.Image(256, 1, image.GRAYSCALE)
for i in range(256):
alpha_pal[i] = int(math.pow(abs(i - 128) / 128.0, 2) * 255)
# Setup the color camera sensor.
csi0 = csi.CSI()
csi0.reset(hard=True)
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi1 = csi.CSI(cid=csi.GENX320)
csi1.reset(hard=False)
csi1.pixformat(csi.GRAYSCALE)
csi1.framesize((320, 320))
csi1.brightness(128)
csi1.contrast(64)
clock = time.clock()
img1 = image.Image(csi1.width(), csi1.height(), csi1.pixformat())
while True:
clock.tick()
img0 = csi0.snapshot()
csi1.snapshot(blocking=False, image=img1)
img0.draw_image(img1, 0, 0, color_palette=image.PALETTE_EVT_LIGHT,
alpha_palette=alpha_pal,
hint=image.BILINEAR,
transform=m)
print(clock.fps())
Questa variante esegue la camera a colori a csi.VGA (640x480) e il GENX320 alla sua risoluzione nativa 320x320 — l’omografia proietta il frame più piccolo del GENX320 nel frame a colori più grande come parte del disegno, quindi il fattore di upscaling è incorporato nella matrice stessa anziché applicato separatamente.
Dettagli della camera a eventi¶
Il GENX320 è un sensore di visione basato su eventi — invece di leggere l’intero array 320x320 su un clock di frame fisso, ogni pixel segnala «eventi» asincroni nell’istante in cui rileva una variazione di luminosità. Ogni evento porta con sé una coordinata X/Y, una polarità ON/OFF (da chiaro→scuro o da scuro→chiaro) e un timestamp in microsecondi. È da qui che derivano la precisione temporale al microsecondo del sensore, l’assenza di sfocatura da movimento, la gamma dinamica molto elevata e il consumo proporzionale all’attività. Le scene statiche non generano alcun dato.
Il firmware OpenMV espone il GENX320 attraverso csi.CSI con cid= csi.GENX320. Sono disponibili due modalità operative:
Modalità istogramma (predefinita) — gli eventi vengono accumulati on-chip in bin per-pixel e riportati come frame in scala di grigi 320x320 a una frequenza configurabile (~20-350 FPS). Il sensore si comporta come una normale camera, quindi tutte le routine standard di elaborazione delle immagini (
Image.find_blobs, palette, ecc.) funzionano direttamente.Modalità eventi — gli eventi grezzi confluiscono in un
ndarraynumpy con timestamp completi al microsecondo, per applicazioni che necessitano del dettaglio temporale anziché di un frame pre-aggregato.
Modalità istogramma¶
In modalità istogramma il GENX320 produce frame in scala di grigi in cui ogni pixel codifica l’attività di eventi recente in quella posizione. I pixel al di sopra della linea di base di luminosità sono eventi ON (luminosità in aumento), quelli al di sotto sono eventi OFF (luminosità in diminuzione). La luminosità di base predefinita è 128 e il passo di contrasto per-evento è 16 — aumenta il contrasto per far risaltare gli eventi:
import csi
import time
csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize((320, 320))
csi0.brightness(128) # baseline (default 128)
csi0.contrast(16) # per-event step
csi0.framerate(50) # 20-350 FPS
clock = time.clock()
while True:
clock.tick()
img = csi0.snapshot()
print(clock.fps())
csi.CSI.brightness, csi.CSI.contrast e csi.CSI.framerate sono le tre manopole che modellano l’output dell’istogramma.
Output colorizzato¶
Imposta csi.CSI.color_palette su image.PALETTE_EVT_LIGHT per uno sfondo chiaro o su image.PALETTE_EVT_DARK per uno scuro — il driver emette frame RGB565 usando direttamente la palette:
csi0.color_palette(image.PALETTE_EVT_LIGHT)
Calibrazione dei pixel difettosi¶
I sensori a eventi accumulano «pixel difettosi» che si attivano in modo spurio. Esegui csi.IOCTL_GENX320_CALIBRATE su una scena statica per disabilitarli. Il driver costruisce un conteggio di hit per-pixel 320x320, calcola la media e la deviazione standard, e disabilita qualsiasi pixel il cui conteggio è superiore a mean + sigma * stddev — quindi i pixel disabilitati smettono di emettere eventi a livello di sensore.
Due parametri controllano la calibrazione:
event_count— quanti eventi conteggiare prima di calcolare le statistiche. Il loop acquisisce frame finché il totale corrente degli eventi supera questo budget. Conteggi più alti danno una stima più affidabile al costo di un tempo di calibrazione più lungo.10000è un punto di partenza ragionevole.sigma— moltiplicatore di soglia sulla deviazione standard. Valori più bassi sono più aggressivi (più pixel disabilitati); valori più alti sono più conservativi.0.5è un buon valore predefinito.
Punta prima il sensore verso una scena statica in modo che eventuali eventi generati dal movimento non vengano conteggiati a sfavore di pixel che in realtà sono integri:
csi0.snapshot(time=5000) # let the user steady the camera
disabled = csi0.ioctl(csi.IOCTL_GENX320_CALIBRATE, 10000, 0.5)
print(f"disabled {disabled} hot pixels")
Filtro anti-sfarfallio (AFK)¶
Le sorgenti di luce periodiche (fluorescenti, display a LED) generano enormi quantità di eventi ridondanti. Il filtro AFK rifiuta gli eventi il cui pixel commuta a una frequenza all’interno di una banda — abilitalo tramite csi.IOCTL_GENX320_SET_AFK con i bordi della banda in hertz:
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160) # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0) # disable
Preset di bias¶
Ogni pixel del GenX320 dispone di un front-end analogico con diversi bias configurabili. Insieme governano sensibilità, rumore, larghezza di banda del pixel e frequenza degli eventi — la combinazione giusta dipende dalla scena. I singoli bias sono:
DIFF_ON — la soglia di contrasto positiva del comparatore. Un pixel emette un evento ON quando la sua log-illuminazione è aumentata di questa quantità. Più basso = più sensibile alle transizioni verso il chiaro.
DIFF_OFF — la soglia di contrasto negativa del comparatore (la controparte simmetrica per gli eventi OFF). Più basso = più sensibile alle transizioni verso lo scuro.
FO — la frequenza di taglio passa-basso del pixel. Più alta = larghezza di banda del pixel più ampia (risposta più rapida, latenza inferiore) ma maggiore attività di rumore di fondo.
HPF — la frequenza di taglio passa-alto. Più alta = reiezione più forte delle variazioni lente di luminosità; solo le transizioni rapide raggiungono i comparatori. Utile per ignorare la deriva ambientale.
REFR — il periodo refrattario. Dopo che un pixel si attiva, rimane in reset per questo intervallo prima di potersi attivare di nuovo. Più alto = tempo morto più lungo, utile per limitare la frequenza di eventi per-pixel.
Dopo csi.CSI.reset il driver applica csi.GENX320_BIASES_LOW_NOISE, non csi.GENX320_BIASES_DEFAULT — i valori predefiniti del datasheet emettono una frequenza di eventi di fondo molto più elevata, quindi LOW_NOISE viene usato come punto di partenza per mantenere il flusso silenzioso. Chiama csi.IOCTL_GENX320_SET_BIASES con un preset diverso quando l’applicazione necessita di maggiore sensibilità o larghezza di banda.
csi.IOCTL_GENX320_SET_BIASES applica uno di cinque preset:
csi.GENX320_BIASES_DEFAULT— valori predefiniti del datasheet GenX320. Sensibilità, rumore e larghezza di banda bilanciati per scene generiche.csi.GENX320_BIASES_LOW_LIGHT— entrambe le soglie di contrasto allentate per una maggiore sensibilità, FO abbassato per contenere il rumore e HPF impostato su 0 in modo che le variazioni lente di luminosità vengano comunque registrate — una scena con poca luce genera di per sé pochi eventi, quindi vogliamo che ne passino il più possibile.csi.GENX320_BIASES_ACTIVE_MARKER— ottimizzato per il tracciamento di LED lampeggianti ad alto contrasto. Soglie di contrasto innalzate in modo che solo le transizioni nette si attivino; FO e HPF spinti in alto per massimizzare la larghezza di banda del pixel e respingere ogni deriva ambientale lenta; REFR portato a 0 in modo che ogni fronte di lampeggio venga catturato uno dietro l’altro. Il risultato: un flusso fatto quasi interamente di fronti di LED, facile da tracciare.csi.GENX320_BIASES_LOW_NOISE— predefinito del driver. Entrambe le soglie di contrasto innalzate rispetto aDEFAULT(meno sensibile) e FO abbassato (pixel più lento = pixel più silenzioso). Ideale per scene statiche o lente in cui gli eventi falsi dominerebbero altrimenti.csi.GENX320_BIASES_HIGH_SPEED— FO aumentato in modo che ogni pixel possa rispondere più rapidamente, HPF innalzato per respingere la deriva lenta di luminosità, e REFR innalzato in modo che un singolo fronte in rapido movimento non inondi la lettura — il tempo morto più lungo mantiene limitato il volume di eventi in caso di forte movimento.
Sovrascrivi i singoli bias con csi.IOCTL_GENX320_SET_BIAS più uno tra csi.GENX320_BIAS_DIFF_ON, csi.GENX320_BIAS_DIFF_OFF, csi.GENX320_BIAS_FO, csi.GENX320_BIAS_HPF, o csi.GENX320_BIAS_REFR e un valore DAC. Ogni bias viene impostato in modo indipendente — scegli un preset come punto di partenza, poi regola i bias di cui la tua scena ha bisogno:
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)
Tracciamento¶
Poiché l’output in modalità istogramma è semplicemente un’immagine in scala di grigi, il normale tracciamento dei blob funziona direttamente. Per tracciare un LED active-marker, carica il preset di bias active-marker e trova i blob all’estremità luminosa dell’istogramma:
import csi
import time
csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize((320, 320))
csi0.brightness(128)
csi0.contrast(16)
csi0.framerate(200)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_ACTIVE_MARKER)
clock = time.clock()
while True:
clock.tick()
img = csi0.snapshot()
for blob in img.find_blobs([(120, 140)], invert=True,
pixels_threshold=2, area_threshold=4,
merge=True):
img.draw_detection(blob)
print(clock.fps())
Modalità eventi¶
La modalità eventi bypassa l’istogramma on-chip e fa confluire gli eventi grezzi in un ndarray numpy. Ogni evento è una riga di sei colonne uint16:
[0]tipo di evento — vedi sotto[1]timestamp in secondi[2]timestamp in millisecondi[3]timestamp in microsecondi[4]coordinata X, 0-319[5]coordinata Y, 0-319
Il driver emette sei tipi di evento nella colonna [0]:
csi.PIX_OFF_EVENT— un pixel ha rilevato una diminuzione di luminosità (è stata superata la soglia del comparatoreDIFF_OFF). X/Y indicano il pixel che si è attivato.csi.PIX_ON_EVENT— un pixel ha rilevato un aumento di luminosità (è stata superata la sogliaDIFF_ON). X/Y indicano il pixel.csi.EXT_TRIGGER_FALLING— il pin di trigger esterno del sensore ha visto un fronte di discesa. X/Y non sono utilizzati.csi.EXT_TRIGGER_RISING— il pin di trigger esterno del sensore ha visto un fronte di salita. X/Y non sono utilizzati.csi.RST_TRIGGER_FALLING— trigger di reset del pixel, fronte di discesa. X/Y non sono utilizzati. Non generato dal firmware al momento.csi.RST_TRIGGER_RISING— trigger di reset del pixel, fronte di salita. X/Y non sono utilizzati. Non generato dal firmware al momento.
L’ingresso di trigger esterno del GENX320 è cablato alla linea di sincronizzazione dei frame della camera, che è instradata anche a P10 sia sul processore sia sul connettore dei pin — pilota P10 per iniettare fronti di sincronizzazione nel flusso di eventi e raccoglierli come eventi EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING insieme ai dati dei pixel.
La maggior parte delle applicazioni si interessa solo di PIX_OFF_EVENT e PIX_ON_EVENT; i tipi di trigger ti permettono di correlare gli eventi con segnali di temporizzazione esterni.
Alloca il buffer degli eventi con forma (EVT_res, 6) dove EVT_res è una potenza di due compresa tra 1024 e 65536, quindi entra in modalità eventi tramite csi.IOCTL_GENX320_SET_MODE con csi.GENX320_MODE_EVENT e la dimensione del buffer. Leggi gli eventi con csi.IOCTL_GENX320_READ_EVENTS, che riempie il buffer fino alla sua capacità e restituisce il numero di righe valide.
Image.draw_event_histogram rasterizza gli eventi in un’immagine in scala di grigi — per ogni evento ON aggiunge contrast al bin; per ogni evento OFF lo sottrae. clear=True reimposta prima l’immagine su brightness; clear=False accumula su più chiamate:
import csi
import image
import time
from ulab import numpy as np
img = image.Image(320, 320, image.GRAYSCALE)
events = np.zeros((2048, 6), dtype=np.uint16)
csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0])
clock = time.clock()
while True:
clock.tick()
n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
img.draw_event_histogram(events[:n], clear=True, brightness=128, contrast=64)
img.flush()
print(n, clock.fps())
I preset di bias della modalità istogramma, il filtro AFK e gli ioctl di calibrazione dei pixel difettosi funzionano tutti allo stesso modo in modalità eventi — chiamali dopo csi.IOCTL_GENX320_SET_MODE.
Filtraggio per polarità¶
Affetta l’array degli eventi con ulab per conservare solo gli eventi ON (movimento verso uno stato più luminoso) o solo gli eventi OFF:
TARGET = csi.PIX_ON_EVENT # or csi.PIX_OFF_EVENT
events_slice = events[:n]
indices = np.nonzero(events_slice[:, 0] == TARGET)[0]
if len(indices):
target_events = np.take(events_slice, indices, axis=0)
img.draw_event_histogram(target_events, clear=True,
brightness=128, contrast=64)
Accumulo a lunga esposizione¶
Imposta clear=False per continuare a impilare gli eventi nella stessa immagine su più frame — il risultato è una visualizzazione delle scie di movimento. Reimposta periodicamente per iniziare una nuova esposizione:
EXPOSURE_FRAMES = 30
i = 0
while True:
n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
clear = (i % EXPOSURE_FRAMES) == 0
img.draw_event_histogram(events[:n], clear=clear, brightness=128, contrast=64)
img.flush()
i += 1
Elaborazione ad alta velocità¶
Elimina la visualizzazione per liberare CPU per l’elaborazione degli eventi. Stampa le statistiche solo a ogni N-esima iterazione — eseguire una stampa a ogni iterazione diventa il collo di bottiglia a frequenze di eventi elevate:
csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0])
clock = time.clock()
i = 0
while True:
clock.tick()
n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
i += 1
if not i % 10:
print(f"{n} events {clock.fps()} fps")
Filtro di contrasto spazio-temporale (STC)¶
Un vero fronte di contrasto in movimento tende a innescare una raffica rumorosa di eventi sullo stesso pixel entro una breve finestra temporale — il mismatch tra pixel e il rumore analogico producono eventi extra attorno alla transizione genuina che non sono utili all’applicazione. Il filtro STC è un post-process on-chip che conserva solo uno (o pochi) eventi per raffica e scarta il resto.
Implementa tre strategie, selezionate tramite csi.IOCTL_GENX320_SET_STC e una costante GENX320_STC_*. Ogni modalità è definita da quali eventi inoltra da una raffica:
Modalità |
Conserva |
Scarta |
|---|---|---|
ogni evento |
nulla |
|
secondo evento di una raffica |
primo + eventi successivi |
|
primo evento di una raffica |
eventi successivi |
|
primo + fronti successivi |
solo rumore ridondante |
In dettaglio:
csi.GENX320_STC_DISABLE— filtro disattivato, ogni evento passa attraverso (predefinito).csi.GENX320_STC_ONLY— conserva il secondo evento di una raffica. Parametro:stc_threshold(ms). Se un nuovo evento su un pixel arriva entrostc_thresholdda un evento precedente, viene considerato il «secondo» di una raffica e viene inoltrato — il primo evento e qualsiasi evento successivo della stessa raffica vengono filtrati. Ideale quando vuoi una transizione confermata dal rumore anziché il primissimo hit.csi.GENX320_STC_TRAIL_ONLY— conserva il primo evento di una raffica. Parametro:trail_threshold(ms). Dopo che un pixel si attiva, gli eventi successivi sullo stesso pixel vengono scartati finché non è trascorsotrail_threshold. Preserva la temporizzazione precisa del fronte di salita — utile quando il momento del cambio di polarità conta più della conferma della raffica.csi.GENX320_STC_TRAIL— combina entrambi. Parametri:stc_thresholdetrail_threshold(entrambi in ms). Conserva il fronte di salita come nella modalità Trail più i fronti successivi come nella modalità STC, in modo che più eventi di una raffica passino comunque — throughput di eventi superiore rispetto ai filtri a modalità singola ma con il segnale più ricco.
Le due soglie devono restare entro un rapporto di circa 13:1 — il sensore rifiuta configurazioni in cui una è più di circa 13 volte l’altra:
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)
Profondità del buffer¶
Quando le frequenze di eventi aumentano improvvisamente, la pipeline a triplo buffer predefinita privilegia l’ultimo frame e scarta quelli vecchi. Aumenta la profondità della FIFO tramite csi.CSI.framebuffers per accodare invece gli eventi — al costo di elaborare dati leggermente più vecchi quando l’host rimane indietro:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
Streaming e visualizzazione su desktop¶
Per la visualizzazione GUI in tempo reale su un PC host, lo strumento GenX320 Event Streaming nel repo openmv-projects abbina la cam a un front-end DearPyGui. La GUI del PC esegue due visualizzazioni affiancate: un canvas di accumulo degli eventi (stessa idea di Image.draw_event_histogram ma con palette selezionabili e modalità a finestra scorrevole vs. auto-clear) e una mappa di frequenza per-pixel pilotata da un filtro passa-banda IIR — utile per individuare segnali periodici (ventole rotanti, LED lampeggianti, ecc.) direttamente nel flusso di eventi.
Include due script di streaming on-cam:
Modalità elaborata (
genx320_event_mode_streaming_on_cam.py) — la cam decodifica gli eventi concsi.IOCTL_GENX320_READ_EVENTSe trasmette ogni riga come 12 byte su USB ([0]tipo,[1]sec,[2]ms,[3]us,[4]x,[5]y). Facile da consumare sul PC perché il formato di trasmissione corrisponde al formato ndarray on-cam.Modalità grezza (
genx320_raw_event_mode_streaming_on_cam.py) — la cam trasmette le parole di evento native del chip impacchettate a 32 bit attraversocsi.IOCTL_GENX320_READ_EVENTS_RAW. Si tratta di 4 byte per evento contro i 12 della modalità elaborata (circa 3 volte meno dati su USB), quindi una frequenza di eventi ottenibile circa 3 volte superiore quando il collegamento è il collo di bottiglia. Il PC decodifica le parole impacchettate riportandole allo stesso layout di evento a 6 colonne usando numpy vettorizzato, quindi il codice del visualizzatore a valle è identico.
La modalità grezza è quella predefinita nella GUI perché il throughput USB è il vincolo determinante alle frequenze che il GenX320 può produrre; passa alla modalità elaborata se devi inserire logica di elaborazione nello script on-cam.