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_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_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_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
|
// Image Correction
|
||||||
void imlib_logpolar_int(image_t *dst, image_t *src, rectangle_t *roi, bool linear, bool reverse); // helper/internal
|
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);
|
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_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
|
// 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_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_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_bilateral), MP_ROM_PTR(&py_image_bilateral_obj)},
|
||||||
|
{MP_ROM_QSTR(MP_QSTR_cartoon), MP_ROM_PTR(&py_image_cartoon_obj)},
|
||||||
/* Shadow Removal Methods */
|
/* Shadow Removal Methods */
|
||||||
#ifdef IMLIB_ENABLE_REMOVE_SHADOWS
|
#ifdef IMLIB_ENABLE_REMOVE_SHADOWS
|
||||||
{MP_ROM_QSTR(MP_QSTR_remove_shadows), MP_ROM_PTR(&py_image_remove_shadows_obj)},
|
{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(invert)
|
||||||
// duplicate Q(mask)
|
// duplicate Q(mask)
|
||||||
|
|
||||||
|
// Cartoon
|
||||||
|
Q(cartoon)
|
||||||
|
// duplicate Q(seed_threshold)
|
||||||
|
// duplicate Q(floating_threshold)
|
||||||
|
Q(mask)
|
||||||
|
|
||||||
// Shadow Removal
|
// Shadow Removal
|
||||||
Q(remove_shadows)
|
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