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.

Caméra événementielle GENX320

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 ndarray numpy 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. 10000 constitue 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.5 est 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 comparateur DIFF_OFF a é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 seuil DIFF_ON a é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)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

csi.GENX320_STC_DISABLE

chaque événement

rien

csi.GENX320_STC_ONLY

deuxième événement d’une rafale

premier événement + événements ultérieurs

csi.GENX320_STC_TRAIL_ONLY

premier événement d’une rafale

événements suivants

csi.GENX320_STC_TRAIL

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 de stc_threshold aprè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 que trail_threshold se 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_threshold et trail_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 avec csi.IOCTL_GENX320_READ_EVENTS et 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, via csi.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.