Caméra événementielle GENX320¶
Le module de caméra événementielle GENX320 est un capteur de vision événementielle Prophesee doté d’une résolution de 320x320 et d’une précision temporelle à la microseconde.
Pour la fiche technique complète, les photos et les informations de commande, consultez la page produit de la caméra événementielle GENX320.
Note
Pris en charge sur les OpenMV H7 Plus, RT1062 et N6.
Points forts¶
Capteur de vision événementielle 320x320
Plage dynamique de 140 dB, sans flou de mouvement
Débit de sortie d’histogrammes d’événements supérieur à 375 Hz
La consommation s’adapte à l’activité de la scène — à partir d’environ 3 mW
Fonctionne de moins de 5 lux à la lumière vive du soleil sans exposition automatique
Produit des trames en niveaux de gris ou des flux d’événements bruts
Utilisation¶
Le GENX320 est un capteur de vision événementielle — au lieu de lire l’ensemble du réseau 320x320 sur une horloge de trame fixe, chaque pixel signale des « événements » asynchrones dès qu’il détecte un changement de luminosité. Chaque événement porte une coordonnée X/Y, une polarité ON/OFF (clair→sombre ou sombre→clair) et un horodatage à la microseconde. C’est de là que proviennent la précision temporelle à la microseconde du capteur, l’absence de flou de mouvement, la très haute plage dynamique et la consommation proportionnelle à l’activité. Les scènes statiques ne génèrent aucune donnée.
Le micrologiciel OpenMV expose le GENX320 via csi.CSI avec cid= csi.GENX320. Deux modes de fonctionnement sont disponibles :
Mode histogramme (par défaut) — les événements sont accumulés sur la puce dans des bacs par pixel et signalés sous forme de trame en niveaux de gris 320x320 à un débit configurable (~20-350 FPS). Le capteur se comporte comme une caméra ordinaire, de sorte que toutes les routines standard de traitement d’image (
Image.find_blobs, palettes, etc.) fonctionnent directement.Mode événement — les événements bruts sont diffusés dans un
ndarraynumpy avec des horodatages complets à la microseconde, pour les applications qui ont besoin du détail temporel plutôt que d’une trame pré-regroupée.
Mode histogramme¶
En mode histogramme, le GENX320 produit des trames en niveaux de gris où chaque pixel code l’activité événementielle récente à cet endroit. Les pixels au-dessus de la luminosité de référence sont des événements ON (luminosité en hausse), ceux en dessous sont des événements OFF (luminosité en baisse). La luminosité de référence par défaut est de 128 et le pas de contraste par événement est de 16 — augmentez le contraste pour faire ressortir les événements
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 et csi.CSI.framerate sont les trois réglages qui façonnent la sortie de l’histogramme.
Sortie colorée¶
Définissez csi.CSI.color_palette sur image.PALETTE_EVT_LIGHT pour un arrière-plan clair ou sur image.PALETTE_EVT_DARK pour un arrière-plan sombre — le pilote émet des trames RGB565 en utilisant directement la palette
csi0.color_palette(image.PALETTE_EVT_LIGHT)
Calibration des pixels chauds¶
Les capteurs événementiels accumulent des « pixels chauds » qui se déclenchent de manière parasite. Exécutez csi.IOCTL_GENX320_CALIBRATE sur une scène statique pour les désactiver. Le pilote construit un comptage de déclenchements par pixel 320x320, calcule la moyenne et l’écart type, et désactive tout pixel dont le comptage est supérieur à mean + sigma * stddev — les pixels désactivés cessent alors d’émettre des événements au niveau du capteur.
Deux paramètres contrôlent la calibration :
event_count— le nombre d’événements à comptabiliser avant de calculer les statistiques. La boucle capture des trames jusqu’à ce que le total courant d’événements dépasse ce budget. Des valeurs plus élevées donnent une estimation plus fiable au prix d’un temps de calibration plus long.10000constitue un bon point de départ.sigma— multiplicateur de seuil sur l’écart type. Des valeurs plus faibles sont plus agressives (plus de pixels désactivés) ; des valeurs plus élevées sont plus prudentes.0.5est une bonne valeur par défaut.
Pointez d’abord le capteur sur une scène statique afin que les événements dus au mouvement ne soient pas comptabilisés à l’encontre de pixels qui sont en réalité corrects
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")
Filtre anti-scintillement (AFK)¶
Les sources lumineuses périodiques (fluorescentes, écrans pilotés par LED) génèrent d’énormes volumes d’événements redondants. Le filtre AFK rejette les événements dont le pixel bascule à une fréquence comprise dans une bande — activez-le via csi.IOCTL_GENX320_SET_AFK en spécifiant les bornes de la bande en hertz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160) # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0) # disable
Préréglages de biais¶
Chaque pixel du GenX320 dispose d’un étage analogique d’entrée avec plusieurs biais configurables. Ils gouvernent conjointement la sensibilité, le bruit, la bande passante du pixel et le débit d’événements — la bonne combinaison dépend de la scène. Les biais individuels sont :
DIFF_ON — le seuil de contraste positif du comparateur. Un pixel émet un événement ON lorsque son log-illumination a augmenté de cette valeur. Plus bas = plus sensible aux transitions vers le clair.
DIFF_OFF — le seuil de contraste négatif du comparateur (l’équivalent symétrique pour les événements OFF). Plus bas = plus sensible aux transitions vers le sombre.
FO — la fréquence de coupure passe-bas du pixel. Plus haut = bande passante du pixel plus large (réponse plus rapide, latence plus faible) mais plus d’activité de bruit de fond.
HPF — la fréquence de coupure passe-haut. Plus haut = rejet plus fort des changements lents de luminosité ; seules les transitions rapides atteignent les comparateurs. Utile pour ignorer la dérive ambiante.
REFR — la période réfractaire. Après le déclenchement d’un pixel, celui-ci reste en réinitialisation pendant cette durée avant de pouvoir se redéclencher. Plus haut = temps mort plus long, utile pour plafonner le débit d’événements par pixel.
Après csi.CSI.reset, le pilote applique csi.GENX320_BIASES_LOW_NOISE, et non csi.GENX320_BIASES_DEFAULT — les valeurs par défaut de la fiche technique émettent un débit d’événements de fond beaucoup plus élevé, c’est pourquoi LOW_NOISE est utilisé comme point de départ pour garder le flux silencieux. Appelez csi.IOCTL_GENX320_SET_BIASES avec un préréglage différent lorsque l’application a besoin de plus de sensibilité ou de bande passante.
csi.IOCTL_GENX320_SET_BIASES applique l’un des cinq préréglages :
csi.GENX320_BIASES_DEFAULT— valeurs par défaut de la fiche technique du GenX320. Sensibilité, bruit et bande passante équilibrés pour des scènes générales.csi.GENX320_BIASES_LOW_LIGHT— les deux seuils de contraste sont assouplis pour une sensibilité accrue, FO est abaissé pour limiter le bruit, et HPF est mis à 0 afin que les changements lents de luminosité soient tout de même enregistrés — une scène en faible lumière génère peu d’événements à elle seule, on veut donc en faire passer le plus possible.csi.GENX320_BIASES_ACTIVE_MARKER— réglé pour le suivi de LED clignotantes à fort contraste. Les seuils de contraste sont relevés pour que seules les transitions nettes déclenchent ; FO et HPF poussés au maximum pour maximiser la bande passante du pixel et rejeter toute dérive ambiante lente ; REFR ramené à 0 pour que chaque front de clignotement soit capturé de manière successive. Le résultat : un flux composé presque uniquement de fronts de LED, facile à suivre.csi.GENX320_BIASES_LOW_NOISE— valeur par défaut du pilote. Les deux seuils de contraste sont relevés par rapport àDEFAULT(moins sensible) et FO est abaissé (pixel plus lent = pixel plus silencieux). Idéal pour des scènes statiques ou lentes où les faux événements domineraient autrement.csi.GENX320_BIASES_HIGH_SPEED— FO augmenté pour que chaque pixel puisse répondre plus vite, HPF relevé pour rejeter la dérive lente de luminosité, et REFR relevé pour qu’un seul front se déplaçant rapidement n’inonde pas la lecture — le temps mort plus long maintient le volume d’événements borné sous un mouvement intense.
Remplacez des biais individuels avec csi.IOCTL_GENX320_SET_BIAS plus l’un de csi.GENX320_BIAS_DIFF_ON, csi.GENX320_BIAS_DIFF_OFF, csi.GENX320_BIAS_FO, csi.GENX320_BIAS_HPF ou csi.GENX320_BIAS_REFR et une valeur DAC. Chaque biais est défini indépendamment — choisissez un préréglage comme point de départ, puis ajustez les biais dont votre scène a besoin
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)
Suivi¶
Comme la sortie en mode histogramme n’est qu’une image en niveaux de gris, le suivi de blobs ordinaire fonctionne directement. Pour suivre une LED de type marqueur actif, chargez le préréglage de biais active-marker et recherchez les blobs à l’extrémité claire de l’histogramme
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())
Mode événement¶
Le mode événement contourne l’histogramme sur la puce et diffuse les événements bruts dans un ndarray numpy. Chaque événement est une ligne de six colonnes uint16 :
[0]type d’événement — voir ci-dessous[1]horodatage en secondes[2]horodatage en millisecondes[3]horodatage en microsecondes[4]coordonnée X, 0-319[5]coordonnée Y, 0-319
Le pilote émet six types d’événements dans la colonne [0] :
csi.PIX_OFF_EVENT— un pixel a détecté une diminution de luminosité (le seuil du comparateurDIFF_OFFa été franchi). X/Y pointent vers le pixel qui s’est déclenché.csi.PIX_ON_EVENT— un pixel a détecté une augmentation de luminosité (le seuilDIFF_ONa été franchi). X/Y pointent vers le pixel.csi.EXT_TRIGGER_FALLING— la broche de déclenchement externe du capteur a vu un front descendant. X/Y sont inutilisés.csi.EXT_TRIGGER_RISING— la broche de déclenchement externe du capteur a vu un front montant. X/Y sont inutilisés.csi.RST_TRIGGER_FALLING— déclenchement de réinitialisation de pixel, front descendant. X/Y sont inutilisés. Non généré par le micrologiciel à ce jour.csi.RST_TRIGGER_RISING— déclenchement de réinitialisation de pixel, front montant. X/Y sont inutilisés. Non généré par le micrologiciel à ce jour.
L’entrée de déclenchement externe du GENX320 est câblée à la ligne de synchronisation de trame de la caméra, qui est également routée vers P10 à la fois sur le processeur et sur le connecteur de broches — pilotez P10 pour injecter des fronts de synchronisation dans le flux d’événements et les récupérer sous forme d’événements EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING aux côtés des données de pixel.
La plupart des applications ne se soucient que de PIX_OFF_EVENT et PIX_ON_EVENT ; les types de déclenchement vous permettent de corréler les événements avec des signaux de synchronisation externes.
Allouez le tampon d’événements avec la forme (EVT_res, 6) où EVT_res est une puissance de deux comprise entre 1024 et 65536, puis entrez en mode événement via csi.IOCTL_GENX320_SET_MODE avec csi.GENX320_MODE_EVENT et la taille du tampon. Lisez les événements avec csi.IOCTL_GENX320_READ_EVENTS, qui remplit le tampon jusqu’à sa capacité et renvoie le nombre de lignes valides.
Image.draw_event_histogram rasterise les événements en une image en niveaux de gris — pour chaque événement ON, il ajoute contrast au bac ; pour chaque événement OFF, il soustrait. clear=True réinitialise d’abord l’image à brightness ; clear=False accumule sur de nombreux appels
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())
Les préréglages de biais du mode histogramme, le filtre AFK et les ioctls de calibration des pixels chauds fonctionnent tous de la même manière en mode événement — appelez-les après csi.IOCTL_GENX320_SET_MODE.
Filtrage par polarité¶
Découpez le tableau d’événements avec ulab pour ne conserver que les événements ON (mouvement vers un état plus clair) ou uniquement les événements 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)
Accumulation à longue exposition¶
Définissez clear=False pour continuer à empiler les événements dans la même image sur de nombreuses trames — le résultat est une visualisation de traînée de mouvement. Réinitialisez périodiquement pour commencer une nouvelle exposition
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
Traitement à haute vitesse¶
Abandonnez la visualisation pour libérer le CPU pour le traitement des événements. N’imprimez les statistiques qu’à chaque Nième itération — pousser une ligne d’impression à chaque itération devient le goulot d’étranglement à des débits d’événements élevés
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")
Filtre de contraste spatio-temporel (STC)¶
Un véritable front de contraste en mouvement tend à déclencher une rafale bruyante d’événements sur le même pixel dans une courte fenêtre temporelle — la disparité entre pixels et le bruit analogique produisent des événements supplémentaires autour de la transition authentique qui ne sont pas utiles à l’application. Le filtre STC est un post-traitement sur la puce qui ne conserve qu’un (ou quelques) événement par rafale et abandonne le reste.
Il implémente trois stratégies, sélectionnées via csi.IOCTL_GENX320_SET_STC et une constante GENX320_STC_*. Chaque mode est défini par les événements qu’il transmet à partir d’une rafale :
Mode |
Conserve |
Abandonne |
|---|---|---|
chaque événement |
rien |
|
deuxième événement d’une rafale |
premier événement + événements ultérieurs |
|
premier événement d’une rafale |
événements suivants |
|
premier front + fronts suivants |
uniquement le bruit redondant |
En détail :
csi.GENX320_STC_DISABLE— filtre désactivé, chaque événement passe (par défaut).csi.GENX320_STC_ONLY— conserve le deuxième événement d’une rafale. Paramètre :stc_threshold(ms). Si un nouvel événement sur un pixel arrive dans un délai destc_thresholdaprès un événement précédent, il est considéré comme le « deuxième » d’une rafale et est transmis — le premier événement et tous les événements ultérieurs de la même rafale sont filtrés. Idéal lorsque vous voulez une transition confirmée par le bruit plutôt que le tout premier déclenchement.csi.GENX320_STC_TRAIL_ONLY— conserve le premier événement d’une rafale. Paramètre :trail_threshold(ms). Après le déclenchement d’un pixel, les événements ultérieurs sur le même pixel sont abandonnés jusqu’à ce quetrail_thresholdse soit écoulé. Préserve la temporisation précise du front d’attaque — utile lorsque l’instant du changement de polarité importe plus que la confirmation de rafale.csi.GENX320_STC_TRAIL— combine les deux. Paramètres :stc_thresholdettrail_threshold(tous deux en ms). Conserve le front d’attaque selon le mode Trail plus les fronts suivants selon le mode STC, de sorte que plusieurs événements d’une rafale passent tout de même — débit d’événements plus élevé que les filtres à mode unique mais le signal le plus riche.
Les deux seuils doivent rester dans un rapport d’environ 13:1 — le capteur rejette les configurations où l’un est plus de ~13x supérieur à l’autre
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)
Profondeur du tampon¶
Lorsque les débits d’événements montent en flèche, le pipeline à triple tampon par défaut favorise la dernière trame et rejette les anciennes. Augmentez la profondeur de la FIFO via csi.CSI.framebuffers pour mettre les événements en file d’attente à la place — au prix du traitement de données légèrement plus anciennes lorsque l’hôte prend du retard
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
Diffusion et visualisation sur ordinateur de bureau¶
Pour une visualisation GUI en temps réel sur un PC hôte, l”outil de diffusion d’événements GenX320 du dépôt openmv-projects associe la caméra à une interface DearPyGui. La GUI du PC exécute deux visualisations côte à côte : un canevas d’accumulation d’événements (même idée que Image.draw_event_histogram mais avec des palettes sélectionnables et des modes fenêtre glissante ou effacement automatique) et une carte de fréquence par pixel pilotée par un filtre passe-bande IIR — utile pour repérer des signaux périodiques (ventilateurs en rotation, LED clignotantes, etc.) directement dans le flux d’événements.
Il est livré avec deux scripts de diffusion sur la caméra :
Mode traité (
genx320_event_mode_streaming_on_cam.py) — la caméra décode les événements aveccsi.IOCTL_GENX320_READ_EVENTSet diffuse chaque ligne sous forme de 12 octets via USB ([0]type,[1]sec,[2]ms,[3]us,[4]x,[5]y). Facile à consommer sur le PC car le format réseau correspond au format ndarray de la caméra.Mode brut (
genx320_raw_event_mode_streaming_on_cam.py) — la caméra diffuse les mots d’événement natifs de la puce, empaquetés sur 32 bits, viacsi.IOCTL_GENX320_READ_EVENTS_RAW. Cela représente 4 octets par événement contre 12 en mode traité (environ 3x moins de données via USB), soit un débit d’événements atteignable ~3x plus élevé lorsque la liaison est le goulot d’étranglement. Le PC décode les mots empaquetés pour revenir à la même disposition d’événements à 6 colonnes en utilisant du numpy vectorisé, de sorte que le code du visualiseur en aval est identique.
Le mode brut est le mode par défaut dans la GUI car le débit USB est la contrainte déterminante aux débits que le GenX320 peut produire ; passez en mode traité si vous avez besoin de brancher de la logique de traitement dans le script sur la caméra.