mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Add cartoon filtering
It's not as good as mean shift filtering but can approximate it if you heavily control the image image lighting conditions. That said, it's a lot faster and less memory than mean shift filtering.
This commit is contained in:
parent
63eec63e79
commit
aa7386ac5f
@ -1409,3 +1409,175 @@ void imlib_bilateral_filter(image_t *img, const int ksize, float color_sigma, fl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct imlib_cartoon_filter_mean_state {
|
||||
int r_acc, g_acc, b_acc, pixels;
|
||||
} imlib_cartoon_filter_mean_state_t;
|
||||
|
||||
static void imlib_cartoon_filter_mean(image_t *img, int line, int l, int r, void *data)
|
||||
{
|
||||
imlib_cartoon_filter_mean_state_t *state = (imlib_cartoon_filter_mean_state_t *) data;
|
||||
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line);
|
||||
for (int i = l; i <= r; i++) {
|
||||
state->g_acc += IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, i);
|
||||
state->pixels += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line);
|
||||
for (int i = l; i <= r; i++) {
|
||||
state->g_acc += IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, i);
|
||||
state->pixels += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line);
|
||||
for (int i = l; i <= r; i++) {
|
||||
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, i);
|
||||
state->r_acc += COLOR_RGB565_TO_R5(pixel);
|
||||
state->g_acc += COLOR_RGB565_TO_G6(pixel);
|
||||
state->b_acc += COLOR_RGB565_TO_B5(pixel);
|
||||
state->pixels += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct imlib_cartoon_filter_fill_state {
|
||||
int mean;
|
||||
} imlib_cartoon_filter_fill_state_t;
|
||||
|
||||
static void imlib_cartoon_filter_fill(image_t *img, int line, int l, int r, void *data)
|
||||
{
|
||||
imlib_cartoon_filter_fill_state_t *state = (imlib_cartoon_filter_fill_state_t *) data;
|
||||
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, line);
|
||||
for (int i = l; i <= r; i++) {
|
||||
IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr, i, state->mean);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, line);
|
||||
for (int i = l; i <= r; i++) {
|
||||
IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, i, state->mean);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, line);
|
||||
for (int i = l; i <= r; i++) {
|
||||
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, i, state->mean);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void imlib_cartoon_filter(image_t *img, float seed_threshold, float floating_threshold, image_t *mask)
|
||||
{
|
||||
image_t mean_image, fill_image;
|
||||
|
||||
mean_image.w = img->w;
|
||||
mean_image.h = img->h;
|
||||
mean_image.bpp = IMAGE_BPP_BINARY;
|
||||
mean_image.data = fb_alloc0(image_size(&mean_image));
|
||||
|
||||
fill_image.w = img->w;
|
||||
fill_image.h = img->h;
|
||||
fill_image.bpp = IMAGE_BPP_BINARY;
|
||||
fill_image.data = fb_alloc0(image_size(&fill_image));
|
||||
|
||||
if (mask) {
|
||||
for (int y = 0, yy = fill_image.h; y < yy; y++) {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&fill_image, y);
|
||||
for (int x = 0, xx = fill_image.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;
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0, yy = img->h; y < yy; y++) {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&mean_image, y);
|
||||
for (int x = 0, xx = img->w; x < xx; x++) {
|
||||
if (!IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x)) {
|
||||
|
||||
imlib_cartoon_filter_mean_state_t mean_state;
|
||||
memset(&mean_state, 0, sizeof(imlib_cartoon_filter_mean_state_t));
|
||||
imlib_flood_fill_int(&mean_image, img, x, y, color_seed_threshold, color_floating_threshold,
|
||||
imlib_cartoon_filter_mean, &mean_state);
|
||||
|
||||
imlib_cartoon_filter_fill_state_t fill_state;
|
||||
memset(&fill_state, 0, sizeof(imlib_cartoon_filter_fill_state_t));
|
||||
|
||||
switch(img->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
fill_state.mean = mean_state.g_acc / mean_state.pixels;
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
fill_state.mean = mean_state.g_acc / mean_state.pixels;
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
fill_state.mean = COLOR_R5_G6_B5_TO_RGB565(mean_state.r_acc / mean_state.pixels,
|
||||
mean_state.g_acc / mean_state.pixels,
|
||||
mean_state.b_acc / mean_state.pixels);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
imlib_flood_fill_int(&fill_image, img, x, y, color_seed_threshold, color_floating_threshold,
|
||||
imlib_cartoon_filter_fill, &fill_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fb_free();
|
||||
fb_free();
|
||||
}
|
||||
|
||||
@ -1288,6 +1288,7 @@ void imlib_mode_filter(image_t *img, const int ksize, bool threshold, int offset
|
||||
void imlib_midpoint_filter(image_t *img, const int ksize, float bias, bool threshold, int offset, bool invert, image_t *mask);
|
||||
void imlib_morph(image_t *img, const int ksize, const int *krn, const float m, const int b, bool threshold, int offset, bool invert, image_t *mask);
|
||||
void imlib_bilateral_filter(image_t *img, const int ksize, float color_sigma, float space_sigma, bool threshold, int offset, bool invert, image_t *mask);
|
||||
void imlib_cartoon_filter(image_t *img, float seed_threshold, float floating_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);
|
||||
|
||||
@ -2054,6 +2054,28 @@ STATIC mp_obj_t py_image_bilateral(uint n_args, const mp_obj_t *args, mp_map_t *
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_bilateral_obj, 2, py_image_bilateral);
|
||||
|
||||
STATIC mp_obj_t py_image_cartoon(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_seed_threshold =
|
||||
py_helper_keyword_float(n_args, args, 1, 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, 2, 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!");
|
||||
image_t *arg_msk =
|
||||
py_helper_keyword_to_image_mutable_mask(n_args, args, 3, kw_args);
|
||||
|
||||
fb_alloc_mark();
|
||||
imlib_cartoon_filter(arg_img, arg_seed_threshold, arg_floating_threshold, arg_msk);
|
||||
fb_alloc_free_till_mark();
|
||||
return args[0];
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_cartoon_obj, 1, py_image_cartoon);
|
||||
|
||||
/////////////////////////
|
||||
// Shadow Removal Methods
|
||||
/////////////////////////
|
||||
@ -4984,6 +5006,7 @@ static const mp_rom_map_elem_t locals_dict_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR_gaussian_blur), MP_ROM_PTR(&py_image_gaussian_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_laplacian), MP_ROM_PTR(&py_image_laplacian_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_bilateral), MP_ROM_PTR(&py_image_bilateral_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_cartoon), MP_ROM_PTR(&py_image_cartoon_obj)},
|
||||
/* Shadow Removal Methods */
|
||||
#ifdef IMLIB_ENABLE_REMOVE_SHADOWS
|
||||
{MP_ROM_QSTR(MP_QSTR_remove_shadows), MP_ROM_PTR(&py_image_remove_shadows_obj)},
|
||||
|
||||
@ -560,6 +560,12 @@ Q(space_sigma)
|
||||
// duplicate Q(invert)
|
||||
// duplicate Q(mask)
|
||||
|
||||
// Cartoon
|
||||
Q(cartoon)
|
||||
// duplicate Q(seed_threshold)
|
||||
// duplicate Q(floating_threshold)
|
||||
Q(mask)
|
||||
|
||||
// Shadow Removal
|
||||
Q(remove_shadows)
|
||||
|
||||
|
||||
29
usr/examples/04-Image-Filters/cartoon_filter.py
Normal file
29
usr/examples/04-Image-Filters/cartoon_filter.py
Normal file
@ -0,0 +1,29 @@
|
||||
# Cartoon Filter
|
||||
#
|
||||
# This example shows off a simple cartoon filter on images. The cartoon
|
||||
# filter works by joining similar pixel areas of an image and replacing
|
||||
# the pixels in those areas with the area mean.
|
||||
|
||||
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 area growth of a colored
|
||||
# region. Making this larger will merge more pixels.
|
||||
|
||||
# floating_threshold controls the maximum pixel-to-pixel difference
|
||||
# when growing a region. Settings this very high will quickly combine
|
||||
# all pixels in the image. You should keep this small.
|
||||
|
||||
# cartoon() will grow regions while both thresholds are statisfied...
|
||||
|
||||
img = sensor.snapshot().cartoon(seed_threshold=0.05, floating_thresholds=0.05)
|
||||
|
||||
print(clock.fps())
|
||||
Loading…
Reference in New Issue
Block a user