מצלמת אירועים רב-ספקטרלית

מודול מצלמת האירועים הרב-ספקטרלית משלב את חיישן האירועים GENX320 עם חיישן צבע global-shutter מסוג PAG7936 ברזולוציה של 1 MP על מודול יחיד — צינור עיבוד מסונכרן של אירועים + צבע למעקב מהיר אחר עצמים, מעקב אחר LED, זרימת נוזלים וסצנות דינמיות אחרות.

Multispectral Event Camera

לדף נתונים מלא, תמונות ופרטי הזמנה ראו את דף המוצר של מצלמת האירועים הרב-ספקטרלית.

הערה

נתמך ב-OpenMV N6 בלבד.

עיקרי הדברים

  • חיישן אירועים בגודל 320x320, טווח דינמי >140 dB, היסטוגרמות בקצב 375 Hz+

  • צבע PAG7936: 1280x800 @ 120 FPS, 640x400 @ 240 FPS

  • חותמות זמן אירוע מסונכרנות עם טריגר חשיפה משותף

  • רואה מתחת ל-5 lux ללא חשיפה אוטומטית

  • צריכת החשמל מתחילה ב-~3 mW עבור הזרמת אירועים

  • מיועד למעקב מהיר, מעקב אחר LED וזרימת נוזלים/חלקיקים

שימוש

חיישן הצבע וחיישן האירועים GENX320 מקבלים כל אחד מופע csi.CSI משלו. הקריאה הראשונה מוגדרת כברירת מחדל לחיישן הראשי (ה-PAG7936); השנייה נקשרת ל-GENX320 על ידי העברת cid= csi.GENX320. בצעו hard-reset לחיישן הצבע באמצעות csi.CSI.reset (hard=True) כדי להעלות את קו המתח, והגדירו את ה-GENX320 עם hard=False כך שמנהל ההתקן שלו רק יתכנת מחדש את השבב מבלי לבצע reset שוב.

ה-GENX320 מפיק 320x320 במצב היסטוגרמה; ה-PAG7936 ב-csi.QVGA מפיק 320x200. שכבת העל הבסיסית למטה חותכת את 120 השורות התחתונות של פריים ה-GENX320. השתמשו בטרנספורמציית ההומוגרפיה (למטה) עבור שכבת על מותאמת או גודל פריים גדול יותר של PAG7936.

שני חוצצי עבודה נשארים קבועים לאורך לולאת הפריימים — פלטת אלפא בגודל 256x1 המאוחסנת כ-image.Image כך שפיקסלי היסטוגרמה בקו הבסיס של אפור-ביניים (128) הופכים לשקופים והדגשות אירועי-ON וגם צללי אירועי-OFF הופכים לאטומים, וכן חוצץ פריימים (frame buffer) של GENX320 שהוקצה מראש עם 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())

כל איטרציה לוקחת תמונת בזק (snapshot) חוסמת של צבע ותמונת בזק (snapshot) לא-חוסמת של GENX320. Image.draw_image לאחר מכן מרכיב את השניים: color_palette= image.PALETTE_EVT_LIGHT (או image.PALETTE_EVT_DARK לרקע כהה) ממפה את היסטוגרמת גווני האפור של ה-GENX320 לרמפת צבע, alpha_palette= מערבב כל פיקסל באמצעות מפת האלפא בצורת v כך שאזורים שקטים בסצנה נופלים אל תמונת הצבע, ו-hint= image.BILINEAR מחליק את הגדלת הדגימה כאשר חיישן הצבע פועל ברזולוציה גבוהה יותר מה-GENX320.

ערכות הגדרות ה-bias של ה-GENX320, מסנן ה-AFK, כיול הפיקסלים החמים, וה-ioctls של מסנן ה-STC כולם פועלים באותו אופן בהגדרה זו של מצלמה כפולה — קראו להם על csi1 לאחר csi.CSI.reset. ראו את הסעיפים למטה לפרטים.

יישור מואץ GPU

Image.draw_image מקבל ארגומנט transform= — מטריצת הומוגרפיה 3x3 כמערך ulab.numpy דו-ממדי. ב-OpenMV N6 ה-GPU מריץ את ההיטל לכל-פיקסל במהלך אותו ציור, כך שניתן ליישר מחדש את פריים ה-GENX320 ביחס לפרספקטיבה של מצלמת הצבע ללא מעבר warp נפרד — שימושי כאשר לשני החיישנים אופטיקה או שדות ראייה שונים מעט, או כאשר מצלמת הצבע פועלת ברזולוציה גבוהה יותר. כיילו את המטריצה לכל מצלמה באמצעות כלי כיול שכבת העל של GenX320, המציג לוח שחמט מהבהב כך שחיישן האירועים מפיק אירועי פינה ללא כל תנועה פיזית:

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())

וריאנט זה מריץ את מצלמת הצבע ב-csi.VGA (640x480) ואת ה-GENX320 ברזולוציה הטבעית שלו 320x320 — ההומוגרפיה מטילה את פריים ה-GENX320 הקטן יותר אל תוך פריים הצבע הגדול יותר כחלק מהציור, כך שגורם הגדלת הדגימה מוטמע במטריצה עצמה במקום להיות מיושם בנפרד.

פרטי מצלמת האירועים

ה-GENX320 הוא חיישן ראייה מבוסס-אירועים — במקום לקרוא את כל מערך ה-320x320 בשעון פריים קבוע, כל פיקסל מדווח על ”אירועים“ אסינכרוניים ברגע שהוא מזהה שינוי בהירות. כל אירוע נושא קואורדינטת X/Y, קוטביות ON/OFF (בהיר→כהה או כהה→בהיר), וחותמת זמן במיקרו-שניות. משם נובעים הדיוק הזמני של החיישן ברמת המיקרו-שנייה, היעדר טשטוש תנועה, הטווח הדינמי הגבוה מאוד, וצריכת החשמל המוגדלת לפי פעילות. סצנות סטטיות אינן מייצרות נתונים.

הקושחה של OpenMV חושפת את ה-GENX320 דרך csi.CSI עם cid= csi.GENX320. זמינים שני מצבי הפעלה:

  • מצב היסטוגרמה (ברירת מחדל) — אירועים נצברים על-גבי-השבב לתאי-פיקסל ומדווחים כפריים בגווני אפור בגודל 320x320 בקצב הניתן להגדרה (~20-350 FPS). החיישן מתנהג כמצלמה רגילה, כך שכל שגרות עיבוד התמונה הסטנדרטיות (Image.find_blobs, פלטות וכו«) פועלות ישירות.

  • מצב אירועים — אירועים גולמיים זורמים אל ndarray של numpy עם חותמות זמן מלאות במיקרו-שניות, עבור יישומים הזקוקים לפירוט הזמני במקום לפריים מתויק מראש.

מצב היסטוגרמה

במצב היסטוגרמה ה-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

ערכות הגדרות bias מוגדרות מראש

כל פיקסל ב-GenX320 מריץ קצה-קדמי אנלוגי עם מספר ערכי bias הניתנים להגדרה. הם שולטים יחד ברגישות, רעש, רוחב פס הפיקסל וקצב האירועים — השילוב הנכון תלוי בסצנה. ערכי ה-bias הבודדים הם:

  • DIFF_ON — סף ניגודיות המשווה החיובי. פיקסל פולט אירוע ON כאשר תאורת-הלוג שלו עלתה בכמות זו. נמוך יותר = רגיש יותר למעברים בהירים.

  • DIFF_OFF — סף ניגודיות המשווה השלילי (המקבילה הסימטרית עבור אירועי OFF). נמוך יותר = רגיש יותר למעברים כהים.

  • FO — תדירות החיתוך הנמוכה של הפיקסל (low-pass). גבוה יותר = רוחב פס פיקסל רחב יותר (תגובה מהירה יותר, השהיה נמוכה יותר) אך יותר פעילות רעש-רקע.

  • HPF — תדירות החיתוך הגבוהה (high-pass). גבוה יותר = דחייה חזקה יותר של שינויי בהירות איטיים; רק מעברים מהירים מגיעים אל המשווים. שימושי להתעלמות מנדידת תאורת סביבה.

  • REFR — תקופת הרפרקטוריות. לאחר שפיקסל יורה, הוא נשאר ב-reset למשך זמן זה לפני שהוא יכול לירות שוב. גבוה יותר = זמן מת ארוך יותר, שימושי להגבלת קצב האירועים לכל-פיקסל.

לאחר 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 מועלה כך שקצה בודד הנע במהירות אינו מציף את הקריאה — הזמן המת הארוך יותר שומר על נפח האירועים תחום תחת תנועה כבדה.

עקפו ערכי bias בודדים באמצעות 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 מוגדר באופן עצמאי — בחרו ערכה מוגדרת מראש כנקודת התחלה, ואז כווננו את ערכי ה-bias שהסצנה שלכם זקוקה להם:

csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)

מעקב

מכיוון שפלט מצב-ההיסטוגרמה הוא פשוט תמונה בגווני אפור, מעקב רכיבים (blob) רגיל פועל ישירות. כדי לעקוב אחר LED מסוג active-marker, טענו את ערכת ה-bias של active-marker ומצאו רכיבים (blobs) בקצה הבהיר של ההיסטוגרמה:

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())

מצב אירועים

מצב אירועים עוקף את ההיסטוגרמה שעל-גבי-השבב ומזרים אירועים גולמיים אל ndarray של numpy. כל אירוע הוא שורה של שש עמודות 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 — טריגר reset-פיקסל, קצה יורד. X/Y אינם בשימוש. אינו מיוצר על ידי הקושחה בשלב זה.

  • csi.RST_TRIGGER_RISING — טריגר reset-פיקסל, קצה עולה. X/Y אינם בשימוש. אינו מיוצר על ידי הקושחה בשלב זה.

כניסת הטריגר החיצוני של ה-GENX320 מחווטת לקו frame-sync של המצלמה, המנותב גם אל P10 הן במעבד והן בשורת הפינים — הזינו את P10 כדי להחדיר קצוות סנכרון לזרם האירועים ולקלוט אותם כאירועי EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING לצד נתוני הפיקסל.

רוב היישומים מתעניינים רק ב-PIX_OFF_EVENT וב-PIX_ON_EVENT; סוגי הטריגר מאפשרים לכם לקשר אירועים עם אותות תזמון חיצוניים.

הקצו את חוצץ האירועים עם הצורה (EVT_res, 6) כאשר EVT_res הוא חזקה של שתיים בין 1024 ל-65536, ואז היכנסו למצב אירועים דרך 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())

ערכות ה-bias של מצב ההיסטוגרמה, מסנן ה-AFK, וה-ioctls של כיול הפיקסלים החמים כולם פועלים באותו אופן במצב אירועים — קראו להם לאחר 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

אירוע שני של מטח

אירוע ראשון + מאוחרים

csi.GENX320_STC_TRAIL_ONLY

אירוע ראשון של מטח

אירועים עוקבים

csi.GENX320_STC_TRAIL

קצה ראשון + קצוות עוקבים

רעש מיותר בלבד

בפירוט:

  • 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)

עומק חוצץ

כאשר קצבי האירועים מזנקים, צינור החוצץ המשולש המוגדר כברירת מחדל מעדיף את הפריים האחרון ומשליך את הישנים. העלו את עומק ה-FIFO באמצעות csi.CSI.framebuffers כדי להעמיד אירועים בתור במקום — במחיר עיבוד נתונים מעט ישנים יותר כאשר המארח נופל מאחור:

csi0.framebuffers(10)  # FIFO depth, > 3 enables queueing

הזרמה וויזואליזציה בשולחן העבודה

לויזואליזציית GUI בזמן אמת על מחשב מארח, כלי הזרמת האירועים של GenX320 במאגר openmv-projects מצמיד את המצלמה לחזית DearPyGui. ה-GUI במחשב מריץ שתי ויזואליזציות זו לצד זו: קנבס צבירת אירועים (אותו רעיון כמו Image.draw_event_histogram אך עם פלטות הניתנות לבחירה ומצבי חלון-נע לעומת ניקוי-אוטומטי) ומפת תדירות לכל-פיקסל המונעת על ידי מסנן IIR band-pass — שימושי לזיהוי אותות מחזוריים (מאווררים מסתובבים, נורות LED מהבהבות וכו«) ישירות בזרם האירועים.

הוא נשלח עם שני סקריפטי הזרמה על-המצלמה:

  • מצב מעובד (genx320_event_mode_streaming_on_cam.py) — המצלמה מפענחת אירועים עם csi.IOCTL_GENX320_READ_EVENTS ומזרימה כל שורה כ-12 בתים על גבי USB ([0] סוג, [1] sec, [2] ms, [3] us, [4] x, [5] y). קל לצריכה במחשב מכיוון שפורמט החיווט תואם לפורמט ה-ndarray שעל-המצלמה.

  • מצב גולמי (genx320_raw_event_mode_streaming_on_cam.py) — המצלמה מזרימה את מילות האירוע ה-32-סיביות הארוזות הטבעיות של השבב דרך csi.IOCTL_GENX320_READ_EVENTS_RAW. זה 4 בתים לכל אירוע לעומת 12 במצב מעובד (פחות נתונים פי ~3 על גבי USB), כך שקצב אירועים בר-השגה גבוה פי ~3 כאשר הקישור הוא צוואר הבקבוק. המחשב מפענח את המילים הארוזות בחזרה לאותה פריסת אירוע בת 6 עמודות באמצעות numpy מווקטר, כך שקוד הויזואלייזר במורד הזרם זהה.

מצב גולמי הוא ברירת המחדל ב-GUI מכיוון שתפוקת ה-USB היא האילוץ הכובל בקצבים שה-GenX320 יכול לייצר; עברו למצב מעובד אם אתם זקוקים לחבר לוגיקת עיבוד אל הסקריפט שעל-המצלמה.