GENX320 事件相機¶
GENX320 事件相機模組是一款 Prophesee 基於事件的視覺感測器,具備 320x320 解析度與微秒級的時間精度。
完整的資料表、照片與訂購資訊請參閱 GENX320 事件相機產品頁面。
備註
支援於 OpenMV H7 Plus、RT1062 與 N6。
重點特色¶
320x320 基於事件的視覺感測器
140 dB 動態範圍,無動態模糊
375 Hz 以上的事件直方圖輸出速率
功耗隨場景活動度而變化 — 起始約 3 mW
可在低於 5 lux 至明亮陽光下運作,無需自動曝光
輸出灰階影格或原始事件串流
用法¶
GENX320 是一款基於事件的視覺感測器 — 它不會以固定的影格時脈讀出整個 320x320 陣列,而是每個像素在偵測到亮度變化的瞬間即非同步回報「事件」。每個事件帶有 X/Y 座標、ON/OFF 極性(由亮轉暗或由暗轉亮)以及微秒級的時間戳記。感測器的微秒級時間精度、無動態模糊、極高動態範圍以及隨活動度變化的功耗皆源自於此。靜態場景不會產生任何資料。
OpenMV 韌體透過 csi.CSI 並搭配 cid= csi.GENX320 來公開 GENX320。提供兩種運作模式:
直方圖模式(預設)— 事件在晶片上累積至各像素的分箱中,並以可設定的速率(約 20-350 FPS)回報為 320x320 灰階影格。感測器表現得像一般相機,因此所有標準影像處理常式(
Image.find_blobs、調色盤等)都能直接運作。事件模式 — 原始事件串流進入帶有完整微秒時間戳記的 numpy
ndarray,適用於需要時間細節而非預先分箱影格的應用。
直方圖模式¶
在直方圖模式下,GENX320 輸出灰階影格,每個像素編碼該位置最近的事件活動。高於亮度基準的像素為 ON 事件(亮度上升),低於基準的為 OFF 事件(亮度下降)。預設基準亮度為 128,每個事件的對比步進為 16 — 提高對比可讓事件更突出:
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 與 csi.CSI.framerate 是塑造直方圖輸出的三個調節旋鈕。
彩色化輸出¶
將 csi.CSI.color_palette 設為 image.PALETTE_EVT_LIGHT 可得淺色背景,或設為 image.PALETTE_EVT_DARK 可得深色背景 — 驅動程式會直接使用調色盤輸出 RGB565 影格:
csi0.color_palette(image.PALETTE_EVT_LIGHT)
熱像素校正¶
事件感測器會累積一些會虛假觸發的「熱像素」。對著靜態場景執行 csi.IOCTL_GENX320_CALIBRATE 即可停用它們。驅動程式會建立一個 320x320 的各像素命中計數,計算平均值與標準差,並停用任何計數高於 mean + sigma * stddev 的像素 — 之後這些被停用的像素就會在感測器層級停止發出事件。
有兩個參數可控制校正:
event_count— 在計算統計數據之前要統計多少事件。迴圈會持續擷取影格,直到累計事件總數超過此額度。較高的計數能提供更可靠的估計,但代價是較長的校正時間。10000是合理的起始點。sigma— 標準差的閾值乘數。較低的值較為積極(停用較多像素);較高的值較為保守。0.5是不錯的預設值。
請先將感測器對準靜態場景,以免任何由運動產生的事件被計入實際上正常的像素:
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")
抗閃爍(AFK)濾波器¶
週期性光源(螢光燈、LED 驅動的顯示器)會產生大量冗餘事件。AFK 濾波器會拒絕那些像素切換頻率落在某頻帶內的事件 — 透過 csi.IOCTL_GENX320_SET_AFK 並以赫茲為單位指定頻帶邊界來啟用:
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160) # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0) # disable
偏壓預設¶
GenX320 中的每個像素都運行一個帶有多項可設定偏壓的類比前端。它們共同決定靈敏度、雜訊、像素頻寬與事件速率 — 正確的組合取決於場景。各個偏壓如下:
DIFF_ON — 正向比較器的對比閾值。當像素的對數照度上升達此幅度時,會發出 ON 事件。較低 = 對亮度上升轉變較為靈敏。
DIFF_OFF — 負向比較器的對比閾值(OFF 事件的對稱對應項)。較低 = 對亮度下降轉變較為靈敏。
FO — 像素的低通截止頻率。較高 = 較寬的像素頻寬(反應較快、延遲較低),但背景雜訊活動也較多。
HPF — 高通截止頻率。較高 = 對緩慢亮度變化的抑制較強;只有快速轉變才能到達比較器。適用於忽略環境漂移。
REFR — 不應期。像素觸發後,會保持重置狀態此段時間後才能再次觸發。較高 = 較長的死區時間,適用於限制各像素的事件速率。
在 csi.CSI.reset 之後,驅動程式會套用 csi.GENX320_BIASES_LOW_NOISE,而非 csi.GENX320_BIASES_DEFAULT — 資料表預設值會發出高得多的背景事件速率,因此使用 LOW_NOISE 作為起始點以保持串流安靜。當應用需要更高靈敏度或頻寬時,請以不同的預設呼叫 csi.IOCTL_GENX320_SET_BIASES。
csi.IOCTL_GENX320_SET_BIASES 會套用五種預設之一:
csi.GENX320_BIASES_DEFAULT— GenX320 資料表預設值。對一般場景在靈敏度、雜訊與頻寬之間取得平衡。csi.GENX320_BIASES_LOW_LIGHT— 兩個對比閾值都放寬以提高靈敏度,FO 降低以抑制雜訊,HPF 設為 0 使緩慢的亮度變化仍能被記錄 — 低光場景本身產生的事件很少,因此我們希望盡可能讓更多事件通過。csi.GENX320_BIASES_ACTIVE_MARKER— 針對追蹤高對比閃爍 LED 進行調校。對比閾值提高,使只有銳利的轉變才觸發;FO 與 HPF 調至高位以最大化像素頻寬並拒絕任何緩慢的環境漂移;REFR 拉至 0 使每個閃爍邊緣都能連續被捕捉。結果是:一個幾乎全是 LED 邊緣的串流,易於追蹤。csi.GENX320_BIASES_LOW_NOISE— 驅動程式預設值。兩個對比閾值都較DEFAULT提高(較不靈敏),FO 降低(較慢的像素 = 較安靜的像素)。最適合靜態或緩慢的場景,否則錯誤事件會在其中佔據主導。csi.GENX320_BIASES_HIGH_SPEED— FO 提高使每個像素能更快反應,HPF 提高以拒絕緩慢的亮度漂移,REFR 提高使單一快速移動的邊緣不會淹沒讀出 — 較長的死區時間能在劇烈運動下將事件量維持在有界範圍內。
使用 csi.IOCTL_GENX320_SET_BIAS 加上 csi.GENX320_BIAS_DIFF_ON、csi.GENX320_BIAS_DIFF_OFF、csi.GENX320_BIAS_FO、csi.GENX320_BIAS_HPF 或 csi.GENX320_BIAS_REFR 其中之一以及一個 DAC 值,即可覆寫個別偏壓。每個偏壓都是獨立設定的 — 挑選一個預設作為起始點,然後調整你的場景所需的任何偏壓:
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)
追蹤¶
由於直方圖模式的輸出就只是一張灰階影像,一般的色塊追蹤可直接運作。若要追蹤一個 active-marker LED,請載入 active-marker 偏壓預設,並在直方圖的亮端尋找色塊:
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())
事件模式¶
事件模式會繞過晶片上的直方圖,將原始事件串流進 numpy ndarray。每個事件是一列六個 uint16 欄位:
[0]事件類型 — 見下文[1]秒時間戳記[2]毫秒時間戳記[3]微秒時間戳記[4]X 座標,0-319[5]Y 座標,0-319
驅動程式在欄位 [0] 中發出六種事件類型:
csi.PIX_OFF_EVENT— 某像素偵測到亮度下降(跨越了DIFF_OFF比較器閾值)。X/Y 指向觸發的像素。csi.PIX_ON_EVENT— 某像素偵測到亮度上升(跨越了DIFF_ON閾值)。X/Y 指向該像素。csi.EXT_TRIGGER_FALLING— 感測器的外部觸發接腳偵測到下降邊緣。X/Y 未使用。csi.EXT_TRIGGER_RISING— 感測器的外部觸發接腳偵測到上升邊緣。X/Y 未使用。csi.RST_TRIGGER_FALLING— 像素重置觸發,下降邊緣。X/Y 未使用。目前韌體不會產生此類事件。csi.RST_TRIGGER_RISING— 像素重置觸發,上升邊緣。X/Y 未使用。目前韌體不會產生此類事件。
GENX320 的外部觸發輸入連接至相機的影格同步線,該線也被連到處理器與接腳排針上的 P10 — 驅動 P10 即可將同步邊緣注入事件串流,並與像素資料一起以 EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING 事件的形式擷取它們。
大多數應用只關心 PIX_OFF_EVENT 與 PIX_ON_EVENT;觸發類型則讓你能將事件與外部時序訊號關聯起來。
以形狀 (EVT_res, 6) 配置事件緩衝區,其中 EVT_res 是介於 1024 與 65536 之間的 2 的次方,接著透過 csi.IOCTL_GENX320_SET_MODE 並搭配 csi.GENX320_MODE_EVENT 與緩衝區大小進入事件模式。使用 csi.IOCTL_GENX320_READ_EVENTS 讀取事件,它會將緩衝區填滿至其容量並回傳有效列的數量。
Image.draw_event_histogram 會將事件柵格化為灰階影像 — 每個 ON 事件會在分箱中加上 contrast;每個 OFF 事件則減去。clear=True 會先將影像重置為 brightness;clear=False 則會在多次呼叫間累積:
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())
直方圖模式的偏壓預設、AFK 濾波器與熱像素校正 ioctl 在事件模式中的運作方式完全相同 — 在 csi.IOCTL_GENX320_SET_MODE 之後呼叫它們即可。
依極性過濾¶
使用 ulab 對事件陣列進行切片,以僅保留 ON 事件(朝較亮狀態的運動)或僅保留 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)
長曝光累積¶
設定 clear=False 即可在多個影格間持續將事件堆疊進同一張影像 — 結果是一個運動軌跡的視覺化。定期重置以開始新的曝光:
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
高速處理¶
捨棄視覺化以釋出 CPU 用於事件處理。僅每第 N 次迭代列印一次統計數據 — 在每次迭代都推送一行列印,在高事件速率下會成為瓶頸:
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")
時空對比(STC)濾波器¶
一個真實移動的對比邊緣往往會在短時間窗口內於同一像素觸發一連串吵雜的事件爆發 — 像素失配與類比雜訊會在真正的轉變周圍產生對應用無用的額外事件。STC 濾波器是一種晶片上的後處理,只保留每次爆發中的一個(或少數幾個)事件並捨棄其餘的。
它實作三種策略,透過 csi.IOCTL_GENX320_SET_STC 與一個 GENX320_STC_* 常數來選擇。每種模式由它從一次爆發中轉發哪些事件來定義:
模式 |
保留 |
捨棄 |
|---|---|---|
每個事件 |
無 |
|
爆發中的第二個事件 |
第一個 + 後續事件 |
|
爆發中的第一個事件 |
後續事件 |
|
第一個 + 後續邊緣 |
僅冗餘雜訊 |
詳細說明:
csi.GENX320_STC_DISABLE— 濾波器關閉,每個事件都會通過(預設)。csi.GENX320_STC_ONLY— 保留爆發中的第二個事件。參數:stc_threshold(ms)。若某像素上的新事件在前一事件的stc_threshold時間內到達,便會被視為一次爆發的「第二個」並予以轉發 — 第一個事件以及同一次爆發中任何後續事件都會被濾除。最適合你想要一個經雜訊確認的轉變,而非最初的那一次命中時使用。csi.GENX320_STC_TRAIL_ONLY— 保留爆發中的第一個事件。參數:trail_threshold(ms)。像素觸發後,同一像素上的後續事件會被捨棄,直到trail_threshold經過為止。保留前緣的精確時序 — 當極性切換的時刻比爆發確認更重要時很有用。csi.GENX320_STC_TRAIL— 兩者兼具。參數:stc_threshold與trail_threshold(皆為 ms)。依 Trail 模式保留前緣,再依 STC 模式保留後續邊緣,因此一次爆發中的多個事件仍能通過 — 比單一模式濾波器有更高的事件吞吐量,但訊號最豐富。
兩個閾值必須維持在大致 13:1 的比例內 — 感測器會拒絕其中一者超過另一者約 13 倍的設定:
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)
緩衝區深度¶
當事件速率飆升時,預設的三重緩衝管線會偏好最新的影格並捨棄舊的。透過 csi.CSI.framebuffers 提高 FIFO 深度可改為將事件排入佇列 — 代價是當主機落後時會處理稍微較舊的資料:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
桌面串流與視覺化¶
若要在主機 PC 上進行即時 GUI 視覺化,openmv-projects 儲存庫中的 GenX320 事件串流工具 將相機與一個 DearPyGui 前端配對。此 PC GUI 並排運行兩種視覺化:一個事件累積畫布(與 Image.draw_event_histogram 概念相同,但具有可選的調色盤以及滑動視窗對自動清除模式)以及一個由 IIR 帶通濾波器驅動的各像素頻率圖 — 適用於直接在事件串流中發現週期性訊號(旋轉的風扇、閃爍的 LED 等)。
它隨附兩個於相機上運行的串流指令碼:
已處理模式(
genx320_event_mode_streaming_on_cam.py)— 相機以csi.IOCTL_GENX320_READ_EVENTS解碼事件,並透過 USB 將每一列以 12 個位元組串流([0]類型、[1]秒、[2]ms、[3]us、[4]x、[5]y)。在 PC 上易於消費,因為其線路格式與相機上的 ndarray 格式相符。原始模式(
genx320_raw_event_mode_streaming_on_cam.py)— 相機透過csi.IOCTL_GENX320_READ_EVENTS_RAW串流晶片的原生 32 位元封裝事件字。這是每個事件 4 個位元組,相對於已處理模式的 12 個(透過 USB 約少 3 倍的資料量),因此當連結成為瓶頸時可達到約 3 倍更高的事件速率。PC 端使用向量化的 numpy 將封裝字解碼回相同的 6 欄事件配置,因此下游的視覺化程式碼完全相同。
原始模式是 GUI 中的預設值,因為在 GenX320 能產生的速率下,USB 吞吐量是綁定的限制條件;若你需要將處理邏輯插入相機上的指令碼,則切換至已處理模式。