Merge pull request #89 from kwagyeman/master

Add new morph function.
This commit is contained in:
Ibrahim Abd Elkader 2016-03-01 01:21:45 +02:00
commit 0b07f4349f
7 changed files with 170 additions and 11 deletions

View File

@ -128,6 +128,7 @@ OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
fmath.o \
haar.o \
imlib.o \
morph.o \
integral.o \
integral_mw.o \
kmeans.o \

View File

@ -31,6 +31,7 @@ SRCS += $(addprefix img/, \
fmath.c \
haar.c \
imlib.c \
morph.c \
integral.c \
integral_mw.c \
kmeans.c \

View File

@ -614,7 +614,7 @@ static void imlib_erode_dilate(image_t *img, int ksize, int threshold, int e_or_
if ((!!buffer[buffer_idx]) == e_or_d) {
continue; // short circuit (makes this very fast - usually)
}
int acc = 0;
int acc = -1; // don't count center pixel...
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)) {
@ -648,7 +648,7 @@ static void imlib_erode_dilate(image_t *img, int ksize, int threshold, int e_or_
if ((!!((uint16_t *) buffer)[buffer_idx]) == e_or_d) {
continue; // short circuit (makes this very fast - usually)
}
int acc = 0;
int acc = -1; // don't count center pixel...
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)) {

View File

@ -407,6 +407,9 @@ void imlib_dilate(image_t *img, int ksize, int threshold);
void imlib_negate(image_t *img);
void imlib_difference(image_t *img, const char *path, image_t *other);
/* Image Morphing */
void imlib_morph(image_t *img, const int ksize, const int8_t *krn, const float m, const int b);
/* Clustering functions */
array_t *cluster_kmeans(array_t *points, int k);

93
src/omv/img/morph.c Normal file
View File

@ -0,0 +1,93 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013-2016 Kwabena W. Agyeman <kwagyeman@openmv.io>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Generic image convolution function.
*
*/
#include <string.h>
#include "imlib.h"
#include "fb_alloc.h"
// krn_s == 0 -> 1x1 kernel
// krn_s == 1 -> 3x3 kernel
// ...
// krn_s == n -> ((n*2)+1)x((n*2)+1) kernel
//
// pixel = (krn_sum / m) + b
//
// http://www.fmwconcepts.com/imagemagick/digital_image_filtering.pdf
void imlib_morph(image_t *img, const int ksize, const int8_t *krn, const float m, const int b)
{
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++) {
int acc = 0;
int ptr = 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 += krn[ptr++] * IM_GET_GS_PIXEL(img, x+k, y+j);
}
}
}
acc = (acc * m) + b; // scale, offset, and clamp
acc = IM_MAX(IM_MIN(acc, IM_MAX_GS), 0);
// We're writing into the buffer like if it were a window.
buffer[((y%brows)*img->w)+x] = acc;
}
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++) {
int r_acc = 0;
int g_acc = 0;
int b_acc = 0;
int ptr = 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)) {
const uint16_t pixel = IM_GET_RGB565_PIXEL(img, x+k, y+j);
r_acc += krn[ptr] * IM_R565(pixel);
g_acc += krn[ptr] * IM_G565(pixel);
b_acc += krn[ptr++] * IM_B565(pixel);
}
}
}
r_acc = (r_acc * m) + b; // scale, offset, and clamp
r_acc = IM_MAX(IM_MIN(r_acc, IM_MAX_R5), 0);
g_acc = (g_acc * m) + b; // scale, offset, and clamp
g_acc = IM_MAX(IM_MIN(g_acc, IM_MAX_G6), 0);
b_acc = (b_acc * m) + b; // scale, offset, and clamp
b_acc = IM_MAX(IM_MIN(b_acc, IM_MAX_B5), 0);
// We're writing into the buffer like if it were a window.
((uint16_t *) buffer)[((y%brows)*img->w)+x] = IM_RGB565(r_acc, g_acc, b_acc);
}
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();
}

View File

@ -363,7 +363,6 @@ static mp_obj_t py_image_draw_keypoints(uint n_args, const mp_obj_t *args, mp_ma
} else {
py_kp_obj_t *kpts_obj = ((py_kp_obj_t*)args[1]);
PY_ASSERT_TYPE(kpts_obj, &py_kp_type);
for (int i=0; i<array_length(kpts_obj->kpts); i++) {
kp_t *kp = array_at(kpts_obj->kpts, i);
imlib_draw_circle(arg_img, kp->x, kp->y, (arg_s-2)/2, arg_c);
@ -581,11 +580,11 @@ static mp_obj_t py_image_erode(uint n_args, const mp_obj_t *args, mp_map_t *kw_a
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,
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), ((ksize*2)+1)*((ksize*2)+1)));
MP_OBJ_NEW_QSTR(MP_QSTR_threshold), ((arg_ksize*2)+1)*((arg_ksize*2)+1)-1));
return mp_const_none;
}
@ -595,11 +594,11 @@ static mp_obj_t py_image_dilate(uint n_args, const mp_obj_t *args, mp_map_t *kw_
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,
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), 1));
MP_OBJ_NEW_QSTR(MP_QSTR_threshold), 0));
return mp_const_none;
}
@ -628,6 +627,41 @@ static mp_obj_t py_image_difference(mp_obj_t img_obj, mp_obj_t other_obj)
return mp_const_none;
}
static mp_obj_t py_image_morph(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 arg_ksize = mp_obj_get_int(args[1]);
PY_ASSERT_TRUE_MSG(arg_ksize >= 0, "Kernel Size must be >= 0");
int array_size = ((arg_ksize*2)+1)*((arg_ksize*2)+1);
mp_obj_t *krn;
mp_obj_get_array_fixed_n(args[2], array_size, &krn);
int8_t arg_krn[array_size];
int arg_m = 0;
for (int i = 0; i < array_size; i++) {
int value = mp_obj_get_int(krn[i]);
PY_ASSERT_FALSE_MSG((value < -128) || (127 < value),
"Kernel Values must be between [-128:127] inclusive");
arg_krn[i] = value;
arg_m += arg_krn[i];
}
if (arg_m == 0) {
arg_m = 1;
}
imlib_morph(arg_img, arg_ksize, arg_krn,
py_helper_lookup_float(kw_args,
MP_OBJ_NEW_QSTR(MP_QSTR_mul), 1.0 / ((float) arg_m)),
py_helper_lookup_int(kw_args,
MP_OBJ_NEW_QSTR(MP_QSTR_add), 0));
return mp_const_none;
}
static mp_obj_t py_image_scale(mp_obj_t image_obj, mp_obj_t size_obj)
{
int w,h;
@ -1110,6 +1144,8 @@ 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);
/* Image Morphing */
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_morph_obj, 3, py_image_morph);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_scale_obj, py_image_scale);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_scaled_obj, py_image_scaled);
@ -1164,6 +1200,8 @@ static const mp_map_elem_t locals_dict_table[] = {
/* 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},
/* Image Morphing */
{MP_OBJ_NEW_QSTR(MP_QSTR_morph), (mp_obj_t)&py_image_morph_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_scale), (mp_obj_t)&py_image_scale_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_scaled), (mp_obj_t)&py_image_scaled_obj},

View File

@ -0,0 +1,23 @@
# Edge Detection Example:
#
# This example demonstrates using the morph function on an image to do edge
# detection and then thresholding and filtering that image afterwards.
import sensor, image
kernel_size = 1 # kernel width = (size*2)+1, kernel height = (size*2)+1
kernel = [-1, -1, -1,\
-1, +8, -1,\
-1, -1, -1]
# This is a high pass filter kernel. ee here for more kernels:
# http://www.fmwconcepts.com/imagemagick/digital_image_filtering.pdf
thresholds = [(100, 255)] # grayscale thresholds
sensor.reset()
sensor.set_framesize(sensor.QQVGA) # smaller resolution to go faster
sensor.set_pixformat(sensor.GRAYSCALE)
while(True):
img = sensor.snapshot()
img.morph(kernel_size, kernel)
img.binary(thresholds)
img.erode(1, threshold = 2) # erode pixels with less than 2 neighbors