Событийная камера GENX320¶
Модуль событийной камеры GENX320 — это событийный датчик зрения Prophesee с разрешением 320x320 и микросекундной временной точностью.
Полный datasheet, фотографии и информацию для заказа см. на странице продукта GENX320 Event Camera.
Примечание
Поддерживается на OpenMV H7 Plus, RT1062 и N6.
Ключевые особенности¶
Событийный датчик зрения 320x320
Динамический диапазон 140 дБ, отсутствие смазывания при движении
Скорость вывода событийных гистограмм от 375 Гц
Энергопотребление масштабируется с активностью сцены — начинается с ~3 мВт
Работает от <5 люкс до яркого солнечного света без автоэкспозиции
Выводит кадры в оттенках серого или необработанные потоки событий
Использование¶
GENX320 — это событийный датчик зрения: вместо считывания всего массива 320x320 по фиксированному кадровому тактовому сигналу каждый пиксель сообщает об асинхронных «событиях» в момент обнаружения изменения яркости. Каждое событие несёт координату X/Y, полярность ON/OFF (светлее→темнее или темнее→светлее) и микросекундную метку времени. Отсюда и берутся микросекундная временная точность датчика, отсутствие смазывания при движении, очень высокий динамический диапазон и масштабируемое по активности энергопотребление. Статичные сцены не генерируют данных.
Прошивка OpenMV предоставляет доступ к GENX320 через csi.CSI с cid= csi.GENX320. Доступны два режима работы:
Режим гистограммы (по умолчанию) — события накапливаются на кристалле в бины по пикселям и выдаются как кадр 320x320 в оттенках серого с настраиваемой частотой (~20-350 FPS). Датчик ведёт себя как обычная камера, поэтому все стандартные процедуры обработки изображений (
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)¶
Периодические источники света (люминесцентные лампы, дисплеи со светодиодной подсветкой) генерируют огромные объёмы избыточных событий. Фильтр 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 — значения по умолчанию из datasheet выдают намного более высокую частоту фоновых событий, поэтому в качестве отправной точки используется LOW_NOISE, чтобы поток оставался тихим. Вызовите csi.IOCTL_GENX320_SET_BIASES с другой предустановкой, когда приложению нужна большая чувствительность или полоса пропускания.
csi.IOCTL_GENX320_SET_BIASES применяет одну из пяти предустановок:
csi.GENX320_BIASES_DEFAULT— значения по умолчанию из datasheet GenX320. Сбалансированные чувствительность, шум и полоса пропускания для общих сцен.csi.GENX320_BIASES_LOW_LIGHT— оба порога контраста ослаблены для повышения чувствительности, FO понижен для снижения шума, а HPF установлен в 0, чтобы медленные изменения яркости всё ещё регистрировались — слабоосвещённая сцена сама по себе генерирует мало событий, поэтому мы хотим, чтобы их прошло как можно больше.csi.GENX320_BIASES_ACTIVE_MARKER— настроена для отслеживания мигающих светодиодов с высоким контрастом. Пороги контраста повышены, чтобы срабатывали только резкие переходы; FO и HPF подняты до максимума, чтобы максимизировать полосу пропускания пикселя и отклонить любой медленный дрейф окружающего освещения; REFR установлен в 0, чтобы каждый фронт мигания захватывался один за другим. В результате получается поток, состоящий почти полностью из фронтов светодиода, который легко отслеживать.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)
Отслеживание¶
Поскольку вывод в режиме гистограммы — это просто изображение в оттенках серого, обычное отслеживание блобов работает напрямую. Чтобы отследить светодиод активного маркера, загрузите предустановку смещений активного маркера и найдите блобы в ярком конце гистограммы:
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, затем войдите в событийный режим через 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
Высокоскоростная обработка¶
Отбросьте визуализацию, чтобы освободить процессор для обработки событий. Печатайте статистику только на каждой N-й итерации — вывод строки print на каждой итерации становится узким местом при высоких частотах событий:
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(мс). Если новое событие на пикселе приходит в пределахstc_thresholdот предыдущего события, оно считается «вторым» во всплеске и передаётся — первое событие и любые последующие события в том же всплеске отфильтровываются. Лучше всего, когда вам нужен подтверждённый шумом переход, а не самое первое срабатывание.csi.GENX320_STC_TRAIL_ONLY— сохраняет первое событие всплеска. Параметр:trail_threshold(мс). После срабатывания пикселя последующие события на том же пикселе отбрасываются, пока не истечётtrail_threshold. Сохраняет точное время ведущего фронта — полезно, когда момент переключения полярности важнее подтверждения всплеска.csi.GENX320_STC_TRAIL— объединяет оба. Параметры:stc_thresholdиtrail_threshold(оба в мс). Сохраняет ведущий фронт по режиму 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 Event Streaming в репозитории openmv-projects соединяет камеру с фронтендом DearPyGui. GUI на ПК запускает две визуализации бок о бок: холст накопления событий (та же идея, что и Image.draw_event_histogram, но с выбираемыми палитрами и режимами скользящего окна или автоочистки) и карту частот по пикселям, управляемую полосовым IIR-фильтром — полезно для обнаружения периодических сигналов (вращающиеся вентиляторы, мигающие светодиоды и т. д.) прямо в потоке событий.
Он поставляется с двумя скриптами потоковой передачи на камере:
Обработанный режим (
genx320_event_mode_streaming_on_cam.py) — камера декодирует события с помощьюcsi.IOCTL_GENX320_READ_EVENTSи передаёт каждую строку как 12 байт по USB ([0]тип,[1]сек,[2]мс,[3]мкс,[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; переключитесь на обработанный режим, если вам нужно встроить логику обработки в скрипт на камере.