Multispektralna kamera zdarzeniowa¶
Moduł Multispectral Event Camera łączy sensor zdarzeniowy GENX320 z 1-megapikselowym sensorem kolorowym PAG7936 z migawką globalną na jednym module — zsynchronizowany potok zdarzenia + kolor do śledzenia szybko poruszających się obiektów, śledzenia diod LED, przepływu cieczy i innych dynamicznych scen.
Pełną kartę katalogową, zdjęcia i informacje o zamawianiu znajdziesz na stronie produktu Multispectral Event Camera.
Informacja
Obsługiwane wyłącznie na OpenMV N6.
Najważniejsze cechy¶
Sensor zdarzeniowy 320x320, zakres dynamiki >140 dB, histogramy 375 Hz+
Kolor PAG7936: 1280x800 @ 120 FPS, 640x400 @ 240 FPS
Zsynchronizowane znaczniki czasu zdarzeń ze wspólnym wyzwalaniem ekspozycji
Widzi poniżej 5 luksów bez automatycznej ekspozycji
Pobór mocy zaczyna się od ~3 mW dla strumienia zdarzeń
Przeznaczona do szybkiego śledzenia, śledzenia diod LED oraz przepływu cieczy/cząstek
Użycie¶
Sensor kolorowy oraz sensor zdarzeniowy GENX320 otrzymują każdy własną instancję csi.CSI. Pierwsze wywołanie domyślnie odnosi się do sensora głównego (PAG7936); drugie wiąże się z GENX320 poprzez przekazanie cid= csi.GENX320. Wykonaj twardy reset sensora kolorowego za pomocą csi.CSI.reset (hard=True), aby podnieść szynę zasilania, a GENX320 skonfiguruj z hard=False, tak aby jego sterownik jedynie przeprogramował układ bez ponownego przełączania resetu.
GENX320 wyprowadza obraz 320x320 w trybie histogramu; PAG7936 przy csi.QVGA wyprowadza 320x200. Poniższa podstawowa nakładka obcina dolne 120 wierszy ramki GENX320. Użyj transformacji homograficznej (poniżej), aby uzyskać dopasowaną nakładkę lub większy rozmiar ramki PAG7936.
Dwa bufory robocze pozostają niezmienne przez całą pętlę ramek — paleta alfa 256x1 przechowywana jako image.Image, dzięki której piksele histogramu na poziomie bazowym średniej szarości (128) stają się przezroczyste, a zarówno wyróżnienia zdarzeń ON, jak i cienie zdarzeń OFF stają się nieprzezroczyste, oraz bufor ramki GENX320 wstępnie zaalokowany za pomocą image.Image, tak aby csi.CSI.snapshot (blocking=False, image=...) mógł go wypełniać w miejscu w każdej iteracji bez ponownej alokacji:
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())
Każda iteracja wykonuje blokujący zrzut obrazu kolorowego i nieblokujący zrzut GENX320. Następnie Image.draw_image komponuje oba: color_palette= image.PALETTE_EVT_LIGHT (lub image.PALETTE_EVT_DARK dla ciemnego tła) odwzorowuje histogram w skali szarości z GENX320 na rampę kolorów, alpha_palette= miesza każdy piksel przy użyciu mapy alfa w kształcie litery v, tak aby spokojne obszary sceny przepuszczały obraz kolorowy, a hint= image.BILINEAR wygładza skalowanie w górę, gdy sensor kolorowy pracuje w wyższej rozdzielczości niż GENX320.
Presety biasów GENX320, filtr AFK, kalibracja gorących pikseli oraz ioctl filtra STC działają w tej konfiguracji z dwiema kamerami dokładnie tak samo — wywołuj je na csi1 po csi.CSI.reset. Szczegóły znajdziesz w sekcjach poniżej.
Wyrównanie z akceleracją GPU¶
Image.draw_image przyjmuje argument transform= — macierz homograficzną 3x3 jako 2-wymiarową tablicę ulab.numpy. Na OpenMV N6 GPU wykonuje projekcję per-piksel podczas tego samego rysowania, dzięki czemu ramkę GENX320 można ponownie wyrównać do perspektywy kamery kolorowej bez osobnego przebiegu warpowania — przydatne, gdy dwa sensory mają nieco inną optykę lub pola widzenia, albo gdy kamera kolorowa pracuje w wyższej rozdzielczości. Skalibruj macierz dla każdej kamery za pomocą narzędzia GenX320 Overlay Calibration, które wyświetla migającą szachownicę, dzięki czemu sensor zdarzeniowy generuje zdarzenia narożne bez żadnego fizycznego ruchu:
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())
Ten wariant uruchamia kamerę kolorową w csi.VGA (640x480), a GENX320 w jego natywnej rozdzielczości 320x320 — homografia rzutuje mniejszą ramkę GENX320 na większą ramkę kolorową w ramach rysowania, więc współczynnik skalowania w górę jest wbudowany w samą macierz, a nie stosowany osobno.
Szczegóły kamery zdarzeniowej¶
GENX320 jest sensorem wizyjnym opartym na zdarzeniach — zamiast odczytywać całą macierz 320x320 według stałego zegara ramek, każdy piksel zgłasza asynchroniczne „zdarzenia” w momencie wykrycia zmiany jasności. Każde zdarzenie niesie współrzędną X/Y, polaryzację ON/OFF (jasny→ciemny lub ciemny→jasny) oraz mikrosekundowy znacznik czasu. Stąd biorą się mikrosekundowa precyzja czasowa sensora, brak rozmycia ruchu, bardzo wysoki zakres dynamiki oraz pobór mocy skalujący się z aktywnością. Statyczne sceny nie generują żadnych danych.
Oprogramowanie układowe OpenMV udostępnia GENX320 poprzez csi.CSI z cid= csi.GENX320. Dostępne są dwa tryby pracy:
Tryb histogramu (domyślny) — zdarzenia są akumulowane na układzie do binów per-piksel i zgłaszane jako ramka 320x320 w skali szarości z konfigurowalną częstotliwością (~20-350 FPS). Sensor zachowuje się jak zwykła kamera, więc wszystkie standardowe procedury przetwarzania obrazu (
Image.find_blobs, palety itd.) działają bezpośrednio.Tryb zdarzeń — surowe zdarzenia są strumieniowane do tablicy numpy
ndarrayz pełnymi mikrosekundowymi znacznikami czasu, dla zastosowań wymagających szczegółów czasowych zamiast wstępnie zbinowanej ramki.
Tryb histogramu¶
W trybie histogramu GENX320 wyprowadza ramki w skali szarości, gdzie każdy piksel koduje niedawną aktywność zdarzeń w danym miejscu. Piksele powyżej poziomu bazowego jasności to zdarzenia ON (jasność rosnąca), poniżej to zdarzenia OFF (jasność malejąca). Domyślna jasność bazowa wynosi 128, a krok kontrastu na zdarzenie wynosi 16 — zwiększ kontrast, aby zdarzenia były bardziej widoczne:
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 oraz csi.CSI.framerate to trzy pokrętła kształtujące wyjście histogramu.
Wyjście kolorowe¶
Ustaw csi.CSI.color_palette na image.PALETTE_EVT_LIGHT dla jasnego tła lub image.PALETTE_EVT_DARK dla ciemnego — sterownik emituje ramki RGB565, używając palety bezpośrednio:
csi0.color_palette(image.PALETTE_EVT_LIGHT)
Kalibracja gorących pikseli¶
Sensory zdarzeniowe gromadzą „gorące piksele”, które wyzwalają się fałszywie. Uruchom csi.IOCTL_GENX320_CALIBRATE na statycznej scenie, aby je wyłączyć. Sterownik buduje licznik trafień per-piksel 320x320, oblicza średnią i odchylenie standardowe, a następnie wyłącza każdy piksel, którego licznik jest powyżej mean + sigma * stddev — wówczas wyłączone piksele przestają emitować zdarzenia na poziomie sensora.
Kalibracją sterują dwa parametry:
event_count— ile zdarzeń zliczyć przed obliczeniem statystyk. Pętla przechwytuje ramki, aż bieżąca suma zdarzeń przekroczy ten budżet. Wyższe wartości dają bardziej wiarygodne oszacowanie kosztem dłuższego czasu kalibracji.10000to rozsądny punkt wyjścia.sigma— mnożnik progu na odchyleniu standardowym. Niższe wartości są bardziej agresywne (więcej wyłączonych pikseli); wyższe są bardziej zachowawcze.0.5to dobra wartość domyślna.
Skieruj najpierw sensor na statyczną scenę, tak aby żadne zdarzenia wywołane ruchem nie zostały policzone przeciwko pikselom, które są w rzeczywistości sprawne:
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")
Filtr antymigotaniowy (AFK)¶
Okresowe źródła światła (świetlówki, wyświetlacze sterowane diodami LED) generują ogromne ilości nadmiarowych zdarzeń. Filtr AFK odrzuca zdarzenia, których piksel przełącza się z częstotliwością wewnątrz danego pasma — włącz go za pomocą csi.IOCTL_GENX320_SET_AFK z krawędziami pasma w hercach:
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160) # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0) # disable
Presety biasów¶
Każdy piksel w GenX320 ma analogowy front-end z kilkoma konfigurowalnymi biasami. Wspólnie regulują one czułość, szum, pasmo piksela oraz częstość zdarzeń — właściwa kombinacja zależy od sceny. Poszczególne biasy to:
DIFF_ON — dodatni próg kontrastu komparatora. Piksel emituje zdarzenie ON, gdy jego log-naświetlenie wzrosło o tę wartość. Niżej = większa czułość na jasne przejścia.
DIFF_OFF — ujemny próg kontrastu komparatora (symetryczny odpowiednik dla zdarzeń OFF). Niżej = większa czułość na ciemne przejścia.
FO — dolnoprzepustowa częstotliwość odcięcia piksela. Wyżej = szersze pasmo piksela (szybsza odpowiedź, niższe opóźnienie), ale większa aktywność szumu tła.
HPF — górnoprzepustowa częstotliwość odcięcia. Wyżej = silniejsze odrzucanie powolnych zmian jasności; tylko szybkie przejścia docierają do komparatorów. Przydatne do ignorowania dryftu otoczenia.
REFR — okres refrakcji. Po wyzwoleniu piksel pozostaje w resecie przez ten czas, zanim będzie mógł wyzwolić się ponownie. Wyżej = dłuższy czas martwy, przydatny do ograniczania częstości zdarzeń per-piksel.
Po csi.CSI.reset sterownik stosuje csi.GENX320_BIASES_LOW_NOISE, a nie csi.GENX320_BIASES_DEFAULT — domyślne wartości z karty katalogowej emitują znacznie wyższą częstość zdarzeń tła, więc LOW_NOISE jest używany jako punkt wyjścia, aby utrzymać strumień spokojnym. Wywołaj csi.IOCTL_GENX320_SET_BIASES z innym presetem, gdy zastosowanie wymaga większej czułości lub pasma.
csi.IOCTL_GENX320_SET_BIASES stosuje jeden z pięciu presetów:
csi.GENX320_BIASES_DEFAULT— domyślne wartości z karty katalogowej GenX320. Zrównoważona czułość, szum i pasmo dla ogólnych scen.csi.GENX320_BIASES_LOW_LIGHT— oba progi kontrastu poluzowane dla większej czułości, FO obniżone, aby ograniczyć szum, oraz HPF ustawione na 0, tak aby powolne zmiany jasności wciąż były rejestrowane — scena słabo oświetlona sama generuje niewiele zdarzeń, więc chcemy, aby przeszło ich jak najwięcej.csi.GENX320_BIASES_ACTIVE_MARKER— dostrojony do śledzenia migających diod LED o wysokim kontraście. Progi kontrastu podniesione, tak aby wyzwalały tylko ostre przejścia; FO i HPF podkręcone w górę, aby zmaksymalizować pasmo piksela i odrzucić wszelki powolny dryft otoczenia; REFR ściągnięte do 0, tak aby każda krawędź migotania była przechwytywana jedna po drugiej. Rezultat: strumień, który składa się niemal wyłącznie z krawędzi diod LED, łatwy do śledzenia.csi.GENX320_BIASES_LOW_NOISE— domyślny dla sterownika. Oba progi kontrastu podniesione względemDEFAULT(mniejsza czułość) i FO obniżone (wolniejszy piksel = cichszy piksel). Najlepszy dla scen statycznych lub powolnych, gdzie fałszywe zdarzenia inaczej by dominowały.csi.GENX320_BIASES_HIGH_SPEED— FO podbite, tak aby każdy piksel mógł reagować szybciej, HPF podniesione, aby odrzucać powolny dryft jasności, oraz REFR podniesione, tak aby pojedyncza szybko poruszająca się krawędź nie zalewała odczytu — dłuższy czas martwy utrzymuje wolumen zdarzeń w ryzach przy intensywnym ruchu.
Nadpisz poszczególne biasy za pomocą csi.IOCTL_GENX320_SET_BIAS oraz jednego z csi.GENX320_BIAS_DIFF_ON, csi.GENX320_BIAS_DIFF_OFF, csi.GENX320_BIAS_FO, csi.GENX320_BIAS_HPF lub csi.GENX320_BIAS_REFR i wartości DAC. Każdy bias ustawiany jest niezależnie — wybierz preset jako punkt wyjścia, a następnie dostrój te biasy, których potrzebuje twoja scena:
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)
Śledzenie¶
Ponieważ wyjście trybu histogramu to po prostu obraz w skali szarości, zwykłe śledzenie plam (blob) działa bezpośrednio. Aby śledzić diodę LED jako aktywny znacznik, załaduj preset biasów active-marker i znajdź plamy (blob) na jasnym końcu histogramu:
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())
Tryb zdarzeń¶
Tryb zdarzeń pomija histogram na układzie i strumieniuje surowe zdarzenia do tablicy numpy ndarray. Każde zdarzenie to wiersz sześciu kolumn uint16:
[0]typ zdarzenia — patrz poniżej[1]znacznik czasu w sekundach[2]znacznik czasu w milisekundach[3]znacznik czasu w mikrosekundach[4]współrzędna X, 0-319[5]współrzędna Y, 0-319
Sterownik emituje sześć typów zdarzeń w kolumnie [0]:
csi.PIX_OFF_EVENT— piksel wykrył spadek jasności (przekroczony został próg komparatoraDIFF_OFF). X/Y wskazują piksel, który się wyzwolił.csi.PIX_ON_EVENT— piksel wykrył wzrost jasności (przekroczony został prógDIFF_ON). X/Y wskazują piksel.csi.EXT_TRIGGER_FALLING— pin zewnętrznego wyzwalania sensora zaobserwował zbocze opadające. X/Y są nieużywane.csi.EXT_TRIGGER_RISING— pin zewnętrznego wyzwalania sensora zaobserwował zbocze narastające. X/Y są nieużywane.csi.RST_TRIGGER_FALLING— wyzwalanie resetu piksela, zbocze opadające. X/Y są nieużywane. Obecnie niegenerowane przez oprogramowanie układowe.csi.RST_TRIGGER_RISING— wyzwalanie resetu piksela, zbocze narastające. X/Y są nieużywane. Obecnie niegenerowane przez oprogramowanie układowe.
Wejście zewnętrznego wyzwalania GENX320 jest podłączone do linii frame-sync kamery, która jest również wyprowadzona na P10 zarówno na procesorze, jak i na listwie pinów — wysteruj P10, aby wstrzyknąć zbocza synchronizacji do strumienia zdarzeń i odebrać je jako zdarzenia EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING obok danych pikselowych.
Większość zastosowań interesuje tylko PIX_OFF_EVENT i PIX_ON_EVENT; typy wyzwalania pozwalają skorelować zdarzenia z zewnętrznymi sygnałami czasowymi.
Zaalokuj bufor zdarzeń o kształcie (EVT_res, 6), gdzie EVT_res jest potęgą dwójki pomiędzy 1024 a 65536, a następnie wejdź w tryb zdarzeń poprzez csi.IOCTL_GENX320_SET_MODE z csi.GENX320_MODE_EVENT i rozmiarem bufora. Odczytuj zdarzenia za pomocą csi.IOCTL_GENX320_READ_EVENTS, które wypełnia bufor do jego pojemności i zwraca liczbę poprawnych wierszy.
Image.draw_event_histogram rasteryzuje zdarzenia do obrazu w skali szarości — dla każdego zdarzenia ON dodaje contrast do binu; dla każdego zdarzenia OFF odejmuje. clear=True najpierw resetuje obraz do brightness; clear=False akumuluje przez wiele wywołań:
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())
Presety biasów trybu histogramu, filtr AFK oraz ioctl kalibracji gorących pikseli działają w trybie zdarzeń dokładnie tak samo — wywołuj je po csi.IOCTL_GENX320_SET_MODE.
Filtrowanie według polaryzacji¶
Wytnij fragment tablicy zdarzeń za pomocą ulab, aby zachować tylko zdarzenia ON (ruch w stronę jaśniejszego stanu) lub tylko zdarzenia 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)
Akumulacja długiej ekspozycji¶
Ustaw clear=False, aby układać zdarzenia w tym samym obrazie przez wiele ramek — rezultatem jest wizualizacja śladu ruchu. Resetuj okresowo, aby rozpocząć nową ekspozycję:
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
Przetwarzanie z dużą szybkością¶
Zrezygnuj z wizualizacji, aby zwolnić CPU na przetwarzanie zdarzeń. Wypisuj statystyki tylko co N-tą iterację — wypychanie linii wydruku w każdej iteracji staje się wąskim gardłem przy wysokich częstościach zdarzeń:
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")
Filtr kontrastu czasowo-przestrzennego (STC)¶
Rzeczywista poruszająca się krawędź kontrastu zwykle wyzwala zaszumiony wybuch zdarzeń na tym samym pikselu w krótkim oknie czasowym — niedopasowanie pikseli i szum analogowy generują dodatkowe zdarzenia wokół prawdziwego przejścia, które nie są przydatne dla aplikacji. Filtr STC to wykonywany na układzie post-proces, który zachowuje tylko jedno (lub kilka) zdarzeń na wybuch, a resztę odrzuca.
Implementuje trzy strategie, wybierane za pomocą csi.IOCTL_GENX320_SET_STC i stałej GENX320_STC_*. Każdy tryb definiowany jest przez to, które zdarzenia przekazuje z wybuchu:
Tryb |
Zachowuje |
Odrzuca |
|---|---|---|
każde zdarzenie |
nic |
|
drugie zdarzenie wybuchu |
pierwsze + późniejsze zdarzenia |
|
pierwsze zdarzenie wybuchu |
kolejne zdarzenia |
|
pierwsze + kolejne krawędzie |
tylko nadmiarowy szum |
Szczegółowo:
csi.GENX320_STC_DISABLE— filtr wyłączony, każde zdarzenie przechodzi (domyślnie).csi.GENX320_STC_ONLY— zachowuje drugie zdarzenie wybuchu. Parametr:stc_threshold(ms). Jeśli nowe zdarzenie na pikselu nadejdzie w czasiestc_thresholdod poprzedniego zdarzenia, uznawane jest za „drugie” w wybuchu i zostaje przekazane — pierwsze zdarzenie oraz wszelkie kolejne zdarzenia w tym samym wybuchu są odfiltrowywane. Najlepsze, gdy chcesz przejścia potwierdzonego szumem, a nie samego pierwszego trafienia.csi.GENX320_STC_TRAIL_ONLY— zachowuje pierwsze zdarzenie wybuchu. Parametr:trail_threshold(ms). Po wyzwoleniu piksela kolejne zdarzenia na tym samym pikselu są odrzucane, dopóki nie upłynietrail_threshold. Zachowuje precyzyjny czas zbocza wiodącego — przydatne, gdy moment przełączenia polaryzacji ma większe znaczenie niż potwierdzenie wybuchu.csi.GENX320_STC_TRAIL— łączy oba. Parametry:stc_thresholditrail_threshold(oba w ms). Zachowuje zbocze wiodące zgodnie z trybem Trail plus kolejne zbocza zgodnie z trybem STC, tak aby przeszło wiele zdarzeń z wybuchu — wyższa przepustowość zdarzeń niż w filtrach jednotrybowych, ale najbogatszy sygnał.
Oba progi muszą pozostawać w przybliżeniu w stosunku 13:1 — sensor odrzuca konfiguracje, w których jeden jest większy niż ~13x od drugiego:
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)
Głębokość bufora¶
Gdy częstości zdarzeń gwałtownie rosną, domyślny potok z potrójnym buforowaniem faworyzuje najnowszą ramkę i odrzuca stare. Zwiększ głębokość FIFO za pomocą csi.CSI.framebuffers, aby zamiast tego kolejkować zdarzenia — kosztem przetwarzania nieco starszych danych, gdy host się opóźnia:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
Strumieniowanie i wizualizacja na komputerze¶
Do wizualizacji GUI w czasie rzeczywistym na komputerze PC, narzędzie GenX320 Event Streaming w repozytorium openmv-projects łączy kamerę z front-endem DearPyGui. GUI na PC uruchamia dwie wizualizacje obok siebie: kanwę akumulacji zdarzeń (ta sama idea co Image.draw_event_histogram, ale z wybieralnymi paletami oraz trybami okna przesuwnego vs. automatycznego czyszczenia) oraz mapę częstotliwości per-piksel sterowaną filtrem pasmowo-przepustowym IIR — przydatną do wykrywania sygnałów okresowych (obracające się wentylatory, migające diody LED itd.) bezpośrednio w strumieniu zdarzeń.
Dostarcza dwa skrypty strumieniowania działające na kamerze:
Tryb przetworzony (
genx320_event_mode_streaming_on_cam.py) — kamera dekoduje zdarzenia za pomocącsi.IOCTL_GENX320_READ_EVENTSi strumieniuje każdy wiersz jako 12 bajtów przez USB ([0]typ,[1]sek,[2]ms,[3]us,[4]x,[5]y). Łatwy do konsumpcji na PC, ponieważ format przesyłany odpowiada formatowi ndarray na kamerze.Tryb surowy (
genx320_raw_event_mode_streaming_on_cam.py) — kamera strumieniuje natywne 32-bitowe upakowane słowa zdarzeń układu poprzezcsi.IOCTL_GENX320_READ_EVENTS_RAW. To 4 bajty na zdarzenie zamiast 12 w trybie przetworzonym (około 3x mniej danych przez USB), więc ~3x wyższa osiągalna częstość zdarzeń, gdy łącze jest wąskim gardłem. PC dekoduje upakowane słowa z powrotem do tego samego 6-kolumnowego układu zdarzeń przy użyciu zwektoryzowanego numpy, więc kod wizualizatora po stronie pobierającej jest identyczny.
Tryb surowy jest domyślny w GUI, ponieważ przepustowość USB stanowi wiążące ograniczenie przy częstościach, jakie GenX320 jest w stanie wygenerować; przełącz na tryb przetworzony, jeśli musisz wpiąć logikę przetwarzania w skrypt działający na kamerze.