From 7f798a82825e79d35839c45a861f010ab164e5ad Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 6 Apr 2018 00:19:36 -0400 Subject: [PATCH] Add flood fill The algorithm itself is setup to be used for future filters. --- src/omv/img/blob.c | 338 ++++++++++++++++++++++++++ src/omv/img/draw.c | 102 ++++++++ src/omv/img/imlib.c | 28 ++- src/omv/img/imlib.h | 38 ++- src/omv/py/py_image.c | 41 +++- src/omv/py/qstrdefsomv.h | 22 +- usr/examples/03-Drawing/flood_fill.py | 35 +++ 7 files changed, 581 insertions(+), 23 deletions(-) create mode 100644 usr/examples/03-Drawing/flood_fill.py diff --git a/src/omv/img/blob.c b/src/omv/img/blob.c index 0695dbf19..c0f1e122f 100644 --- a/src/omv/img/blob.c +++ b/src/omv/img/blob.c @@ -605,3 +605,341 @@ void imlib_find_blobs(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int } } } + +void imlib_flood_fill_int(image_t *out, image_t *img, int x, int y, + int seed_threshold, int floating_threshold, + flood_fill_call_back_t cb, void *data) +{ + lifo_t lifo; + size_t lifo_len; + lifo_alloc_all(&lifo, &lifo_len, sizeof(xylf_t)); + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + for(int seed_pixel = IMAGE_GET_BINARY_PIXEL(img, x, y);;) { + int left = x, right = x; + uint32_t *row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); + uint32_t *out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y); + + while ((left > 0) + && (!IMAGE_GET_BINARY_PIXEL_FAST(out_row, left - 1)) + && COLOR_BOUND_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, left - 1), seed_pixel, seed_threshold) + && COLOR_BOUND_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, left - 1), + IMAGE_GET_BINARY_PIXEL_FAST(row, left), floating_threshold)) { + left--; + } + + while ((right < (img->w - 1)) + && (!IMAGE_GET_BINARY_PIXEL_FAST(out_row, right + 1)) + && COLOR_BOUND_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, right + 1), seed_pixel, seed_threshold) + && COLOR_BOUND_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, right + 1), + IMAGE_GET_BINARY_PIXEL_FAST(row, right), floating_threshold)) { + right++; + } + + for (int i = left; i <= right; i++) { + IMAGE_SET_BINARY_PIXEL_FAST(out_row, i); + } + + bool break_out = false; + for(;;) { + if (lifo_size(&lifo) < lifo_len) { + uint32_t *old_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); + + if (y > 0) { + row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y - 1); + out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y - 1); + + bool recurse = false; + for (int i = left; i <= right; i++) { + if ((!IMAGE_GET_BINARY_PIXEL_FAST(out_row, i)) + && COLOR_BOUND_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, i), seed_pixel, seed_threshold) + && COLOR_BOUND_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, i), + IMAGE_GET_BINARY_PIXEL_FAST(old_row, i), floating_threshold)) { + xylf_t context; + context.x = x; + context.y = y; + context.l = left; + context.r = right; + lifo_enqueue(&lifo, &context); + x = i; + y = y - 1; + recurse = true; + break; + } + } + if (recurse) { + break; + } + } + + if (y < (img->h - 1)) { + row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y + 1); + out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y + 1); + + bool recurse = false; + for (int i = left; i <= right; i++) { + if ((!IMAGE_GET_BINARY_PIXEL_FAST(out_row, i)) + && COLOR_BOUND_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, i), seed_pixel, seed_threshold) + && COLOR_BOUND_BINARY(IMAGE_GET_BINARY_PIXEL_FAST(row, i), + IMAGE_GET_BINARY_PIXEL_FAST(old_row, i), floating_threshold)) { + xylf_t context; + context.x = x; + context.y = y; + context.l = left; + context.r = right; + lifo_enqueue(&lifo, &context); + x = i; + y = y + 1; + recurse = true; + break; + } + } + if (recurse) { + break; + } + } + } + + if (cb) cb(img, y, left, right, data); + + if (!lifo_size(&lifo)) { + break_out = true; + break; + } + + xylf_t context; + lifo_dequeue(&lifo, &context); + x = context.x; + y = context.y; + left = context.l; + right = context.r; + } + + if (break_out) { + break; + } + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + for(int seed_pixel = IMAGE_GET_GRAYSCALE_PIXEL(img, x, y);;) { + int left = x, right = x; + uint8_t *row = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); + uint32_t *out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y); + + while ((left > 0) + && (!IMAGE_GET_BINARY_PIXEL_FAST(out_row, left - 1)) + && COLOR_BOUND_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, left - 1), seed_pixel, seed_threshold) + && COLOR_BOUND_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, left - 1), + IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, left), floating_threshold)) { + left--; + } + + while ((right < (img->w - 1)) + && (!IMAGE_GET_BINARY_PIXEL_FAST(out_row, right + 1)) + && COLOR_BOUND_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, right + 1), seed_pixel, seed_threshold) + && COLOR_BOUND_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, right + 1), + IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, right), floating_threshold)) { + right++; + } + + for (int i = left; i <= right; i++) { + IMAGE_SET_BINARY_PIXEL_FAST(out_row, i); + } + + bool break_out = false; + for(;;) { + if (lifo_size(&lifo) < lifo_len) { + uint8_t *old_row = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); + + if (y > 0) { + row = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y - 1); + out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y - 1); + + bool recurse = false; + for (int i = left; i <= right; i++) { + if ((!IMAGE_GET_BINARY_PIXEL_FAST(out_row, i)) + && COLOR_BOUND_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, i), seed_pixel, seed_threshold) + && COLOR_BOUND_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, i), + IMAGE_GET_GRAYSCALE_PIXEL_FAST(old_row, i), floating_threshold)) { + xylf_t context; + context.x = x; + context.y = y; + context.l = left; + context.r = right; + lifo_enqueue(&lifo, &context); + x = i; + y = y - 1; + recurse = true; + break; + } + } + if (recurse) { + break; + } + } + + if (y < (img->h - 1)) { + row = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y + 1); + out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y + 1); + + bool recurse = false; + for (int i = left; i <= right; i++) { + if ((!IMAGE_GET_BINARY_PIXEL_FAST(out_row, i)) + && COLOR_BOUND_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, i), seed_pixel, seed_threshold) + && COLOR_BOUND_GRAYSCALE(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row, i), + IMAGE_GET_GRAYSCALE_PIXEL_FAST(old_row, i), floating_threshold)) { + xylf_t context; + context.x = x; + context.y = y; + context.l = left; + context.r = right; + lifo_enqueue(&lifo, &context); + x = i; + y = y + 1; + recurse = true; + break; + } + } + if (recurse) { + break; + } + } + } + + if (cb) cb(img, y, left, right, data); + + if (!lifo_size(&lifo)) { + break_out = true; + break; + } + + xylf_t context; + lifo_dequeue(&lifo, &context); + x = context.x; + y = context.y; + left = context.l; + right = context.r; + } + + if (break_out) { + break; + } + } + break; + } + case IMAGE_BPP_RGB565: { + for(int seed_pixel = IMAGE_GET_RGB565_PIXEL(img, x, y);;) { + int left = x, right = x; + uint16_t *row = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); + uint32_t *out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y); + + while ((left > 0) + && (!IMAGE_GET_BINARY_PIXEL_FAST(out_row, left - 1)) + && COLOR_BOUND_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, left - 1), seed_pixel, seed_threshold) + && COLOR_BOUND_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, left - 1), + IMAGE_GET_RGB565_PIXEL_FAST(row, left), floating_threshold)) { + left--; + } + + while ((right < (img->w - 1)) + && (!IMAGE_GET_BINARY_PIXEL_FAST(out_row, right + 1)) + && COLOR_BOUND_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, right + 1), seed_pixel, seed_threshold) + && COLOR_BOUND_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, right + 1), + IMAGE_GET_RGB565_PIXEL_FAST(row, right), floating_threshold)) { + right++; + } + + for (int i = left; i <= right; i++) { + IMAGE_SET_BINARY_PIXEL_FAST(out_row, i); + } + + bool break_out = false; + for(;;) { + if (lifo_size(&lifo) < lifo_len) { + uint16_t *old_row = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); + + if (y > 0) { + row = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y - 1); + out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y - 1); + + bool recurse = false; + for (int i = left; i <= right; i++) { + if ((!IMAGE_GET_BINARY_PIXEL_FAST(out_row, i)) + && COLOR_BOUND_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, i), seed_pixel, seed_threshold) + && COLOR_BOUND_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, i), + IMAGE_GET_RGB565_PIXEL_FAST(old_row, i), floating_threshold)) { + xylf_t context; + context.x = x; + context.y = y; + context.l = left; + context.r = right; + lifo_enqueue(&lifo, &context); + x = i; + y = y - 1; + recurse = true; + break; + } + } + if (recurse) { + break; + } + } + + if (y < (img->h - 1)) { + row = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y + 1); + out_row = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(out, y + 1); + + bool recurse = false; + for (int i = left; i <= right; i++) { + if ((!IMAGE_GET_BINARY_PIXEL_FAST(out_row, i)) + && COLOR_BOUND_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, i), seed_pixel, seed_threshold) + && COLOR_BOUND_RGB565(IMAGE_GET_RGB565_PIXEL_FAST(row, i), + IMAGE_GET_RGB565_PIXEL_FAST(old_row, i), floating_threshold)) { + xylf_t context; + context.x = x; + context.y = y; + context.l = left; + context.r = right; + lifo_enqueue(&lifo, &context); + x = i; + y = y + 1; + recurse = true; + break; + } + } + if (recurse) { + break; + } + } + } + + if (cb) cb(img, y, left, right, data); + + if (!lifo_size(&lifo)) { + break_out = true; + break; + } + + xylf_t context; + lifo_dequeue(&lifo, &context); + x = context.x; + y = context.y; + left = context.l; + right = context.r; + } + + if (break_out) { + break; + } + } + break; + } + default: { + break; + } + } + + lifo_free(&lifo); +} diff --git a/src/omv/img/draw.c b/src/omv/img/draw.c index 8001776cf..80440f620 100644 --- a/src/omv/img/draw.c +++ b/src/omv/img/draw.c @@ -209,3 +209,105 @@ void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int x_off += (g->w * scale) + x_spacing; } } + +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) +{ + if ((0 <= x) && (x < img->w) && (0 <= y) && (y < img->h)) { + image_t out; + out.w = img->w; + out.h = img->h; + out.bpp = IMAGE_BPP_BINARY; + out.data = fb_alloc0(image_size(&out)); + + if (mask) { + for (int y = 0, yy = out.h; y < yy; y++) { + uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y); + for (int x = 0, xx = out.w; x < xx; x++) { + if (image_get_mask_pixel(mask, x, y)) IMAGE_SET_BINARY_PIXEL_FAST(row_ptr, x); + } + } + } + + int color_seed_threshold = 0; + int color_floating_threshold = 0; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + color_seed_threshold = fast_roundf(seed_threshold * COLOR_BINARY_MAX); + color_floating_threshold = fast_roundf(floating_threshold * COLOR_BINARY_MAX); + break; + } + case IMAGE_BPP_GRAYSCALE: { + color_seed_threshold = fast_roundf(seed_threshold * COLOR_GRAYSCALE_MAX); + color_floating_threshold = fast_roundf(floating_threshold * COLOR_GRAYSCALE_MAX); + break; + } + case IMAGE_BPP_RGB565: { + color_seed_threshold = COLOR_R5_G6_B5_TO_RGB565(fast_roundf(seed_threshold * COLOR_R5_MAX), + fast_roundf(seed_threshold * COLOR_G6_MAX), + fast_roundf(seed_threshold * COLOR_B5_MAX)); + color_floating_threshold = COLOR_R5_G6_B5_TO_RGB565(fast_roundf(floating_threshold * COLOR_R5_MAX), + fast_roundf(floating_threshold * COLOR_G6_MAX), + fast_roundf(floating_threshold * COLOR_B5_MAX)); + break; + } + default: { + break; + } + } + + imlib_flood_fill_int(&out, img, x, y, color_seed_threshold, color_floating_threshold, NULL, NULL); + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + for (int y = 0, yy = out.h; y < yy; y++) { + uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); + uint32_t *out_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y); + for (int x = 0, xx = out.w; x < xx; x++) { + if (IMAGE_GET_BINARY_PIXEL_FAST(out_row_ptr, x) ^ invert) { + IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr, x, c); + } else if (clear_background) { + IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr, x, 0); + } + } + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + for (int y = 0, yy = out.h; y < yy; y++) { + uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); + uint32_t *out_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y); + for (int x = 0, xx = out.w; x < xx; x++) { + if (IMAGE_GET_BINARY_PIXEL_FAST(out_row_ptr, x) ^ invert) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, x, c); + } else if (clear_background) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, x, 0); + } + } + } + break; + } + case IMAGE_BPP_RGB565: { + for (int y = 0, yy = out.h; y < yy; y++) { + uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); + uint32_t *out_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y); + for (int x = 0, xx = out.w; x < xx; x++) { + if (IMAGE_GET_BINARY_PIXEL_FAST(out_row_ptr, x) ^ invert) { + IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, c); + } else if (clear_background) { + IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, 0); + } + } + } + break; + } + default: { + break; + } + } + + fb_free(); + } +} diff --git a/src/omv/img/imlib.c b/src/omv/img/imlib.c index f1424e199..4367f607b 100644 --- a/src/omv/img/imlib.c +++ b/src/omv/img/imlib.c @@ -194,20 +194,24 @@ size_t image_size(image_t *ptr) bool image_get_mask_pixel(image_t *ptr, int x, int y) { - switch (ptr->bpp) { - case IMAGE_BPP_BINARY: { - return IMAGE_GET_BINARY_PIXEL(ptr, x, y); - } - case IMAGE_BPP_GRAYSCALE: { - return COLOR_GRAYSCALE_TO_BINARY(IMAGE_GET_GRAYSCALE_PIXEL(ptr, x, y)); - } - case IMAGE_BPP_RGB565: { - return COLOR_RGB565_TO_BINARY(IMAGE_GET_RGB565_PIXEL(ptr, x, y)); - } - default: { - return false; + if ((0 <= x) && (x < ptr->w) && (0 <= y) && (y < ptr->h)) { + switch (ptr->bpp) { + case IMAGE_BPP_BINARY: { + return IMAGE_GET_BINARY_PIXEL(ptr, x, y); + } + case IMAGE_BPP_GRAYSCALE: { + return COLOR_GRAYSCALE_TO_BINARY(IMAGE_GET_GRAYSCALE_PIXEL(ptr, x, y)); + } + case IMAGE_BPP_RGB565: { + return COLOR_RGB565_TO_BINARY(IMAGE_GET_RGB565_PIXEL(ptr, x, y)); + } + default: { + return false; + } } } + + return false; } // Gamma uncompress diff --git a/src/omv/img/imlib.h b/src/omv/img/imlib.h index c9ed1c411..2c72bdb3d 100644 --- a/src/omv/img/imlib.h +++ b/src/omv/img/imlib.h @@ -151,7 +151,35 @@ color_thresholds_list_lnk_data_t; uint8_t _l = COLOR_RGB565_TO_L(_pixel); \ int8_t _a = COLOR_RGB565_TO_A(_pixel); \ int8_t _b = COLOR_RGB565_TO_B(_pixel); \ - ((_threshold->LMin <= _l) && (_l <= _threshold->LMax) && (_threshold->AMin <= _a) && (_a <= _threshold->AMax) && (_threshold->BMin <= _b) && (_b <= _threshold->BMax)) ^ _invert; \ + ((_threshold->LMin <= _l) && (_l <= _threshold->LMax) && \ + (_threshold->AMin <= _a) && (_a <= _threshold->AMax) && \ + (_threshold->BMin <= _b) && (_b <= _threshold->BMax)) ^ _invert; \ +}) + +#define COLOR_BOUND_BINARY(pixel0, pixel1, threshold) \ +({ \ + __typeof__ (pixel0) _pixel0 = (pixel0); \ + __typeof__ (pixel1) _pixel1 = (pixel1); \ + __typeof__ (threshold) _threshold = (threshold); \ + (abs(_pixel0 - _pixel1) <= _threshold); \ +}) + +#define COLOR_BOUND_GRAYSCALE(pixel0, pixel1, threshold) \ +({ \ + __typeof__ (pixel0) _pixel0 = (pixel0); \ + __typeof__ (pixel1) _pixel1 = (pixel1); \ + __typeof__ (threshold) _threshold = (threshold); \ + (abs(_pixel0 - _pixel1) <= _threshold); \ +}) + +#define COLOR_BOUND_RGB565(pixel0, pixel1, threshold) \ +({ \ + __typeof__ (pixel0) _pixel0 = (pixel0); \ + __typeof__ (pixel1) _pixel1 = (pixel1); \ + __typeof__ (threshold) _threshold = (threshold); \ + (abs(COLOR_RGB565_TO_R5(_pixel0) - COLOR_RGB565_TO_R5(_pixel1)) <= COLOR_RGB565_TO_R5(_threshold)) && \ + (abs(COLOR_RGB565_TO_G6(_pixel0) - COLOR_RGB565_TO_G6(_pixel1)) <= COLOR_RGB565_TO_G6(_threshold)) && \ + (abs(COLOR_RGB565_TO_B5(_pixel0) - COLOR_RGB565_TO_B5(_pixel1)) <= COLOR_RGB565_TO_B5(_threshold)); \ }) #define COLOR_BINARY_MIN 0 @@ -942,6 +970,7 @@ typedef struct img_read_settings { } img_read_settings_t; typedef void (*line_op_t)(image_t*, int, void*, void*, bool); +typedef void (*flood_fill_call_back_t)(image_t *, int, int, int, void *); typedef enum descriptor_type { DESC_LBP, @@ -1210,6 +1239,10 @@ void imlib_edge_canny(image_t *src, rectangle_t *roi, int low_thresh, int high_t // HoG void imlib_find_hog(image_t *src, rectangle_t *roi, int cell_size); +// Helper Functions +void imlib_flood_fill_int(image_t *out, image_t *img, int x, int y, + int seed_threshold, int floating_threshold, + flood_fill_call_back_t cb, void *data); // Drawing Functions int imlib_get_pixel(image_t *img, int x, int y); void imlib_set_pixel(image_t *img, int x, int y, int p); @@ -1217,6 +1250,9 @@ void imlib_draw_line(image_t *img, int x0, int y0, int x1, int y1, int c, int th void imlib_draw_rectangle(image_t *img, int rx, int ry, int rw, int rh, int c, int thickness, bool fill); void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c, int thickness, bool fill); void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c, int scale, int x_spacing, int y_spacing); +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); // Binary Functions void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero, image_t *mask); void imlib_invert(image_t *img); diff --git a/src/omv/py/py_image.c b/src/omv/py/py_image.c index ec3654f2a..46eef4b33 100644 --- a/src/omv/py/py_image.c +++ b/src/omv/py/py_image.c @@ -1070,7 +1070,7 @@ STATIC mp_obj_t py_image_draw_circle(uint n_args, const mp_obj_t *args, mp_map_t imlib_draw_circle(arg_img, arg_cx, arg_cy, arg_cr, arg_c, arg_thickness, arg_fill); return args[0]; } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_circle_obj, 4, py_image_draw_circle); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_circle_obj, 2, py_image_draw_circle); STATIC mp_obj_t py_image_draw_string(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { @@ -1094,7 +1094,7 @@ STATIC mp_obj_t py_image_draw_string(uint n_args, const mp_obj_t *args, mp_map_t imlib_draw_string(arg_img, arg_x_off, arg_y_off, arg_str, arg_c, arg_scale, arg_x_spacing, arg_y_spacing); return args[0]; } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_string_obj, 4, py_image_draw_string); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_string_obj, 2, py_image_draw_string); STATIC mp_obj_t py_image_draw_cross(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { @@ -1185,6 +1185,40 @@ STATIC mp_obj_t py_image_draw_keypoints(uint n_args, const mp_obj_t *args, mp_ma } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_keypoints_obj, 2, py_image_draw_keypoints); +STATIC mp_obj_t py_image_flood_fill(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]); + + const mp_obj_t *arg_vec; + uint offset = py_helper_consume_array(n_args, args, 1, 2, &arg_vec); + int arg_x_off = mp_obj_get_int(arg_vec[0]); + int arg_y_off = mp_obj_get_int(arg_vec[1]); + + float arg_seed_threshold = + py_helper_keyword_float(n_args, args, offset + 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_seed_threshold), 0.05); + PY_ASSERT_TRUE_MSG((0.0f <= arg_seed_threshold) && (arg_seed_threshold <= 1.0f), + "Error: 0.0 <= seed_threshold <= 1.0!"); + float arg_floating_threshold = + py_helper_keyword_float(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_floating_threshold), 0.05); + PY_ASSERT_TRUE_MSG((0.0f <= arg_floating_threshold) && (arg_floating_threshold <= 1.0f), + "Error: 0.0 <= floating_threshold <= 1.0!"); + int arg_c = + py_helper_keyword_color(arg_img, n_args, args, offset + 2, kw_args, -1); // White. + bool arg_invert = + py_helper_keyword_float(n_args, args, offset + 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), false); + bool clear_background = + py_helper_keyword_float(n_args, args, offset + 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_clear_background), false); + image_t *arg_msk = + py_helper_keyword_to_image_mutable_mask(n_args, args, offset + 5, kw_args); + + fb_alloc_mark(); + imlib_flood_fill(arg_img, arg_x_off, arg_y_off, arg_seed_threshold, arg_floating_threshold, + arg_c, arg_invert, clear_background, arg_msk); + fb_alloc_free_till_mark(); + return args[0]; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_flood_fill_obj, 2, py_image_flood_fill); + ///////////////// // Binary Methods ///////////////// @@ -2002,10 +2036,8 @@ STATIC mp_obj_t py_image_bilateral(uint n_args, const mp_obj_t *args, mp_map_t * py_helper_arg_to_ksize(args[1]); float arg_color_sigma = py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_color_sigma), 0.1); - PY_ASSERT_TRUE_MSG((0 <= arg_color_sigma), "Error: 0 <= color_sigma!"); float arg_space_sigma = py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_space_sigma), 1); - PY_ASSERT_TRUE_MSG((0 <= arg_space_sigma), "Error: 0 <= space_sigma!"); bool arg_threshold = py_helper_keyword_int(n_args, args, 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), false); int arg_offset = @@ -4906,6 +4938,7 @@ static const mp_rom_map_elem_t locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_draw_string), MP_ROM_PTR(&py_image_draw_string_obj)}, {MP_ROM_QSTR(MP_QSTR_draw_cross), MP_ROM_PTR(&py_image_draw_cross_obj)}, {MP_ROM_QSTR(MP_QSTR_draw_arrow), MP_ROM_PTR(&py_image_draw_arrow_obj)}, + {MP_ROM_QSTR(MP_QSTR_flood_fill), MP_ROM_PTR(&py_image_flood_fill_obj)}, {MP_ROM_QSTR(MP_QSTR_draw_keypoints), MP_ROM_PTR(&py_image_draw_keypoints_obj)}, /* Binary Methods */ {MP_ROM_QSTR(MP_QSTR_binary), MP_ROM_PTR(&py_image_binary_obj)}, diff --git a/src/omv/py/qstrdefsomv.h b/src/omv/py/qstrdefsomv.h index d333f4199..f9fb52cf2 100644 --- a/src/omv/py/qstrdefsomv.h +++ b/src/omv/py/qstrdefsomv.h @@ -38,7 +38,6 @@ Q(width) Q(height) Q(format) Q(size) -Q(gaussian) Q(midpoint_pool) Q(midpoint_pooled) Q(mean_pool) @@ -47,7 +46,6 @@ Q(find_template) Q(kp_desc) Q(lbp_desc) Q(Cascade) -Q(mask_ellipse) Q(find_features) Q(find_keypoints) Q(find_lbp) @@ -317,6 +315,9 @@ Q(rgbtuple) Q(set_pixel) Q(color) +// Clear +Q(clear) + // Draw Line Q(draw_line) // duplicate Q(color) @@ -360,11 +361,20 @@ Q(draw_keypoints) // duplicate Q(thickness) // duplicate Q(fill) +// Flood Fill +Q(flood_fill) +Q(seed_threshold) +Q(floating_threshold) +// duplicate Q(color) +Q(invert) +Q(clear_background) +Q(mask) + // Binary Q(binary) -Q(invert) +// duplicate Q(invert) Q(zero) -Q(mask) +// duplicate Q(mask) // Invert // duplicate Q(invert) @@ -401,12 +411,12 @@ Q(b_xnor) // Erode Q(erode) -Q(threshold) +// duplicate Q(threshold) // duplicate Q(mask) // Dilate Q(dilate) -// duplicate Q(threshold) +Q(threshold) // duplicate Q(mask) // Open diff --git a/usr/examples/03-Drawing/flood_fill.py b/usr/examples/03-Drawing/flood_fill.py new file mode 100644 index 000000000..4857ce3ca --- /dev/null +++ b/usr/examples/03-Drawing/flood_fill.py @@ -0,0 +1,35 @@ +# Flood Fill +# +# This example shows off flood filling areas in the image. + +import sensor, image, time + +sensor.reset() +sensor.set_pixformat(sensor.RGB565) # or GRAYSCALE... +sensor.set_framesize(sensor.QVGA) # or QQVGA... +sensor.skip_frames(time = 2000) +clock = time.clock() + +while(True): + clock.tick() + + # seed_threshold controls the maximum allowed difference between + # the initial pixel and any filled pixels. It's important to + # set this such that flood fill doesn't fill the whole image. + + # floating_threshold controls the maximum allowed difference + # between any two pixels. This can easily fill the whole image + # with even a very low threshold. + + # flood_fill will fill pixels that both thresholds. + + # You can invert what gets filled with "invert" and clear + # everything but the filled area with "clear_background". + + x = sensor.width() // 2 + y = sensor.height() // 2 + img = sensor.snapshot().flood_fill(x, y, \ + seed_threshold=0.05, floating_thresholds=0.05, \ + color=(255, 0, 0), invert=False, clear_background=False) + + print(clock.fps())