diff --git a/src/Makefile b/src/Makefile index 8106b5d33..636616957 100755 --- a/src/Makefile +++ b/src/Makefile @@ -144,6 +144,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/, \ ) FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\ + binary.o \ blob.o \ qrcode.o \ apriltag.o \ diff --git a/src/omv/Makefile b/src/omv/Makefile index cc26aade4..cdfa41e59 100644 --- a/src/omv/Makefile +++ b/src/omv/Makefile @@ -22,6 +22,7 @@ SRCS += $(addprefix , \ ) SRCS += $(addprefix img/, \ + binary.c \ blob.c \ qrcode.c \ apriltag.c \ diff --git a/src/omv/img/binary.c b/src/omv/img/binary.c new file mode 100644 index 000000000..21b235f33 --- /dev/null +++ b/src/omv/img/binary.c @@ -0,0 +1,713 @@ +/* This file is part of the OpenMV project. + * Copyright (c) 2013-2018 Ibrahim Abdelkader & Kwabena W. Agyeman + * This work is licensed under the MIT license, see the file LICENSE for details. + */ + +#include "imlib.h" + +void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero) +{ + for (list_lnk_t *it = iterator_start_from_head(thresholds); it; it = iterator_next(it)) { + color_thresholds_list_lnk_data_t lnk_data; + iterator_get(thresholds, it, &lnk_data); + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + break; + } + case IMAGE_BPP_GRAYSCALE: { + if (!zero) { + for (uint8_t *start = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, 0), + *end = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, img->h); + start < end; start++) { + *start = COLOR_THRESHOLD_GRAYSCALE(*start, &lnk_data, invert) + ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN; + } + } else { + for (uint8_t *start = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, 0), + *end = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, img->h); + start < end; start++) { + if (COLOR_THRESHOLD_GRAYSCALE(*start, &lnk_data, invert)) *start = + COLOR_GRAYSCALE_BINARY_MIN; + } + } + break; + } + case IMAGE_BPP_RGB565: { + if (!zero) { + for (uint16_t *start = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, 0), + *end = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, img->h); + start < end; start++) { + *start = COLOR_THRESHOLD_RGB565(*start, &lnk_data, invert) + ? COLOR_RGB565_BINARY_MAX : COLOR_RGB565_BINARY_MIN; + } + } else { + for (uint16_t *start = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, 0), + *end = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, img->h); + start < end; start++) { + if (COLOR_THRESHOLD_RGB565(*start, &lnk_data, invert)) *start = + COLOR_RGB565_BINARY_MIN; + } + } + break; + } + default: { + break; + } + } + } +} + +void imlib_invert(image_t *img) +{ + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + for (uint32_t *start = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, 0), + *end = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, img->h); + start < end; start++) { + *start = ~*start; + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + for (uint8_t *start = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, 0), + *end = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, img->h); + start < end; start++) { + *start = ~*start; + } + break; + } + case IMAGE_BPP_RGB565: { + for (uint16_t *start = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, 0), + *end = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, img->h); + start < end; start++) { + *start = ~*start; + } + break; + } + default: { + break; + } + } +} + +static void imlib_b_and_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) +{ + image_t *mask = (image_t *) data; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_BINARY_LINE_LEN(img); i < j; i++) { + data[i] &= ((uint32_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_BINARY_PIXEL_FAST(data, i, + (IMAGE_GET_BINARY_PIXEL_FAST(data, i) + & IMAGE_GET_BINARY_PIXEL_FAST(((uint32_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_GRAYSCALE_LINE_LEN(img); i < j; i++) { + data[i] &= ((uint8_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, i, + (IMAGE_GET_GRAYSCALE_PIXEL_FAST(data, i) + & IMAGE_GET_GRAYSCALE_PIXEL_FAST(((uint8_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_RGB565: { + uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_RGB565_LINE_LEN(img); i < j; i++) { + data[i] &= ((uint16_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_RGB565_PIXEL_FAST(data, i, + (IMAGE_GET_RGB565_PIXEL_FAST(data, i) + & IMAGE_GET_RGB565_PIXEL_FAST(((uint16_t *) other), i))); + } + } + } + break; + } + default: { + break; + } + } +} + +void imlib_b_and(image_t *img, const char *path, image_t *other, image_t *mask) +{ + imlib_image_operation(img, path, other, imlib_b_and_line_op, mask); +} + +static void imlib_b_nand_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) +{ + image_t *mask = (image_t *) data; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_BINARY_LINE_LEN(img); i < j; i++) { + data[i] &= ~((uint32_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_BINARY_PIXEL_FAST(data, i, + (IMAGE_GET_BINARY_PIXEL_FAST(data, i) + & ~IMAGE_GET_BINARY_PIXEL_FAST(((uint32_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_GRAYSCALE_LINE_LEN(img); i < j; i++) { + data[i] &= ~((uint8_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, i, + (IMAGE_GET_GRAYSCALE_PIXEL_FAST(data, i) + & ~IMAGE_GET_GRAYSCALE_PIXEL_FAST(((uint8_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_RGB565: { + uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_RGB565_LINE_LEN(img); i < j; i++) { + data[i] &= ~((uint16_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_RGB565_PIXEL_FAST(data, i, + (IMAGE_GET_RGB565_PIXEL_FAST(data, i) + & ~IMAGE_GET_RGB565_PIXEL_FAST(((uint16_t *) other), i))); + } + } + } + break; + } + default: { + break; + } + } +} + +void imlib_b_nand(image_t *img, const char *path, image_t *other, image_t *mask) +{ + imlib_image_operation(img, path, other, imlib_b_nand_line_op, mask); +} + +static void imlib_b_or_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) +{ + image_t *mask = (image_t *) data; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_BINARY_LINE_LEN(img); i < j; i++) { + data[i] |= ((uint32_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_BINARY_PIXEL_FAST(data, i, + (IMAGE_GET_BINARY_PIXEL_FAST(data, i) + | IMAGE_GET_BINARY_PIXEL_FAST(((uint32_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_GRAYSCALE_LINE_LEN(img); i < j; i++) { + data[i] |= ((uint8_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, i, + (IMAGE_GET_GRAYSCALE_PIXEL_FAST(data, i) + | IMAGE_GET_GRAYSCALE_PIXEL_FAST(((uint8_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_RGB565: { + uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_RGB565_LINE_LEN(img); i < j; i++) { + data[i] |= ((uint16_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_RGB565_PIXEL_FAST(data, i, + (IMAGE_GET_RGB565_PIXEL_FAST(data, i) + | IMAGE_GET_RGB565_PIXEL_FAST(((uint16_t *) other), i))); + } + } + } + break; + } + default: { + break; + } + } +} + +void imlib_b_or(image_t *img, const char *path, image_t *other, image_t *mask) +{ + imlib_image_operation(img, path, other, imlib_b_or_line_op, mask); +} + +static void imlib_b_nor_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) +{ + image_t *mask = (image_t *) data; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_BINARY_LINE_LEN(img); i < j; i++) { + data[i] |= ~((uint32_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_BINARY_PIXEL_FAST(data, i, + (IMAGE_GET_BINARY_PIXEL_FAST(data, i) + | ~IMAGE_GET_BINARY_PIXEL_FAST(((uint32_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_GRAYSCALE_LINE_LEN(img); i < j; i++) { + data[i] |= ~((uint8_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, i, + (IMAGE_GET_GRAYSCALE_PIXEL_FAST(data, i) + | ~IMAGE_GET_GRAYSCALE_PIXEL_FAST(((uint8_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_RGB565: { + uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_RGB565_LINE_LEN(img); i < j; i++) { + data[i] |= ~((uint16_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_RGB565_PIXEL_FAST(data, i, + (IMAGE_GET_RGB565_PIXEL_FAST(data, i) + | ~IMAGE_GET_RGB565_PIXEL_FAST(((uint16_t *) other), i))); + } + } + } + break; + } + default: { + break; + } + } +} + +void imlib_b_nor(image_t *img, const char *path, image_t *other, image_t *mask) +{ + imlib_image_operation(img, path, other, imlib_b_nor_line_op, mask); +} + +static void imlib_b_xor_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) +{ + image_t *mask = (image_t *) data; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_BINARY_LINE_LEN(img); i < j; i++) { + data[i] ^= ((uint32_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_BINARY_PIXEL_FAST(data, i, + (IMAGE_GET_BINARY_PIXEL_FAST(data, i) + ^ IMAGE_GET_BINARY_PIXEL_FAST(((uint32_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_GRAYSCALE_LINE_LEN(img); i < j; i++) { + data[i] ^= ((uint8_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, i, + (IMAGE_GET_GRAYSCALE_PIXEL_FAST(data, i) + ^ IMAGE_GET_GRAYSCALE_PIXEL_FAST(((uint8_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_RGB565: { + uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_RGB565_LINE_LEN(img); i < j; i++) { + data[i] ^= ((uint16_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_RGB565_PIXEL_FAST(data, i, + (IMAGE_GET_RGB565_PIXEL_FAST(data, i) + ^ IMAGE_GET_RGB565_PIXEL_FAST(((uint16_t *) other), i))); + } + } + } + break; + } + default: { + break; + } + } +} + +void imlib_b_xor(image_t *img, const char *path, image_t *other, image_t *mask) +{ + imlib_image_operation(img, path, other, imlib_b_xor_line_op, mask); +} + +static void imlib_b_xnor_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) +{ + image_t *mask = (image_t *) data; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + uint32_t *data = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_BINARY_LINE_LEN(img); i < j; i++) { + data[i] ^= ~((uint32_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_BINARY_PIXEL_FAST(data, i, + (IMAGE_GET_BINARY_PIXEL_FAST(data, i) + ^ ~IMAGE_GET_BINARY_PIXEL_FAST(((uint32_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_GRAYSCALE: { + uint8_t *data = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_GRAYSCALE_LINE_LEN(img); i < j; i++) { + data[i] ^= ~((uint8_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(data, i, + (IMAGE_GET_GRAYSCALE_PIXEL_FAST(data, i) + ^ ~IMAGE_GET_GRAYSCALE_PIXEL_FAST(((uint8_t *) other), i))); + } + } + } + break; + } + case IMAGE_BPP_RGB565: { + uint16_t *data = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line); + + if(!mask) { + for (int i = 0, j = IMAGE_RGB565_LINE_LEN(img); i < j; i++) { + data[i] ^= ~((uint16_t *) other)[i]; + } + } else { + for (int i = 0, j = img->w; i < j; i++) { + if (image_get_mask_pixel(mask, i, line)) { + IMAGE_PUT_RGB565_PIXEL_FAST(data, i, + (IMAGE_GET_RGB565_PIXEL_FAST(data, i) + ^ ~IMAGE_GET_RGB565_PIXEL_FAST(((uint16_t *) other), i))); + } + } + } + break; + } + default: { + break; + } + } +} + +void imlib_b_xnor(image_t *img, const char *path, image_t *other, image_t *mask) +{ + imlib_image_operation(img, path, other, imlib_b_xnor_line_op, mask); +} + +static void imlib_erode_dilate(image_t *img, int ksize, int threshold, int e_or_d, image_t *mask) +{ + int brows = ksize + 1; + image_t buf; + buf.w = img->w; + buf.h = brows; + buf.bpp = img->bpp; + + switch(img->bpp) { + case IMAGE_BPP_BINARY: { + buf.data = fb_alloc(IMAGE_BINARY_LINE_LEN_BYTES(img) * brows); + + for (int y = 0, yy = img->h; y < yy; y++) { + uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); + uint32_t *buf_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&buf, (y % brows)); + + for (int x = 0, xx = img->w; x < xx; x++) { + int pixel = IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x); + IMAGE_PUT_BINARY_PIXEL_FAST(buf_row_ptr, x, pixel); + + if ((mask && (!image_get_mask_pixel(mask, x, y))) + || (pixel == e_or_d)) { + continue; // Short circuit. + } + + int acc = e_or_d ? 0 : -1; // Don't count center pixel... + + for (int j = -ksize; j <= ksize; j++) { + uint32_t *k_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, + IM_MIN(IM_MAX(y + j, 0), (img->h - 1))); + + for (int k = -ksize; k <= ksize; k++) { + acc += IMAGE_GET_BINARY_PIXEL_FAST(k_row_ptr, + IM_MIN(IM_MAX(x + k, 0), (img->w - 1))); + } + } + + if (!e_or_d) { + // Preserve original pixel value... or clear it. + if (acc < threshold) IMAGE_CLEAR_BINARY_PIXEL_FAST(buf_row_ptr, x); + } else { + // Preserve original pixel value... or set it. + if (acc > threshold) IMAGE_SET_BINARY_PIXEL_FAST(buf_row_ptr, x); + } + } + + if (y >= ksize) { // Transfer buffer lines... + memcpy(IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, (y - ksize)), + IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)), + IMAGE_BINARY_LINE_LEN_BYTES(img)); + } + } + + // Copy any remaining lines from the buffer image... + for (int y = img->h - ksize, yy = img->h; y < yy; y++) { + memcpy(IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y), + IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&buf, (y % brows)), + IMAGE_BINARY_LINE_LEN_BYTES(img)); + } + + fb_free(); + break; + } + case IMAGE_BPP_GRAYSCALE: { + buf.data = fb_alloc(IMAGE_GRAYSCALE_LINE_LEN_BYTES(img) * brows); + + for (int y = 0, yy = img->h; y < yy; y++) { + uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); + uint8_t *buf_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows)); + + for (int x = 0, xx = img->w; x < xx; x++) { + int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x); + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, pixel); + + if ((mask && (!image_get_mask_pixel(mask, x, y))) + || (COLOR_GRAYSCALE_TO_BINARY(pixel) == e_or_d)) { + continue; // Short circuit. + } + + int acc = e_or_d ? 0 : -1; // Don't count center pixel... + + for (int j = -ksize; j <= ksize; j++) { + uint8_t *k_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, + IM_MIN(IM_MAX(y + j, 0), (img->h - 1))); + + for (int k = -ksize; k <= ksize; k++) { + acc += COLOR_GRAYSCALE_TO_BINARY(IMAGE_GET_GRAYSCALE_PIXEL_FAST(k_row_ptr, + IM_MIN(IM_MAX(x + k, 0), (img->w - 1)))); + } + } + + if (!e_or_d) { + // Preserve original pixel value... or clear it. + if (acc < threshold) IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, + COLOR_GRAYSCALE_BINARY_MIN); + } else { + // Preserve original pixel value... or set it. + if (acc > threshold) IMAGE_PUT_GRAYSCALE_PIXEL_FAST(buf_row_ptr, x, + COLOR_GRAYSCALE_BINARY_MAX); + } + } + + if (y >= ksize) { // Transfer buffer lines... + memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, (y - ksize)), + IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)), + IMAGE_GRAYSCALE_LINE_LEN_BYTES(img)); + } + } + + // Copy any remaining lines from the buffer image... + for (int y = img->h - ksize, yy = img->h; y < yy; y++) { + memcpy(IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y), + IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&buf, (y % brows)), + IMAGE_GRAYSCALE_LINE_LEN_BYTES(img)); + } + + fb_free(); + break; + } + case IMAGE_BPP_RGB565: { + buf.data = fb_alloc(IMAGE_RGB565_LINE_LEN_BYTES(img) * brows); + + for (int y = 0, yy = img->h; y < yy; y++) { + uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); + uint16_t *buf_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows)); + + for (int x = 0, xx = img->w; x < xx; x++) { + int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x); + IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, pixel); + + if ((mask && (!image_get_mask_pixel(mask, x, y))) + || (COLOR_RGB565_TO_BINARY(pixel) == e_or_d)) { + continue; // Short circuit. + } + + int acc = e_or_d ? 0 : -1; // Don't count center pixel... + + for (int j = -ksize; j <= ksize; j++) { + uint16_t *k_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, + IM_MIN(IM_MAX(y + j, 0), (img->h - 1))); + + for (int k = -ksize; k <= ksize; k++) { + acc += COLOR_RGB565_TO_BINARY(IMAGE_GET_RGB565_PIXEL_FAST(k_row_ptr, + IM_MIN(IM_MAX(x + k, 0), (img->w - 1)))); + } + } + + if (!e_or_d) { + // Preserve original pixel value... or clear it. + if (acc < threshold) IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, + COLOR_RGB565_BINARY_MIN); + } else { + // Preserve original pixel value... or set it. + if (acc > threshold) IMAGE_PUT_RGB565_PIXEL_FAST(buf_row_ptr, x, + COLOR_RGB565_BINARY_MAX); + } + } + + if (y >= ksize) { // Transfer buffer lines... + memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, (y - ksize)), + IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, ((y - ksize) % brows)), + IMAGE_RGB565_LINE_LEN_BYTES(img)); + } + } + + // Copy any remaining lines from the buffer image... + for (int y = img->h - ksize, yy = img->h; y < yy; y++) { + memcpy(IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y), + IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&buf, (y % brows)), + IMAGE_RGB565_LINE_LEN_BYTES(img)); + } + + fb_free(); + break; + } + default: { + break; + } + } +} + +void imlib_erode(image_t *img, int ksize, int threshold, image_t *mask) +{ + // Threshold should be equal to (((ksize*2)+1)*((ksize*2)+1))-1 + // for normal operation. E.g. for ksize==3 -> threshold==8 + // Basically you're adjusting the number of data that + // must be set in the kernel (besides the center) for the output to be 1. + // Erode normally requires all data to be 1. + imlib_erode_dilate(img, ksize, threshold, 0, mask); +} + +void imlib_dilate(image_t *img, int ksize, int threshold, image_t *mask) +{ + // Threshold should be equal to 0 + // for normal operation. E.g. for ksize==3 -> threshold==0 + // Basically you're adjusting the number of data that + // must be set in the kernel (besides the center) for the output to be 1. + // Dilate normally requires one pixel to be 1. + imlib_erode_dilate(img, ksize, threshold, 1, mask); +} diff --git a/src/omv/img/edge.c b/src/omv/img/edge.c index dea88225d..029f438b4 100644 --- a/src/omv/img/edge.c +++ b/src/omv/img/edge.c @@ -20,10 +20,15 @@ typedef struct gvec { void imlib_edge_simple(image_t *src, rectangle_t *roi, int low_thresh, int high_thresh) { imlib_morph(src, 1, kernel_high_pass_3, 1.0f, 0.0f); - simple_color_t lt = {.G=low_thresh}; - simple_color_t ht = {.G=high_thresh}; - imlib_binary(src, 1, <, &ht, false); - imlib_erode(src, 1, 2); + list_t thresholds; + list_init(&thresholds, sizeof(color_thresholds_list_lnk_data_t)); + color_thresholds_list_lnk_data_t lnk_data; + lnk_data.LMin=low_thresh; + lnk_data.LMax=high_thresh; + list_push_back(&thresholds, &lnk_data); + imlib_binary(src, &thresholds, false, false); + list_free(&thresholds); + imlib_erode(src, 1, 2, NULL); } void imlib_edge_canny(image_t *src, rectangle_t *roi, int low_thresh, int high_thresh) diff --git a/src/omv/img/imlib.c b/src/omv/img/imlib.c index fffa45f60..d1270f411 100644 --- a/src/omv/img/imlib.c +++ b/src/omv/img/imlib.c @@ -171,17 +171,17 @@ void image_copy(image_t *dst, image_t *src) memcpy(dst, src, sizeof(image_t)); } -uint32_t image_size(image_t *ptr) +size_t image_size(image_t *ptr) { switch (ptr->bpp) { case IMAGE_BPP_BINARY: { - return ((ptr->w + UINT32_T_MASK) >> UINT32_T_SHIFT) * ptr->h; + return IMAGE_BINARY_LINE_LEN_BYTES(ptr) * ptr->h; } case IMAGE_BPP_GRAYSCALE: { - return (ptr->w * ptr->h) * sizeof(uint8_t); + return IMAGE_GRAYSCALE_LINE_LEN_BYTES(ptr) * ptr->h; } case IMAGE_BPP_RGB565: { - return (ptr->w * ptr->h) * sizeof(uint16_t); + return IMAGE_RGB565_LINE_LEN_BYTES(ptr) * ptr->h; } case IMAGE_BPP_BAYER: { return ptr->w * ptr->h; @@ -192,6 +192,24 @@ uint32_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; + } + } +} + // Gamma uncompress extern const float xyz_table[256]; @@ -636,292 +654,6 @@ void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int //////////////////////////////////////////////////////////////////////////////// -void imlib_binary(image_t *img, - int num_thresholds, simple_color_t *l_thresholds, simple_color_t *h_thresholds, - bool invert) -{ - if (IM_IS_GS(img)) { - uint8_t *pixels = img->pixels; - for (int i=0, j=img->w*img->h; ipixels; - for (int i=0, j=img->w*img->h; ipixels; - for (int i=0, j=img->w*img->h; ipixels; - for (int i=0, j=img->w*img->h; ipixels + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] &= other[i]; - } - } else { - uint16_t *pixels = ((uint16_t *) img->pixels) + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] &= ((uint16_t *) other)[i]; - } - } -} - -void imlib_b_and(image_t *img, const char *path, image_t *other) -{ - imlib_image_operation(img, path, other, imlib_b_and_line_op, NULL); -} - -static void imlib_b_nand_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) -{ - data = data; vflipped = vflipped; - - if (IM_IS_GS(img)) { - uint8_t *pixels = img->pixels + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] = ~(pixels[i] & other[i]); - } - } else { - uint16_t *pixels = ((uint16_t *) img->pixels) + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] = ~(pixels[i] & ((uint16_t *) other)[i]); - } - } -} - -void imlib_b_nand(image_t *img, const char *path, image_t *other) -{ - imlib_image_operation(img, path, other, imlib_b_nand_line_op, NULL); -} - -static void imlib_b_or_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) -{ - data = data; vflipped = vflipped; - - if (IM_IS_GS(img)) { - uint8_t *pixels = img->pixels + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] |= other[i]; - } - } else { - uint16_t *pixels = ((uint16_t *) img->pixels) + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] |= ((uint16_t *) other)[i]; - } - } -} - -void imlib_b_or(image_t *img, const char *path, image_t *other) -{ - imlib_image_operation(img, path, other, imlib_b_or_line_op, NULL); -} - -static void imlib_b_nor_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) -{ - data = data; vflipped = vflipped; - - if (IM_IS_GS(img)) { - uint8_t *pixels = img->pixels + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] = ~(pixels[i] | other[i]); - } - } else { - uint16_t *pixels = ((uint16_t *) img->pixels) + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] = ~(pixels[i] | ((uint16_t *) other)[i]); - } - } -} - -void imlib_b_nor(image_t *img, const char *path, image_t *other) -{ - imlib_image_operation(img, path, other, imlib_b_nor_line_op, NULL); -} - -static void imlib_b_xor_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) -{ - data = data; vflipped = vflipped; - - if (IM_IS_GS(img)) { - uint8_t *pixels = img->pixels + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] ^= other[i]; - } - } else { - uint16_t *pixels = ((uint16_t *) img->pixels) + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] ^= ((uint16_t *) other)[i]; - } - } -} - -void imlib_b_xor(image_t *img, const char *path, image_t *other) -{ - imlib_image_operation(img, path, other, imlib_b_xor_line_op, NULL); -} - -static void imlib_b_xnor_line_op(image_t *img, int line, uint8_t *other, void *data, bool vflipped) -{ - data = data; vflipped = vflipped; - - if (IM_IS_GS(img)) { - uint8_t *pixels = img->pixels + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] = ~(pixels[i] ^ other[i]); - } - } else { - uint16_t *pixels = ((uint16_t *) img->pixels) + (img->w * line); - for (int i=0; iw; i++) { - pixels[i] = ~(pixels[i] ^ ((uint16_t *) other)[i]); - } - } -} - -void imlib_b_xnor(image_t *img, const char *path, image_t *other) -{ - imlib_image_operation(img, path, other, imlib_b_xnor_line_op, NULL); -} - -static void imlib_erode_dilate(image_t *img, int ksize, int threshold, int e_or_d) -{ - int brows = ksize + 1; - uint8_t *buffer = fb_alloc(img->w * brows * img->bpp); - if (IM_IS_GS(img)) { - for (int y=0; yh; y++) { - for (int x=0; xw; x++) { - // We're writing into the buffer like if it were a window. - int buffer_idx = ((y%brows)*img->w)+x; - buffer[buffer_idx] = IM_GET_GS_PIXEL(img, x, y); - if ((!!buffer[buffer_idx]) == e_or_d) { - continue; // short circuit (makes this very fast - usually) - } - int acc = e_or_d ? 0 : -1; // don't count center pixel... - for (int j=-ksize; j<=ksize; j++) { - for (int k=-ksize; k<=ksize; k++) { - int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1); - int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1); - acc += !!IM_GET_GS_PIXEL(img, x_k, y_j); - } - } - if (!e_or_d) { - // Preserve original pixel value... - if (acc < threshold) buffer[buffer_idx] = 0; // clear - } else { - // Preserve original pixel value... - if (acc > threshold) buffer[buffer_idx] = -1; // set - } - } - if (y>=ksize) { - memcpy(img->pixels+((y-ksize)*img->w), - buffer+(((y-ksize)%brows)*img->w), - img->w * sizeof(uint8_t)); - } - } - for (int y=img->h-ksize; yh; y++) { - memcpy(img->pixels+(y*img->w), - buffer+((y%brows)*img->w), - img->w * sizeof(uint8_t)); - } - } else { - for (int y=0; yh; y++) { - for (int x=0; xw; x++) { - // We're writing into the buffer like if it were a window. - int buffer_idx = ((y%brows)*img->w)+x; - ((uint16_t *) buffer)[buffer_idx] = IM_GET_RGB565_PIXEL(img, x, y); - if ((!!((uint16_t *) buffer)[buffer_idx]) == e_or_d) { - continue; // short circuit (makes this very fast - usually) - } - int acc = e_or_d ? 0 : -1; // don't count center pixel... - for (int j=-ksize; j<=ksize; j++) { - for (int k=-ksize; k<=ksize; k++) { - int x_k = IM_MIN(IM_MAX(x+k, 0), img->w-1); - int y_j = IM_MIN(IM_MAX(y+j, 0), img->h-1); - acc += !!IM_GET_RGB565_PIXEL(img, x_k, y_j); - } - } - if (!e_or_d) { - // Preserve original pixel value... - if (acc < threshold) ((uint16_t *) buffer)[buffer_idx] = 0; // clear - } else { - // Preserve original pixel value... - if (acc > threshold) ((uint16_t *) buffer)[buffer_idx] = -1; // set - } - } - if (y>=ksize) { - memcpy(((uint16_t *) img->pixels)+((y-ksize)*img->w), - ((uint16_t *) buffer)+(((y-ksize)%brows)*img->w), - img->w * sizeof(uint16_t)); - } - } - for (int y=img->h-ksize; yh; y++) { - memcpy(((uint16_t *) img->pixels)+(y*img->w), - ((uint16_t *) buffer)+((y%brows)*img->w), - img->w * sizeof(uint16_t)); - } - } - fb_free(); -} - -void imlib_erode(image_t *img, int ksize, int threshold) -{ - // Threshold should be equal to ((ksize*2)+1)*((ksize*2)+1)-1 - // for normal operation. E.g. for ksize==3 -> threshold==8 - // Basically you're adjusting the number of pixels that - // must be set in the kernel (besides the center) for the output to be 1. - // Erode normally requires all pixels to be 1. - imlib_erode_dilate(img, ksize, threshold, 0); -} - -void imlib_dilate(image_t *img, int ksize, int threshold) -{ - // Threshold should be equal to 0 - // for normal operation. E.g. for ksize==3 -> threshold==0 - // Basically you're adjusting the number of pixels that - // must be set in the kernel (besides the center) for the output to be 1. - // Dilate normally requires one pixel to be 1. - imlib_erode_dilate(img, ksize, threshold, 1); -} - -//////////////////////////////////////////////////////////////////////////////// - void imlib_negate(image_t *img) { if (IM_IS_GS(img)) { diff --git a/src/omv/img/imlib.h b/src/omv/img/imlib.h index b9bd211c4..7b4befc94 100644 --- a/src/omv/img/imlib.h +++ b/src/omv/img/imlib.h @@ -145,6 +145,10 @@ color_thresholds_list_lnk_data_t; #define COLOR_BINARY_MIN 0 #define COLOR_BINARY_MAX 1 +#define COLOR_GRAYSCALE_BINARY_MIN 0x00 +#define COLOR_GRAYSCALE_BINARY_MAX 0xFF +#define COLOR_RGB565_BINARY_MIN 0x0000 +#define COLOR_RGB565_BINARY_MAX 0xFFFF #define COLOR_GRAYSCALE_MIN 0 #define COLOR_GRAYSCALE_MAX 255 @@ -319,9 +323,9 @@ extern const int8_t yuv_table[196608]; #define COLOR_BINARY_TO_GRAYSCALE(pixel) ((pixel) * COLOR_GRAYSCALE_MAX) #define COLOR_BINARY_TO_RGB565(pixel) COLOR_YUV_TO_RGB565((pixel) * 127, 0, 0) -#define COLOR_RGB565_TO_BINARY(pixel) (COLOR_RGB565_TO_Y(pixel) == 127) +#define COLOR_RGB565_TO_BINARY(pixel) (COLOR_RGB565_TO_Y(pixel) > (((COLOR_Y_MAX - COLOR_Y_MIN) / 2) + COLOR_Y_MIN)) #define COLOR_RGB565_TO_GRAYSCALE(pixel) (COLOR_RGB565_TO_Y(pixel) + 128) -#define COLOR_GRAYSCALE_TO_BINARY(pixel) ((pixel) == COLOR_GRAYSCALE_MAX) +#define COLOR_GRAYSCALE_TO_BINARY(pixel) ((pixel) > (((COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN) / 2) + COLOR_GRAYSCALE_MIN)) #define COLOR_GRAYSCALE_TO_RGB565(pixel) COLOR_YUV_TO_RGB565((pixel) - 128, 0, 0) ///////////////// @@ -350,7 +354,17 @@ typedef struct image { void image_init(image_t *ptr, int w, int h, int bpp, void *data); void image_copy(image_t *dst, image_t *src); -uint32_t image_size(image_t *ptr); +size_t image_size(image_t *ptr); +bool image_get_mask_pixel(image_t *ptr, int x, int y); + +#define IMAGE_BINARY_LINE_LEN(image) (((image)->w + UINT32_T_MASK) >> UINT32_T_SHIFT) +#define IMAGE_BINARY_LINE_LEN_BYTES(image) (IMAGE_BINARY_LINE_LEN(image) * sizeof(uint32_t)) + +#define IMAGE_GRAYSCALE_LINE_LEN(image) ((image)->w) +#define IMAGE_GRAYSCALE_LINE_LEN_BYTES(image) (IMAGE_GRAYSCALE_LINE_LEN(image) * sizeof(uint8_t)) + +#define IMAGE_RGB565_LINE_LEN(image) ((image)->w) +#define IMAGE_RGB565_LINE_LEN_BYTES(image) (IMAGE_RGB565_LINE_LEN(image) * sizeof(uint16_t)) #define IMAGE_GET_BINARY_PIXEL(image, x, y) \ ({ \ @@ -1106,20 +1120,6 @@ void imlib_draw_rectangle(image_t *img, int rx, int ry, int rw, int rh, int c); void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c); void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c); -/* Binary functions */ -void imlib_binary(image_t *img, - int num_thresholds, simple_color_t *l_thresholds, simple_color_t *h_thresholds, - bool invert); -void imlib_invert(image_t *img); -void imlib_b_and(image_t *img, const char *path, image_t *other); -void imlib_b_nand(image_t *img, const char *path, image_t *other); -void imlib_b_or(image_t *img, const char *path, image_t *other); -void imlib_b_nor(image_t *img, const char *path, image_t *other); -void imlib_b_xor(image_t *img, const char *path, image_t *other); -void imlib_b_xnor(image_t *img, const char *path, image_t *other); -void imlib_erode(image_t *img, int ksize, int threshold); -void imlib_dilate(image_t *img, int ksize, int threshold); - /* Background Subtraction (Frame Differencing) functions */ void imlib_negate(image_t *img); void imlib_difference(image_t *img, const char *path, image_t *other); @@ -1213,6 +1213,17 @@ 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); +// Binary Functions +void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero); +void imlib_invert(image_t *img); +void imlib_b_and(image_t *img, const char *path, image_t *other, image_t *mask); +void imlib_b_nand(image_t *img, const char *path, image_t *other, image_t *mask); +void imlib_b_or(image_t *img, const char *path, image_t *other, image_t *mask); +void imlib_b_nor(image_t *img, const char *path, image_t *other, image_t *mask); +void imlib_b_xor(image_t *img, const char *path, image_t *other, image_t *mask); +void imlib_b_xnor(image_t *img, const char *path, image_t *other, image_t *mask); +void imlib_erode(image_t *img, int ksize, int threshold, image_t *mask); +void imlib_dilate(image_t *img, int ksize, int threshold, image_t *mask); // Image Correction void imlib_logpolar_int(image_t *dst, image_t *src, rectangle_t *roi, bool linear, bool reverse); // helper/internal void imlib_logpolar(image_t *img, bool linear, bool reverse); diff --git a/src/omv/img/shadow_removal.c b/src/omv/img/shadow_removal.c index 75d2d0d1a..922e3750d 100644 --- a/src/omv/img/shadow_removal.c +++ b/src/omv/img/shadow_removal.c @@ -218,17 +218,21 @@ void imlib_remove_shadows(image_t *img, const char *path, image_t *other) threshold_t t; imlib_get_threshold(&t, temp_image.bpp, &h); - simple_color_t t_l, t_h; - t_l.L = COLOR_L_MIN; - t_l.A = COLOR_A_MIN; - t_l.B = COLOR_B_MIN; - t_h.L = t.LValue; - t_h.A = COLOR_A_MAX; - t_h.B = COLOR_B_MAX; - imlib_binary(&temp_image, 1, &t_l, &t_h, false); + list_t thresholds; + list_init(&thresholds, sizeof(color_thresholds_list_lnk_data_t)); + color_thresholds_list_lnk_data_t lnk_data; + lnk_data.LMin = COLOR_L_MIN; + lnk_data.AMin = COLOR_A_MIN; + lnk_data.BMin = COLOR_B_MIN; + lnk_data.LMax = t.LValue; + lnk_data.AMax = COLOR_A_MAX; + lnk_data.BMax = COLOR_B_MAX; + list_push_back(&thresholds, &lnk_data); + imlib_binary(&temp_image, &thresholds, false, false); + list_free(&thresholds); - imlib_erode(&temp_image, 3, 30); - imlib_dilate(&temp_image, 1, 1); + imlib_erode(&temp_image, 3, 30, NULL); + imlib_dilate(&temp_image, 1, 1, NULL); // Get Shadow Average @@ -239,7 +243,7 @@ void imlib_remove_shadows(image_t *img, const char *path, image_t *other) temp_image_2.data = fb_alloc(image_size(&temp_image)); memcpy(temp_image_2.data, temp_image.data, image_size(&temp_image)); - imlib_erode(&temp_image_2, 3, 48); + imlib_erode(&temp_image_2, 3, 48, NULL); int shadow_r_sum = 0; int shadow_g_sum = 0; @@ -266,10 +270,10 @@ void imlib_remove_shadows(image_t *img, const char *path, image_t *other) memcpy(temp_image_2.data, temp_image.data, image_size(&temp_image)); imlib_invert(&temp_image_2); - imlib_erode(&temp_image_2, 5, 120); + imlib_erode(&temp_image_2, 5, 120, NULL); imlib_invert(&temp_image_2); - imlib_b_xor(&temp_image_2, NULL, &temp_image); - imlib_erode(&temp_image_2, 2, 24); + imlib_b_xor(&temp_image_2, NULL, &temp_image, NULL); + imlib_erode(&temp_image_2, 2, 24, NULL); int not_shadow_r_sum = 0; int not_shadow_g_sum = 0; @@ -337,9 +341,9 @@ void imlib_remove_shadows(image_t *img, const char *path, image_t *other) memcpy(temp_image.data, temp_image_2.data, image_size(&temp_image_2)); - imlib_erode(&temp_image_2, 1, 8); - imlib_b_xor(&temp_image, NULL, &temp_image_2); - imlib_dilate(&temp_image, 3, 0); + imlib_erode(&temp_image_2, 1, 8, NULL); + imlib_b_xor(&temp_image, NULL, &temp_image_2, NULL); + imlib_dilate(&temp_image, 3, 0, NULL); imlib_median_filter(img, 2, 12, false, 0, false, &temp_image); fb_free(); // temp_image_2 diff --git a/src/omv/py/py_image.c b/src/omv/py/py_image.c index 394e01c22..99665f36c 100644 --- a/src/omv/py/py_image.c +++ b/src/omv/py/py_image.c @@ -955,44 +955,19 @@ static mp_obj_t py_image_binary(uint n_args, const mp_obj_t *args, mp_map_t *kw_ image_t *arg_img = py_image_cobj(args[0]); PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); - mp_uint_t arg_t_len; - mp_obj_t *arg_t; - mp_obj_get_array(args[1], &arg_t_len, &arg_t); - if (!arg_t_len) return mp_const_none; + list_t thresholds; + list_init(&thresholds, sizeof(color_thresholds_list_lnk_data_t)); + py_helper_arg_to_thresholds(args[1], &thresholds); - simple_color_t l_t[arg_t_len], u_t[arg_t_len]; - if (IM_IS_GS(arg_img)) { - for (int i=0; i 2) ? mp_obj_get_int(args[2]) : false); + + bool zero = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_zero), + (n_args > 3) ? mp_obj_get_int(args[3]) : false); + + imlib_binary(arg_img, &thresholds, invert, zero); + list_free(&thresholds); - int arg_invert = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), 0); - imlib_binary(arg_img, arg_t_len, l_t, u_t, arg_invert ? 1 : 0); return args[0]; } @@ -1005,88 +980,154 @@ static mp_obj_t py_image_invert(mp_obj_t img_obj) return img_obj; } -static mp_obj_t py_image_b_and(mp_obj_t img_obj, mp_obj_t other_obj) +static mp_obj_t py_image_b_and(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_image_cobj(img_obj); + image_t *arg_img = py_image_cobj(args[0]); PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); - if (MP_OBJ_IS_STR(other_obj)) { - imlib_b_and(arg_img, mp_obj_str_get_str(other_obj), NULL); + mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP); + + if (MP_OBJ_IS_STR(args[1])) { + fb_alloc_mark(); + imlib_b_and(arg_img, mp_obj_str_get_str(args[1]), NULL, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_mark(); } else { - image_t *arg_other = py_image_cobj(other_obj); - imlib_b_and(arg_img, NULL, arg_other); + image_t *arg_other = py_image_cobj(args[1]); + fb_alloc_mark(); + imlib_b_and(arg_img, NULL, arg_other, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_free_till_mark(); } - return img_obj; + + return args[0]; } -static mp_obj_t py_image_b_nand(mp_obj_t img_obj, mp_obj_t other_obj) +static mp_obj_t py_image_b_nand(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_image_cobj(img_obj); + image_t *arg_img = py_image_cobj(args[0]); PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); - if (MP_OBJ_IS_STR(other_obj)) { - imlib_b_nand(arg_img, mp_obj_str_get_str(other_obj), NULL); + mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP); + + if (MP_OBJ_IS_STR(args[1])) { + fb_alloc_mark(); + imlib_b_nand(arg_img, mp_obj_str_get_str(args[1]), NULL, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_mark(); } else { - image_t *arg_other = py_image_cobj(other_obj); - imlib_b_nand(arg_img, NULL, arg_other); + image_t *arg_other = py_image_cobj(args[1]); + fb_alloc_mark(); + imlib_b_nand(arg_img, NULL, arg_other, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_free_till_mark(); } - return img_obj; + + return args[0]; } -static mp_obj_t py_image_b_or(mp_obj_t img_obj, mp_obj_t other_obj) +static mp_obj_t py_image_b_or(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_image_cobj(img_obj); + image_t *arg_img = py_image_cobj(args[0]); PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); - if (MP_OBJ_IS_STR(other_obj)) { - imlib_b_or(arg_img, mp_obj_str_get_str(other_obj), NULL); + mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP); + + if (MP_OBJ_IS_STR(args[1])) { + fb_alloc_mark(); + imlib_b_or(arg_img, mp_obj_str_get_str(args[1]), NULL, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_mark(); } else { - image_t *arg_other = py_image_cobj(other_obj); - imlib_b_or(arg_img, NULL, arg_other); + image_t *arg_other = py_image_cobj(args[1]); + fb_alloc_mark(); + imlib_b_or(arg_img, NULL, arg_other, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_free_till_mark(); } - return img_obj; + + return args[0]; } -static mp_obj_t py_image_b_nor(mp_obj_t img_obj, mp_obj_t other_obj) +static mp_obj_t py_image_b_nor(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_image_cobj(img_obj); + image_t *arg_img = py_image_cobj(args[0]); PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); - if (MP_OBJ_IS_STR(other_obj)) { - imlib_b_nor(arg_img, mp_obj_str_get_str(other_obj), NULL); + mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP); + + if (MP_OBJ_IS_STR(args[1])) { + fb_alloc_mark(); + imlib_b_nor(arg_img, mp_obj_str_get_str(args[1]), NULL, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_mark(); } else { - image_t *arg_other = py_image_cobj(other_obj); - imlib_b_nor(arg_img, NULL, arg_other); + image_t *arg_other = py_image_cobj(args[1]); + fb_alloc_mark(); + imlib_b_nor(arg_img, NULL, arg_other, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_free_till_mark(); } - return img_obj; + + return args[0]; } -static mp_obj_t py_image_b_xor(mp_obj_t img_obj, mp_obj_t other_obj) +static mp_obj_t py_image_b_xor(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_image_cobj(img_obj); + image_t *arg_img = py_image_cobj(args[0]); PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); - if (MP_OBJ_IS_STR(other_obj)) { - imlib_b_xor(arg_img, mp_obj_str_get_str(other_obj), NULL); + mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP); + + if (MP_OBJ_IS_STR(args[1])) { + fb_alloc_mark(); + imlib_b_xor(arg_img, mp_obj_str_get_str(args[1]), NULL, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_mark(); } else { - image_t *arg_other = py_image_cobj(other_obj); - imlib_b_xor(arg_img, NULL, arg_other); + image_t *arg_other = py_image_cobj(args[1]); + fb_alloc_mark(); + imlib_b_xor(arg_img, NULL, arg_other, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_free_till_mark(); } - return img_obj; + + return args[0]; } -static mp_obj_t py_image_b_xnor(mp_obj_t img_obj, mp_obj_t other_obj) +static mp_obj_t py_image_b_xnor(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_image_cobj(img_obj); + image_t *arg_img = py_image_cobj(args[0]); PY_ASSERT_TRUE_MSG(IM_IS_MUTABLE(arg_img), "Image format is not supported."); - if (MP_OBJ_IS_STR(other_obj)) { - imlib_b_xnor(arg_img, mp_obj_str_get_str(other_obj), NULL); + mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP); + + if (MP_OBJ_IS_STR(args[1])) { + fb_alloc_mark(); + imlib_b_xnor(arg_img, mp_obj_str_get_str(args[1]), NULL, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_mark(); } else { - image_t *arg_other = py_image_cobj(other_obj); - imlib_b_xnor(arg_img, NULL, arg_other); + image_t *arg_other = py_image_cobj(args[1]); + fb_alloc_mark(); + imlib_b_xnor(arg_img, NULL, arg_other, + (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 2) ? py_image_cobj(args[2]) : NULL)); + fb_alloc_free_till_mark(); } - return img_obj; + + return args[0]; } static mp_obj_t py_image_erode(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) @@ -1096,9 +1137,16 @@ static mp_obj_t py_image_erode(uint n_args, const mp_obj_t *args, mp_map_t *kw_a int arg_ksize = mp_obj_get_int(args[1]); PY_ASSERT_TRUE_MSG(arg_ksize >= 0, "Kernel Size must be >= 0"); - imlib_erode(arg_img, arg_ksize, - py_helper_lookup_int(kw_args, - MP_OBJ_NEW_QSTR(MP_QSTR_threshold), ((arg_ksize*2)+1)*((arg_ksize*2)+1)-1)); + + int threshold = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), + (n_args > 2) ? mp_obj_get_int(args[2]) : ((((arg_ksize*2)+1)*((arg_ksize*2)+1))-1)); + mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP); + + fb_alloc_mark(); + imlib_erode(arg_img, arg_ksize, threshold, (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 3) ? py_image_cobj(args[3]) : NULL)); + fb_alloc_free_till_mark(); + return args[0]; } @@ -1109,9 +1157,16 @@ static mp_obj_t py_image_dilate(uint n_args, const mp_obj_t *args, mp_map_t *kw_ int arg_ksize = mp_obj_get_int(args[1]); PY_ASSERT_TRUE_MSG(arg_ksize >= 0, "Kernel Size must be >= 0"); - imlib_dilate(arg_img, arg_ksize, - py_helper_lookup_int(kw_args, - MP_OBJ_NEW_QSTR(MP_QSTR_threshold), 0)); + + int threshold = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), + (n_args > 2) ? mp_obj_get_int(args[2]) : 0); + mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_mask), MP_MAP_LOOKUP); + + fb_alloc_mark(); + imlib_dilate(arg_img, arg_ksize, threshold, (kw_arg != NULL) ? py_image_cobj(kw_arg->value) : + ((n_args > 3) ? py_image_cobj(args[3]) : NULL)); + fb_alloc_free_till_mark(); + return args[0]; } @@ -4293,15 +4348,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_circle_obj, 4, py_image_draw_cir 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_cross_obj, 3, py_image_draw_cross); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_keypoints_obj, 2, py_image_draw_keypoints); -/* Binary functions */ +/* Binary Methods */ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_binary_obj, 2, py_image_binary); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_invert_obj, py_image_invert); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_b_and_obj, py_image_b_and); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_b_nand_obj, py_image_b_nand); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_b_or_obj, py_image_b_or); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_b_nor_obj, py_image_b_nor); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_b_xor_obj, py_image_b_xor); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_b_xnor_obj, py_image_b_xnor); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_b_and_obj, 2, py_image_b_and); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_b_nand_obj, 2, py_image_b_nand); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_b_or_obj, 2, py_image_b_or); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_b_nor_obj, 2, py_image_b_nor); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_b_xor_obj, 2, py_image_b_xor); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_b_xnor_obj, 2, py_image_b_xnor); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_erode_obj, 2, py_image_erode); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_dilate_obj, 2, py_image_dilate); /* Background Subtraction (Frame Differencing) functions */ @@ -4418,7 +4473,7 @@ static const mp_map_elem_t locals_dict_table[] = { {MP_OBJ_NEW_QSTR(MP_QSTR_draw_string), (mp_obj_t)&py_image_draw_string_obj}, {MP_OBJ_NEW_QSTR(MP_QSTR_draw_cross), (mp_obj_t)&py_image_draw_cross_obj}, {MP_OBJ_NEW_QSTR(MP_QSTR_draw_keypoints), (mp_obj_t)&py_image_draw_keypoints_obj}, - /* Binary functions */ + /* Binary Methods */ {MP_OBJ_NEW_QSTR(MP_QSTR_binary), (mp_obj_t)&py_image_binary_obj}, {MP_OBJ_NEW_QSTR(MP_QSTR_invert), (mp_obj_t)&py_image_invert_obj}, {MP_OBJ_NEW_QSTR(MP_QSTR_and), (mp_obj_t)&py_image_b_and_obj}, diff --git a/src/omv/py/qstrdefsomv.h b/src/omv/py/qstrdefsomv.h index 6d7886fd9..243d8c7b0 100644 --- a/src/omv/py/qstrdefsomv.h +++ b/src/omv/py/qstrdefsomv.h @@ -46,22 +46,6 @@ Q(draw_circle) Q(draw_string) Q(draw_cross) Q(draw_keypoints) -Q(binary) -Q(invert) -Q(and) -Q(b_and) -Q(nand) -Q(b_nand) -Q(or) -Q(b_or) -Q(nor) -Q(b_nor) -Q(xor) -Q(b_xor) -Q(xnor) -Q(b_xnor) -Q(erode) -Q(dilate) Q(negate) Q(difference) Q(replace) @@ -349,6 +333,54 @@ Q(CPUFREQ_216MHZ) Q(get_frequency) Q(set_frequency) +// Binary +Q(binary) +Q(invert) +Q(zero) + +// Invert +// duplicate Q(invert) + +// And +Q(and) +Q(b_and) +Q(mask) + +// Nand +Q(nand) +Q(b_nand) +// duplicate Q(mask) + +// Or +Q(or) +Q(b_or) +// duplicate Q(mask) + +// Nor +Q(nor) +Q(b_nor) +// duplicate Q(mask) + +// Xor +Q(xor) +Q(b_xor) +// duplicate Q(mask) + +// Xnor +Q(xnor) +Q(b_xnor) +// duplicate Q(mask) + +// Erode +Q(erode) +// duplicate Q(threshold) +// duplicate Q(mask) + +// Dilate +Q(dilate) +// duplicate Q(threshold) +// duplicate Q(mask) + // Max // duplicate Q(max) diff --git a/usr/examples/04-Image-Filters/color_binary_filter.py b/usr/examples/04-Image-Filters/color_binary_filter.py index c5989e0c8..b8686f7bc 100644 --- a/usr/examples/04-Image-Filters/color_binary_filter.py +++ b/usr/examples/04-Image-Filters/color_binary_filter.py @@ -1,14 +1,17 @@ # Color Binary Filter Example # -# This script shows off the binary image filter. This script was originally a -# test script... but, it can be useful for showing how to use binary. +# This script shows off the binary image filter. You may pass binary any +# number of thresholds to segment the image by. -import pyb, sensor, image, math +import sensor, image, time sensor.reset() sensor.set_framesize(sensor.QVGA) sensor.set_pixformat(sensor.RGB565) +sensor.skip_frames(time = 2000) +clock = time.clock() +# Use the Tools -> Machine Vision -> Threshold Edtor to pick better thresholds. red_threshold = (0,100, 0,127, 0,127) # L A B green_threshold = (0,100, -128,0, 0,127) # L A B blue_threshold = (0,100, -128,127, -128,0) # L A B @@ -17,25 +20,42 @@ while(True): # Test red threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([red_threshold]) + print(clock.fps()) + # Test green threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([green_threshold]) + print(clock.fps()) + # Test blue threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([blue_threshold]) + print(clock.fps()) + # Test not red threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([red_threshold], invert = 1) + print(clock.fps()) + # Test not green threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([green_threshold], invert = 1) + print(clock.fps()) + # Test not blue threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([blue_threshold], invert = 1) + print(clock.fps()) diff --git a/usr/examples/04-Image-Filters/color_light_removal.py b/usr/examples/04-Image-Filters/color_light_removal.py new file mode 100644 index 000000000..270104cfb --- /dev/null +++ b/usr/examples/04-Image-Filters/color_light_removal.py @@ -0,0 +1,25 @@ +# Color Light Removal +# +# This example shows off how to remove bright lights from the image. +# You can do this using the binary() method with the "zero=" argument. +# +# Removing bright lights from the image allows you to now use +# histeq() on the image without outliers from oversaturated +# parts of the image breaking the algorithm... + +import sensor, image, time + +sensor.reset() # Initialize the camera sensor. +sensor.set_pixformat(sensor.RGB565) # or sensor.GRAYSCALE +sensor.set_framesize(sensor.QQVGA) # or sensor.QVGA (or others) +sensor.skip_frames(time = 2000) # Let new settings take affect. +clock = time.clock() # Tracks FPS. + +thresholds = (90, 100, -128, 127, -128, 127) + +while(True): + clock.tick() # Track elapsed milliseconds between snapshots(). + img = sensor.snapshot().binary([thresholds], invert=False, zero=True) + + print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while + # connected to your computer. The FPS should increase once disconnected. diff --git a/usr/examples/04-Image-Filters/grayscale_binary_filter.py b/usr/examples/04-Image-Filters/grayscale_binary_filter.py index 2c8b5d535..dfaed5012 100644 --- a/usr/examples/04-Image-Filters/grayscale_binary_filter.py +++ b/usr/examples/04-Image-Filters/grayscale_binary_filter.py @@ -1,31 +1,45 @@ # Grayscale Binary Filter Example # -# This script shows off the binary image filter. This script was originally a -# test script... but, it can be useful for showing how to use binary. +# This script shows off the binary image filter. You may pass binary any +# number of thresholds to segment the image by. -import pyb, sensor, image, math +import sensor, image, time sensor.reset() sensor.set_framesize(sensor.QVGA) sensor.set_pixformat(sensor.GRAYSCALE) +sensor.skip_frames(time = 2000) +clock = time.clock() low_threshold = (0, 50) high_threshold = (205, 255) while(True): + # Test low threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([low_threshold]) + print(clock.fps()) + # Test high threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([high_threshold]) + print(clock.fps()) + # Test not low threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([low_threshold], invert = 1) + print(clock.fps()) + # Test not high threshold for i in range(100): + clock.tick() img = sensor.snapshot() img.binary([high_threshold], invert = 1) + print(clock.fps()) diff --git a/usr/examples/04-Image-Filters/grayscale_light_removal.py b/usr/examples/04-Image-Filters/grayscale_light_removal.py new file mode 100644 index 000000000..d42b8a8e9 --- /dev/null +++ b/usr/examples/04-Image-Filters/grayscale_light_removal.py @@ -0,0 +1,25 @@ +# Grayscale Light Removal +# +# This example shows off how to remove bright lights from the image. +# You can do this using the binary() method with the "zero=" argument. +# +# Removing bright lights from the image allows you to now use +# histeq() on the image without outliers from oversaturated +# parts of the image breaking the algorithm... + +import sensor, image, time + +sensor.reset() # Initialize the camera sensor. +sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565 +sensor.set_framesize(sensor.QQVGA) # or sensor.QVGA (or others) +sensor.skip_frames(time = 2000) # Let new settings take affect. +clock = time.clock() # Tracks FPS. + +thresholds = (220, 255) + +while(True): + clock.tick() # Track elapsed milliseconds between snapshots(). + img = sensor.snapshot().binary([thresholds], invert=False, zero=True) + + print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while + # connected to your computer. The FPS should increase once disconnected.