mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
commit
c83ae21c63
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
35
usr/examples/09-Feature-Detection/find_circles.py
Normal file
35
usr/examples/09-Feature-Detection/find_circles.py
Normal 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())
|
||||
Loading…
Reference in New Issue
Block a user