diff --git a/common/average.c b/common/average.c new file mode 100644 index 000000000..3e8819f44 --- /dev/null +++ b/common/average.c @@ -0,0 +1,81 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2013-2025 OpenMV, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Averaging utility functions. + */ +#include "average.h" +#include + +void rgb_moving_average_init(rgb_moving_average_t *ma, uint32_t max_ms, + size_t size, rgb_moving_average_val_t *buffer) { + memset(ma, 0, sizeof(rgb_moving_average_t)); + ma->size = size; + ma->max_ms = max_ms; + ma->buffer = buffer; +} + +void rgb_moving_average_update(rgb_moving_average_t *ma, + uint32_t *r_value, uint32_t *g_value, uint32_t *b_value, + uint32_t current_ms) { + if (!ma->size) { + return; + } + + // Drop if the buffer is full and all values older than max_ms (handles wrap-around correctly). + while (ma->count == ma->size || + (ma->count > 0 && (current_ms - ma->buffer[ma->head].timestamp_ms) > ma->max_ms)) { + rgb_moving_average_val_t *val = &ma->buffer[ma->head]; + ma->r_sum -= val->r_value; + ma->g_sum -= val->g_value; + ma->b_sum -= val->b_value; + ma->head = (ma->head + 1) % ma->size; + ma->count--; + } + + // Add the new value. + rgb_moving_average_val_t *val = &ma->buffer[ma->tail]; + val->r_value = *r_value; + val->g_value = *g_value; + val->b_value = *b_value; + val->timestamp_ms = current_ms; + ma->tail = (ma->tail + 1) % ma->size; + ma->count++; + ma->r_sum += val->r_value; + ma->g_sum += val->g_value; + ma->b_sum += val->b_value; + + // Return the current average. + *r_value = (ma->r_sum / ma->count); + *g_value = (ma->g_sum / ma->count); + *b_value = (ma->b_sum / ma->count); +} + +void rgb_moving_average_get(rgb_moving_average_t *ma, uint32_t *r_value, uint32_t *g_value, uint32_t *b_value) { + if (!ma->size || ma->count == 0) { + return; + } + + *r_value = (ma->r_sum / ma->count); + *g_value = (ma->g_sum / ma->count); + *b_value = (ma->b_sum / ma->count); +} diff --git a/common/average.h b/common/average.h new file mode 100644 index 000000000..4e1dc296d --- /dev/null +++ b/common/average.h @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (C) 2013-2025 OpenMV, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Averaging utility functions. + */ +#ifndef __AVERAGE_H__ +#define __AVERAGE_H__ + +#include +#include + +typedef struct rgb_moving_average_val { + uint32_t r_value; + uint32_t g_value; + uint32_t b_value; + uint32_t timestamp_ms; +} rgb_moving_average_val_t; + +typedef struct rgb_moving_average { + size_t size; + size_t tail; + size_t head; + size_t count; + uint32_t max_ms; + rgb_moving_average_val_t *buffer; + uint64_t r_sum; + uint64_t g_sum; + uint64_t b_sum; +} rgb_moving_average_t; + +void rgb_moving_average_init(rgb_moving_average_t *ma, uint32_t max_ms, + size_t size, rgb_moving_average_val_t *buffer); +void rgb_moving_average_update(rgb_moving_average_t *ma, + uint32_t *r_value, uint32_t *g_value, uint32_t *b_value, + uint32_t current_ms); +void rgb_moving_average_get(rgb_moving_average_t *ma, uint32_t *r_value, uint32_t *g_value, uint32_t *b_value); +#endif // __AVERAGE_H__ diff --git a/common/common.mk b/common/common.mk index 090db25f1..5bdcf8dce 100644 --- a/common/common.mk +++ b/common/common.mk @@ -24,6 +24,7 @@ COMMON_SRC_C += \ array.c \ + average.c \ dma_alloc.c \ fb_alloc.c \ file_utils.c \ diff --git a/common/omv_csi.c b/common/omv_csi.c index 805d0724c..beaa7cc0c 100644 --- a/common/omv_csi.c +++ b/common/omv_csi.c @@ -65,6 +65,8 @@ #define OMV_CSI_CLK_TOLERANCE (500000) #endif +#define OMV_CSI_RGB_AVG_MAX_MS (250) + #ifndef __weak #define __weak __attribute__((weak)) #endif @@ -322,6 +324,12 @@ __weak int omv_csi_reset(omv_csi_t *csi, bool hard) { NULL, NULL }; + #if defined(OMV_CSI_RGB_AVG_SIZE) + // Setup AWB moving average. + csi->awb_enabled = true; + rgb_moving_average_init(&csi->rgb_avg, OMV_CSI_RGB_AVG_MAX_MS, OMV_CSI_RGB_AVG_SIZE, csi->rgb_avg_buf); + #endif // defined(OMV_CSI_RGB_AVG_SIZE) + // Restore shutdown state on reset. if (!csi->power_on) { omv_csi_shutdown(csi, false); diff --git a/drivers/sensors/pag7936.c b/drivers/sensors/pag7936.c index feacc19e8..83c8f4bc4 100644 --- a/drivers/sensors/pag7936.c +++ b/drivers/sensors/pag7936.c @@ -652,6 +652,23 @@ static int get_exposure_us(omv_csi_t *csi, int *exposure_us) { return ret; } +static int set_auto_whitebal(omv_csi_t *csi, int enable, float r_gain_db, float g_gain_db, float b_gain_db) { + csi->awb_enabled = enable; + return 0; +} + +static int get_rgb_gain_db(omv_csi_t *csi, float *r_gain_db, float *g_gain_db, float *b_gain_db) { + uint32_t r_avg, g_avg, b_avg; + rgb_moving_average_get(&csi->rgb_avg, &r_avg, &g_avg, &b_avg); + float g_avg_f = (float) g_avg; + + *r_gain_db = 20.0f * log10f(g_avg_f / r_avg); + *g_gain_db = 0.0f; + *b_gain_db = 20.0f * log10f(g_avg_f / b_avg); + + return 0; +} + static int set_hmirror(omv_csi_t *csi, int enable) { uint8_t reg; int ret = omv_i2c_readb2(csi->i2c, csi->slv_addr, TG_FLIP, ®); @@ -723,6 +740,8 @@ int pag7936_init(omv_csi_t *csi) { csi->get_gain_db = get_gain_db; csi->set_auto_exposure = set_auto_exposure; csi->get_exposure_us = get_exposure_us; + csi->set_auto_whitebal = set_auto_whitebal; + csi->get_rgb_gain_db = get_rgb_gain_db; csi->set_hmirror = set_hmirror; csi->set_vflip = set_vflip; diff --git a/drivers/sensors/ps5520.c b/drivers/sensors/ps5520.c index 3e454ad3c..7646abc98 100644 --- a/drivers/sensors/ps5520.c +++ b/drivers/sensors/ps5520.c @@ -1442,6 +1442,23 @@ static int get_exposure_us(omv_csi_t *csi, int *exposure_us) { return ret; } +static int set_auto_whitebal(omv_csi_t *csi, int enable, float r_gain_db, float g_gain_db, float b_gain_db) { + csi->awb_enabled = enable; + return 0; +} + +static int get_rgb_gain_db(omv_csi_t *csi, float *r_gain_db, float *g_gain_db, float *b_gain_db) { + uint32_t r_avg, g_avg, b_avg; + rgb_moving_average_get(&csi->rgb_avg, &r_avg, &g_avg, &b_avg); + float g_avg_f = (float) g_avg; + + *r_gain_db = 20.0f * log10f(g_avg_f / r_avg); + *g_gain_db = 0.0f; + *b_gain_db = 20.0f * log10f(g_avg_f / b_avg); + + return 0; +} + static int set_hmirror(omv_csi_t *csi, int enable) { uint8_t reg1, reg2; uint16_t hsize; @@ -1590,6 +1607,8 @@ int ps5520_init(omv_csi_t *csi) { csi->get_gain_db = get_gain_db; csi->set_auto_exposure = set_auto_exposure; csi->get_exposure_us = get_exposure_us; + csi->set_auto_whitebal = set_auto_whitebal; + csi->get_rgb_gain_db = get_rgb_gain_db; csi->set_hmirror = set_hmirror; csi->set_vflip = set_vflip; diff --git a/lib/imlib/bayer.c b/lib/imlib/bayer.c index 7c45f6cf9..2b93a0423 100644 --- a/lib/imlib/bayer.c +++ b/lib/imlib/bayer.c @@ -559,6 +559,10 @@ static inline v128_t vdebayer_to_cr(vrgb_pixels_t pixels) { return pixels.r; } +// FIXME: This needs to be placed in the DTCM. .bss is in the DTCM on Alif. But, this is not portable. +// Move this to a proper DTCM section for other platforms. +static uint8_t gamma_lut[256]; + // In the case of vectors larger than 32-bits the pattern is repeated for every 32-bits. // // pixels.r = MSB [0, R1, 0, R0] LSB pixels where each pixel is 8-bits. @@ -569,6 +573,9 @@ static inline v128_t vdebayer_to_cr(vrgb_pixels_t pixels) { static inline vrgb_pixels_t vdebayer_apply_rb_gain(vrgb_pixels_t pixels, uint32_t red_gain, uint32_t blue_gain) { pixels.r = vusat_s16_narrow_u8_lo(pixels.r, vmul_n_u32(pixels.r, red_gain), 5); pixels.b = vusat_s16_narrow_u8_lo(pixels.b, vmul_n_u32(pixels.b, blue_gain), 5); + pixels.r = vldr_u8_gather(gamma_lut, pixels.r); + pixels.g = vldr_u8_gather(gamma_lut, pixels.g); + pixels.b = vldr_u8_gather(gamma_lut, pixels.b); return pixels; } @@ -2291,13 +2298,19 @@ static void vdebayer_rggb_to_rgb565_awb_quarter(image_t *src, image_t *dst, uint // GRAYSCALE: src->data == dst->data // RGB565: src->data == dst->data + image_size(src) // YUV422: Not supported -void imlib_debayer_image_awb(image_t *dst, image_t *src, bool fast, uint32_t r_out, uint32_t g_out, uint32_t b_out) { +void imlib_debayer_image_awb(image_t *dst, image_t *src, bool fast, uint32_t r_out, uint32_t g_out, uint32_t b_out, + float brightness, float contrast, float gamma) { uint32_t red_gain = IM_DIV(g_out * 32, r_out); red_gain = IM_MIN(red_gain, 128U); uint32_t blue_gain = IM_DIV(g_out * 32, b_out); blue_gain = IM_MIN(blue_gain, 128U); + for (int i = 0; i < 256; i++) { + int v = fast_roundf(((fast_powf(i * (1.0f / 255.0f), gamma) * contrast) + brightness) * 255.0f); + gamma_lut[i] = IM_CLAMP(v, 0, 255); + } + if (fast) { switch (src->pixfmt) { case PIXFORMAT_BAYER_BGGR: { diff --git a/lib/imlib/imlib.h b/lib/imlib/imlib.h index a3a87ff74..d622dd4e7 100644 --- a/lib/imlib/imlib.h +++ b/lib/imlib/imlib.h @@ -1207,7 +1207,8 @@ pixformat_t imlib_bayer_shift(pixformat_t pixfmt, int x, int y, bool transpose); void imlib_debayer_ycbcr(image_t *src, rectangle_t *roi, int8_t *Y0, int8_t *CB, int8_t *CR); void imlib_debayer_line(int x_start, int x_end, int y_row, void *dst_row_ptr, pixformat_t pixfmt, image_t *src); void imlib_debayer_image(image_t *dst, image_t *src); -void imlib_debayer_image_awb(image_t *dst, image_t *src, bool fast, uint32_t r_out, uint32_t g_out, uint32_t b_out); +void imlib_debayer_image_awb(image_t *dst, image_t *src, bool fast, uint32_t r_out, uint32_t g_out, uint32_t b_out, + float brightness, float contrast, float gamma); // YUV Image Processing pixformat_t imlib_yuv_shift(pixformat_t pixfmt, int x); diff --git a/lib/imlib/simd.h b/lib/imlib/simd.h index ffc195c9e..d31e5adf9 100644 --- a/lib/imlib/simd.h +++ b/lib/imlib/simd.h @@ -1514,6 +1514,19 @@ static inline v128_t vldr_u8_pred(const uint8_t *p, v128_predicate_t pred) { #endif } +static inline v128_t vldr_u8_gather(const uint8_t *p, v128_t offsets) { + #if (__ARM_ARCH >= 8) + return (v128_t) vldrbq_gather_offset(p, offsets.u8); + #else + v128_t v0; + v0.u8[0] = *(p + offsets.u8[0]); + v0.u8[1] = *(p + offsets.u8[1]); + v0.u8[2] = *(p + offsets.u8[2]); + v0.u8[3] = *(p + offsets.u8[3]); + return v0; + #endif +} + static inline v128_t vldr_u8_widen_u32_gather_pred(const uint8_t *p, v128_t offsets, v128_predicate_t pred) { diff --git a/ports/alif/omv_csi.c b/ports/alif/omv_csi.c index 5907a34c7..c4b0568c3 100644 --- a/ports/alif/omv_csi.c +++ b/ports/alif/omv_csi.c @@ -48,6 +48,7 @@ #include "omv_gpu.h" #include "omv_i2c.h" #include "omv_csi.h" +#include "average.h" #include "unaligned_memcpy.h" // Bits missing from cpi.h @@ -230,10 +231,7 @@ static uint32_t omv_csi_get_fb_offset(omv_csi_t *csi) { int alif_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { CPI_Type *cpi = csi->base; framebuffer_t *fb = csi->fb; - vbuffer_t *buffer = NULL; - static uint32_t frames = 0; - static uint32_t r_stat, gb_stat, gr_stat, b_stat; // Configure and re/start the capture if it's not alrady active // and there are no pending buffers (from non-blocking capture). @@ -390,13 +388,23 @@ int alif_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { // Set the target pixel format before debayer. dst_image->pixfmt = fb->pixfmt; - // Update AWB stats every n frames. - if ((frames++ % 100) == 0) { - omv_csi_ioctl(csi, OMV_CSI_IOCTL_GET_RGB_STATS, &r_stat, &gb_stat, &gr_stat, &b_stat); + uint32_t r_stat, g_stat, b_stat; + if (csi->awb_enabled) { + uint32_t gb_stat, gr_stat; + int ret = omv_csi_ioctl(csi, OMV_CSI_IOCTL_GET_RGB_STATS, &r_stat, &gb_stat, &gr_stat, &b_stat); + + if (ret != 0) { + return ret; + } + + g_stat = (gb_stat + gr_stat) / 2; + rgb_moving_average_update(&csi->rgb_avg, &r_stat, &g_stat, &b_stat, mp_hal_ticks_ms()); + } else { + rgb_moving_average_get(&csi->rgb_avg, &r_stat, &g_stat, &b_stat); } // Debayer frame. - imlib_debayer_image_awb(dst_image, &src_image, false, r_stat, (gb_stat + gr_stat) / 2, b_stat); + imlib_debayer_image_awb(dst_image, &src_image, false, r_stat, g_stat, b_stat, 0.0f, 1.0f, 0.5f); } return 0; } diff --git a/ports/alif/omv_portconfig.h b/ports/alif/omv_portconfig.h index 122190db5..c45058675 100644 --- a/ports/alif/omv_portconfig.h +++ b/ports/alif/omv_portconfig.h @@ -34,6 +34,7 @@ #define __OMV_PORTCONFIG_H__ #include +#include "average.h" #include "clk.h" #include "i2c.h" #include "i3c.h" @@ -137,9 +138,14 @@ struct { \ uint8_t datasize; \ }; -#define OMV_CSI_PORT_BITS \ - struct { \ - CPI_Type *base; \ +#define OMV_CSI_RGB_AVG_SIZE (60) + +#define OMV_CSI_PORT_BITS \ + struct { \ + CPI_Type *base; \ + bool awb_enabled; \ + rgb_moving_average_t rgb_avg; \ + rgb_moving_average_val_t rgb_avg_buf[OMV_CSI_RGB_AVG_SIZE]; \ }; #endif // __OMV_PORTCONFIG_H__ diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index b2ffd07dd..de723e677 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -751,7 +751,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { #if USE_DCMIPP if (csi->raw_output) { - float luminance = stm_isp_update_awb(&csi->dcmipp, DCMIPP_PIPE, fb->u * fb->v); + float luminance = stm_isp_update_awb(csi, DCMIPP_PIPE, fb->u * fb->v); if (csi->ioctl) { omv_csi_ioctl(csi, OMV_CSI_IOCTL_UPDATE_AGC_AEC, fast_floorf(luminance)); } diff --git a/ports/stm32/omv_portconfig.h b/ports/stm32/omv_portconfig.h index 216fa3b68..6cee1c86b 100644 --- a/ports/stm32/omv_portconfig.h +++ b/ports/stm32/omv_portconfig.h @@ -27,6 +27,8 @@ #define __OMV_PORTCONFIG_H__ #include STM32_HAL_H +#include "average.h" + // GPIO speeds. #define OMV_GPIO_SPEED_LOW GPIO_SPEED_FREQ_LOW #define OMV_GPIO_SPEED_MED GPIO_SPEED_FREQ_MEDIUM @@ -147,12 +149,15 @@ typedef I2C_HandleTypeDef *omv_i2c_dev_t; #define OMV_CSI_PORT_BITS_MDMA #endif - #if defined(STM32N6) -#define OMV_CSI_PORT_BITS_DCMIPP \ - struct { \ - DCMIPP_HandleTypeDef dcmipp; \ - DMA_QListTypeDef dma_queue; \ +#define OMV_CSI_RGB_AVG_SIZE (120) +#define OMV_CSI_PORT_BITS_DCMIPP \ + struct { \ + DCMIPP_HandleTypeDef dcmipp; \ + DMA_QListTypeDef dma_queue; \ + bool awb_enabled; \ + rgb_moving_average_t rgb_avg; \ + rgb_moving_average_val_t rgb_avg_buf[OMV_CSI_RGB_AVG_SIZE]; \ }; #else #define OMV_CSI_PORT_BITS_DCMIPP diff --git a/ports/stm32/stm_isp.c b/ports/stm32/stm_isp.c index d5449262e..272247e1c 100644 --- a/ports/stm32/stm_isp.c +++ b/ports/stm32/stm_isp.c @@ -33,24 +33,32 @@ #include #include "imlib.h" +#include "average.h" +#include "py/mphal.h" #include "stm_isp.h" #include "omv_boardconfig.h" #ifdef DCMIPP -float stm_isp_update_awb(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe, uint32_t n_pixels) { +float stm_isp_update_awb(omv_csi_t *csi, uint32_t pipe, uint32_t n_pixels) { uint32_t avg[3]; uint32_t shift[3]; uint32_t multi[3]; - for (int i = 0; i < 3; i++) { - // DCMIPP_STATEXT_MODULE1 - HAL_DCMIPP_PIPE_GetISPAccumulatedStatisticsCounter(dcmipp, pipe, i + 1, &avg[i]); - } + if (csi->awb_enabled) { + for (int i = 0; i < 3; i++) { + // DCMIPP_STATEXT_MODULE1 + HAL_DCMIPP_PIPE_GetISPAccumulatedStatisticsCounter(&csi->dcmipp, pipe, i + 1, &avg[i]); + } - // Averages are collected from bayer components (4R 2G 4B). - avg[0] = OMV_MAX((avg[0] * 256 * 4) / n_pixels, 1); - avg[1] = OMV_MAX((avg[1] * 256 * 2) / n_pixels, 1); - avg[2] = OMV_MAX((avg[2] * 256 * 4) / n_pixels, 1); + // Averages are collected from bayer components (4R 2G 4B). + avg[0] = OMV_MAX((avg[0] * 256 * 4) / n_pixels, 1); + avg[1] = OMV_MAX((avg[1] * 256 * 2) / n_pixels, 1); + avg[2] = OMV_MAX((avg[2] * 256 * 4) / n_pixels, 1); + + rgb_moving_average_update(&csi->rgb_avg, &avg[0], &avg[1], &avg[2], mp_hal_ticks_ms()); + } else { + rgb_moving_average_get(&csi->rgb_avg, &avg[0], &avg[1], &avg[2]); + } // Compute global luminance float luminance = avg[0] * 0.299 + avg[1] * 0.587 + avg[2] * 0.114; @@ -75,7 +83,7 @@ float stm_isp_update_awb(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe, uint32_t n .MultiplierBlue = multi[2], }; - HAL_DCMIPP_PIPE_SetISPExposureConfig(dcmipp, pipe, &expcfg); + HAL_DCMIPP_PIPE_SetISPExposureConfig(&csi->dcmipp, pipe, &expcfg); return luminance; } @@ -159,6 +167,10 @@ int stm_isp_config_pipeline(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe, } } + if (HAL_DCMIPP_PIPE_EnableGammaConversion(dcmipp, pipe) != HAL_OK) { + return -1; + } + return 0; } #endif // DCMIPP diff --git a/ports/stm32/stm_isp.h b/ports/stm32/stm_isp.h index 549edb6cf..daa1df06f 100644 --- a/ports/stm32/stm_isp.h +++ b/ports/stm32/stm_isp.h @@ -35,11 +35,12 @@ #include #include #include STM32_HAL_H +#include "omv_csi.h" #ifdef DCMIPP int stm_isp_config_pipeline(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe, pixformat_t pixformat, bool raw_output); -float stm_isp_update_awb(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe, uint32_t n_pixels); +float stm_isp_update_awb(omv_csi_t *csi, uint32_t pipe, uint32_t n_pixels); #endif #endif // __STM_ISP_H__