กล้อง Multispectral Event¶
โมดูล Multispectral Event Camera จับคู่ GENX320 event sensor เข้ากับ PAG7936 global-shutter colour sensor ขนาด 1 MP บนโมดูลเดียว — เป็น pipeline แบบ event + colour ที่ซิงโครไนซ์กัน สำหรับการติดตามวัตถุความเร็วสูง การติดตาม LED การไหลของของไหล และฉากไดนามิกอื่นๆ
สำหรับ datasheet ฉบับสมบูรณ์ รูปภาพ และข้อมูลการสั่งซื้อ ดูได้ที่ หน้าผลิตภัณฑ์ Multispectral Event Camera
Note
รองรับเฉพาะ OpenMV N6 เท่านั้น
จุดเด่น¶
event sensor ขนาด 320x320 ช่วงไดนามิก >140 dB ฮิสโตแกรม 375 Hz ขึ้นไป
PAG7936 colour: 1280x800 @ 120 FPS, 640x400 @ 240 FPS
เวลา timestamp ของ event ที่ซิงโครไนซ์กันด้วย shared exposure trigger
มองเห็นได้ในแสงต่ำกว่า 5 lux โดยไม่ต้องใช้ auto-exposure
การใช้พลังงานเริ่มต้นที่ ~3 mW สำหรับการสตรีม event
เหมาะสำหรับการติดตามความเร็วสูง การติดตาม LED และการไหลของของไหล/อนุภาค
การใช้งาน¶
colour sensor และ GENX320 event sensor แต่ละตัวจะมี instance ของ csi.CSI เป็นของตัวเอง การเรียกครั้งแรกจะใช้ sensor หลัก (PAG7936) เป็นค่าเริ่มต้น และการเรียกครั้งที่สองจะผูกกับ GENX320 โดยส่ง cid= csi.GENX320 รีเซ็ต colour sensor แบบ hard reset ด้วย csi.CSI.reset (hard=True) เพื่อเปิดใช้งาน rail และกำหนดค่า GENX320 ด้วย hard=False เพื่อให้ driver เพียงแค่โปรแกรมชิปใหม่โดยไม่ต้องสลับ reset อีกครั้ง
GENX320 ส่งออกขนาด 320x320 ในโหมดฮิสโตแกรม และ PAG7936 ที่ csi.QVGA ส่งออกขนาด 320x200 overlay พื้นฐานด้านล่างจะตัดแถวล่าง 120 แถวของเฟรม GENX320 ออก ใช้ homography transform (ด้านล่าง) สำหรับ overlay ที่พอดีหรือขนาดเฟรม PAG7936 ที่ใหญ่ขึ้น
scratch buffer สองตัวจะคงที่ตลอด frame loop — alpha palette ขนาด 256x1 ที่จัดเก็บเป็น image.Image เพื่อให้พิกเซลของฮิสโตแกรมที่ค่า mid-gray baseline (128) โปร่งใส และทั้งไฮไลท์ ON-event และเงา OFF-event จะทึบแสง และ GENX320 frame buffer ที่จัดสรรล่วงหน้าด้วย image.Image เพื่อให้ csi.CSI.snapshot (blocking=False, image=...) สามารถเติมข้อมูลในตำแหน่งนั้นได้ในแต่ละรอบโดยไม่ต้องจัดสรรใหม่:
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())
แต่ละรอบจะถ่าย colour snapshot แบบ blocking และ GENX320 snapshot แบบ non-blocking Image.draw_image จะ composite ทั้งสองเข้าด้วยกัน: color_palette= image.PALETTE_EVT_LIGHT (หรือ image.PALETTE_EVT_DARK สำหรับพื้นหลังสีเข้ม) แปลงฮิสโตแกรม grayscale ของ GENX320 เป็น colour ramp, alpha_palette= ผสมแต่ละพิกเซลโดยใช้ alpha map รูปตัว V เพื่อให้บริเวณเงียบในฉากส่องผ่านไปยังภาพ colour และ hint= image.BILINEAR ทำให้การขยายขนาดนุ่มนวลขึ้นเมื่อ colour sensor ทำงานที่ความละเอียดสูงกว่า GENX320
bias presets, AFK filter, hot-pixel calibration และ STC filter ioctl ของ GENX320 ทั้งหมดทำงานในลักษณะเดียวกันในการตั้งค่า dual-camera นี้ — เรียกบน csi1 หลังจาก csi.CSI.reset ดูรายละเอียดในส่วนด้านล่าง
การจัดแนวที่เร่งด้วย GPU¶
Image.draw_image รับอาร์กิวเมนต์ transform= — เป็น homography matrix ขนาด 3x3 ในรูปแบบ ulab.numpy array 2 มิติ บน OpenMV N6 GPU จะรัน per-pixel projection ระหว่าง draw เดิม ดังนั้นเฟรม GENX320 สามารถจัดแนวใหม่กับ perspective ของ colour camera ได้โดยไม่ต้องผ่าน warp pass แยกต่างหาก — มีประโยชน์เมื่อ sensor สองตัวมี optics หรือ field of view ที่แตกต่างกันเล็กน้อย หรือเมื่อ colour camera ทำงานที่ความละเอียดสูงกว่า คาลิเบรต matrix ต่อกล้องด้วย GenX320 Overlay Calibration tool ซึ่งแสดง checkerboard กะพริบเพื่อให้ event sensor สร้าง corner event โดยไม่ต้องเคลื่อนที่จริง:
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())
variant นี้รัน colour camera ที่ csi.VGA (640x480) และ GENX320 ที่ native 320x320 — homography จะฉาย GENX320 frame ที่เล็กกว่าลงใน colour frame ที่ใหญ่กว่าเป็นส่วนหนึ่งของการ draw ดังนั้น upscale factor จึงถูกฝังอยู่ใน matrix เองแทนที่จะใช้แยกกัน
รายละเอียด event camera¶
GENX320 เป็น event-based vision sensor — แทนที่จะอ่านอาร์เรย์ 320x320 ทั้งหมดตามนาฬิกาเฟรมที่กำหนด แต่ละพิกเซลจะรายงาน "events" แบบ asynchronous ทันทีที่ตรวจพบการเปลี่ยนแปลงความสว่าง ทุก event มีพิกัด X/Y ขั้วบวกหรือลบ ON/OFF (สว่าง→มืด หรือ มืด→สว่าง) และ timestamp ระดับ microsecond นั่นคือที่มาของความแม่นยำด้าน temporal ระดับ microsecond ของ sensor การไม่มี motion blur ช่วงไดนามิกสูงมาก และการใช้พลังงานที่ปรับตามกิจกรรม ฉากที่นิ่งไม่สร้างข้อมูลใดๆ
เฟิร์มแวร์ OpenMV เปิดเผย GENX320 ผ่าน csi.CSI ด้วย cid= csi.GENX320 มีโหมดการทำงานสองแบบ:
โหมดฮิสโตแกรม (ค่าเริ่มต้น) — events จะสะสมบนชิปในถัง per-pixel และรายงานเป็นเฟรม grayscale ขนาด 320x320 ที่อัตราที่กำหนดค่าได้ (~20-350 FPS) sensor ทำงานเหมือนกล้องทั่วไป ดังนั้น routines การประมวลผลภาพมาตรฐานทั้งหมด (
Image.find_blobs, palettes และอื่นๆ) จึงทำงานได้โดยตรงโหมด Event — raw events สตรีมเข้า numpy
ndarrayพร้อม timestamp ระดับ microsecond แบบสมบูรณ์ สำหรับแอปพลิเคชันที่ต้องการรายละเอียดด้าน temporal มากกว่าเฟรมที่ pre-binned แล้ว
โหมดฮิสโตแกรม¶
ในโหมดฮิสโตแกรม GENX320 ส่งออกเฟรม grayscale โดยแต่ละพิกเซลเข้ารหัสกิจกรรม event ล่าสุด ณ ตำแหน่งนั้น พิกเซลที่สูงกว่า brightness baseline คือ ON events (ความสว่างเพิ่มขึ้น) และต่ำกว่าคือ OFF events (ความสว่างลดลง) brightness baseline เริ่มต้นคือ 128 และ contrast step ต่อ event คือ 16 — เพิ่ม contrast เพื่อให้ events เด่นชัดขึ้น:
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 คือสามตัวปรับที่กำหนดรูปแบบ histogram output
output ที่เป็นสี¶
ตั้งค่า csi.CSI.color_palette เป็น image.PALETTE_EVT_LIGHT สำหรับพื้นหลังสีอ่อน หรือ image.PALETTE_EVT_DARK สำหรับพื้นหลังสีเข้ม — driver จะส่ง RGB565 frames โดยใช้ palette โดยตรง:
csi0.color_palette(image.PALETTE_EVT_LIGHT)
การคาลิเบรต hot-pixel¶
event sensor สะสม "hot pixels" ที่เกิด event แบบผิดปกติ รัน csi.IOCTL_GENX320_CALIBRATE กับฉากที่นิ่งเพื่อปิดการใช้งาน driver จะสร้าง per-pixel hit count ขนาด 320x320 คำนวณค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐาน และปิดการใช้งานพิกเซลที่มีค่าสูงกว่า mean + sigma * stddev — จากนั้นพิกเซลที่ปิดการใช้งานจะหยุดส่ง event ที่ระดับ sensor
พารามิเตอร์สองตัวควบคุมการคาลิเบรต:
event_count— จำนวน event ที่ต้องนับก่อนคำนวณสถิติ loop จะจับเฟรมจนกว่ายอด event รวมที่ทำงานอยู่จะเกิน budget นี้ จำนวนที่มากขึ้นให้การประมาณที่น่าเชื่อถือมากขึ้นแต่ใช้เวลาคาลิเบรตนานกว่า10000เป็นจุดเริ่มต้นที่เหมาะสมsigma— ตัวคูณค่าขีดแบ่งบนส่วนเบี่ยงเบนมาตรฐาน ค่าต่ำกว่าจะรุนแรงมากขึ้น (ปิดการใช้งานพิกเซลมากขึ้น) ค่าสูงกว่าจะระมัดระวังมากขึ้น0.5เป็นค่าเริ่มต้นที่ดี
ชี้ sensor ไปที่ฉากที่นิ่งก่อนเพื่อไม่ให้ event ที่ขับเคลื่อนด้วยการเคลื่อนไหวถูกนับกับพิกเซลที่จริงๆ แล้วไม่มีปัญหา:
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 (AFK) filter¶
แหล่งกำเนิดแสงแบบคาบ (ฟลูออเรสเซนต์ จอแสดงผลที่ขับเคลื่อนด้วย LED) สร้าง event ซ้ำซ้อนจำนวนมาก AFK filter จะปฏิเสธ event ที่พิกเซลสลับกันที่ความถี่ในช่วงหนึ่ง — เปิดใช้งานผ่าน 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
Bias presets¶
แต่ละพิกเซลใน GenX320 รัน analog front-end ที่มี biases หลายตัวที่กำหนดค่าได้ โดยร่วมกันควบคุม sensitivity, noise, pixel bandwidth และ event rate — การผสมที่เหมาะสมขึ้นอยู่กับฉาก biases แต่ละตัวคือ:
DIFF_ON — ค่าขีดแบ่ง contrast ของ comparator เชิงบวก พิกเซลจะส่ง ON event เมื่อ log-illumination เพิ่มขึ้นตามจำนวนนี้ ค่าต่ำ = sensitive ต่อการเปลี่ยนผ่านสู่สว่างมากขึ้น
DIFF_OFF — ค่าขีดแบ่ง contrast ของ comparator เชิงลบ (คู่สมมาตรสำหรับ OFF events) ค่าต่ำ = sensitive ต่อการเปลี่ยนผ่านสู่มืดมากขึ้น
FO — ความถี่ cut-off แบบ low-pass ของพิกเซล ค่าสูง = pixel bandwidth ที่กว้างขึ้น (ตอบสนองเร็วขึ้น latency ต่ำลง) แต่มีกิจกรรม background-noise มากขึ้น
HPF — ความถี่ cut-off แบบ high-pass ค่าสูง = ปฏิเสธการเปลี่ยนแปลงความสว่างช้าได้แรงขึ้น มีเฉพาะการเปลี่ยนผ่านเร็วเท่านั้นที่ไปถึง comparators มีประโยชน์สำหรับการละเว้น ambient drift
REFR — refractory period หลังจากพิกเซลเกิด event จะอยู่ใน reset ตามเวลานี้ก่อนจะเกิด event ได้อีกครั้ง ค่าสูง = dead time ที่ยาวนานขึ้น มีประโยชน์สำหรับจำกัด per-pixel event rate
หลังจาก csi.CSI.reset driver จะใช้ csi.GENX320_BIASES_LOW_NOISE ไม่ใช่ csi.GENX320_BIASES_DEFAULT — ค่าเริ่มต้นจาก datasheet ส่ง background event rate ที่สูงมาก ดังนั้น LOW_NOISE จึงถูกใช้เป็นจุดเริ่มต้นเพื่อให้ stream เงียบ เรียก csi.IOCTL_GENX320_SET_BIASES พร้อม preset ที่ต่างออกไปเมื่อแอปพลิเคชันต้องการ sensitivity หรือ bandwidth มากขึ้น
csi.IOCTL_GENX320_SET_BIASES ใช้หนึ่งใน five presets:
csi.GENX320_BIASES_DEFAULT— ค่าเริ่มต้นจาก GenX320 datasheet ความสมดุลระหว่าง sensitivity, noise และ bandwidth สำหรับฉากทั่วไปcsi.GENX320_BIASES_LOW_LIGHT— ค่าขีดแบ่ง contrast ทั้งสองผ่อนลงเพื่อ sensitivity สูงขึ้น FO ลดลงเพื่อควบคุม noise และ HPF ตั้งเป็น 0 เพื่อให้การเปลี่ยนแปลงความสว่างช้ายังคงลงทะเบียนได้ — ฉากที่มีแสงน้อยสร้าง event ไม่มากเอง ดังนั้นเราต้องการให้ได้มากที่สุดเท่าที่เป็นไปได้csi.GENX320_BIASES_ACTIVE_MARKER— ปรับแต่งสำหรับการติดตาม LED กะพริบที่มี contrast สูง ค่าขีดแบ่ง contrast เพิ่มขึ้นเพื่อให้เฉพาะการเปลี่ยนผ่านที่คมชัดเท่านั้นที่ trigger ได้ FO และ HPF เพิ่มสูงสุดเพื่อขยาย pixel bandwidth และปฏิเสธ ambient drift ช้า REFR ลดเป็น 0 เพื่อให้จับทุก blink edge ต่อเนื่องกัน ผลลัพธ์: stream ที่เกือบทั้งหมดเป็น LED edges ติดตามได้ง่ายcsi.GENX320_BIASES_LOW_NOISE— ค่าเริ่มต้นของ driver ค่าขีดแบ่ง contrast ทั้งสองเพิ่มขึ้นเมื่อเทียบกับDEFAULT(sensitive น้อยกว่า) และ FO ต่ำลง (พิกเซลช้าลง = พิกเซลเงียบลง) ดีที่สุดสำหรับฉากที่นิ่งหรือช้าซึ่ง false events จะครอบงำcsi.GENX320_BIASES_HIGH_SPEED— FO เพิ่มขึ้นเพื่อให้แต่ละพิกเซลตอบสนองได้เร็วขึ้น HPF เพิ่มขึ้นเพื่อปฏิเสธ slow brightness drift และ REFR เพิ่มขึ้นเพื่อไม่ให้ขอบที่เคลื่อนที่เร็วเพียงขอบเดียวทำให้ readout ล้นออกมา — dead time ที่ยาวขึ้นช่วยควบคุม event volume ภายใต้การเคลื่อนไหวที่หนัก
แทนที่ biases แต่ละตัวด้วย 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 แต่ละ bias จะถูกตั้งค่าอิสระ — เลือก preset เป็นจุดเริ่มต้น แล้วปรับ biases ที่ฉากของคุณต้องการ:
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)
การติดตาม¶
เนื่องจาก output ในโหมดฮิสโตแกรมเป็นเพียงภาพ grayscale การติดตาม blob ทั่วไปจึงทำงานได้โดยตรง ในการติดตาม active-marker LED ให้โหลด active-marker bias preset และค้นหา blob ที่ปลายสว่างของฮิสโตแกรม:
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¶
โหมด Event จะข้ามฮิสโตแกรมบนชิปและสตรีม raw events เข้า numpy ndarray แต่ละ event คือแถวของหกคอลัมน์ uint16:
[0]ประเภท event — ดูด้านล่าง[1]timestamp วินาที[2]timestamp มิลลิวินาที[3]timestamp ไมโครวินาที[4]พิกัด X, 0-319[5]พิกัด Y, 0-319
driver ส่ง event หกประเภทในคอลัมน์ [0]:
csi.PIX_OFF_EVENT— พิกเซลตรวจพบการลดลงของความสว่าง (ข้ามค่าขีดแบ่งDIFF_OFFcomparator) X/Y ชี้ไปที่พิกเซลที่เกิด eventcsi.PIX_ON_EVENT— พิกเซลตรวจพบการเพิ่มขึ้นของความสว่าง (ข้ามค่าขีดแบ่งDIFF_ON) X/Y ชี้ไปที่พิกเซลที่เกิด eventcsi.EXT_TRIGGER_FALLING— external trigger pin ของ sensor ตรวจพบ falling edge X/Y ไม่ได้ใช้งานcsi.EXT_TRIGGER_RISING— external trigger pin ของ sensor ตรวจพบ rising edge X/Y ไม่ได้ใช้งานcsi.RST_TRIGGER_FALLING— pixel-reset trigger, falling edge X/Y ไม่ได้ใช้งาน ยังไม่สร้างโดยเฟิร์มแวร์ในขณะนี้csi.RST_TRIGGER_RISING— pixel-reset trigger, rising edge X/Y ไม่ได้ใช้งาน ยังไม่สร้างโดยเฟิร์มแวร์ในขณะนี้
external trigger input ของ GENX320 เชื่อมต่อกับ frame-sync line ของกล้อง ซึ่งยังถูกเชื่อมต่อไปที่ P10 ทั้งบน processor และ pin header — ขับ P10 เพื่อฉีด sync edges เข้า event stream และรับเป็น EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING events พร้อมกับข้อมูลพิกเซล
แอปพลิเคชันส่วนใหญ่สนใจเพียง PIX_OFF_EVENT และ PIX_ON_EVENT เท่านั้น ประเภท trigger ช่วยให้คุณสัมพันธ์ events กับสัญญาณ timing ภายนอกได้
จัดสรร event buffer ด้วย shape (EVT_res, 6) โดยที่ EVT_res เป็น power of two ระหว่าง 1024 ถึง 65536 จากนั้นเข้าสู่โหมด event ผ่าน csi.IOCTL_GENX320_SET_MODE พร้อม csi.GENX320_MODE_EVENT และขนาด buffer อ่าน events ด้วย csi.IOCTL_GENX320_READ_EVENTS ซึ่งเติมข้อมูลใน buffer จนถึง capacity และคืนจำนวนแถวที่ valid
Image.draw_event_histogram rasterizes events เป็นภาพ grayscale — สำหรับแต่ละ ON event จะเพิ่ม contrast ลงในถัง สำหรับแต่ละ OFF event จะลบออก 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())
histogram-mode bias presets, AFK filter และ hot-pixel calibration ioctl ทั้งหมดทำงานในลักษณะเดียวกันในโหมด event — เรียกหลังจาก csi.IOCTL_GENX320_SET_MODE
การกรองตาม polarity¶
Slice events array ด้วย ulab เพื่อเก็บเฉพาะ ON events (การเคลื่อนไหวสู่สถานะที่สว่างขึ้น) หรือเฉพาะ OFF events:
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)
การสะสมแบบ long-exposure¶
ตั้งค่า clear=False เพื่อสะสม events ต่อเนื่องในภาพเดิมตลอดหลายเฟรม — ผลลัพธ์คือการแสดงภาพ motion-trail รีเซ็ตเป็นระยะเพื่อเริ่ม exposure ใหม่:
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
การประมวลผลความเร็วสูง¶
ลบ visualization ออกเพื่อเพิ่ม CPU สำหรับการประมวลผล event พิมพ์สถิติทุก N รอบเท่านั้น — การส่ง print line ทุกรอบจะกลายเป็น bottleneck ที่ event rate สูง:
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-temporal contrast (STC) filter¶
ขอบ contrast เคลื่อนที่จริงมักจะ trigger burst ของ events ที่มีเสียง rattle บนพิกเซลเดิมภายในช่วงเวลาสั้น — ความไม่ตรงกันของพิกเซลและ analog noise สร้าง events พิเศษรอบการเปลี่ยนผ่านที่แท้จริงซึ่งไม่มีประโยชน์ต่อแอปพลิเคชัน STC filter เป็น on-chip post-process ที่เก็บเฉพาะหนึ่ง (หรือไม่กี่) events ต่อ burst และทิ้งส่วนที่เหลือ
filter ใช้สามกลยุทธ์ เลือกผ่าน csi.IOCTL_GENX320_SET_STC และค่าคงที่ GENX320_STC_* แต่ละโหมดถูกกำหนดโดย events ที่มันส่งผ่านจาก burst:
โหมด |
เก็บ |
ทิ้ง |
|---|---|---|
ทุก event |
ไม่มี |
|
event ที่สองของ burst |
event แรก + event หลังๆ |
|
event แรกของ burst |
events ที่ตามมา |
|
ขอบแรก + ขอบที่ตามมา |
เฉพาะ noise ซ้ำซ้อน |
รายละเอียด:
csi.GENX320_STC_DISABLE— filter ปิด ทุก event ผ่าน (ค่าเริ่มต้น)csi.GENX320_STC_ONLY— เก็บ event ที่สอง ของ burst พารามิเตอร์:stc_threshold(ms) หาก event ใหม่บนพิกเซลมาถึงภายในstc_thresholdของ event ก่อนหน้า จะถือว่าเป็น "ที่สอง" ของ burst และส่งผ่าน — event แรกและ events ที่ตามมาใน burst เดิมจะถูก filter ออก ดีที่สุดเมื่อคุณต้องการการเปลี่ยนผ่านที่ยืนยันด้วย noise แทนที่จะเป็น hit แรกcsi.GENX320_STC_TRAIL_ONLY— เก็บ event แรก ของ burst พารามิเตอร์:trail_threshold(ms) หลังจากพิกเซลเกิด event events ที่ตามมาบนพิกเซลเดิมจะถูกทิ้งจนกว่าtrail_thresholdจะผ่านไป รักษา timing ที่แม่นยำของ leading edge — มีประโยชน์เมื่อช่วงเวลาที่ polarity เปลี่ยนสำคัญกว่าการยืนยัน burstcsi.GENX320_STC_TRAIL— รวมทั้งสอง พารามิเตอร์:stc_thresholdและtrail_threshold(ทั้งคู่เป็น ms) เก็บ leading edge ตาม Trail mode บวกกับ edges ที่ตามมาตาม STC mode ดังนั้น events หลายตัวจาก burst ยังผ่านได้ — event throughput สูงกว่า single-mode filters แต่มีสัญญาณที่หลากหลายที่สุด
threshold ทั้งสองต้องอยู่ในอัตราส่วนประมาณ 13:1 — sensor จะปฏิเสธการกำหนดค่าที่ threshold หนึ่งมากกว่าอีกอัน ~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)
ความลึกของ buffer¶
เมื่อ event rate พุ่งสูงขึ้น triple-buffer pipeline เริ่มต้นจะเลือกเฟรมล่าสุดและทิ้งเฟรมเก่า เพิ่ม FIFO depth ผ่าน csi.CSI.framebuffers เพื่อจัดคิว events แทน — แต่แลกกับการประมวลผลข้อมูลที่เก่าเล็กน้อยเมื่อ host ล้าหลัง:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
การสตรีมและการแสดงผลบน Desktop¶
สำหรับการแสดงผล GUI แบบ real-time บน host PC GenX320 Event Streaming tool ใน repo openmv-projects จับคู่กล้องกับ DearPyGui front-end GUI บน PC รันการแสดงผลสองแบบควบคู่กัน: event accumulation canvas (แนวคิดเดียวกับ Image.draw_event_histogram แต่มี palette ที่เลือกได้และโหมด sliding-window กับ auto-clear) และ per-pixel frequency map ที่ขับเคลื่อนด้วย IIR band-pass filter — มีประโยชน์สำหรับการระบุสัญญาณคาบ (พัดลมหมุน LED กะพริบ ฯลฯ) โดยตรงใน event stream
มาพร้อม on-cam streaming scripts สองตัว:
Processed mode (
genx320_event_mode_streaming_on_cam.py) — กล้อง decode events ด้วยcsi.IOCTL_GENX320_READ_EVENTSและสตรีมแต่ละแถวเป็น 12 bytes ผ่าน USB ([0]type,[1]sec,[2]ms,[3]us,[4]x,[5]y) ใช้งานได้ง่ายบน PC เพราะ wire format ตรงกับ on-cam ndarray formatRaw mode (
genx320_raw_event_mode_streaming_on_cam.py) — กล้องสตรีม native 32-bit packed event words ของชิปผ่านcsi.IOCTL_GENX320_READ_EVENTS_RAWนั่นคือ 4 bytes ต่อ event เทียบกับ 12 ใน processed mode (ข้อมูลน้อยกว่าประมาณ 3x ผ่าน USB) ดังนั้น achievable event rate สูงกว่า ~3x เมื่อ link เป็น bottleneck PC decode packed words กลับเป็น 6-column event layout เดิมโดยใช้ vectorized numpy ดังนั้น downstream visualizer code จึงเหมือนกัน
Raw mode เป็นค่าเริ่มต้นใน GUI เพราะ USB throughput เป็น binding constraint ที่ rate ที่ GenX320 สามารถสร้างได้ เปลี่ยนเป็น processed mode หากคุณต้องการเสียบ processing logic เข้ากับ on-cam script