Multispektrale Event-Kamera¶
Das multispektrale Event-Kamera-Modul kombiniert den GENX320-Event-Sensor mit einem 1-MP-PAG7936-Global-Shutter-Farbsensor auf einem einzigen Modul — eine synchronisierte Event- und Farb-Pipeline für Hochgeschwindigkeits-Objektverfolgung, LED-Tracking, Fluidströmungen und andere dynamische Szenen.
Vollständiges Datenblatt, Fotos und Bestellinformationen finden Sie auf der Produktseite der multispektralen Event-Kamera.
Bemerkung
Nur auf der OpenMV N6 unterstützt.
Highlights¶
320x320-Event-Sensor, >140 dB Dynamikbereich, 375 Hz+ Histogramme
PAG7936-Farbe: 1280x800 @ 120 FPS, 640x400 @ 240 FPS
Synchronisierte Event-Zeitstempel mit gemeinsam genutztem Belichtungs-Trigger
Sieht unter 5 Lux ohne automatische Belichtung
Leistungsaufnahme beginnt bei ~3 mW für Event-Streaming
Ausgerichtet auf Hochgeschwindigkeits-Tracking, LED-Tracking und Fluid-/Partikelströmung
Verwendung¶
Der Farbsensor und der GENX320-Event-Sensor erhalten jeweils ihre eigene csi.CSI-Instanz. Der erste Aufruf verwendet standardmäßig den primären Sensor (den PAG7936); der zweite bindet den GENX320, indem cid= csi.GENX320 übergeben wird. Führen Sie mit csi.CSI.reset (hard=True) einen Hard-Reset des Farbsensors durch, um die Versorgungsschiene hochzufahren, und konfigurieren Sie den GENX320 mit hard=False, damit sein Treiber den Chip nur neu programmiert, ohne den Reset erneut auszulösen.
Der GENX320 gibt im Histogramm-Modus 320x320 aus; der PAG7936 bei csi.QVGA gibt 320x200 aus. Das einfache Overlay unten schneidet die unteren 120 Zeilen des GENX320-Einzelbildes ab. Verwenden Sie die Homographie-Transformation (unten) für ein angepasstes Overlay oder eine größere PAG7936-Bildgröße.
Zwei Scratch-Puffer bleiben über die Einzelbildschleife hinweg konstant — eine 256x1-Alpha-Palette, die als image.Image gespeichert wird, sodass Histogramm-Pixel an der Mittelgrau-Basislinie (128) transparent werden und sowohl ON-Event-Highlights als auch OFF-Event-Schatten undurchsichtig werden, sowie ein GENX320-Framebuffer, der mit image.Image vorab zugewiesen wird, damit csi.CSI.snapshot (blocking=False, image=...) ihn in jeder Iteration an Ort und Stelle füllen kann, ohne ihn neu zuzuweisen:
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())
Jede Iteration nimmt einen blockierenden Farb-Schnappschuss und einen nicht-blockierenden GENX320-Schnappschuss auf. Image.draw_image setzt die beiden dann zusammen: color_palette= image.PALETTE_EVT_LIGHT (oder image.PALETTE_EVT_DARK für einen dunklen Hintergrund) bildet das Graustufen-Histogramm des GENX320 auf eine Farbrampe ab, alpha_palette= mischt jedes Pixel mithilfe der V-förmigen Alpha-Map, sodass ruhige Bereiche der Szene auf das Farbbild durchscheinen, und hint= image.BILINEAR glättet die Hochskalierung, wenn der Farbsensor mit einer höheren Auflösung als der GENX320 läuft.
Die Bias-Voreinstellungen, der AFK-Filter, die Hot-Pixel-Kalibrierung und die STC-Filter-ioctls des GENX320 funktionieren in diesem Dual-Kamera-Setup alle auf die gleiche Weise — rufen Sie sie nach csi.CSI.reset an csi1 auf. Einzelheiten finden Sie in den folgenden Abschnitten.
GPU-beschleunigte Ausrichtung¶
Image.draw_image akzeptiert ein transform=-Argument — eine 3x3-Homographie-Matrix als 2D-ulab.numpy-Array. Auf der OpenMV N6 führt die GPU die Projektion pro Pixel während desselben Zeichenvorgangs aus, sodass das GENX320-Einzelbild ohne separaten Warp-Durchlauf an der Perspektive der Farbkamera neu ausgerichtet werden kann — nützlich, wenn die beiden Sensoren leicht unterschiedliche Optiken oder Sichtfelder haben oder wenn die Farbkamera mit einer höheren Auflösung läuft. Kalibrieren Sie die Matrix pro Kamera mit dem GenX320 Overlay Calibration tool, das ein flackerndes Schachbrettmuster anzeigt, sodass der Event-Sensor Eckereignisse ohne jegliche physische Bewegung erzeugt:
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())
Diese Variante betreibt die Farbkamera bei csi.VGA (640x480) und den GENX320 bei seiner nativen Auflösung 320x320 — die Homographie projiziert das kleinere GENX320-Einzelbild als Teil des Zeichenvorgangs in das größere Farb-Einzelbild, sodass der Hochskalierungsfaktor in die Matrix selbst eingebaut ist, anstatt separat angewendet zu werden.
Details zur Event-Kamera¶
Der GENX320 ist ein ereignisbasierter Bildsensor — anstatt das gesamte 320x320-Array mit einem festen Einzelbildtakt auszulesen, meldet jedes Pixel asynchrone „Events“ in dem Moment, in dem es eine Helligkeitsänderung erkennt. Jedes Event trägt eine X/Y-Koordinate, eine ON/OFF-Polarität (hell→dunkel oder dunkel→hell) und einen Mikrosekunden-Zeitstempel. Daher rühren die zeitliche Mikrosekundenpräzision des Sensors, die fehlende Bewegungsunschärfe, der sehr hohe Dynamikbereich und die aktivitätsskalierte Leistungsaufnahme. Statische Szenen erzeugen keine Daten.
Die OpenMV-Firmware stellt den GENX320 über csi.CSI mit cid= csi.GENX320 bereit. Es stehen zwei Betriebsmodi zur Verfügung:
Histogramm-Modus (Standard) — Events werden auf dem Chip pro Pixel in Bins akkumuliert und mit einer konfigurierbaren Rate (~20-350 FPS) als 320x320-Graustufen-Einzelbild gemeldet. Der Sensor verhält sich wie eine normale Kamera, sodass alle Standard-Bildverarbeitungsroutinen (
Image.find_blobs, Paletten usw.) direkt funktionieren.Event-Modus — rohe Events strömen in ein numpy-
ndarraymit vollständigen Mikrosekunden-Zeitstempeln, für Anwendungen, die das zeitliche Detail anstelle eines vorgebinnten Einzelbildes benötigen.
Histogramm-Modus¶
Im Histogramm-Modus gibt der GENX320 Graustufen-Einzelbilder aus, in denen jedes Pixel die jüngste Event-Aktivität an dieser Stelle codiert. Pixel oberhalb der Helligkeits-Basislinie sind ON-Events (Helligkeit steigt), darunter sind OFF-Events (Helligkeit fällt). Die Standard-Basislinienhelligkeit ist 128 und der Kontrastschritt pro Event ist 16 — erhöhen Sie den Kontrast, um Events hervorzuheben:
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 und csi.CSI.framerate sind die drei Stellschrauben, die die Histogramm-Ausgabe formen.
Kolorierte Ausgabe¶
Setzen Sie csi.CSI.color_palette auf image.PALETTE_EVT_LIGHT für einen hellen Hintergrund oder image.PALETTE_EVT_DARK für einen dunklen — der Treiber gibt RGB565-Einzelbilder aus, die die Palette direkt verwenden:
csi0.color_palette(image.PALETTE_EVT_LIGHT)
Hot-Pixel-Kalibrierung¶
Event-Sensoren sammeln „Hot Pixels“ an, die fälschlicherweise auslösen. Führen Sie csi.IOCTL_GENX320_CALIBRATE gegen eine statische Szene aus, um sie zu deaktivieren. Der Treiber erstellt eine 320x320-Trefferzählung pro Pixel, berechnet den Mittelwert und die Standardabweichung und deaktiviert jedes Pixel, dessen Zählung über mean + sigma * stddev liegt — danach geben die deaktivierten Pixel auf Sensorebene keine Events mehr ab.
Zwei Parameter steuern die Kalibrierung:
event_count— wie viele Events gezählt werden, bevor die Statistik berechnet wird. Die Schleife erfasst Einzelbilder, bis die laufende Event-Gesamtzahl dieses Budget überschreitet. Höhere Werte ergeben eine zuverlässigere Schätzung auf Kosten einer längeren Kalibrierungszeit.10000ist ein sinnvoller Ausgangspunkt.sigma— Schwellenwert-Multiplikator für die Standardabweichung. Niedrigere Werte sind aggressiver (mehr Pixel deaktiviert); höhere Werte sind konservativer.0.5ist ein guter Standardwert.
Richten Sie den Sensor zunächst auf eine statische Szene, damit bewegungsbedingte Events nicht gegen Pixel gezählt werden, die eigentlich in Ordnung sind:
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")
Anti-Flicker-Filter (AFK)¶
Periodische Lichtquellen (Leuchtstofflampen, LED-gesteuerte Displays) erzeugen riesige Mengen redundanter Events. Der AFK-Filter verwirft Events, deren Pixel mit einer Frequenz innerhalb eines Bandes umschaltet — aktivieren Sie ihn über csi.IOCTL_GENX320_SET_AFK mit den Bandgrenzen in Hertz:
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160) # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0) # disable
Bias-Voreinstellungen¶
Jedes Pixel im GenX320 betreibt ein analoges Front-End mit mehreren konfigurierbaren Biases. Sie steuern gemeinsam Empfindlichkeit, Rauschen, Pixelbandbreite und Event-Rate — die richtige Kombination hängt von der Szene ab. Die einzelnen Biases sind:
DIFF_ON — der positive Komparator-Kontrastschwellenwert. Ein Pixel gibt ein ON-Event ab, wenn seine logarithmische Beleuchtungsstärke um diesen Betrag gestiegen ist. Niedriger = empfindlicher für helle Übergänge.
DIFF_OFF — der negative Komparator-Kontrastschwellenwert (das symmetrische Gegenstück für OFF-Events). Niedriger = empfindlicher für dunkle Übergänge.
FO — die Tiefpass-Grenzfrequenz des Pixels. Höher = breitere Pixelbandbreite (schnellere Reaktion, geringere Latenz), aber mehr Hintergrundrausch-Aktivität.
HPF — die Hochpass-Grenzfrequenz. Höher = stärkere Unterdrückung langsamer Helligkeitsänderungen; nur schnelle Übergänge erreichen die Komparatoren. Nützlich, um Umgebungsdrift zu ignorieren.
REFR — die Refraktärzeit. Nachdem ein Pixel ausgelöst hat, bleibt es so lange im Reset, bevor es erneut auslösen kann. Höher = längere Totzeit, nützlich zur Begrenzung der Event-Rate pro Pixel.
Nach csi.CSI.reset wendet der Treiber csi.GENX320_BIASES_LOW_NOISE an, nicht csi.GENX320_BIASES_DEFAULT — die Datenblatt-Standardwerte erzeugen eine deutlich höhere Hintergrund-Event-Rate, daher wird LOW_NOISE als Ausgangspunkt verwendet, um den Stream ruhig zu halten. Rufen Sie csi.IOCTL_GENX320_SET_BIASES mit einer anderen Voreinstellung auf, wenn die Anwendung mehr Empfindlichkeit oder Bandbreite benötigt.
csi.IOCTL_GENX320_SET_BIASES wendet eine von fünf Voreinstellungen an:
csi.GENX320_BIASES_DEFAULT— GenX320-Datenblatt-Standardwerte. Ausgewogene Empfindlichkeit, Rauschen und Bandbreite für allgemeine Szenen.csi.GENX320_BIASES_LOW_LIGHT— beide Kontrastschwellenwerte gelockert für höhere Empfindlichkeit, FO gesenkt, um das Rauschen niedrig zu halten, und HPF auf 0 gesetzt, sodass langsame Helligkeitsänderungen weiterhin registriert werden — eine Szene mit wenig Licht erzeugt von sich aus wenige Events, daher wollen wir, dass so viele wie möglich durchkommen.csi.GENX320_BIASES_ACTIVE_MARKER— abgestimmt auf das Tracking kontraststarker blinkender LEDs. Kontrastschwellenwerte angehoben, sodass nur scharfe Übergänge auslösen; FO und HPF hochgekurbelt, um die Pixelbandbreite zu maximieren und jegliche langsame Umgebungsdrift zu verwerfen; REFR auf 0 gezogen, sodass jede Blink-Flanke unmittelbar hintereinander erfasst wird. Das Ergebnis: ein Stream, der fast ausschließlich aus LED-Flanken besteht und leicht zu verfolgen ist.csi.GENX320_BIASES_LOW_NOISE— Treiber-Standard. Beide Kontrastschwellenwerte gegenüberDEFAULTangehoben (weniger empfindlich) und FO gesenkt (langsameres Pixel = ruhigeres Pixel). Am besten für statische oder langsame Szenen, in denen Fehl-Events sonst dominieren würden.csi.GENX320_BIASES_HIGH_SPEED— FO angehoben, sodass jedes Pixel schneller reagieren kann, HPF angehoben, um langsame Helligkeitsdrift zu verwerfen, und REFR angehoben, sodass eine einzelne schnell bewegte Flanke das Auslesen nicht überflutet — die längere Totzeit hält das Event-Volumen bei starker Bewegung begrenzt.
Überschreiben Sie einzelne Biases mit csi.IOCTL_GENX320_SET_BIAS plus einem von csi.GENX320_BIAS_DIFF_ON, csi.GENX320_BIAS_DIFF_OFF, csi.GENX320_BIAS_FO, csi.GENX320_BIAS_HPF oder csi.GENX320_BIAS_REFR und einem DAC-Wert. Jeder Bias wird unabhängig gesetzt — wählen Sie eine Voreinstellung als Ausgangspunkt und passen Sie dann die Biases an, die Ihre Szene benötigt:
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)
Tracking¶
Da die Ausgabe im Histogramm-Modus lediglich ein Graustufenbild ist, funktioniert reguläres Blob-Tracking direkt. Um eine Active-Marker-LED zu verfolgen, laden Sie die Active-Marker-Bias-Voreinstellung und suchen Sie Blobs am hellen Ende des Histogramms:
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())
Event-Modus¶
Der Event-Modus umgeht das On-Chip-Histogramm und streamt rohe Events in ein numpy-ndarray. Jedes Event ist eine Zeile mit sechs uint16-Spalten:
[0]Event-Typ — siehe unten[1]Sekunden-Zeitstempel[2]Millisekunden-Zeitstempel[3]Mikrosekunden-Zeitstempel[4]X-Koordinate, 0-319[5]Y-Koordinate, 0-319
Der Treiber gibt in Spalte [0] sechs Event-Typen aus:
csi.PIX_OFF_EVENT— ein Pixel hat eine Helligkeitsabnahme erkannt (der Komparator-SchwellenwertDIFF_OFFwurde überschritten). X/Y verweisen auf das Pixel, das ausgelöst hat.csi.PIX_ON_EVENT— ein Pixel hat eine Helligkeitszunahme erkannt (der SchwellenwertDIFF_ONwurde überschritten). X/Y verweisen auf das Pixel.csi.EXT_TRIGGER_FALLING— der externe Trigger-Pin des Sensors hat eine fallende Flanke erkannt. X/Y werden nicht verwendet.csi.EXT_TRIGGER_RISING— der externe Trigger-Pin des Sensors hat eine steigende Flanke erkannt. X/Y werden nicht verwendet.csi.RST_TRIGGER_FALLING— Pixel-Reset-Trigger, fallende Flanke. X/Y werden nicht verwendet. Wird derzeit von der Firmware nicht erzeugt.csi.RST_TRIGGER_RISING— Pixel-Reset-Trigger, steigende Flanke. X/Y werden nicht verwendet. Wird derzeit von der Firmware nicht erzeugt.
Der externe Trigger-Eingang des GENX320 ist mit der Frame-Sync-Leitung der Kamera verbunden, die auch auf P10 sowohl am Prozessor als auch an der Pin-Leiste geführt ist — treiben Sie P10 an, um Sync-Flanken in den Event-Stream einzuspeisen, und greifen Sie sie als EXT_TRIGGER_RISING- / EXT_TRIGGER_FALLING-Events neben den Pixeldaten ab.
Die meisten Anwendungen interessieren sich nur für PIX_OFF_EVENT und PIX_ON_EVENT; die Trigger-Typen erlauben es Ihnen, Events mit externen Zeitsignalen zu korrelieren.
Weisen Sie den Event-Puffer mit der Form (EVT_res, 6) zu, wobei EVT_res eine Zweierpotenz zwischen 1024 und 65536 ist, und wechseln Sie dann über csi.IOCTL_GENX320_SET_MODE mit csi.GENX320_MODE_EVENT und der Puffergröße in den Event-Modus. Lesen Sie Events mit csi.IOCTL_GENX320_READ_EVENTS, das den Puffer bis zu seiner Kapazität füllt und die Anzahl der gültigen Zeilen zurückgibt.
Image.draw_event_histogram rastert Events in ein Graustufenbild — für jedes ON-Event addiert es contrast zum Bin; für jedes OFF-Event subtrahiert es. clear=True setzt das Bild zuerst auf brightness zurück; clear=False akkumuliert über viele Aufrufe hinweg:
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())
Die Bias-Voreinstellungen des Histogramm-Modus, der AFK-Filter und die Hot-Pixel-Kalibrierungs-ioctls funktionieren im Event-Modus alle auf die gleiche Weise — rufen Sie sie nach csi.IOCTL_GENX320_SET_MODE auf.
Filterung nach Polarität¶
Schneiden Sie das Events-Array mit ulab, um nur ON-Events (Bewegung in einen helleren Zustand) oder nur OFF-Events zu behalten:
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)
Langzeitbelichtungs-Akkumulation¶
Setzen Sie clear=False, um Events über viele Einzelbilder hinweg im selben Bild zu stapeln — das Ergebnis ist eine Visualisierung der Bewegungsspur. Setzen Sie periodisch zurück, um eine neue Belichtung zu beginnen:
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
Hochgeschwindigkeitsverarbeitung¶
Lassen Sie die Visualisierung weg, um CPU für die Event-Verarbeitung freizugeben. Geben Sie Statistiken nur bei jeder N-ten Iteration aus — eine Print-Zeile bei jeder Iteration wird bei hohen Event-Raten zum Flaschenhals:
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")
Spatio-temporaler Kontrastfilter (STC)¶
Eine echte bewegte Kontrastkante neigt dazu, einen verrauschten Burst von Events auf demselben Pixel innerhalb eines kurzen Zeitfensters auszulösen — Pixel-Mismatch und analoges Rauschen erzeugen zusätzliche Events rund um den eigentlichen Übergang, die für die Anwendung nicht nützlich sind. Der STC-Filter ist ein On-Chip-Nachbearbeitungsschritt, der nur eines (oder einige wenige) Events pro Burst behält und den Rest verwirft.
Er implementiert drei Strategien, die über csi.IOCTL_GENX320_SET_STC und eine GENX320_STC_*-Konstante ausgewählt werden. Jeder Modus ist dadurch definiert, welche Events er aus einem Burst weiterleitet:
Modus |
Behält |
Verwirft |
|---|---|---|
jedes Event |
nichts |
|
zweites Event eines Bursts |
erstes + spätere Events |
|
erstes Event eines Bursts |
nachfolgende Events |
|
erste + nachfolgende Flanken |
nur redundantes Rauschen |
Im Detail:
csi.GENX320_STC_DISABLE— Filter aus, jedes Event wird durchgelassen (Standard).csi.GENX320_STC_ONLY— behält das zweite Event eines Bursts. Parameter:stc_threshold(ms). Wenn ein neues Event auf einem Pixel innerhalb vonstc_thresholdnach einem vorherigen Event eintrifft, wird es als das „zweite“ eines Bursts betrachtet und weitergeleitet — das erste Event und alle nachfolgenden Events desselben Bursts werden herausgefiltert. Am besten, wenn Sie einen rausch-bestätigten Übergang statt des allerersten Treffers wünschen.csi.GENX320_STC_TRAIL_ONLY— behält das erste Event eines Bursts. Parameter:trail_threshold(ms). Nachdem ein Pixel ausgelöst hat, werden nachfolgende Events auf demselben Pixel verworfen, bistrail_thresholdverstrichen ist. Bewahrt das präzise Timing der führenden Flanke — nützlich, wenn der Moment des Polaritätswechsels wichtiger ist als die Burst-Bestätigung.csi.GENX320_STC_TRAIL— kombiniert beides. Parameter:stc_thresholdundtrail_threshold(beide ms). Behält die führende Flanke gemäß Trail-Modus plus nachfolgende Flanken gemäß STC-Modus, sodass mehrere Events aus einem Burst weiterhin durchkommen — höherer Event-Durchsatz als die Einzelmodus-Filter, aber das reichhaltigste Signal.
Die beiden Schwellenwerte müssen innerhalb eines Verhältnisses von etwa 13:1 bleiben — der Sensor lehnt Konfigurationen ab, bei denen einer mehr als ~13x so groß wie der andere ist:
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)
Puffertiefe¶
Wenn die Event-Raten in die Höhe schnellen, bevorzugt die standardmäßige Triple-Buffer-Pipeline das neueste Einzelbild und verwirft alte. Erhöhen Sie die FIFO-Tiefe über csi.CSI.framebuffers, um Events stattdessen zu puffern — auf Kosten der Verarbeitung etwas älterer Daten, wenn der Host zurückfällt:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
Desktop-Streaming und Visualisierung¶
Für die Echtzeit-GUI-Visualisierung auf einem Host-PC kombiniert das GenX320 Event Streaming tool im openmv-projects-Repository die Kamera mit einem DearPyGui-Frontend. Die PC-GUI führt zwei Visualisierungen nebeneinander aus: eine Event-Akkumulations-Leinwand (dieselbe Idee wie Image.draw_event_histogram, aber mit auswählbaren Paletten und Schiebefenster- vs. Auto-Clear-Modi) und eine Frequenzkarte pro Pixel, die von einem IIR-Bandpassfilter angetrieben wird — nützlich, um periodische Signale (rotierende Lüfter, blinkende LEDs usw.) direkt im Event-Stream aufzuspüren.
Es bringt zwei On-Cam-Streaming-Skripte mit:
Verarbeiteter Modus (
genx320_event_mode_streaming_on_cam.py) — die Kamera decodiert Events mitcsi.IOCTL_GENX320_READ_EVENTSund streamt jede Zeile als 12 Bytes über USB ([0]Typ,[1]Sek.,[2]ms,[3]us,[4]x,[5]y). Auf dem PC leicht zu verarbeiten, da das Wire-Format mit dem On-Cam-ndarray-Format übereinstimmt.Roh-Modus (
genx320_raw_event_mode_streaming_on_cam.py) — die Kamera streamt die nativen, 32-Bit gepackten Event-Wörter des Chips übercsi.IOCTL_GENX320_READ_EVENTS_RAW. Das sind 4 Bytes pro Event gegenüber 12 im verarbeiteten Modus (etwa 3x weniger Daten über USB), also eine ~3x höhere erreichbare Event-Rate, wenn die Verbindung der Flaschenhals ist. Der PC decodiert die gepackten Wörter mithilfe von vektorisiertem numpy zurück in dasselbe 6-Spalten-Event-Layout, sodass der nachgelagerte Visualisierer-Code identisch ist.
Der Roh-Modus ist in der GUI der Standard, da der USB-Durchsatz bei den Raten, die der GenX320 erzeugen kann, die bindende Einschränkung darstellt; wechseln Sie in den verarbeiteten Modus, wenn Sie Verarbeitungslogik in das On-Cam-Skript einklinken müssen.