mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
drivers/sensors: Add calibration ioctl for the genx320.
This commit is contained in:
parent
b200ec4b1f
commit
3908807509
@ -236,6 +236,7 @@ typedef enum {
|
||||
OMV_CSI_IOCTL_GENX320_SET_AFK = 0x22,
|
||||
OMV_CSI_IOCTL_GENX320_SET_MODE = 0x23,
|
||||
OMV_CSI_IOCTL_GENX320_READ_EVENTS = 0x24,
|
||||
OMV_CSI_IOCTL_GENX320_CALIBRATE = 0x25,
|
||||
OMV_CSI_IOCTL_UPDATE_AGC_AEC = 0x7F
|
||||
} omv_csi_ioctl_t;
|
||||
|
||||
|
@ -233,6 +233,68 @@ static int set_vflip(omv_csi_t *csi, int enable) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_hot_pixels(omv_csi_t *csi, uint8_t *histogram, float sigma) {
|
||||
// Compute average
|
||||
int32_t avg = 0;
|
||||
|
||||
for (uint32_t i = 0; i < ACTIVE_SENSOR_SIZE; i++) {
|
||||
avg += histogram[i];
|
||||
}
|
||||
|
||||
avg /= ACTIVE_SENSOR_SIZE;
|
||||
|
||||
// Compute std
|
||||
int64_t std = 0;
|
||||
|
||||
for (uint32_t i = 0; i < ACTIVE_SENSOR_SIZE; i++) {
|
||||
int32_t diff = histogram[i] - avg;
|
||||
std += diff * diff;
|
||||
}
|
||||
|
||||
std = fast_sqrtf(std / ((float) ACTIVE_SENSOR_SIZE));
|
||||
|
||||
int32_t threshold = fast_roundf(avg + (std * sigma));
|
||||
int ret = 0;
|
||||
|
||||
for (uint32_t y = 0; y < ACTIVE_SENSOR_HEIGHT; y++) {
|
||||
// Reset all blocks
|
||||
for (uint32_t i = 0; i < (ACTIVE_SENSOR_WIDTH / UINT32_T_BITS); i++) {
|
||||
psee_write_ROI_X(csi, i * sizeof(uint32_t), 0);
|
||||
}
|
||||
|
||||
// Select line
|
||||
uint32_t offset = y / UINT32_T_BITS;
|
||||
psee_write_ROI_Y(csi, offset * sizeof(uint32_t), 1 << (y % UINT32_T_BITS));
|
||||
|
||||
// Trigger shadow
|
||||
psee_write_ROI_CTRL(csi, ROI_CTRL_PX_SW_RSTN | ROI_CTRL_TD_SHADOW_TRIGGER);
|
||||
|
||||
uint32_t tmp[ACTIVE_SENSOR_WIDTH / UINT32_T_BITS] = {};
|
||||
for (uint32_t x = 0; x < ACTIVE_SENSOR_WIDTH; x++) {
|
||||
if (histogram[(y * ACTIVE_SENSOR_WIDTH) + x] > threshold) {
|
||||
tmp[x / UINT32_T_BITS] |= 1 << (x % UINT32_T_BITS);
|
||||
ret += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write x values to disable
|
||||
for (uint32_t i = 0; i < (ACTIVE_SENSOR_WIDTH / UINT32_T_BITS); i++) {
|
||||
psee_write_ROI_X(csi, i * sizeof(uint32_t), tmp[i]);
|
||||
}
|
||||
|
||||
// Activate block
|
||||
psee_write_ROI_CTRL(csi, ROI_CTRL_PX_SW_RSTN | ROI_CTRL_TD_SHADOW_TRIGGER | ROI_CTRL_TD_EN);
|
||||
|
||||
// Disable roi block
|
||||
psee_write_ROI_CTRL(csi, ROI_CTRL_PX_SW_RSTN);
|
||||
psee_write_ROI_Y(csi, offset * sizeof(uint32_t), 0);
|
||||
|
||||
mp_printf(MP_PYTHON_PRINTER, "CSI: Calibrating - %d%%\n", ((y * 50) / ACTIVE_SENSOR_HEIGHT) + 50);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ioctl(omv_csi_t *csi, int request, va_list ap) {
|
||||
genx_state_t *genx = csi->priv;
|
||||
int ret = 0;
|
||||
@ -403,6 +465,69 @@ static int ioctl(omv_csi_t *csi, int request, va_list ap) {
|
||||
ret = omv_csi_snapshot(csi, &image, 0);
|
||||
break;
|
||||
}
|
||||
case OMV_CSI_IOCTL_GENX320_CALIBRATE: {
|
||||
uint32_t event_count = va_arg(ap, uint32_t);
|
||||
float sigma = va_arg(ap, double);
|
||||
|
||||
if (omv_csi_get_cropped(csi)) {
|
||||
return OMV_CSI_ERROR_CAPTURE_FAILED;
|
||||
}
|
||||
|
||||
if (csi->transpose) {
|
||||
return OMV_CSI_ERROR_CAPTURE_FAILED;
|
||||
}
|
||||
|
||||
uint8_t *histogram = fb_alloc0(ACTIVE_SENSOR_SIZE, FB_ALLOC_NO_HINT);
|
||||
|
||||
// Collect events to calibrate hot pixels.
|
||||
for (uint32_t i = 0; i < event_count; ) {
|
||||
// Print something to prevent the user from thinking the camera is stuck.
|
||||
mp_printf(MP_PYTHON_PRINTER, "CSI: Calibrating - %d%%\n", ((i * 50) / event_count));
|
||||
|
||||
image_t image;
|
||||
ret = omv_csi_snapshot(csi, &image, OMV_CSI_FLAG_NO_POST);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (genx->mode == OMV_CSI_GENX320_MODE_HISTO) {
|
||||
// Invalidate frame.
|
||||
csi->fb->pixfmt = PIXFORMAT_INVALID;
|
||||
|
||||
for (uint32_t j = 0; j < ACTIVE_SENSOR_SIZE; j++) {
|
||||
uint32_t val = abs(((int8_t *) image.data)[j]);
|
||||
histogram[j] = __USAT(histogram[j] + val, UINT8_T_BITS);
|
||||
i += val;
|
||||
}
|
||||
} else {
|
||||
uint32_t len = resolution[csi->framesize][0] *
|
||||
(resolution[csi->framesize][1] / sizeof(uint32_t));
|
||||
for (uint32_t j = 0; j < len; j++) {
|
||||
uint32_t val = ((uint32_t *) image.data)[j];
|
||||
switch (__EVT20_TYPE(val)) {
|
||||
case TD_LOW:
|
||||
case TD_HIGH: {
|
||||
uint32_t x = __EVT20_X(val);
|
||||
uint32_t y = __EVT20_Y(val);
|
||||
if ((x < ACTIVE_SENSOR_WIDTH) && (y < ACTIVE_SENSOR_HEIGHT)) {
|
||||
uint32_t index = (y * ACTIVE_SENSOR_WIDTH) + x;
|
||||
histogram[index] = __USAT(histogram[index] + 1, UINT8_T_BITS);
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = disable_hot_pixels(csi, histogram, sigma);
|
||||
fb_free();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ret = -1;
|
||||
break;
|
||||
|
@ -1221,6 +1221,16 @@ static mp_obj_t py_csi_ioctl(size_t n_args, const mp_obj_t *args) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OMV_CSI_IOCTL_GENX320_CALIBRATE: {
|
||||
if (n_args == 2) {
|
||||
error = omv_csi_ioctl(self->csi, request, mp_obj_get_int(args[0]),
|
||||
(double) mp_obj_get_float(args[1]));
|
||||
if (error > 0) {
|
||||
ret_obj = mp_obj_new_int(error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif // (OMV_GENX320_ENABLE == 1)
|
||||
|
||||
default: {
|
||||
@ -1557,6 +1567,7 @@ static const mp_rom_map_elem_t globals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_GENX320_MODE_HISTO), MP_ROM_INT(OMV_CSI_GENX320_MODE_HISTO) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_GENX320_MODE_EVENT), MP_ROM_INT(OMV_CSI_GENX320_MODE_EVENT) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_IOCTL_GENX320_READ_EVENTS), MP_ROM_INT(OMV_CSI_IOCTL_GENX320_READ_EVENTS)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_IOCTL_GENX320_CALIBRATE), MP_ROM_INT(OMV_CSI_IOCTL_GENX320_CALIBRATE)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_PIX_OFF_EVENT), MP_ROM_INT(EC_PIX_OFF_EVENT)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_PIX_ON_EVENT), MP_ROM_INT(EC_PIX_ON_EVENT)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_RST_TRIGGER_RISING), MP_ROM_INT(EC_RST_TRIGGER_RISING)},
|
||||
|
@ -0,0 +1,37 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2024 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# This example shows off hot pixel calibration
|
||||
# using the genx320 event camera from Prophesee.
|
||||
|
||||
import csi
|
||||
import time
|
||||
|
||||
csi0 = csi.CSI(cid=csi.GENX320)
|
||||
csi0.reset()
|
||||
csi0.pixformat(csi.GRAYSCALE) # Must always be grayscale.
|
||||
csi0.framesize(csi.B320X320) # Must always be 320x320.
|
||||
csi0.brightness(128) # Leave at 128 generally (this is the default).
|
||||
csi0.contrast(16) # Increase to make the image pop.
|
||||
|
||||
# The default frame rate is 50 FPS. You can change it between ~20 FPS and ~350 FPS.
|
||||
csi0.framerate(50)
|
||||
|
||||
# Show uncalibrated image first.
|
||||
csi0.snapshot(time=5000)
|
||||
|
||||
CAL_EVENT_COUNT = 10000 # Number of events to collect for calibration.
|
||||
CAL_SIGMA = 0.5 # Standard deviation for hot pixel detection.
|
||||
disabled_pixels = csi0.ioctl(csi.IOCTL_GENX320_CALIBRATE, CAL_EVENT_COUNT, CAL_SIGMA)
|
||||
print(f'Disabled {disabled_pixels} hot pixels.')
|
||||
|
||||
clock = time.clock()
|
||||
|
||||
while True:
|
||||
clock.tick()
|
||||
|
||||
img = csi0.snapshot()
|
||||
# img.median(1) # noise cleanup.
|
||||
|
||||
print(clock.fps())
|
@ -0,0 +1,66 @@
|
||||
# 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 and calibrating the camera.
|
||||
|
||||
import csi
|
||||
import image
|
||||
import time
|
||||
|
||||
# https://micropython-ulab.readthedocs.io/en/latest/index.html
|
||||
from ulab import numpy as np
|
||||
|
||||
CAL_EVENT_COUNT = 10000 # Number of events to collect for calibration.
|
||||
CAL_SIGMA = 0.5 # Standard deviation for hot pixel detection.
|
||||
|
||||
# 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
|
||||
# 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((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)
|
||||
|
||||
clock = time.clock()
|
||||
|
||||
t = time.ticks_ms()
|
||||
calibrated = False
|
||||
while True:
|
||||
clock.tick()
|
||||
|
||||
# Reads 2048 events from the camera.
|
||||
# Returns the number of valid events (0-2048) or a negative error code.
|
||||
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=64)
|
||||
|
||||
# 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()
|
||||
|
||||
# Show uncalibrated image first.
|
||||
if not calibrated and time.ticks_diff(time.ticks_ms(), t) > 5000:
|
||||
disabled_pixels = csi0.ioctl(csi.IOCTL_GENX320_CALIBRATE, CAL_EVENT_COUNT, CAL_SIGMA)
|
||||
print(f'Disabled {disabled_pixels} hot pixels.')
|
||||
calibrated = True
|
||||
|
||||
print(event_count, clock.fps())
|
Loading…
Reference in New Issue
Block a user