From c231f66555780b69b00e43f58f00ce2d4b3fb1fb Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Thu, 3 Jul 2025 18:39:17 -0700 Subject: [PATCH] drivers/sensors: Add different event buffer sizes for genx320. --- drivers/sensors/genx320.c | 30 +++++---- modules/py_csi_ng.c | 10 ++- .../02-Genx320/genx320_event_mode.py | 3 +- .../genx320_event_mode_calibration.py | 3 +- .../genx320_event_mode_deep_buffer.py | 56 ++++++++++++++++ ...enx320_event_mode_deep_buffer_filtering.py | 64 +++++++++++++++++++ .../genx320_event_mode_filtering.py | 3 +- .../genx320_event_mode_high_speed.py | 3 +- ...genx320_event_mode_high_speed_filtering.py | 3 +- .../genx320_event_mode_long_exposure.py | 7 +- ...x320_event_mode_long_exposure_filtering.py | 7 +- 11 files changed, 165 insertions(+), 24 deletions(-) create mode 100644 scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_deep_buffer.py create mode 100644 scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_deep_buffer_filtering.py diff --git a/drivers/sensors/genx320.c b/drivers/sensors/genx320.c index 4af8bdf76..bfe277ab3 100644 --- a/drivers/sensors/genx320.c +++ b/drivers/sensors/genx320.c @@ -88,7 +88,7 @@ typedef struct genx_state { static genx_state_t genx = {}; -static int set_active_mode(omv_csi_t *csi, genx_mode_t mode); +static int set_active_mode(omv_csi_t *csi, genx_mode_t mode, int framesize); static int reset(omv_csi_t *csi) { genx_state_t *genx = csi->priv; @@ -99,7 +99,7 @@ static int reset(omv_csi_t *csi) { genx->event_time_us = 0; // Set histogram mode by default. - if (set_active_mode(csi, OMV_CSI_GENX320_MODE_HISTO)) { + if (set_active_mode(csi, OMV_CSI_GENX320_MODE_HISTO, OMV_CSI_FRAMESIZE_320X320)) { return OMV_CSI_ERROR_CSI_INIT_FAILED; } @@ -153,7 +153,7 @@ static int set_framesize(omv_csi_t *csi, omv_csi_framesize_t framesize) { if (genx->mode == OMV_CSI_GENX320_MODE_HISTO) { return (framesize == OMV_CSI_FRAMESIZE_320X320) ? 0 : -1; } else { - return (framesize == OMV_CSI_FRAMESIZE_128X64) ? 0 : -1; + return (framesize == OMV_CSI_FRAMESIZE_CUSTOM) ? 0 : -1; } } @@ -420,7 +420,7 @@ static int ioctl(omv_csi_t *csi, int request, va_list ap) { int mode = va_arg(ap, int); if (mode == OMV_CSI_GENX320_MODE_HISTO) { - if ((ret = set_active_mode(csi, OMV_CSI_GENX320_MODE_HISTO))) { + if ((ret = set_active_mode(csi, OMV_CSI_GENX320_MODE_HISTO, OMV_CSI_FRAMESIZE_320X320))) { break; } @@ -432,17 +432,25 @@ static int ioctl(omv_csi_t *csi, int request, va_list ap) { break; } } else if (mode == OMV_CSI_GENX320_MODE_EVENT) { - if ((ret = set_active_mode(csi, OMV_CSI_GENX320_MODE_EVENT))) { + size_t ndarray_size = va_arg(ap, size_t); + + if (ndarray_size < 1024 || ndarray_size > 65536 || (ndarray_size & (ndarray_size - 1))) { + ret = -1; + break; + } + + resolution[OMV_CSI_FRAMESIZE_CUSTOM][0] = 1024; + resolution[OMV_CSI_FRAMESIZE_CUSTOM][1] = ndarray_size >> 8; + + if ((ret = set_active_mode(csi, OMV_CSI_GENX320_MODE_EVENT, OMV_CSI_FRAMESIZE_CUSTOM))) { break; } - // 1 byte per pixel. if ((ret = omv_csi_set_pixformat(csi, PIXFORMAT_GRAYSCALE))) { break; } - // 128*64 bytes / 4 bytes per event = 2048 events. - if ((ret = omv_csi_set_framesize(csi, OMV_CSI_FRAMESIZE_128X64))) { + if ((ret = omv_csi_set_framesize(csi, OMV_CSI_FRAMESIZE_CUSTOM))) { break; } } else { @@ -607,7 +615,7 @@ static int post_process_event(omv_csi_t *csi, image_t *image, uint32_t flags) { return valid_count; } -static int set_active_mode(omv_csi_t *csi, genx_mode_t mode) { +static int set_active_mode(omv_csi_t *csi, genx_mode_t mode, int framesize) { genx_state_t *genx = csi->priv; if (genx->issd) { @@ -638,8 +646,8 @@ static int set_active_mode(omv_csi_t *csi, genx_mode_t mode) { } // Configure Packet and Frame sizes - uint32_t packet_width = (mode == OMV_CSI_GENX320_MODE_EVENT) ? 128 : ACTIVE_SENSOR_WIDTH; - uint32_t packet_height = (mode == OMV_CSI_GENX320_MODE_EVENT) ? 64 : ACTIVE_SENSOR_HEIGHT; + uint32_t packet_width = resolution[framesize][0]; + uint32_t packet_height = resolution[framesize][1]; uint32_t packet_hsync = (mode == OMV_CSI_GENX320_MODE_EVENT) ? EVENT_HSYNC_CLOCK_CYCLES : HISTO_HSYNC_CLOCK_CYCLES; diff --git a/modules/py_csi_ng.c b/modules/py_csi_ng.c index 565b346ab..67e05e0e5 100644 --- a/modules/py_csi_ng.c +++ b/modules/py_csi_ng.c @@ -1196,6 +1196,8 @@ static mp_obj_t py_csi_ioctl(size_t n_args, const mp_obj_t *args) { case OMV_CSI_IOCTL_GENX320_SET_MODE: { if (n_args == 1) { error = omv_csi_ioctl(self->csi, request, mp_obj_get_int(args[0])); + } else if (n_args == 2) { + error = omv_csi_ioctl(self->csi, request, mp_obj_get_int(args[0]), mp_obj_get_int(args[1])); } break; } @@ -1207,11 +1209,15 @@ static mp_obj_t py_csi_ioctl(size_t n_args, const mp_obj_t *args) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Expected a ndarray with dtype uint16")); } + uint32_t expected_len = resolution[self->csi->framesize][0] * + (resolution[self->csi->framesize][1] / sizeof(uint32_t)); + if (!(ndarray_is_dense(array) && (array->ndim == 2) && - (array->shape[ULAB_MAX_DIMS - 2] == 2048) && + (array->shape[ULAB_MAX_DIMS - 2] == expected_len) && (array->shape[ULAB_MAX_DIMS - 1] == EC_EVENT_SIZE))) { mp_raise_msg_varg(&mp_type_ValueError, - MP_ERROR_TEXT("Expected a dense ndarray with shape (2048, %d)"), EC_EVENT_SIZE); + MP_ERROR_TEXT("Expected a dense ndarray with shape (%d, %d)"), + expected_len, EC_EVENT_SIZE); } error = omv_csi_ioctl(self->csi, request, array->array); diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode.py index 1bf0d0119..947ab28b9 100644 --- a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode.py +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode.py @@ -16,6 +16,7 @@ img = image.Image(320, 320, image.GRAYSCALE) # Stores camera events # Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. # Columns: # [0] Event type # [1] Seconds timestamp @@ -28,7 +29,7 @@ events = np.zeros((2048, 6), dtype=np.uint16) # Initialize the sensor. csi0 = csi.CSI(cid=csi.GENX320) csi0.reset() -csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT) +csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0]) clock = time.clock() diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_calibration.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_calibration.py index 7be8cee4d..fee4764a4 100644 --- a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_calibration.py +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_calibration.py @@ -20,6 +20,7 @@ img = image.Image(320, 320, image.GRAYSCALE) # Stores camera events # Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. # Columns: # [0] Event type # [1] Seconds timestamp @@ -32,7 +33,7 @@ events = np.zeros((2048, 6), dtype=np.uint16) # Initialize the sensor. csi0 = csi.CSI(cid=csi.GENX320) csi0.reset() -csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT) +csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0]) clock = time.clock() diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_deep_buffer.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_deep_buffer.py new file mode 100644 index 000000000..405447e10 --- /dev/null +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_deep_buffer.py @@ -0,0 +1,56 @@ +# This work is licensed under the MIT license. +# Copyright (c) 2013-2025 OpenMV LLC. All rights reserved. +# https://github.com/openmv/openmv/blob/master/LICENSE +# +# This example shows off using the genx320 event camera from Prophesee +# using event streaming mode with a very deep event buffer for capturing events. + +import csi +import image +import time +# https://micropython-ulab.readthedocs.io/en/latest/index.html +from ulab import numpy as np + +# Surface to draw the histogram image on. +img = image.Image(320, 320, image.GRAYSCALE) + +# Stores camera events +# Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. +# Columns: +# [0] Event type +# [1] Seconds timestamp +# [2] Milliseconds timestamp +# [3] Microseconds timestamp +# [4] X coordinate 0 to 319 for GENX320 +# [5] Y coordinate 0 to 319 for GENX320 +events = np.zeros((32768, 6), dtype=np.uint16) + +# Initialize the sensor. +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() + + # Reads up to 32768 events from the camera. + # Returns the number of valid events (0-32768) or a negative error code. + # Note that old events in the buffer are not cleared to save CPU time. + event_count = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events) + + # Render events into a histogram image. + # If clear=True, the image is reset to "brightness" before drawing. + # For each PIX_ON_EVENT, add "contrast" to the bin value; + # for each PIX_OFF_EVENT, subtract it and clamp to [0, 255]. + # If clear=False, histogram accumulates over multiple calls. + img.draw_event_histogram(events[:event_count], clear=True, brightness=128, contrast=16) + + # Push the image to the jpeg buffer for the IDE to pull and display. + # The IDE pulls frames off the camera at a much lower rate than the + # onboard camera frame rate printed below. + img.flush() + + print(event_count, clock.fps()) diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_deep_buffer_filtering.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_deep_buffer_filtering.py new file mode 100644 index 000000000..ab8c790a8 --- /dev/null +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_deep_buffer_filtering.py @@ -0,0 +1,64 @@ +# This work is licensed under the MIT license. +# Copyright (c) 2013-2025 OpenMV LLC. All rights reserved. +# https://github.com/openmv/openmv/blob/master/LICENSE +# +# This example shows off using the genx320 event camera from Prophesee +# using event streaming mode with a very deep event buffer for capturing events. +# +# Adds filtering of events based on the event type (PIX_ON_EVENT or PIX_OFF_EVENT). + +import csi +import image +import time +# https://micropython-ulab.readthedocs.io/en/latest/index.html +from ulab import numpy as np + +TARGET_EVENT_TYPE = csi.PIX_ON_EVENT # Change to PIX_OFF_EVENT to filter low events. + +# Surface to draw the histogram image on. +img = image.Image(320, 320, image.GRAYSCALE) + +# Stores camera events +# Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. +# Columns: +# [0] Event type +# [1] Seconds timestamp +# [2] Milliseconds timestamp +# [3] Microseconds timestamp +# [4] X coordinate 0 to 319 for GENX320 +# [5] Y coordinate 0 to 319 for GENX320 +events = np.zeros((32768, 6), dtype=np.uint16) + +# Initialize the sensor. +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() + + # Reads up to 32768 events from the camera. + # Returns the number of valid events (0-32768) or a negative error code. + # Note that old events in the buffer are not cleared to save CPU time. + event_count = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events) + events_slice = events[:event_count] + indices = np.nonzero(events_slice[:, 0] == TARGET_EVENT_TYPE)[0] + if len(indices): + target_events = np.take(events_slice, indices, axis=0) + + # Render events into a histogram image. + # If clear=True, the image is reset to "brightness" before drawing. + # For each PIX_ON_EVENT, add "contrast" to the bin value; + # for each PIX_OFF_EVENT, subtract it and clamp to [0, 255]. + # If clear=False, histogram accumulates over multiple calls. + img.draw_event_histogram(target_events, clear=True, brightness=128, contrast=16) + + # Push the image to the jpeg buffer for the IDE to pull and display. + # The IDE pulls frames off the camera at a much lower rate than the + # onboard camera frame rate printed below. + img.flush() + + print(len(target_events), clock.fps()) diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_filtering.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_filtering.py index 3d11baf81..297b1de06 100644 --- a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_filtering.py +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_filtering.py @@ -20,6 +20,7 @@ img = image.Image(320, 320, image.GRAYSCALE) # Stores camera events # Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. # Columns: # [0] Event type # [1] Seconds timestamp @@ -32,7 +33,7 @@ events = np.zeros((2048, 6), dtype=np.uint16) # Initialize the sensor. csi0 = csi.CSI(cid=csi.GENX320) csi0.reset() -csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT) +csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0]) clock = time.clock() diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_high_speed.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_high_speed.py index bbf7534dc..61a4b87c0 100644 --- a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_high_speed.py +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_high_speed.py @@ -14,6 +14,7 @@ from ulab import numpy as np # Stores camera events # Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. # Columns: # [0] Event type # [1] Seconds timestamp @@ -26,7 +27,7 @@ events = np.zeros((2048, 6), dtype=np.uint16) # Initialize the sensor. csi0 = csi.CSI(cid=csi.GENX320) csi0.reset() -csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT) +csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0]) clock = time.clock() t = time.ticks_us() diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_high_speed_filtering.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_high_speed_filtering.py index 818e314f0..041b8f64c 100644 --- a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_high_speed_filtering.py +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_high_speed_filtering.py @@ -18,6 +18,7 @@ TARGET_EVENT_TYPE = csi.PIX_ON_EVENT # Change to PIX_OFF_EVENT to filter low ev # Stores camera events # Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. # Columns: # [0] Event type # [1] Seconds timestamp @@ -30,7 +31,7 @@ events = np.zeros((2048, 6), dtype=np.uint16) # Initialize the sensor. csi0 = csi.CSI(cid=csi.GENX320) csi0.reset() -csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT) +csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0]) clock = time.clock() t = time.ticks_us() diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_long_exposure.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_long_exposure.py index 48f6b0a70..628f30edf 100644 --- a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_long_exposure.py +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_long_exposure.py @@ -11,13 +11,14 @@ import time # https://micropython-ulab.readthedocs.io/en/latest/index.html from ulab import numpy as np -EXPOSURE_FRAMES = 10 +EXPOSURE_FRAMES = 30 # Surface to draw the histogram image on. img = image.Image(320, 320, image.GRAYSCALE) # Stores camera events # Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. # Columns: # [0] Event type # [1] Seconds timestamp @@ -25,12 +26,12 @@ img = image.Image(320, 320, image.GRAYSCALE) # [3] Microseconds timestamp # [4] X coordinate 0 to 319 for GENX320 # [5] Y coordinate 0 to 319 for GENX320 -events = np.zeros((2048, 6), dtype=np.uint16) +events = np.zeros((32768, 6), dtype=np.uint16) # Initialize the sensor. csi0 = csi.CSI(cid=csi.GENX320) csi0.reset() -csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT) +csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0]) clock = time.clock() i = 0 diff --git a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_long_exposure_filtering.py b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_long_exposure_filtering.py index 211295fdf..93432a3c6 100644 --- a/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_long_exposure_filtering.py +++ b/scripts/examples/01-Camera/03-Event-Cameras/02-Genx320/genx320_event_mode_long_exposure_filtering.py @@ -15,13 +15,14 @@ from ulab import numpy as np TARGET_EVENT_TYPE = csi.PIX_ON_EVENT # Change to PIX_OFF_EVENT to filter low events. -EXPOSURE_FRAMES = 10 +EXPOSURE_FRAMES = 30 # Surface to draw the histogram image on. img = image.Image(320, 320, image.GRAYSCALE) # Stores camera events # Shape: (EVT_res, 6) where EVT_res is the event resolution +# EVT_res: must be a power of two between 1024 and 65536. # Columns: # [0] Event type # [1] Seconds timestamp @@ -34,7 +35,7 @@ events = np.zeros((32768, 6), dtype=np.uint16) # Initialize the sensor. csi0 = csi.CSI(cid=csi.GENX320) csi0.reset() -csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT) +csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0]) clock = time.clock() i = 0 @@ -59,7 +60,7 @@ while True: # For each PIX_ON_EVENT, add "contrast" to the bin value; # for each PIX_OFF_EVENT, subtract it and clamp to [0, 255]. # If clear=False, histogram accumulates over multiple calls. - img.draw_event_histogram(target_events, clear=c, brightness=128, contrast=64) + img.draw_event_histogram(target_events, clear=c, brightness=128, contrast=16) # Push the image to the jpeg buffer for the IDE to pull and display. # The IDE pulls frames off the camera at a much lower rate than the