Merge pull request #87 from kwagyeman/master

Fix dilate and erode.
This commit is contained in:
Ibrahim Abd Elkader 2016-02-29 14:51:30 +02:00
commit b5c20cd022
4 changed files with 184 additions and 127 deletions

View File

@ -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)
{

View File

@ -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);

View File

@ -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},

View 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)