From 8bcf5ac0c37df082ff6229ebd6cadb5935dbcbc3 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Tue, 1 Jan 2019 02:28:40 -0500 Subject: [PATCH] Add gamma correction A user requested this a while ago. This feature also ads contrast and brightness correction. The code is fast! --- .../04-Image-Filters/gamma_correction.py | 21 ++++ src/omv/img/fmath.c | 7 ++ src/omv/img/fmath.h | 1 + src/omv/img/imlib.h | 1 + src/omv/img/mathop.c | 96 +++++++++++++++++++ src/omv/py/py_image.c | 20 ++++ src/omv/py/qstrdefsomv.h | 6 ++ 7 files changed, 152 insertions(+) create mode 100644 scripts/examples/04-Image-Filters/gamma_correction.py diff --git a/scripts/examples/04-Image-Filters/gamma_correction.py b/scripts/examples/04-Image-Filters/gamma_correction.py new file mode 100644 index 000000000..2dd1137c5 --- /dev/null +++ b/scripts/examples/04-Image-Filters/gamma_correction.py @@ -0,0 +1,21 @@ +# Gamma Correction +# +# This example shows off gamma correction to make the image brighter. The gamma +# correction method can also fix contrast and brightness too. + +import sensor, image, time + +sensor.reset() +sensor.set_pixformat(sensor.RGB565) +sensor.set_framesize(sensor.QVGA) +sensor.skip_frames(time = 2000) +clock = time.clock() + +while(True): + clock.tick() + + # Gamma, contrast, and brightness correction are applied to each color channel. The + # values are scaled to the range per color channel per image type... + img = sensor.snapshot().gamma_corr(gamma = 0.5, contrast = 1.0, brightness = 0.0) + + print(clock.fps()) diff --git a/src/omv/img/fmath.c b/src/omv/img/fmath.c index f6c6a1a51..84889f654 100644 --- a/src/omv/img/fmath.c +++ b/src/omv/img/fmath.c @@ -189,3 +189,10 @@ float fast_log(float x) { return 0.69314718f * fast_log2 (x); } + +float fast_powf(float a, float b) +{ + union { float d; int x; } u = { a }; + u.x = (int)((b * (u.x - 1064866805)) + 1064866805); + return u.d; +} diff --git a/src/omv/img/fmath.h b/src/omv/img/fmath.h index 195c476b0..8cd350940 100644 --- a/src/omv/img/fmath.h +++ b/src/omv/img/fmath.h @@ -20,6 +20,7 @@ float fast_cbrtf(float d); float fast_fabsf(float d); float fast_log(float x); float fast_log2(float x); +float fast_powf(float a, float b); extern const float cos_table[360]; extern const float sin_table[360]; #endif // __FMATH_H__ diff --git a/src/omv/img/imlib.h b/src/omv/img/imlib.h index 5816dd343..88672e925 100644 --- a/src/omv/img/imlib.h +++ b/src/omv/img/imlib.h @@ -1296,6 +1296,7 @@ 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, 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/img/mathop.c b/src/omv/img/mathop.c index ceccf7bcb..77027bcd3 100644 --- a/src/omv/img/mathop.c +++ b/src/omv/img/mathop.c @@ -6,6 +6,102 @@ #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->bpp) { + case IMAGE_BPP_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)); + + 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 IMAGE_BPP_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)); + + 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 IMAGE_BPP_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)); + int *g_lut = fb_alloc((COLOR_G6_MAX - COLOR_G6_MIN + 1) * sizeof(int)); + int *b_lut = fb_alloc((COLOR_B5_MAX - COLOR_B5_MIN + 1) * sizeof(int)); + + 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->bpp) { diff --git a/src/omv/py/py_image.c b/src/omv/py/py_image.c index d6803e729..8d4dd1410 100644 --- a/src/omv/py/py_image.c +++ b/src/omv/py/py_image.c @@ -1823,6 +1823,25 @@ STATIC mp_obj_t py_image_black_hat(uint n_args, const mp_obj_t *args, mp_map_t * } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_black_hat_obj, 2, py_image_black_hat); +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) { fb_alloc_mark(); @@ -5349,6 +5368,7 @@ static const mp_rom_map_elem_t locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_top_hat), MP_ROM_PTR(&py_image_top_hat_obj)}, {MP_ROM_QSTR(MP_QSTR_black_hat), MP_ROM_PTR(&py_image_black_hat_obj)}, /* 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_replace), MP_ROM_PTR(&py_image_replace_obj)}, {MP_ROM_QSTR(MP_QSTR_add), MP_ROM_PTR(&py_image_add_obj)}, diff --git a/src/omv/py/qstrdefsomv.h b/src/omv/py/qstrdefsomv.h index 7d7889361..7c88b820c 100644 --- a/src/omv/py/qstrdefsomv.h +++ b/src/omv/py/qstrdefsomv.h @@ -518,6 +518,12 @@ Q(black_hat) // duplicate Q(threshold) // duplicate Q(mask) +// Gamma Correct +Q(gamma_corr) +Q(gamma) +Q(contrast) +Q(brightness) + // Negate Q(negate)