mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Dilate and errode working.
The old code did not actually implement the errode anhd dilate kernels correctly. However, it migh have been a little faster because it avoided the boundary problem. In the future we can optimize all the kernel code to have different loops for doing the edges of image versus the center. But, for now, this is good enough. QVGA color tracking with kernels will be slow, but, the speed can be improved with QQVGA resolution. Using a 3x3 kernel is plenty fast. Larger ones are slower. I also added the ability for you to set the threshold for erode and dialte. This lets you make the kenrel a little bit smarter so that it won't errode or dilate a pixel unless the threshold is met. Meaning, you'll be able to use erode to erode an image down to 1 pixel wide lines.
This commit is contained in:
parent
d4e1e3d323
commit
fb8b357c24
@ -126,36 +126,6 @@ static void imlib_read_pixels(FIL *fp, image_t *img, int line_start, int line_en
|
||||
}
|
||||
}
|
||||
|
||||
// Main Ram = 196,608 bytes
|
||||
// -
|
||||
// struct framebuffer = 20 bytes
|
||||
// FB_JPEG_OFFS_SIZE = 1,024 bytes
|
||||
// GRAYSCALE 320x240x1 image = 76,800 bytes
|
||||
// fb_alloc = 4 bytes
|
||||
// flash cache = 16,384 bytes
|
||||
// =
|
||||
// 102,376 bytes
|
||||
// /
|
||||
// GRAYSCALE 320x1 lines
|
||||
// =
|
||||
// 319 GRAYSCALE 320x1 lines
|
||||
#define GS_LINE_BUFFER_SIZE (64) // double rgb565
|
||||
|
||||
// Main Ram = 196,608 bytes
|
||||
// -
|
||||
// struct framebuffer = 20 bytes
|
||||
// FB_JPEG_OFFS_SIZE = 1,024 bytes
|
||||
// RGB565 320x240x2 image = 153,600 bytes
|
||||
// fb_alloc = 4 bytes
|
||||
// flash cache = 16,384 bytes
|
||||
// =
|
||||
// 25,576 bytes
|
||||
// /
|
||||
// RGB565 320x2 lines
|
||||
// =
|
||||
// 39 RGB565 320x2 lines
|
||||
#define RGB565_LINE_BUFFER_SIZE (32)
|
||||
|
||||
void imlib_image_operation(image_t *img, const char *path, image_t *other, line_op_t op)
|
||||
{
|
||||
if (path) {
|
||||
@ -631,6 +601,102 @@ float imlib_orientation_degrees(image_t *img, int *sum, int *x_center, int *y_ce
|
||||
return (imlib_orientation_radians(img, sum, x_center, y_center, r) * 180.0) / M_PI;
|
||||
}
|
||||
|
||||
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; y<img->h; y++) {
|
||||
for (int x=0; x<img->w; 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 = 0;
|
||||
for (int j=-ksize; j<=ksize; j++) {
|
||||
for (int k=-ksize; k<=ksize; k++) {
|
||||
if (IM_X_INSIDE(img, x+k) && IM_Y_INSIDE(img, y+j)) {
|
||||
acc += !!IM_GET_GS_PIXEL(img, x+k, y+j);
|
||||
} else { // outer pixels should not affect result.
|
||||
acc += e_or_d ? 0 : 1;
|
||||
// 1 for erode prevents acc from being lower.
|
||||
// 0 for dilate prevents acc from being higher.
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer[buffer_idx] = (acc >= threshold) ? 0xFF : 0;
|
||||
}
|
||||
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; y<img->h; y++) {
|
||||
memcpy(img->pixels+(y*img->w),
|
||||
buffer+((y%brows)*img->w),
|
||||
img->w * sizeof(uint8_t));
|
||||
}
|
||||
} else {
|
||||
for (int y=0; y<img->h; y++) {
|
||||
for (int x=0; x<img->w; 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 = 0;
|
||||
for (int j=-ksize; j<=ksize; j++) {
|
||||
for (int k=-ksize; k<=ksize; k++) {
|
||||
if (IM_X_INSIDE(img, x+k) && IM_Y_INSIDE(img, y+j)) {
|
||||
acc += !!IM_GET_RGB565_PIXEL(img, x+k, y+j);
|
||||
} else { // outer pixels should not affect result.
|
||||
acc += e_or_d ? 0 : 1;
|
||||
// 1 for erode prevents acc from being lower.
|
||||
// 0 for dilate prevents acc from being higher.
|
||||
}
|
||||
}
|
||||
}
|
||||
((uint16_t *) buffer)[buffer_idx] = (acc >= threshold) ? 0xFFFF : 0;
|
||||
}
|
||||
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; y<img->h; 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)
|
||||
// for normal operation. E.g. for ksize==3 -> threshold==9
|
||||
// Basically you're adjusting the number of pixels that
|
||||
// must be set in the kernel 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 1
|
||||
// for normal operation. E.g. for ksize==3 -> threshold==1
|
||||
// Basically you're adjusting the number of pixels that
|
||||
// must be set in the kernel 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)
|
||||
@ -782,70 +848,6 @@ void imlib_grayscale_to_rgb565(struct image *image)
|
||||
#endif
|
||||
}
|
||||
|
||||
void imlib_erode(image_t *src, int ksize)
|
||||
{
|
||||
int c = ksize/2;// center pixel
|
||||
int k_rows = (ksize+1)*2;
|
||||
uint8_t *dst = fb_alloc0(src->w * k_rows);
|
||||
|
||||
for (int y=1; y<src->h-ksize; y++) {
|
||||
for (int x=1; x<src->w-ksize; x++) {
|
||||
int i = y*src->w+x;
|
||||
int di = (y%k_rows)*src->w+x;
|
||||
if ((dst[di] = src->pixels[i])==0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j=-c; j<c; j++) {
|
||||
for (int k=-c; k<c; k++) {
|
||||
if (src->pixels[(y+j)*src->w+x+k]==0) {
|
||||
dst[di]=0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
done:;
|
||||
}
|
||||
|
||||
if ((y+1)%k_rows==0) {
|
||||
memcpy(src->pixels+((y/k_rows)*k_rows*src->w), dst, src->w*k_rows);
|
||||
}
|
||||
}
|
||||
fb_free();
|
||||
}
|
||||
|
||||
void imlib_dilate(image_t *src, int ksize)
|
||||
{
|
||||
int c = ksize/2;// center pixel
|
||||
int k_rows = (ksize+1)*2;
|
||||
uint8_t *dst = fb_alloc0(src->w * k_rows);
|
||||
|
||||
for (int y=1; y<src->h-ksize; y++) {
|
||||
for (int x=1; x<src->w-ksize; x++) {
|
||||
int i = y*src->w+x;
|
||||
int di = (y%k_rows)*src->w+x;
|
||||
if ((dst[di] = src->pixels[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j=-c; j<c; j++) {
|
||||
for (int k=-c; k<c; k++) {
|
||||
if (src->pixels[(y+j)*src->w+x+k]) {
|
||||
dst[di]=src->pixels[(y+j)*src->w+x+k];
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
done:;
|
||||
}
|
||||
|
||||
if ((y+1)%k_rows==0) {
|
||||
memcpy(src->pixels+((y/k_rows)*k_rows*src->w), dst, src->w*k_rows);
|
||||
}
|
||||
}
|
||||
fb_free();
|
||||
}
|
||||
|
||||
#ifdef OPENMV1
|
||||
void imlib_threshold(image_t *src, image_t *dst, color_t *color, int color_size, int threshold)
|
||||
{
|
||||
|
||||
@ -146,6 +146,36 @@ extern const uint8_t g826_table[256];
|
||||
__typeof__ (img1) _img1 = (img1); \
|
||||
(_img0->w==_img1->w)&&(_img0->h==_img1->h)&&(_img0->bpp==_img1->bpp); })
|
||||
|
||||
// Main Ram = 196,608 bytes
|
||||
// -
|
||||
// struct framebuffer = 20 bytes
|
||||
// FB_JPEG_OFFS_SIZE = 1,024 bytes
|
||||
// GRAYSCALE 320x240x1 image = 76,800 bytes
|
||||
// fb_alloc = 4 bytes
|
||||
// flash cache = 16,384 bytes
|
||||
// =
|
||||
// 102,376 bytes
|
||||
// /
|
||||
// GRAYSCALE 320x1 lines
|
||||
// =
|
||||
// 319 GRAYSCALE 320x1 lines
|
||||
#define GS_LINE_BUFFER_SIZE (64) // double rgb565
|
||||
|
||||
// Main Ram = 196,608 bytes
|
||||
// -
|
||||
// struct framebuffer = 20 bytes
|
||||
// FB_JPEG_OFFS_SIZE = 1,024 bytes
|
||||
// RGB565 320x240x2 image = 153,600 bytes
|
||||
// fb_alloc = 4 bytes
|
||||
// flash cache = 16,384 bytes
|
||||
// =
|
||||
// 25,576 bytes
|
||||
// /
|
||||
// RGB565 320x2 lines
|
||||
// =
|
||||
// 39 RGB565 320x2 lines
|
||||
#define RGB565_LINE_BUFFER_SIZE (32)
|
||||
|
||||
typedef struct size {
|
||||
int w;
|
||||
int h;
|
||||
@ -370,6 +400,8 @@ int imlib_pixels(image_t *img, rectangle_t *r);
|
||||
int imlib_centroid(image_t *img, int *x_center, int *y_center, rectangle_t *r);
|
||||
float imlib_orientation_radians(image_t *img, int *sum, int *x_center, int *y_center, rectangle_t *r);
|
||||
float imlib_orientation_degrees(image_t *img, int *sum, int *x_center, int *y_center, rectangle_t *r);
|
||||
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);
|
||||
@ -391,8 +423,6 @@ void imlib_rgb_to_hsv(struct color *rgb, struct color *hsv);
|
||||
int imlib_image_mean(struct image *src);
|
||||
void imlib_histeq(struct image *src);
|
||||
void imlib_median_filter(image_t *src, int r);
|
||||
void imlib_erode(image_t *src, int ksize);
|
||||
void imlib_dilate(image_t *src, int ksize);
|
||||
void imlib_threshold(image_t *src, image_t *dst, color_t *color, int color_size, int threshold);
|
||||
void imlib_rainbow(image_t *src, struct image *dst);
|
||||
array_t *imlib_count_blobs(struct image *image);
|
||||
|
||||
@ -575,6 +575,34 @@ static mp_obj_t py_image_orientation_degrees(uint n_args, const mp_obj_t *args,
|
||||
{mp_obj_new_int(sum), mp_obj_new_int(x), mp_obj_new_int(y), mp_obj_new_float(o)});
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_erode(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
|
||||
{
|
||||
image_t *arg_img = py_image_cobj(args[0]);
|
||||
PY_ASSERT_FALSE_MSG(IM_IS_JPEG(arg_img),
|
||||
"Operation not supported on JPEG");
|
||||
|
||||
int ksize = mp_obj_get_int(args[1]);
|
||||
PY_ASSERT_TRUE_MSG(ksize >= 0, "Kernel Size must be >= 0");
|
||||
imlib_erode(arg_img, ksize,
|
||||
py_helper_lookup_int(kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_threshold), ((ksize*2)+1)*((ksize*2)+1)));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_dilate(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
|
||||
{
|
||||
image_t *arg_img = py_image_cobj(args[0]);
|
||||
PY_ASSERT_FALSE_MSG(IM_IS_JPEG(arg_img),
|
||||
"Operation not supported on JPEG");
|
||||
|
||||
int ksize = mp_obj_get_int(args[1]);
|
||||
PY_ASSERT_TRUE_MSG(ksize >= 0, "Kernel Size must be >= 0");
|
||||
imlib_dilate(arg_img, ksize,
|
||||
py_helper_lookup_int(kw_args,
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_threshold), 1));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_negate(mp_obj_t img_obj)
|
||||
{
|
||||
image_t *arg_img = py_image_cobj(img_obj);
|
||||
@ -845,32 +873,6 @@ static mp_obj_t py_image_compress(mp_obj_t image_obj, mp_obj_t quality)
|
||||
return py_image_from_struct(&cimage);
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_erode(mp_obj_t image_obj, mp_obj_t ksize_obj)
|
||||
{
|
||||
image_t *image = NULL;
|
||||
image = py_image_cobj(image_obj);
|
||||
|
||||
/* sanity checks */
|
||||
PY_ASSERT_TRUE_MSG(image->bpp == 1,
|
||||
"This function is only supported on GRAYSCALE images");
|
||||
|
||||
imlib_erode(image, mp_obj_get_int(ksize_obj));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_dilate(mp_obj_t image_obj, mp_obj_t ksize_obj)
|
||||
{
|
||||
image_t *image = NULL;
|
||||
image = py_image_cobj(image_obj);
|
||||
|
||||
/* sanity checks */
|
||||
PY_ASSERT_TRUE_MSG(image->bpp == 1,
|
||||
"This function is only supported on GRAYSCALE images");
|
||||
|
||||
imlib_dilate(image, mp_obj_get_int(ksize_obj));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t py_image_find_blobs(mp_obj_t image_obj)
|
||||
{
|
||||
// Get image pointer
|
||||
@ -1103,6 +1105,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_pixels_obj, 1, py_image_pixels);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_centroid_obj, 1, py_image_centroid);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_orientation_radians_obj, 1, py_image_orientation_radians);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_orientation_degrees_obj, 1, py_image_orientation_degrees);
|
||||
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 */
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_negate_obj, py_image_negate);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_difference_obj, py_image_difference);
|
||||
@ -1116,8 +1120,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_histeq_obj, py_image_histeq);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_median_obj, 1, py_image_median);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(py_image_threshold_obj, py_image_threshold);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_rainbow_obj, py_image_rainbow);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_erode_obj, py_image_erode);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_dilate_obj, py_image_dilate);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_compress_obj, py_image_compress);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_find_blobs_obj, py_image_find_blobs);
|
||||
@ -1157,6 +1159,8 @@ static const mp_map_elem_t locals_dict_table[] = {
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_centroid), (mp_obj_t)&py_image_centroid_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_orientation_radians), (mp_obj_t)&py_image_orientation_radians_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_orientation_degrees), (mp_obj_t)&py_image_orientation_degrees_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_erode), (mp_obj_t)&py_image_erode_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_dilate), (mp_obj_t)&py_image_dilate_obj},
|
||||
/* Background Subtraction (Frame Differencing) functions */
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_negate), (mp_obj_t)&py_image_negate_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_difference), (mp_obj_t)&py_image_difference_obj},
|
||||
@ -1170,10 +1174,7 @@ static const mp_map_elem_t locals_dict_table[] = {
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_median), (mp_obj_t)&py_image_median_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_threshold), (mp_obj_t)&py_image_threshold_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_rainbow), (mp_obj_t)&py_image_rainbow_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_erode), (mp_obj_t)&py_image_erode_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_dilate), (mp_obj_t)&py_image_dilate_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_compress), (mp_obj_t)&py_image_compress_obj},
|
||||
|
||||
/* objects/feature detection */
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_find_blobs), (mp_obj_t)&py_image_find_blobs_obj},
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_find_template), (mp_obj_t)&py_image_find_template_obj},
|
||||
|
||||
24
usr/tests/test_erode_and_dilate.py
Normal file
24
usr/tests/test_erode_and_dilate.py
Normal file
@ -0,0 +1,24 @@
|
||||
import pyb, sensor, image, math
|
||||
sensor.reset()
|
||||
sensor.set_framesize(sensor.QVGA)
|
||||
grayscale_thres = (170, 255)
|
||||
rgb565_thres = (70, 100, -128, 127, -128, 127)
|
||||
while(True):
|
||||
sensor.set_pixformat(sensor.GRAYSCALE)
|
||||
for i in range(100):
|
||||
img = sensor.snapshot()
|
||||
img.binary([grayscale_thres])
|
||||
img.erode(2)
|
||||
for i in range(100):
|
||||
img = sensor.snapshot()
|
||||
img.binary([grayscale_thres])
|
||||
img.dilate(2)
|
||||
sensor.set_pixformat(sensor.RGB565)
|
||||
for i in range(100):
|
||||
img = sensor.snapshot()
|
||||
img.binary([rgb565_thres])
|
||||
img.erode(2)
|
||||
for i in range(100):
|
||||
img = sensor.snapshot()
|
||||
img.binary([rgb565_thres])
|
||||
img.dilate(2)
|
||||
Loading…
Reference in New Issue
Block a user