From 0e0326ffabbeb4f9ccf757e9f7235374b959dba6 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Tue, 25 Apr 2023 21:27:35 -0700 Subject: [PATCH] imlib: Add support for AWB and CCM. --- src/omv/Makefile | 1 + .../imlib_config.h | 3 + src/omv/boards/NANO33/imlib_config.h | 3 + src/omv/boards/NICLAV/imlib_config.h | 3 + src/omv/boards/OPENMV2/imlib_config.h | 3 + src/omv/boards/OPENMV3/imlib_config.h | 3 + src/omv/boards/OPENMV4/imlib_config.h | 3 + src/omv/boards/OPENMV4P/imlib_config.h | 3 + src/omv/boards/OPENMV4_PRO/imlib_config.h | 3 + src/omv/boards/OPENMVPT/imlib_config.h | 3 + src/omv/boards/PICO/imlib_config.h | 3 + src/omv/boards/PORTENTA/imlib_config.h | 3 + src/omv/imlib/imlib.h | 5 +- src/omv/imlib/isp.c | 1151 +++++++++++++++++ src/omv/imlib/mathop.c | 96 -- src/omv/modules/py_image.c | 92 +- src/omv/ports/nrf/omv_portconfig.mk | 1 + src/omv/ports/rp2/omv_portconfig.cmake | 1 + src/omv/ports/stm32/omv_portconfig.mk | 1 + 19 files changed, 1265 insertions(+), 116 deletions(-) create mode 100644 src/omv/imlib/isp.c diff --git a/src/omv/Makefile b/src/omv/Makefile index e088a1344..0268ad6fa 100644 --- a/src/omv/Makefile +++ b/src/omv/Makefile @@ -72,6 +72,7 @@ SRCS += $(addprefix imlib/, \ imlib.c \ integral.c \ integral_mw.c \ + isp.c \ jpegd.c \ jpeg.c \ lodepng.c \ diff --git a/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/imlib_config.h b/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/imlib_config.h index 6486d2843..56c810a3d 100644 --- a/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/imlib_config.h +++ b/src/omv/boards/ARDUINO_NANO_RP2040_CONNECT/imlib_config.h @@ -30,6 +30,9 @@ // Enable midpoint pooling //#define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +//#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops //#define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/NANO33/imlib_config.h b/src/omv/boards/NANO33/imlib_config.h index 8ad0ba16e..b07c5fd69 100644 --- a/src/omv/boards/NANO33/imlib_config.h +++ b/src/omv/boards/NANO33/imlib_config.h @@ -30,6 +30,9 @@ // Enable midpoint pooling //#define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +//#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops //#define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/NICLAV/imlib_config.h b/src/omv/boards/NICLAV/imlib_config.h index 6e1edfdb1..57caf0252 100644 --- a/src/omv/boards/NICLAV/imlib_config.h +++ b/src/omv/boards/NICLAV/imlib_config.h @@ -29,6 +29,9 @@ // Enable midpoint pooling #define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops #define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/OPENMV2/imlib_config.h b/src/omv/boards/OPENMV2/imlib_config.h index f3c805840..8d06d0dba 100644 --- a/src/omv/boards/OPENMV2/imlib_config.h +++ b/src/omv/boards/OPENMV2/imlib_config.h @@ -29,6 +29,9 @@ // Enable midpoint pooling #define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops #define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/OPENMV3/imlib_config.h b/src/omv/boards/OPENMV3/imlib_config.h index 366c73e76..ca2cdde26 100644 --- a/src/omv/boards/OPENMV3/imlib_config.h +++ b/src/omv/boards/OPENMV3/imlib_config.h @@ -29,6 +29,9 @@ // Enable midpoint pooling #define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops #define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/OPENMV4/imlib_config.h b/src/omv/boards/OPENMV4/imlib_config.h index e8943aaf4..293b67872 100644 --- a/src/omv/boards/OPENMV4/imlib_config.h +++ b/src/omv/boards/OPENMV4/imlib_config.h @@ -29,6 +29,9 @@ // Enable midpoint pooling #define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops #define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/OPENMV4P/imlib_config.h b/src/omv/boards/OPENMV4P/imlib_config.h index e136098e3..54f34a214 100644 --- a/src/omv/boards/OPENMV4P/imlib_config.h +++ b/src/omv/boards/OPENMV4P/imlib_config.h @@ -29,6 +29,9 @@ // Enable midpoint pooling #define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops #define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/OPENMV4_PRO/imlib_config.h b/src/omv/boards/OPENMV4_PRO/imlib_config.h index e136098e3..54f34a214 100644 --- a/src/omv/boards/OPENMV4_PRO/imlib_config.h +++ b/src/omv/boards/OPENMV4_PRO/imlib_config.h @@ -29,6 +29,9 @@ // Enable midpoint pooling #define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops #define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/OPENMVPT/imlib_config.h b/src/omv/boards/OPENMVPT/imlib_config.h index e136098e3..54f34a214 100644 --- a/src/omv/boards/OPENMVPT/imlib_config.h +++ b/src/omv/boards/OPENMVPT/imlib_config.h @@ -29,6 +29,9 @@ // Enable midpoint pooling #define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops #define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/PICO/imlib_config.h b/src/omv/boards/PICO/imlib_config.h index 6486d2843..56c810a3d 100644 --- a/src/omv/boards/PICO/imlib_config.h +++ b/src/omv/boards/PICO/imlib_config.h @@ -30,6 +30,9 @@ // Enable midpoint pooling //#define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +//#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops //#define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/boards/PORTENTA/imlib_config.h b/src/omv/boards/PORTENTA/imlib_config.h index dc0cf067c..6fc81f158 100644 --- a/src/omv/boards/PORTENTA/imlib_config.h +++ b/src/omv/boards/PORTENTA/imlib_config.h @@ -29,6 +29,9 @@ // Enable midpoint pooling #define IMLIB_ENABLE_MIDPOINT_POOLING +// Enable ISP ops +#define IMLIB_ENABLE_ISP_OPS + // Enable binary ops #define IMLIB_ENABLE_BINARY_OPS diff --git a/src/omv/imlib/imlib.h b/src/omv/imlib/imlib.h index 91dcc4c4a..0632c8a8f 100644 --- a/src/omv/imlib/imlib.h +++ b/src/omv/imlib/imlib.h @@ -1287,6 +1287,10 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d void imlib_flood_fill(image_t *img, int x, int y, float seed_threshold, float floating_threshold, int c, bool invert, bool clear_background, image_t *mask); +// ISP Functions +void imlib_awb(image_t *img, bool max); +void imlib_ccm(image_t *img, float *ccm, bool offset); +void imlib_gamma(image_t *img, float gamma, float scale, float offset); // Binary Functions void imlib_binary(image_t *out, image_t *img, list_t *thresholds, bool invert, bool zero, image_t *mask); void imlib_invert(image_t *img); @@ -1303,7 +1307,6 @@ void imlib_close(image_t *img, int ksize, int threshold, image_t *mask); void imlib_top_hat(image_t *img, int ksize, int threshold, image_t *mask); void imlib_black_hat(image_t *img, int ksize, int threshold, image_t *mask); // Math Functions -void imlib_gamma_corr(image_t *img, float gamma, float scale, float offset); void imlib_negate(image_t *img); void imlib_replace(image_t *img, const char *path, image_t *other, int scalar, bool hmirror, bool vflip, bool transpose, image_t *mask); void imlib_add(image_t *img, const char *path, image_t *other, int scalar, image_t *mask); diff --git a/src/omv/imlib/isp.c b/src/omv/imlib/isp.c new file mode 100644 index 000000000..accd7807d --- /dev/null +++ b/src/omv/imlib/isp.c @@ -0,0 +1,1151 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2023 Ibrahim Abdelkader + * Copyright (c) 2013-2023 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * AWB Functions + */ +#include "imlib.h" + +#ifdef IMLIB_ENABLE_ISP_OPS + +static void imlib_rgb_avg(image_t *img, uint32_t *r_out, uint32_t *g_out, uint32_t *b_out) +{ + uint32_t area = img->w * img->h; + uint32_t r_acc = 0, g_acc = 0, b_acc = 0; + + switch (img->pixfmt) { + case PIXFORMAT_RGB565: { + uint16_t *ptr = (uint16_t *) img->data; + long n = area; // must be signed for count down loop + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 1; n -= 2) { + uint32_t pixels = *ptr32++; + + long r = (pixels >> 11) & 0x1F001F; + r_acc = __USADA8(r, 0, r_acc); + + long g = (pixels >> 5) & 0x3F003F; + g_acc = __USADA8(g, 0, g_acc); + + long b = pixels & 0x1F001F; + b_acc = __USADA8(b, 0, b_acc); + } + + ptr = (uint16_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + int pixel = *ptr++; + r_acc += COLOR_RGB565_TO_R5(pixel); + g_acc += COLOR_RGB565_TO_G6(pixel); + b_acc += COLOR_RGB565_TO_B5(pixel); + } + + break; + } + case PIXFORMAT_BAYER_BGGR: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long g = __UXTB16_RORn(pixels, 0); + g_acc = __USADA8(g, 0, g_acc); + + long b = __UXTB16_RORn(pixels, 8); + b_acc = __USADA8(b, 0, b_acc); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + b_acc += *ptr++; + } else { + g_acc += *ptr++; + } + } + } else { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long r = __UXTB16_RORn(pixels, 0); + r_acc = __USADA8(r, 0, r_acc); + + long g = __UXTB16_RORn(pixels, 8); + g_acc = __USADA8(g, 0, g_acc); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + g_acc += *ptr++; + } else { + r_acc += *ptr++; + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_GBRG: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long b = __UXTB16_RORn(pixels, 0); + b_acc = __USADA8(b, 0, b_acc); + + long g = __UXTB16_RORn(pixels, 8); + g_acc = __USADA8(g, 0, g_acc); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + g_acc += *ptr++; + } else { + b_acc += *ptr++; + } + } + } else { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long g = __UXTB16_RORn(pixels, 0); + g_acc = __USADA8(g, 0, g_acc); + + long r = __UXTB16_RORn(pixels, 8); + r_acc = __USADA8(r, 0, r_acc); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + r_acc += *ptr++; + } else { + g_acc += *ptr++; + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_GRBG: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long r = __UXTB16_RORn(pixels, 0); + r_acc = __USADA8(r, 0, r_acc); + + long g = __UXTB16_RORn(pixels, 8); + g_acc = __USADA8(g, 0, g_acc); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + g_acc += *ptr++; + } else { + r_acc += *ptr++; + } + } + } else { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long g = __UXTB16_RORn(pixels, 0); + g_acc = __USADA8(g, 0, g_acc); + + long b = __UXTB16_RORn(pixels, 8); + b_acc = __USADA8(b, 0, b_acc); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + b_acc += *ptr++; + } else { + g_acc += *ptr++; + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_RGGB: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long g = __UXTB16_RORn(pixels, 0); + g_acc = __USADA8(g, 0, g_acc); + + long r = __UXTB16_RORn(pixels, 8); + r_acc = __USADA8(r, 0, r_acc); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + r_acc += *ptr++; + } else { + g_acc += *ptr++; + } + } + } else { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long b = __UXTB16_RORn(pixels, 0); + b_acc = __USADA8(b, 0, b_acc); + + long g = __UXTB16_RORn(pixels, 8); + g_acc = __USADA8(g, 0, g_acc); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + g_acc += *ptr++; + } else { + b_acc += *ptr++; + } + } + } + } + + break; + } + default: { + break; + } + } + + if (img->is_bayer) { + *r_out = ((r_acc * 4) + (area >> 1)) / area; + *g_out = ((g_acc * 2) + (area >> 1)) / area; + *b_out = ((b_acc * 4) + (area >> 1)) / area; + } else { + *r_out = ((r_acc * 2) + (area >> 1)) / area; + *g_out = (g_acc + (area >> 1)) / area; + *b_out = ((b_acc * 2) + (area >> 1)) / area; + } +} + +static void imlib_rgb_max(image_t *img, uint32_t *r_out, uint32_t *g_out, uint32_t *b_out) +{ + uint32_t area = img->w * img->h; + uint32_t r_acc = 0, g_acc = 0, b_acc = 0; + + switch (img->pixfmt) { + case PIXFORMAT_RGB565: { + uint16_t *ptr = (uint16_t *) img->data; + long n = area; // must be signed for count down loop + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 1; n -= 2) { + uint32_t pixels = *ptr32++; + + long r = (pixels >> 11) & 0x1F001F; + long r_tmp = __USUB8(r, r_acc); (void) r_tmp; + r_acc = __SEL(r, r_acc); + + long g = (pixels >> 5) & 0x3F003F; + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + + long b = pixels & 0x1F001F; + long b_tmp = __USUB8(b, b_acc); (void) b_tmp; + b_acc = __SEL(b, b_acc); + } + + long r_tmp = r_acc >> 16; + long r_tmp2 = __USUB8(r_tmp, r_acc); (void) r_tmp2; + r_acc = __SEL(r_tmp, r_acc) & 0xff; + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + long b_tmp = b_acc >> 16; + long b_tmp2 = __USUB8(b_tmp, b_acc); (void) b_tmp2; + b_acc = __SEL(b_tmp, b_acc) & 0xff; + + ptr = (uint16_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + int pixel = *ptr++; + int r = COLOR_RGB565_TO_R5(pixel); + r_acc = (r > r_acc) ? r : r_acc; + int g = COLOR_RGB565_TO_G6(pixel); + g_acc = (g > g_acc) ? g : g_acc; + int b = COLOR_RGB565_TO_B5(pixel); + b_acc = (b > b_acc) ? b : b_acc; + } + + break; + } + case PIXFORMAT_BAYER_BGGR: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long g = __UXTB16_RORn(pixels, 0); + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + + long b = __UXTB16_RORn(pixels, 8); + long b_tmp = __USUB8(b, b_acc); (void) b_tmp; + b_acc = __SEL(b, b_acc); + } + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + long b_tmp = b_acc >> 16; + long b_tmp2 = __USUB8(b_tmp, b_acc); (void) b_tmp2; + b_acc = __SEL(b_tmp, b_acc) & 0xff; + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + int b = *ptr++; + b_acc = (b > b_acc) ? b : b_acc; + } else { + int g = *ptr++; + g_acc = (g > g_acc) ? g : g_acc; + } + } + } else { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long r = __UXTB16_RORn(pixels, 0); + long r_tmp = __USUB8(r, r_acc); (void) r_tmp; + r_acc = __SEL(r, r_acc); + + long g = __UXTB16_RORn(pixels, 8); + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + } + + long r_tmp = r_acc >> 16; + long r_tmp2 = __USUB8(r_tmp, r_acc); (void) r_tmp2; + r_acc = __SEL(r_tmp, r_acc) & 0xff; + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + int g = *ptr++; + g_acc = (g > g_acc) ? g : g_acc; + } else { + int r = *ptr++; + r_acc = (r > r_acc) ? r : r_acc; + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_GBRG: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long b = __UXTB16_RORn(pixels, 0); + long b_tmp = __USUB8(b, b_acc); (void) b_tmp; + b_acc = __SEL(b, b_acc); + + long g = __UXTB16_RORn(pixels, 8); + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + } + + long b_tmp = b_acc >> 16; + long b_tmp2 = __USUB8(b_tmp, b_acc); (void) b_tmp2; + b_acc = __SEL(b_tmp, b_acc) & 0xff; + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + int g = *ptr++; + g_acc = (g > g_acc) ? g : g_acc; + } else { + int b = *ptr++; + b_acc = (b > b_acc) ? b : b_acc; + } + } + } else { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long g = __UXTB16_RORn(pixels, 0); + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + + long r = __UXTB16_RORn(pixels, 8); + long r_tmp = __USUB8(r, r_acc); (void) r_tmp; + r_acc = __SEL(r, r_acc); + } + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + long r_tmp = r_acc >> 16; + long r_tmp2 = __USUB8(r_tmp, r_acc); (void) r_tmp2; + r_acc = __SEL(r_tmp, r_acc) & 0xff; + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + int r = *ptr++; + r_acc = (r > r_acc) ? r : r_acc; + } else { + int g = *ptr++; + g_acc = (g > g_acc) ? g : g_acc; + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_GRBG: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long r = __UXTB16_RORn(pixels, 0); + long r_tmp = __USUB8(r, r_acc); (void) r_tmp; + r_acc = __SEL(r, r_acc); + + long g = __UXTB16_RORn(pixels, 8); + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + } + + long r_tmp = r_acc >> 16; + long r_tmp2 = __USUB8(r_tmp, r_acc); (void) r_tmp2; + r_acc = __SEL(r_tmp, r_acc) & 0xff; + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + int g = *ptr++; + g_acc = (g > g_acc) ? g : g_acc; + } else { + int r = *ptr++; + r_acc = (r > r_acc) ? r : r_acc; + } + } + } else { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long g = __UXTB16_RORn(pixels, 0); + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + + long b = __UXTB16_RORn(pixels, 8); + long b_tmp = __USUB8(b, b_acc); (void) b_tmp; + b_acc = __SEL(b, b_acc); + } + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + long b_tmp = b_acc >> 16; + long b_tmp2 = __USUB8(b_tmp, b_acc); (void) b_tmp2; + b_acc = __SEL(b_tmp, b_acc) & 0xff; + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + int b = *ptr++; + b_acc = (b > b_acc) ? b : b_acc; + } else { + int g = *ptr++; + g_acc = (g > g_acc) ? g : g_acc; + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_RGGB: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long g = __UXTB16_RORn(pixels, 0); + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + + long r = __UXTB16_RORn(pixels, 8); + long r_tmp = __USUB8(r, r_acc); (void) r_tmp; + r_acc = __SEL(r, r_acc); + } + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + long r_tmp = r_acc >> 16; + long r_tmp2 = __USUB8(r_tmp, r_acc); (void) r_tmp2; + r_acc = __SEL(r_tmp, r_acc) & 0xff; + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + int r = *ptr++; + r_acc = (r > r_acc) ? r : r_acc; + } else { + int g = *ptr++; + g_acc = (g > g_acc) ? g : g_acc; + } + } + } else { + int n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32++; + + long b = __UXTB16_RORn(pixels, 0); + long b_tmp = __USUB8(b, b_acc); (void) b_tmp; + b_acc = __SEL(b, b_acc); + + long g = __UXTB16_RORn(pixels, 8); + long g_tmp = __USUB8(g, g_acc); (void) g_tmp; + g_acc = __SEL(g, g_acc); + } + + long b_tmp = b_acc >> 16; + long b_tmp2 = __USUB8(b_tmp, b_acc); (void) b_tmp2; + b_acc = __SEL(b_tmp, b_acc) & 0xff; + + long g_tmp = g_acc >> 16; + long g_tmp2 = __USUB8(g_tmp, g_acc); (void) g_tmp2; + g_acc = __SEL(g_tmp, g_acc) & 0xff; + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + if (n % 2) { + int g = *ptr++; + g_acc = (g > g_acc) ? g : g_acc; + } else { + int b = *ptr++; + b_acc = (b > b_acc) ? b : b_acc; + } + } + } + } + + break; + } + default: { + break; + } + } + + if (img->is_bayer) { + *r_out = r_acc; + *g_out = g_acc; + *b_out = b_acc; + } else { + *r_out = r_acc * 2; + *g_out = g_acc; + *b_out = b_acc * 2; + } +} + +void imlib_awb(image_t *img, bool max) +{ + uint32_t area = img->w * img->h; + uint32_t r_out, g_out, b_out; + + if (max) { + imlib_rgb_max(img, &r_out, &g_out, &b_out); // white patch algorithm + } else { + imlib_rgb_avg(img, &r_out, &g_out, &b_out); // gray world algorithm + } + + int red_gain = IM_DIV(g_out * 32, r_out); + red_gain = IM_MIN(red_gain, 128); + int blue_gain = IM_DIV(g_out * 32, b_out); + blue_gain = IM_MIN(blue_gain, 128); + + switch (img->pixfmt) { + case PIXFORMAT_RGB565: { + uint16_t *ptr = (uint16_t *) img->data; + long n = area; // must be signed for count down loop + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 1; n -= 2) { + long pixels = *ptr32; + long r_pixels = (__USAT16(((pixels >> 11) & 0x1F001F) * red_gain, 10) << 6) & 0xF800F800; + long g_pixels = pixels & 0x7E007E0; + long b_pixels = (__USAT16((pixels & 0x1F001F) * blue_gain, 10) >> 5) & 0x1F001F; + *ptr32++ = r_pixels | g_pixels | b_pixels; + } + + ptr = (uint16_t *) ptr32; + #endif + + for (; n > 0; n -= 1) { + int pixel = *ptr; + int r = __USAT_ASR(COLOR_RGB565_TO_R5(pixel) * red_gain, 5, 5); + int g = COLOR_RGB565_TO_G6(pixel); + int b = __USAT_ASR(COLOR_RGB565_TO_B5(pixel) * blue_gain, 5, 5); + *ptr++ = COLOR_R5_G6_B5_TO_RGB565(r, g, b); + } + + break; + } + case PIXFORMAT_BAYER_BGGR: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + long n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32; + long b = __USAT16(__UXTB16_RORn(pixels, 8) * blue_gain, 13) << 3; + long tmp = __USUB8(0xFF00FF, pixels); (void) tmp; + *ptr32++ = __SEL(pixels, b); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1, ptr++) { + if (n % 2) { + *ptr = __USAT_ASR(*ptr * blue_gain, 8, 5); + } + } + } else { + long n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32; + long r = __USAT16(__UXTB16_RORn(pixels, 0) * red_gain, 13) >> 5; + long tmp = __USUB8(0xFF00FF00, pixels); (void) tmp; + *ptr32++ = __SEL(pixels, r); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1, ptr++) { + if (!(n % 2)) { + *ptr = __USAT_ASR(*ptr * red_gain, 8, 5); + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_GBRG: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + long n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32; + long b = __USAT16(__UXTB16_RORn(pixels, 0) * blue_gain, 13) >> 5; + long tmp = __USUB8(0xFF00FF00, pixels); (void) tmp; + *ptr32++ = __SEL(pixels, b); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1, ptr++) { + if (!(n % 2)) { + *ptr = __USAT_ASR(*ptr * blue_gain, 8, 5); + } + } + } else { + long n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32; + long r = __USAT16(__UXTB16_RORn(pixels, 8) * red_gain, 13) << 3; + long tmp = __USUB8(0xFF00FF, pixels); (void) tmp; + *ptr32++ = __SEL(pixels, r); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1, ptr++) { + if (n % 2) { + *ptr = __USAT_ASR(*ptr * red_gain, 8, 5); + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_GRBG: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + long n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32; + long r = __USAT16(__UXTB16_RORn(pixels, 0) * red_gain, 13) >> 5; + long tmp = __USUB8(0xFF00FF00, pixels); (void) tmp; + *ptr32++ = __SEL(pixels, r); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1, ptr++) { + if (!(n % 2)) { + *ptr = __USAT_ASR(*ptr * red_gain, 8, 5); + } + } + } else { + long n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32; + long b = __USAT16(__UXTB16_RORn(pixels, 8) * blue_gain, 13) << 3; + long tmp = __USUB8(0xFF00FF, pixels); (void) tmp; + *ptr32++ = __SEL(pixels, b); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1, ptr++) { + if (n % 2) { + *ptr = __USAT_ASR(*ptr * blue_gain, 8, 5); + } + } + } + } + + break; + } + case PIXFORMAT_BAYER_RGGB: { + uint8_t *ptr = (uint8_t *) img->data; + for (int y = 0, yy = img->h; y < yy; y++) { + if (y % 2) { + long n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32; + long r = __USAT16(__UXTB16_RORn(pixels, 8) * red_gain, 13) << 3; + long tmp = __USUB8(0xFF00FF, pixels); (void) tmp; + *ptr32++ = __SEL(pixels, r); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1, ptr++) { + if (!(n % 2)) { + *ptr = __USAT_ASR(*ptr * red_gain, 8, 5); + } + } + } else { + long n = img->w; + + #if defined(ARM_MATH_DSP) + uint32_t *ptr32 = (uint32_t *) ptr; + + for (; n > 3; n -= 4) { + uint32_t pixels = *ptr32; + long b = __USAT16(__UXTB16_RORn(pixels, 0) * blue_gain, 13) >> 5; + long tmp = __USUB8(0xFF00FF00, pixels); (void) tmp; + *ptr32++ = __SEL(pixels, b); + } + + ptr = (uint8_t *) ptr32; + #endif + + for (; n > 0; n -= 1, ptr++) { + if (!(n % 2)) { + *ptr = __USAT_ASR(*ptr * blue_gain, 8, 5); + } + } + } + } + + break; + } + default: { + break; + } + } +} + +void imlib_ccm(image_t *img, float *ccm, bool offset) +{ + float rr = ccm[0], rg = ccm[3], rb = ccm[6], ro = 0.f; + float gr = ccm[1], gg = ccm[4], gb = ccm[7], go = 0.f; + float br = ccm[2], bg = ccm[5], bb = ccm[8], bo = 0.f; + + if (offset) { + ro = ccm[9]; + go = ccm[10]; + bo = ccm[11]; + } + + int i_rr = IM_MIN(fast_roundf(rr * 64), 1024); + int i_rg = IM_MIN(fast_roundf(rg * 32), 512); + int i_rb = IM_MIN(fast_roundf(rb * 64), 1024); + + int i_gr = IM_MIN(fast_roundf(gr * 64), 1024); + int i_gg = IM_MIN(fast_roundf(gg * 32), 512); + int i_gb = IM_MIN(fast_roundf(gb * 64), 1024); + + int i_br = IM_MIN(fast_roundf(br * 64), 1024); + int i_bg = IM_MIN(fast_roundf(bg * 32), 512); + int i_bb = IM_MIN(fast_roundf(bb * 64), 1024); + + int i_ro = IM_MIN(fast_roundf(ro * 64), 1024); + int i_go = IM_MIN(fast_roundf(go * 32), 512); + int i_bo = IM_MIN(fast_roundf(bo * 64), 1024); + + #if defined(ARM_MATH_DSP) + long smuad_rr_rb = __PKHBT(i_rb, i_rr, 16); + long smuad_gr_gb = __PKHBT(i_gb, i_gr, 16); + long smuad_br_bb = __PKHBT(i_bb, i_br, 16); + #endif + + switch (img->pixfmt) { + case PIXFORMAT_RGB565: { + uint16_t *ptr = (uint16_t *) img->data; + long n = img->w * img->h; // must be signed for count down loop + + if (offset) { + for (; n > 0; n -= 1) { + int pixel = *ptr; + int g = COLOR_RGB565_TO_G6(pixel); + #if defined(ARM_MATH_DSP) + // This code is only slightly faster than the non-DSP version... + int r_b = __PKHBT(pixel & 0x1F, pixel, 5); + int new_r = __USAT_ASR(__SMLAD(r_b, smuad_rr_rb, (i_rg * g) + i_ro), 5, 6); + int new_g = __USAT_ASR(__SMLAD(r_b, smuad_gr_gb, (i_gg * g) + i_go), 6, 5); + int new_b = __USAT_ASR(__SMLAD(r_b, smuad_br_bb, (i_bg * g) + i_bo), 5, 6); + #else + int r = COLOR_RGB565_TO_R5(pixel); + int b = COLOR_RGB565_TO_B5(pixel); + int new_r = __USAT_ASR((i_rr * r) + (i_rg * g) + (i_rb * b) + i_ro, 5, 6); + int new_g = __USAT_ASR((i_gr * r) + (i_gg * g) + (i_gb * b) + i_go, 6, 5); + int new_b = __USAT_ASR((i_br * r) + (i_bg * g) + (i_bb * b) + i_bo, 5, 6); + #endif + *ptr++ = COLOR_R5_G6_B5_TO_RGB565(new_r, new_g, new_b); + } + } else { + for (; n > 0; n -= 1) { + int pixel = *ptr; + int g = COLOR_RGB565_TO_G6(pixel); + #if defined(ARM_MATH_DSP) + // This code is only slightly faster than the non-DSP version... + int r_b = __PKHBT(pixel & 0x1F, pixel, 5); + int new_r = __USAT_ASR(__SMLAD(r_b, smuad_rr_rb, i_rg * g), 5, 6); + int new_g = __USAT_ASR(__SMLAD(r_b, smuad_gr_gb, i_gg * g), 6, 5); + int new_b = __USAT_ASR(__SMLAD(r_b, smuad_br_bb, i_bg * g), 5, 6); + #else + int r = COLOR_RGB565_TO_R5(pixel); + int b = COLOR_RGB565_TO_B5(pixel); + int new_r = __USAT_ASR((i_rr * r) + (i_rg * g) + (i_rb * b), 5, 6); + int new_g = __USAT_ASR((i_gr * r) + (i_gg * g) + (i_gb * b), 6, 5); + int new_b = __USAT_ASR((i_br * r) + (i_bg * g) + (i_bb * b), 5, 6); + #endif + *ptr++ = COLOR_R5_G6_B5_TO_RGB565(new_r, new_g, new_b); + } + } + + break; + } + default: { + break; + } + } +} + +void imlib_gamma(image_t *img, float gamma, float contrast, float brightness) +{ + gamma = IM_DIV(1.0, gamma); + switch (img->pixfmt) { + case PIXFORMAT_BINARY: { + float pScale = COLOR_BINARY_MAX - COLOR_BINARY_MIN; + float pDiv = 1 / pScale; + int *p_lut = fb_alloc((COLOR_BINARY_MAX - COLOR_BINARY_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); + + for (int i = COLOR_BINARY_MIN; i <= COLOR_BINARY_MAX; i++) { + int p = ((fast_powf(i * pDiv, gamma) * contrast) + brightness) * pScale; + p_lut[i] = IM_MIN(IM_MAX(p , COLOR_BINARY_MIN), COLOR_BINARY_MAX); + } + + for (int y = 0, yy = img->h; y < yy; y++) { + uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); + for (int x = 0, xx = img->w; x < xx; x++) { + int dataPixel = IMAGE_GET_BINARY_PIXEL_FAST(data, x); + int p = p_lut[dataPixel]; + IMAGE_PUT_BINARY_PIXEL_FAST(data, x, p); + } + } + + fb_free(); + break; + } + case PIXFORMAT_GRAYSCALE: { + float pScale = COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN; + float pDiv = 1 / pScale; + int *p_lut = fb_alloc((COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); + + for (int i = COLOR_GRAYSCALE_MIN; i <= COLOR_GRAYSCALE_MAX; i++) { + int p = ((fast_powf(i * pDiv, gamma) * contrast) + brightness) * pScale; + p_lut[i] = IM_MIN(IM_MAX(p , COLOR_GRAYSCALE_MIN), COLOR_GRAYSCALE_MAX); + } + + for (int y = 0, yy = img->h; y < yy; y++) { + uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); + for (int x = 0, xx = img->w; x < xx; x++) { + int dataPixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(data, x); + int p = p_lut[dataPixel]; + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, x, p); + } + } + + fb_free(); + break; + } + case PIXFORMAT_RGB565: { + float rScale = COLOR_R5_MAX - COLOR_R5_MIN; + float gScale = COLOR_G6_MAX - COLOR_G6_MIN; + float bScale = COLOR_B5_MAX - COLOR_B5_MIN; + float rDiv = 1 / rScale; + float gDiv = 1 / gScale; + float bDiv = 1 / bScale; + int *r_lut = fb_alloc((COLOR_R5_MAX - COLOR_R5_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); + int *g_lut = fb_alloc((COLOR_G6_MAX - COLOR_G6_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); + int *b_lut = fb_alloc((COLOR_B5_MAX - COLOR_B5_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); + + for (int i = COLOR_R5_MIN; i <= COLOR_R5_MAX; i++) { + int r = ((fast_powf(i * rDiv, gamma) * contrast) + brightness) * rScale; + r_lut[i] = IM_MIN(IM_MAX(r , COLOR_R5_MIN), COLOR_R5_MAX); + } + + for (int i = COLOR_G6_MIN; i <= COLOR_G6_MAX; i++) { + int g = ((fast_powf(i * gDiv, gamma) * contrast) + brightness) * gScale; + g_lut[i] = IM_MIN(IM_MAX(g , COLOR_G6_MIN), COLOR_G6_MAX); + } + + for (int i = COLOR_B5_MIN; i <= COLOR_B5_MAX; i++) { + int b = ((fast_powf(i * bDiv, gamma) * contrast) + brightness) * bScale; + b_lut[i] = IM_MIN(IM_MAX(b , COLOR_B5_MIN), COLOR_B5_MAX); + } + + for (int y = 0, yy = img->h; y < yy; y++) { + uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); + for (int x = 0, xx = img->w; x < xx; x++) { + int dataPixel = IMAGE_GET_RGB565_PIXEL_FAST(data, x); + int r = r_lut[COLOR_RGB565_TO_R5(dataPixel)]; + int g = g_lut[COLOR_RGB565_TO_G6(dataPixel)]; + int b = b_lut[COLOR_RGB565_TO_B5(dataPixel)]; + IMAGE_PUT_RGB565_PIXEL_FAST(data, x, COLOR_R5_G6_B5_TO_RGB565(r, g, b)); + } + } + + fb_free(); + fb_free(); + fb_free(); + break; + } + default: { + break; + } + } +} + +#endif // IMLIB_ENABLE_ISP_OPS diff --git a/src/omv/imlib/mathop.c b/src/omv/imlib/mathop.c index d1ddc9976..c192b5f36 100644 --- a/src/omv/imlib/mathop.c +++ b/src/omv/imlib/mathop.c @@ -11,102 +11,6 @@ #include "imlib.h" #ifdef IMLIB_ENABLE_MATH_OPS -void imlib_gamma_corr(image_t *img, float gamma, float contrast, float brightness) -{ - gamma = IM_DIV(1.0, gamma); - switch (img->pixfmt) { - case PIXFORMAT_BINARY: { - float pScale = COLOR_BINARY_MAX - COLOR_BINARY_MIN; - float pDiv = 1 / pScale; - int *p_lut = fb_alloc((COLOR_BINARY_MAX - COLOR_BINARY_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); - - for (int i = COLOR_BINARY_MIN; i <= COLOR_BINARY_MAX; i++) { - int p = ((fast_powf(i * pDiv, gamma) * contrast) + brightness) * pScale; - p_lut[i] = IM_MIN(IM_MAX(p , COLOR_BINARY_MIN), COLOR_BINARY_MAX); - } - - for (int y = 0, yy = img->h; y < yy; y++) { - uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); - for (int x = 0, xx = img->w; x < xx; x++) { - int dataPixel = IMAGE_GET_BINARY_PIXEL_FAST(data, x); - int p = p_lut[dataPixel]; - IMAGE_PUT_BINARY_PIXEL_FAST(data, x, p); - } - } - - fb_free(); - break; - } - case PIXFORMAT_GRAYSCALE: { - float pScale = COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN; - float pDiv = 1 / pScale; - int *p_lut = fb_alloc((COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); - - for (int i = COLOR_GRAYSCALE_MIN; i <= COLOR_GRAYSCALE_MAX; i++) { - int p = ((fast_powf(i * pDiv, gamma) * contrast) + brightness) * pScale; - p_lut[i] = IM_MIN(IM_MAX(p , COLOR_GRAYSCALE_MIN), COLOR_GRAYSCALE_MAX); - } - - for (int y = 0, yy = img->h; y < yy; y++) { - uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); - for (int x = 0, xx = img->w; x < xx; x++) { - int dataPixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(data, x); - int p = p_lut[dataPixel]; - IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, x, p); - } - } - - fb_free(); - break; - } - case PIXFORMAT_RGB565: { - float rScale = COLOR_R5_MAX - COLOR_R5_MIN; - float gScale = COLOR_G6_MAX - COLOR_G6_MIN; - float bScale = COLOR_B5_MAX - COLOR_B5_MIN; - float rDiv = 1 / rScale; - float gDiv = 1 / gScale; - float bDiv = 1 / bScale; - int *r_lut = fb_alloc((COLOR_R5_MAX - COLOR_R5_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); - int *g_lut = fb_alloc((COLOR_G6_MAX - COLOR_G6_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); - int *b_lut = fb_alloc((COLOR_B5_MAX - COLOR_B5_MIN + 1) * sizeof(int), FB_ALLOC_NO_HINT); - - for (int i = COLOR_R5_MIN; i <= COLOR_R5_MAX; i++) { - int r = ((fast_powf(i * rDiv, gamma) * contrast) + brightness) * rScale; - r_lut[i] = IM_MIN(IM_MAX(r , COLOR_R5_MIN), COLOR_R5_MAX); - } - - for (int i = COLOR_G6_MIN; i <= COLOR_G6_MAX; i++) { - int g = ((fast_powf(i * gDiv, gamma) * contrast) + brightness) * gScale; - g_lut[i] = IM_MIN(IM_MAX(g , COLOR_G6_MIN), COLOR_G6_MAX); - } - - for (int i = COLOR_B5_MIN; i <= COLOR_B5_MAX; i++) { - int b = ((fast_powf(i * bDiv, gamma) * contrast) + brightness) * bScale; - b_lut[i] = IM_MIN(IM_MAX(b , COLOR_B5_MIN), COLOR_B5_MAX); - } - - for (int y = 0, yy = img->h; y < yy; y++) { - uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); - for (int x = 0, xx = img->w; x < xx; x++) { - int dataPixel = IMAGE_GET_RGB565_PIXEL_FAST(data, x); - int r = r_lut[COLOR_RGB565_TO_R5(dataPixel)]; - int g = g_lut[COLOR_RGB565_TO_G6(dataPixel)]; - int b = b_lut[COLOR_RGB565_TO_B5(dataPixel)]; - IMAGE_PUT_RGB565_PIXEL_FAST(data, x, COLOR_R5_G6_B5_TO_RGB565(r, g, b)); - } - } - - fb_free(); - fb_free(); - fb_free(); - break; - } - default: { - break; - } - } -} - void imlib_negate(image_t *img) { switch (img->pixfmt) { diff --git a/src/omv/modules/py_image.c b/src/omv/modules/py_image.c index a03cd6d09..18f4e7f6a 100644 --- a/src/omv/modules/py_image.c +++ b/src/omv/modules/py_image.c @@ -1808,6 +1808,67 @@ STATIC mp_obj_t py_image_flood_fill(uint n_args, const mp_obj_t *args, mp_map_t STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_flood_fill_obj, 2, py_image_flood_fill); #endif // IMLIB_ENABLE_FLOOD_FILL + +#ifdef IMLIB_ENABLE_ISP_OPS +////////////// +// ISP Methods +////////////// + +STATIC mp_obj_t py_awb(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + image_t *arg_img = + py_helper_arg_to_image_not_compressed(args[0]); + bool arg_max = + py_helper_keyword_float(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_max), false); + + imlib_awb(arg_img, arg_max); + return args[0]; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_awb_obj, 1, py_awb); + +STATIC mp_obj_t py_ccm(mp_obj_t img_obj, mp_obj_t ccm_obj) +{ + image_t *arg_img = + py_helper_arg_to_image_mutable(img_obj); + + size_t len; + mp_obj_t *items; + mp_obj_get_array(ccm_obj, &len, &items); + + if ((len != 9) && (len != 12)) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Expected a 3x3 or 4x3 matrix!")); + } + + float ccm[12] = {}; + for (size_t i = 0; i < len; i++) { + ccm[i] = mp_obj_get_float(items[i]); + } + + imlib_ccm(arg_img, ccm, len == 12); + return img_obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_ccm_obj, py_ccm); + +STATIC mp_obj_t py_image_gamma(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + image_t *arg_img = + py_helper_arg_to_image_mutable(args[0]); + float arg_gamma = + py_helper_keyword_float(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_gamma), 1.0f); + float arg_contrast = + py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_contrast), 1.0f); + float arg_brightness = + py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_brightness), 0.0f); + + fb_alloc_mark(); + imlib_gamma(arg_img, arg_gamma, arg_contrast, arg_brightness); + fb_alloc_free_till_mark(); + return args[0]; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_gamma_obj, 1, py_image_gamma); + +#endif // IMLIB_ENABLE_ISP_OPS + #ifdef IMLIB_ENABLE_BINARY_OPS ///////////////// // Binary Methods @@ -2100,24 +2161,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_close_obj, 2, py_image_close); // Math Methods /////////////// -STATIC mp_obj_t py_image_gamma_corr(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) -{ - image_t *arg_img = - py_helper_arg_to_image_mutable(args[0]); - float arg_gamma = - py_helper_keyword_float(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_gamma), 1.0f); - float arg_contrast = - py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_contrast), 1.0f); - float arg_brightness = - py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_brightness), 0.0f); - - fb_alloc_mark(); - imlib_gamma_corr(arg_img, arg_gamma, arg_contrast, arg_brightness); - fb_alloc_free_till_mark(); - return args[0]; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_gamma_corr_obj, 1, py_image_gamma_corr); - STATIC mp_obj_t py_image_negate(mp_obj_t img_obj) { imlib_negate(py_helper_arg_to_image_mutable(img_obj)); @@ -6093,6 +6136,18 @@ static const mp_rom_map_elem_t locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_mask_rectangle), MP_ROM_PTR(&py_image_mask_rectangle_obj)}, {MP_ROM_QSTR(MP_QSTR_mask_circle), MP_ROM_PTR(&py_image_mask_circle_obj)}, {MP_ROM_QSTR(MP_QSTR_mask_ellipse), MP_ROM_PTR(&py_image_mask_ellipse_obj)}, + /* ISP Methods */ +#ifdef IMLIB_ENABLE_ISP_OPS + {MP_ROM_QSTR(MP_QSTR_awb), MP_ROM_PTR(&py_awb_obj)}, + {MP_ROM_QSTR(MP_QSTR_ccm), MP_ROM_PTR(&py_ccm_obj)}, + {MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&py_image_gamma_obj)}, + {MP_ROM_QSTR(MP_QSTR_gamma_corr), MP_ROM_PTR(&py_image_gamma_obj)}, +#else + {MP_ROM_QSTR(MP_QSTR_awb), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_ccm), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_gamma_corr), MP_ROM_PTR(&py_func_unavailable_obj)}, +#endif // IMLIB_ENABLE_ISP_OPS /* Binary Methods */ #ifdef IMLIB_ENABLE_BINARY_OPS {MP_ROM_QSTR(MP_QSTR_binary), MP_ROM_PTR(&py_image_binary_obj)}, @@ -6135,7 +6190,6 @@ static const mp_rom_map_elem_t locals_dict_table[] = { #endif #ifdef IMLIB_ENABLE_MATH_OPS /* Math Methods */ - {MP_ROM_QSTR(MP_QSTR_gamma_corr), MP_ROM_PTR(&py_image_gamma_corr_obj)}, {MP_ROM_QSTR(MP_QSTR_negate), MP_ROM_PTR(&py_image_negate_obj)}, {MP_ROM_QSTR(MP_QSTR_assign), MP_ROM_PTR(&py_image_replace_obj)}, {MP_ROM_QSTR(MP_QSTR_replace), MP_ROM_PTR(&py_image_replace_obj)}, diff --git a/src/omv/ports/nrf/omv_portconfig.mk b/src/omv/ports/nrf/omv_portconfig.mk index 542d2f74a..cf5af132f 100644 --- a/src/omv/ports/nrf/omv_portconfig.mk +++ b/src/omv/ports/nrf/omv_portconfig.mk @@ -150,6 +150,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/imlib/, \ imlib.o \ integral.o \ integral_mw.o \ + isp.o \ jpegd.o \ jpeg.o \ lodepng.o \ diff --git a/src/omv/ports/rp2/omv_portconfig.cmake b/src/omv/ports/rp2/omv_portconfig.cmake index aee585352..6f11b0689 100644 --- a/src/omv/ports/rp2/omv_portconfig.cmake +++ b/src/omv/ports/rp2/omv_portconfig.cmake @@ -143,6 +143,7 @@ target_sources(${MICROPY_TARGET} PRIVATE ${TOP_DIR}/${OMV_DIR}/imlib/imlib.c ${TOP_DIR}/${OMV_DIR}/imlib/integral.c ${TOP_DIR}/${OMV_DIR}/imlib/integral_mw.c + ${TOP_DIR}/${OMV_DIR}/imlib/isp.c ${TOP_DIR}/${OMV_DIR}/imlib/jpegd.c ${TOP_DIR}/${OMV_DIR}/imlib/jpeg.c ${TOP_DIR}/${OMV_DIR}/imlib/lodepng.c diff --git a/src/omv/ports/stm32/omv_portconfig.mk b/src/omv/ports/stm32/omv_portconfig.mk index c4d6b044d..eacca9243 100644 --- a/src/omv/ports/stm32/omv_portconfig.mk +++ b/src/omv/ports/stm32/omv_portconfig.mk @@ -195,6 +195,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/imlib/, \ imlib.o \ integral.o \ integral_mw.o \ + isp.o \ jpegd.o \ jpeg.o \ lodepng.o \