Merge pull request #249 from kwagyeman/master

Add find circles.
This commit is contained in:
Ibrahim Abd Elkader 2017-07-04 20:16:59 +02:00 committed by GitHub
commit c83ae21c63
6 changed files with 532 additions and 7 deletions

View File

@ -26,6 +26,9 @@
// Have built-in RGB->LAB table.
#define OMV_HAVE_LAB_TABLE
// Enable Find_Circles
#define OMV_ENABLE_FIND_CIRCLES
// Enable AprilTags (64 KB).
#define OMV_ENABLE_APRILTAGS

View File

@ -851,3 +851,331 @@ void imlib_find_line_segments(list_t *out, image_t *ptr, rectangle_t *roi, unsig
fb_free(); // mag_buffer
fb_free(); // theta_buffer
}
void imlib_find_circles(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int x_stride, unsigned int y_stride,
uint32_t threshold, unsigned int x_margin, unsigned int y_margin, unsigned int r_margin)
{
uint16_t *theta_acc = fb_alloc0(sizeof(uint16_t) * roi->w * roi->h);
uint16_t *magnitude_acc = fb_alloc0(sizeof(uint16_t) * roi->w * roi->h);
switch (ptr->bpp) {
case IMAGE_BPP_BINARY: {
for (int y = roi->y + 1, yy = roi->y + roi->h - 1; y < yy; y += y_stride) {
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(ptr, y);
for (int x = roi->x + (y % x_stride) + 1, xx = roi->x + roi->w - 1; x < xx; x += x_stride) {
int pixel; // Sobel Algorithm Below
int x_acc = 0;
int y_acc = 0;
row_ptr -= ((ptr->w + UINT32_T_MASK) >> UINT32_T_SHIFT);
pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x - 1));
x_acc += pixel * +1; // x[0,0] -> pixel * +1
y_acc += pixel * +1; // y[0,0] -> pixel * +1
pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x));
// x[0,1] -> pixel * 0
y_acc += pixel * +2; // y[0,1] -> pixel * +2
pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x + 1));
x_acc += pixel * -1; // x[0,2] -> pixel * -1
y_acc += pixel * +1; // y[0,2] -> pixel * +1
row_ptr += ((ptr->w + UINT32_T_MASK) >> UINT32_T_SHIFT);
pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x - 1));
x_acc += pixel * +2; // x[1,0] -> pixel * +2
// y[1,0] -> pixel * 0
// pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x));
// x[1,1] -> pixel * 0
// y[1,1] -> pixel * 0
pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x + 1));
x_acc += pixel * -2; // x[1,2] -> pixel * -2
// y[1,2] -> pixel * 0
row_ptr += ((ptr->w + UINT32_T_MASK) >> UINT32_T_SHIFT);
pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x - 1));
x_acc += pixel * +1; // x[2,0] -> pixel * +1
y_acc += pixel * -1; // y[2,0] -> pixel * -1
pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x));
// x[2,1] -> pixel * 0
y_acc += pixel * -2; // y[2,1] -> pixel * -2
pixel = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x + 1));
x_acc += pixel * -1; // x[2,2] -> pixel * -1
y_acc += pixel * -1; // y[2,2] -> pixel * -1
row_ptr -= ((ptr->w + UINT32_T_MASK) >> UINT32_T_SHIFT);
int theta = fast_roundf(fast_atan2f(y_acc, x_acc) * 57.295780) % 360; // * (180 / PI)
if (theta < 0) theta += 360;
int magnitude = fast_roundf(fast_sqrtf((x_acc * x_acc) + (y_acc * y_acc)));
int index = (roi->w * (y - roi->y)) + (x - roi->x);
theta_acc[index] = theta;
magnitude_acc[index] = magnitude;
}
}
break;
}
case IMAGE_BPP_GRAYSCALE: {
for (int y = roi->y + 1, yy = roi->y + roi->h - 1; y < yy; y += y_stride) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y);
for (int x = roi->x + (y % x_stride) + 1, xx = roi->x + roi->w - 1; x < xx; x += x_stride) {
int pixel; // Sobel Algorithm Below
int x_acc = 0;
int y_acc = 0;
row_ptr -= ptr->w;
pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x - 1);
x_acc += pixel * +1; // x[0,0] -> pixel * +1
y_acc += pixel * +1; // y[0,0] -> pixel * +1
pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x);
// x[0,1] -> pixel * 0
y_acc += pixel * +2; // y[0,1] -> pixel * +2
pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x + 1);
x_acc += pixel * -1; // x[0,2] -> pixel * -1
y_acc += pixel * +1; // y[0,2] -> pixel * +1
row_ptr += ptr->w;
pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x - 1);
x_acc += pixel * +2; // x[1,0] -> pixel * +2
// y[1,0] -> pixel * 0
// pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x);
// x[1,1] -> pixel * 0
// y[1,1] -> pixel * 0
pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x + 1);
x_acc += pixel * -2; // x[1,2] -> pixel * -2
// y[1,2] -> pixel * 0
row_ptr += ptr->w;
pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x - 1);
x_acc += pixel * +1; // x[2,0] -> pixel * +1
y_acc += pixel * -1; // y[2,0] -> pixel * -1
pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x);
// x[2,1] -> pixel * 0
y_acc += pixel * -2; // y[2,1] -> pixel * -2
pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x + 1);
x_acc += pixel * -1; // x[2,2] -> pixel * -1
y_acc += pixel * -1; // y[2,2] -> pixel * -1
row_ptr -= ptr->w;
int theta = fast_roundf(fast_atan2f(y_acc, x_acc) * 57.295780) % 360; // * (180 / PI)
if (theta < 0) theta += 360;
int magnitude = fast_roundf(fast_sqrtf((x_acc * x_acc) + (y_acc * y_acc)));
int index = (roi->w * (y - roi->y)) + (x - roi->x);
theta_acc[index] = theta;
magnitude_acc[index] = magnitude;
}
}
break;
}
case IMAGE_BPP_RGB565: {
for (int y = roi->y + 1, yy = roi->y + roi->h - 1; y < yy; y += y_stride) {
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(ptr, y);
for (int x = roi->x + (y % x_stride) + 1, xx = roi->x + roi->w - 1; x < xx; x += x_stride) {
int pixel; // Sobel Algorithm Below
int x_acc = 0;
int y_acc = 0;
row_ptr -= ptr->w;
pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x - 1));
x_acc += pixel * +1; // x[0,0] -> pixel * +1
y_acc += pixel * +1; // y[0,0] -> pixel * +1
pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
// x[0,1] -> pixel * 0
y_acc += pixel * +2; // y[0,1] -> pixel * +2
pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x + 1));
x_acc += pixel * -1; // x[0,2] -> pixel * -1
y_acc += pixel * +1; // y[0,2] -> pixel * +1
row_ptr += ptr->w;
pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x - 1));
x_acc += pixel * +2; // x[1,0] -> pixel * +2
// y[1,0] -> pixel * 0
// pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
// x[1,1] -> pixel * 0
// y[1,1] -> pixel * 0
pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x + 1));
x_acc += pixel * -2; // x[1,2] -> pixel * -2
// y[1,2] -> pixel * 0
row_ptr += ptr->w;
pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x - 1));
x_acc += pixel * +1; // x[2,0] -> pixel * +1
y_acc += pixel * -1; // y[2,0] -> pixel * -1
pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
// x[2,1] -> pixel * 0
y_acc += pixel * -2; // y[2,1] -> pixel * -2
pixel = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x + 1));
x_acc += pixel * -1; // x[2,2] -> pixel * -1
y_acc += pixel * -1; // y[2,2] -> pixel * -1
row_ptr -= ptr->w;
int theta = fast_roundf(fast_atan2f(y_acc, x_acc) * 57.295780) % 360; // * (180 / PI)
if (theta < 0) theta += 360;
int magnitude = fast_roundf(fast_sqrtf((x_acc * x_acc) + (y_acc * y_acc)));
int index = (roi->w * (y - roi->y)) + (x - roi->x);
theta_acc[index] = theta;
magnitude_acc[index] = magnitude;
}
}
break;
}
default: {
break;
}
}
// Theta Direction (% 180)
//
// 0,0 X_MAX
//
// 090
// 000 000
// 090
//
// Y_MAX
// Theta Direction (% 360)
//
// 0,0 X_MAX
//
// 090
// 000 180
// 270
//
// Y_MAX
list_init(out, sizeof(find_circles_list_lnk_data_t));
for (int r = 2, rr = IM_MIN((roi->w / 2), (roi->h / 2)); r < rr; r += 2) { // ignore r = 0/1
int a_size, b_size, hough_divide = 1; // divides a and b accumulators
int w_size = roi->w - (2 * r);
int h_size = roi->h - (2 * r);
for (;;) { // shrink to fit...
a_size = 1 + ((w_size + hough_divide - 1) / hough_divide) + 1; // left & right padding
b_size = 1 + ((h_size + hough_divide - 1) / hough_divide) + 1; // top & bottom padding
if ((sizeof(uint32_t) * a_size * b_size) <= fb_avail()) break;
hough_divide = hough_divide << 1; // powers of 2...
if (hough_divide > 4) fb_alloc_fail(); // support 1, 2, 4
}
uint32_t *acc = fb_alloc0(sizeof(uint32_t) * a_size * b_size);
for (int y = 0, yy = roi->h; y < yy; y++) {
for (int x = 0, xx = roi->w; x < xx; x++) {
int index = (roi->w * y) + x;
int theta = theta_acc[index];
int magnitude = magnitude_acc[index];
if (!magnitude) continue;
int a = fast_roundf(x + (r * cos_table[theta])) - r;
if ((a < 0) || (w_size <= a)) continue; // circle doesn't fit in the window
int b = fast_roundf(y + (r * sin_table[theta])) - r;
if ((b < 0) || (h_size <= b)) continue; // circle doesn't fit in the window
int acc_index = (((b / hough_divide) + 1) * a_size) + ((a / hough_divide) + 1); // add offset
int acc_value = acc[acc_index] += magnitude;
acc[acc_index] = acc_value;
}
}
for (int y = 1, yy = b_size - 1; y < yy; y++) {
uint32_t *row_ptr = acc + (a_size * y);
for (int x = 1, xx = a_size - 1; x < xx; x++) {
if ((row_ptr[x] >= threshold)
&& (row_ptr[x] >= row_ptr[x-a_size-1])
&& (row_ptr[x] >= row_ptr[x-a_size])
&& (row_ptr[x] >= row_ptr[x-a_size+1])
&& (row_ptr[x] >= row_ptr[x-1])
&& (row_ptr[x] >= row_ptr[x+1])
&& (row_ptr[x] >= row_ptr[x+a_size-1])
&& (row_ptr[x] >= row_ptr[x+a_size])
&& (row_ptr[x] >= row_ptr[x+a_size+1])) {
find_circles_list_lnk_data_t lnk_data;
lnk_data.magnitude = row_ptr[x];
lnk_data.p.x = ((x - 1) * hough_divide) + r + roi->x; // remove offset
lnk_data.p.y = ((y - 1) * hough_divide) + r + roi->y; // remove offset
lnk_data.r = r;
list_push_back(out, &lnk_data);
}
}
}
fb_free(); // acc
}
fb_free(); // magnitude_acc
fb_free(); // theta_acc
for (;;) { // Merge overlapping.
bool merge_occured = false;
list_t out_temp;
list_init(&out_temp, sizeof(find_circles_list_lnk_data_t));
while (list_size(out)) {
find_circles_list_lnk_data_t lnk_data;
list_pop_front(out, &lnk_data);
for (size_t k = 0, l = list_size(out); k < l; k++) {
find_circles_list_lnk_data_t tmp_data;
list_pop_front(out, &tmp_data);
bool x_diff_ok = abs(lnk_data.p.x - tmp_data.p.x) < x_margin;
bool y_diff_ok = abs(lnk_data.p.y - tmp_data.p.y) < y_margin;
bool r_diff_ok = abs(lnk_data.r - tmp_data.r) < r_margin;
if (x_diff_ok && y_diff_ok && r_diff_ok) {
uint32_t magnitude = lnk_data.magnitude + tmp_data.magnitude;
lnk_data.p.x = ((lnk_data.p.x * lnk_data.magnitude) + (tmp_data.p.x * tmp_data.magnitude)) / magnitude;
lnk_data.p.y = ((lnk_data.p.y * lnk_data.magnitude) + (tmp_data.p.y * tmp_data.magnitude)) / magnitude;
lnk_data.r = ((lnk_data.r * lnk_data.magnitude) + (tmp_data.r * tmp_data.magnitude)) / magnitude;
lnk_data.magnitude = magnitude / 2;
merge_occured = true;
} else {
list_push_back(out, &tmp_data);
}
}
list_push_back(&out_temp, &lnk_data);
}
list_copy(out, &out_temp);
if (!merge_occured) {
break;
}
}
}

View File

@ -896,6 +896,12 @@ typedef struct find_lines_list_lnk_data {
int16_t theta, rho;
} find_lines_list_lnk_data_t;
typedef struct find_circles_list_lnk_data {
point_t p;
int r;
uint32_t magnitude;
} find_circles_list_lnk_data_t;
typedef struct find_qrcodes_list_lnk_data {
point_t corners[4];
rectangle_t rect;
@ -1148,6 +1154,8 @@ void imlib_find_lines(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int
void imlib_find_line_segments(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int x_stride, unsigned int y_stride,
uint32_t threshold, unsigned int theta_margin, unsigned int rho_margin,
uint32_t segment_threshold);
void imlib_find_circles(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int x_stride, unsigned int y_stride,
uint32_t threshold, unsigned int x_margin, unsigned int y_margin, unsigned int r_margin);
// 1/2D Bar Codes
void imlib_find_qrcodes(list_t *out, image_t *ptr, rectangle_t *roi);
void imlib_find_apriltags(list_t *out, image_t *ptr, rectangle_t *roi, apriltag_families_t families,

View File

@ -201,19 +201,19 @@ static void py_image_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k
switch(self->_cobj.bpp) {
case IMAGE_BPP_BINARY: {
mp_printf(print, "{w:%d, h:%d, type=\"binary\", size:%d}",
self->_cobj.w, self->_cobj.h,
self->_cobj.w, self->_cobj.h,
((self->_cobj.w + UINT32_T_MASK) >> UINT32_T_SHIFT) * self->_cobj.h);
break;
}
case IMAGE_BPP_GRAYSCALE: {
mp_printf(print, "{w:%d, h:%d, type=\"grayscale\", size:%d}",
self->_cobj.w, self->_cobj.h,
self->_cobj.w, self->_cobj.h,
(self->_cobj.w * self->_cobj.h) * sizeof(uint8_t));
break;
}
case IMAGE_BPP_RGB565: {
mp_printf(print, "{w:%d, h:%d, type=\"rgb565\", size:%d}",
self->_cobj.w, self->_cobj.h,
self->_cobj.w, self->_cobj.h,
(self->_cobj.w * self->_cobj.h) * sizeof(uint16_t));
break;
}
@ -222,7 +222,7 @@ static void py_image_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k
print->print_strn(print->data, (const char *) self->_cobj.data, self->_cobj.bpp);
} else { // not for ide
mp_printf(print, "{w:%d, h:%d, type=\"jpeg\", size:%d}",
self->_cobj.w, self->_cobj.h,
self->_cobj.w, self->_cobj.h,
self->_cobj.bpp);
}
break;
@ -2481,6 +2481,124 @@ static mp_obj_t py_image_find_line_segments(uint n_args, const mp_obj_t *args, m
return objects_list;
}
#ifdef OMV_ENABLE_FIND_CIRCLES
// Circle Object //
#define py_circle_obj_size 4
typedef struct py_circle_obj {
mp_obj_base_t base;
mp_obj_t x, y, r, magnitude;
} py_circle_obj_t;
static void py_circle_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind)
{
py_circle_obj_t *self = self_in;
mp_printf(print,
"{x:%d, y:%d, r:%d, magnitude:%d}",
mp_obj_get_int(self->x),
mp_obj_get_int(self->y),
mp_obj_get_int(self->r),
mp_obj_get_int(self->magnitude));
}
static mp_obj_t py_circle_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value)
{
if (value == MP_OBJ_SENTINEL) { // load
py_circle_obj_t *self = self_in;
if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) {
mp_bound_slice_t slice;
if (!mp_seq_get_fast_slice_indexes(py_circle_obj_size, index, &slice)) {
mp_not_implemented("only slices with step=1 (aka None) are supported");
}
mp_obj_tuple_t *result = mp_obj_new_tuple(slice.stop - slice.start, NULL);
mp_seq_copy(result->items, &(self->x) + slice.start, result->len, mp_obj_t);
return result;
}
switch (mp_get_index(self->base.type, py_circle_obj_size, index, false)) {
case 0: return self->x;
case 1: return self->y;
case 2: return self->r;
case 3: return self->magnitude;
}
}
return MP_OBJ_NULL; // op not supported
}
mp_obj_t py_circle_circle(mp_obj_t self_in)
{
return mp_obj_new_tuple(3, (mp_obj_t []) {((py_circle_obj_t *) self_in)->x,
((py_circle_obj_t *) self_in)->y,
((py_circle_obj_t *) self_in)->r});
}
mp_obj_t py_circle_x(mp_obj_t self_in) { return ((py_circle_obj_t *) self_in)->x; }
mp_obj_t py_circle_y(mp_obj_t self_in) { return ((py_circle_obj_t *) self_in)->y; }
mp_obj_t py_circle_r(mp_obj_t self_in) { return ((py_circle_obj_t *) self_in)->r; }
mp_obj_t py_circle_magnitude(mp_obj_t self_in) { return ((py_circle_obj_t *) self_in)->magnitude; }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_circle_circle_obj, py_circle_circle);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_circle_x_obj, py_circle_x);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_circle_y_obj, py_circle_y);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_circle_r_obj, py_circle_r);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_circle_magnitude_obj, py_circle_magnitude);
STATIC const mp_rom_map_elem_t py_circle_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&py_circle_circle_obj) },
{ MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&py_circle_x_obj) },
{ MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&py_circle_y_obj) },
{ MP_ROM_QSTR(MP_QSTR_r), MP_ROM_PTR(&py_circle_r_obj) },
{ MP_ROM_QSTR(MP_QSTR_magnitude), MP_ROM_PTR(&py_circle_magnitude_obj) },
};
STATIC MP_DEFINE_CONST_DICT(py_circle_locals_dict, py_circle_locals_dict_table);
static const mp_obj_type_t py_circle_type = {
{ &mp_type_type },
.name = MP_QSTR_circle,
.print = py_circle_print,
.subscr = py_circle_subscr,
.locals_dict = (mp_obj_t) &py_circle_locals_dict,
};
static mp_obj_t py_image_find_circles(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 or RAW frames.");
rectangle_t roi;
py_helper_lookup_rectangle(kw_args, arg_img, &roi);
unsigned int x_stride = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_stride), 2);
PY_ASSERT_TRUE_MSG(x_stride > 0, "x_stride must not be zero.");
unsigned int y_stride = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_stride), 1);
PY_ASSERT_TRUE_MSG(y_stride > 0, "y_stride must not be zero.");
list_t out;
fb_alloc_mark();
imlib_find_circles(&out, arg_img, &roi, x_stride, y_stride, py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), 1600),
py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_margin), 10),
py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_margin), 10),
py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_r_margin), 10));
fb_alloc_free_till_mark();
mp_obj_list_t *objects_list = mp_obj_new_list(list_size(&out), NULL);
for (size_t i = 0; list_size(&out); i++) {
find_circles_list_lnk_data_t lnk_data;
list_pop_front(&out, &lnk_data);
py_circle_obj_t *o = m_new_obj(py_circle_obj_t);
o->base.type = &py_circle_type;
o->x = mp_obj_new_int(lnk_data.p.x);
o->y = mp_obj_new_int(lnk_data.p.y);
o->r = mp_obj_new_int(lnk_data.r);
o->magnitude = mp_obj_new_int(lnk_data.magnitude);
objects_list->items[i] = o;
}
return objects_list;
}
#endif // OMV_ENABLE_FIND_CIRCLES
// QRCode Object //
#define py_qrcode_obj_size 10
typedef struct py_qrcode_obj {
@ -3562,6 +3680,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_blobs_obj, 2, py_image_find_blob
/* Shape Detection */
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_lines_obj, 1, py_image_find_lines);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_line_segments_obj, 1, py_image_find_line_segments);
#ifdef OMV_ENABLE_FIND_CIRCLES
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_circles_obj, 1, py_image_find_circles);
#endif
/* Code Detection */
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_qrcodes_obj, 1, py_image_find_qrcodes);
#ifdef OMV_ENABLE_APRILTAGS
@ -3657,6 +3778,9 @@ static const mp_map_elem_t locals_dict_table[] = {
/* Shape Detection */
{MP_OBJ_NEW_QSTR(MP_QSTR_find_lines), (mp_obj_t)&py_image_find_lines_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_find_line_segments), (mp_obj_t)&py_image_find_line_segments_obj},
#ifdef OMV_ENABLE_FIND_CIRCLES
{MP_OBJ_NEW_QSTR(MP_QSTR_find_circles), (mp_obj_t)&py_image_find_circles_obj},
#endif
/* Code Detection */
{MP_OBJ_NEW_QSTR(MP_QSTR_find_qrcodes), (mp_obj_t)&py_image_find_qrcodes_obj},
#ifdef OMV_ENABLE_APRILTAGS

View File

@ -352,12 +352,17 @@ Q(zoom)
// Get Histogram
Q(get_hist)
Q(get_histogram)
// Histogram Object
Q(histogram)
// duplicate Q(roi)
Q(bins)
Q(l_bins)
Q(a_bins)
Q(b_bins)
// Histogram Object
Q(histogram)
// duplicate Q(bins)
// duplicate Q(l_bins)
// duplicate Q(a_bins)
// duplicate Q(b_bins)
Q(get_percentile)
Q(get_stats)
Q(get_statistics)
@ -372,6 +377,11 @@ Q(b_value)
// Get Statistics
// duplicate Q(get_stats)
// duplicate Q(get_statistics)
// duplicate Q(roi)
// duplicate Q(bins)
// duplicate Q(l_bins)
// duplicate Q(a_bins)
// duplicate Q(b_bins)
// Statistics Object
// duplicate Q(statistics)
// duplicate Q(mean)
@ -428,6 +438,7 @@ Q(rho)
// Find Blobs
Q(find_blobs)
// duplicate Q(roi)
// duplicate Q(x_stride)
// duplicate Q(y_stride)
Q(area_threshold)
@ -436,7 +447,6 @@ Q(merge)
Q(margin)
Q(threshold_cb)
Q(merge_cb)
// duplicate Q(roi)
// Blob Object
Q(blob)
Q(rect)
@ -472,6 +482,23 @@ Q(find_line_segments)
// duplicate Q(rho_margin)
Q(segment_threshold)
// Find Circles
Q(find_circles)
// duplicate Q(roi)
// duplicate Q(x_stride)
// duplicate Q(y_stride)
// duplicate Q(threshold)
Q(x_margin)
Q(y_margin)
Q(r_margin)
// Circle Object
Q(circle)
// duplicate Q(circle)
// duplicate Q(x)
// duplicate Q(y)
Q(r)
// duplicate Q(magnitude)
// Find QRCodes
Q(find_qrcodes)
// duplicate Q(roi)

View File

@ -0,0 +1,35 @@
# Find Circles Example
#
# This example shows off how to find circles in the image using the Hough
# Transform. https://en.wikipedia.org/wiki/Circle_Hough_Transform
#
# Note that the find_circles() method will only find circles which are completely
# inside of the image. Circles which go outside of the image/roi are ignored...
import sensor, image, time
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # grayscale is faster
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot().lens_corr(1.8)
# Circle objects have four values: x, y, r (radius), and magnitude. The
# magnitude is the strength of the detection of the circle. Higher is
# better...
# `threshold` controls how many circles are found. Increase its value
# to decrease the number of circles detected...
# `x_margin`, `y_margin`, and `r_margin` control the merging of similar
# circles in the x, y, and r (radius) directions.
for c in img.find_circles(threshold = 2000, x_margin = 10, y_margin = 10, r_margin = 10):
img.draw_circle(c.x(), c.y(), c.r(), color = (255, 0, 0))
print(c)
print("FPS %f" % clock.fps())