كاميرا الأحداث متعددة الأطياف¶
تجمع وحدة كاميرا الأحداث متعددة الأطياف بين مستشعر الأحداث GENX320 ومستشعر ألوان من نوع PAG7936 بدقة 1 ميجابكسل ومصراع شامل (global-shutter) على وحدة واحدة — مسار معالجة متزامن للأحداث والألوان لتتبع الأجسام عالي السرعة، وتتبع LED، وتدفق السوائل، وغيرها من المشاهد الديناميكية.
للاطلاع على ورقة البيانات الكاملة والصور وطلب الشراء راجع صفحة منتج كاميرا الأحداث متعددة الأطياف.
ملاحظة
مدعومة على OpenMV N6 فقط.
أبرز الميزات¶
مستشعر أحداث 320x320، نطاق ديناميكي يزيد عن 140 dB، مدرجات تكرارية بمعدل 375 Hz أو أكثر
ألوان PAG7936: 1280x800 عند 120 FPS، و640x400 عند 240 FPS
طوابع زمنية متزامنة للأحداث مع مشغّل تعريض مشترك
يرى تحت 5 لكس دون تعريض تلقائي
تبدأ الطاقة عند نحو 3 mW لبث الأحداث
موجّهة للتتبع عالي السرعة، وتتبع LED، وتدفق السوائل/الجسيمات
الاستخدام¶
يحصل كل من مستشعر الألوان ومستشعر الأحداث GENX320 على نسخة csi.CSI خاصة به. يفترض الاستدعاء الأول المستشعر الأساسي افتراضيًا (وهو PAG7936)؛ بينما يرتبط الاستدعاء الثاني بـ GENX320 عبر تمرير cid= csi.GENX320. أعد ضبط مستشعر الألوان بإعادة ضبط صلبة عبر csi.CSI.reset (hard=True) لتشغيل خط الطاقة، واضبط GENX320 بـ hard=False بحيث يعيد المشغّل برمجة الشريحة فقط دون إعادة تفعيل إعادة الضبط.
يُخرج GENX320 دقة 320x320 في وضع المدرج التكراري؛ بينما يُخرج PAG7936 عند csi.QVGA دقة 320x200. تقتطع الطبقة الفوقية الأساسية أدناه الصفوف الـ 120 السفلية من إطار GENX320. استخدم تحويل التماثل (الهوموجرافي) (أدناه) للحصول على طبقة فوقية مُلائَمة أو حجم إطار أكبر لـ PAG7936.
يبقى مخزنان مؤقتان مساعدان ثابتين عبر حلقة الإطار — لوحة ألفا بأبعاد 256x1 مخزّنة كـ image.Image بحيث تصبح بكسلات المدرج التكراري عند خط الأساس للرمادي المتوسط (128) شفافة وتصبح كل من الإبرازات الناتجة عن أحداث ON والظلال الناتجة عن أحداث OFF معتمة، ومخزن إطارات 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())
يلتقط كل تكرار لقطة ألوان حاجبة ولقطة GENX320 غير حاجبة. ثم تقوم Image.draw_image بدمج الاثنين معًا: تُحوّل color_palette= image.PALETTE_EVT_LIGHT (أو image.PALETTE_EVT_DARK لخلفية داكنة) المدرج التكراري الرمادي لـ GENX320 إلى تدرّج لوني، وتمزج alpha_palette= كل بكسل باستخدام خريطة ألفا ذات الشكل V بحيث تمرّ المناطق الهادئة من المشهد إلى صورة الألوان، وتنعّم hint= image.BILINEAR عملية تكبير الصورة عندما يعمل مستشعر الألوان بدقة أعلى من GENX320.
تعمل أوامر ioctl الخاصة بإعدادات الانحياز المسبقة لـ GENX320، ومرشّح AFK، ومعايرة البكسلات الساخنة، ومرشّح STC جميعها بالطريقة نفسها في إعداد الكاميرا المزدوجة هذا — استدعِها على csi1 بعد csi.CSI.reset. راجع الأقسام أدناه للتفاصيل.
المحاذاة المسرّعة بوحدة معالجة الرسوميات (GPU)¶
تقبل Image.draw_image وسيطًا transform= — وهو مصفوفة تماثل (هوموجرافي) بأبعاد 3x3 على هيئة مصفوفة ulab.numpy ثنائية الأبعاد. على OpenMV N6 تنفّذ وحدة معالجة الرسوميات الإسقاط لكل بكسل أثناء عملية الرسم نفسها، لذا يمكن إعادة محاذاة إطار 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
إعدادات الانحياز المسبقة¶
يشغّل كل بكسل في 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)
التتبع¶
بما أن خرج وضع المدرج التكراري ما هو إلا صورة رمادية، فإن تتبع الكتل العادي يعمل مباشرة. لتتبع مصباح LED من نوع active-marker، حمّل إعداد انحياز 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())
وضع الأحداث¶
يتجاوز وضع الأحداث المدرج التكراري الموجود على الشريحة ويبثّ الأحداث الخام إلى 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— مشغّل إعادة ضبط البكسل، حافة هابطة. لا يُستخدم 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، ثم ادخل وضع الأحداث عبر 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())
تعمل أوامر ioctl الخاصة بإعدادات الانحياز المسبقة في وضع المدرج التكراري، ومرشّح AFK، ومعايرة البكسلات الساخنة جميعها بالطريقة نفسها في وضع الأحداث — استدعِها بعد 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
المعالجة عالية السرعة¶
أسقط التصوّر لتحرير وحدة المعالجة المركزية لمعالجة الأحداث. اطبع الإحصائيات كل تكرار رقم 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)
عمق المخزن المؤقت¶
عندما ترتفع معدلات الأحداث فجأة، يفضّل خط أنابيب المخزن المؤقت الثلاثي الافتراضي أحدث إطار ويتجاهل القديمة. ارفع عمق FIFO عبر csi.CSI.framebuffers لوضع الأحداث في طابور بدلًا من ذلك — على حساب معالجة بيانات أقدم قليلًا عندما يتأخر المضيف:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
البث والتصوّر على سطح المكتب¶
للتصوّر الرسومي في الوقت الفعلي على حاسوب مضيف، تجمع أداة بث أحداث GenX320 في مستودع openmv-projects بين الكاميرا وواجهة DearPyGui الأمامية. تشغّل الواجهة الرسومية على الحاسوب تصوّرين جنبًا إلى جنب: لوحة تراكم أحداث (الفكرة نفسها مثل Image.draw_event_histogram لكن مع لوحات لونية قابلة للاختيار وأوضاع نافذة منزلقة مقابل المسح التلقائي) وخريطة تردد لكل بكسل مدفوعة بمرشّح تمرير نطاقي من نوع IIR — مفيدة لرصد الإشارات الدورية (المراوح الدوّارة، مصابيح LED الوامضة، إلخ) مباشرة في تدفق الأحداث.
تأتي مع برنامجي بث نصيين يعملان على الكاميرا:
الوضع المعالَج (
genx320_event_mode_streaming_on_cam.py) — تفكّ الكاميرا ترميز الأحداث باستخدامcsi.IOCTL_GENX320_READ_EVENTSوتبثّ كل صف بهيئة 12 بايت عبر USB ([0]النوع،[1]ثانية،[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 أضعاف عندما يكون الرابط هو عنق الزجاجة. ويفكّ الحاسوب ترميز الكلمات المحزومة معيدًا إياها إلى تخطيط الأحداث ذي الأعمدة الستة نفسه باستخدام numpy المتجَّه، بحيث يكون كود المُصوِّر في الطرف اللاحق متطابقًا.
الوضع الخام هو الافتراضي في الواجهة الرسومية لأن إنتاجية USB هي القيد المُلزِم عند المعدلات التي يستطيع GenX320 إنتاجها؛ بدّل إلى الوضع المعالَج إذا احتجت إلى إدراج منطق معالجة في البرنامج النصي العامل على الكاميرا.