mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
commit
0b07f4349f
@ -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 \
|
||||
|
||||
@ -31,6 +31,7 @@ SRCS += $(addprefix img/, \
|
||||
fmath.c \
|
||||
haar.c \
|
||||
imlib.c \
|
||||
morph.c \
|
||||
integral.c \
|
||||
integral_mw.c \
|
||||
kmeans.c \
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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
93
src/omv/img/morph.c
Normal 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();
|
||||
}
|
||||
@ -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},
|
||||
|
||||
23
usr/examples/edge_detection.py
Normal file
23
usr/examples/edge_detection.py
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user