mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Add find_rects() using AprilTag's quad detector.
It's awesome.
This commit is contained in:
parent
d7bbf3a5d9
commit
a4e556e7e3
@ -29,6 +29,9 @@
|
||||
// Enable Find_Circles
|
||||
#define OMV_ENABLE_FIND_CIRCLES
|
||||
|
||||
// Enable Find_Rects
|
||||
#define OMV_ENABLE_FIND_RECTS
|
||||
|
||||
// Enable AprilTags (64 KB).
|
||||
#define OMV_ENABLE_APRILTAGS
|
||||
|
||||
|
||||
@ -9882,7 +9882,7 @@ int quad_segment_maxima(apriltag_detector_t *td, zarray_t *cluster, struct line_
|
||||
}
|
||||
|
||||
// return 1 if the quad looks okay, 0 if it should be discarded
|
||||
int fit_quad(apriltag_detector_t *td, image_u8_t *im, zarray_t *cluster, struct quad *quad)
|
||||
int fit_quad(apriltag_detector_t *td, image_u8_t *im, zarray_t *cluster, struct quad *quad, bool overrideMode)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
@ -9934,7 +9934,7 @@ int fit_quad(apriltag_detector_t *td, image_u8_t *im, zarray_t *cluster, struct
|
||||
}
|
||||
|
||||
// Ensure that the black border is inside the white border.
|
||||
if (dot < 0)
|
||||
if ((!overrideMode) && (dot < 0))
|
||||
return 0;
|
||||
|
||||
// we now sort the points according to theta. This is a prepatory
|
||||
@ -10552,7 +10552,7 @@ image_u8_t *threshold(apriltag_detector_t *td, image_u8_t *im)
|
||||
return threshim;
|
||||
}
|
||||
|
||||
zarray_t *apriltag_quad_thresh(apriltag_detector_t *td, image_u8_t *im)
|
||||
zarray_t *apriltag_quad_thresh(apriltag_detector_t *td, image_u8_t *im, bool overrideMode)
|
||||
{
|
||||
////////////////////////////////////////////////////////
|
||||
// step 1. threshold the image, creating the edge image.
|
||||
@ -10720,7 +10720,7 @@ zarray_t *apriltag_quad_thresh(apriltag_detector_t *td, image_u8_t *im)
|
||||
struct quad quad;
|
||||
memset(&quad, 0, sizeof(struct quad));
|
||||
|
||||
if (fit_quad(td, im, cluster, &quad)) {
|
||||
if (fit_quad(td, im, cluster, &quad, overrideMode)) {
|
||||
|
||||
zarray_add_fail_ok(quads, &quad);
|
||||
}
|
||||
@ -11581,7 +11581,7 @@ zarray_t *apriltag_detector_detect(apriltag_detector_t *td, image_u8_t *im_orig)
|
||||
// and blurring parameters.
|
||||
|
||||
// zarray_t *quads = apriltag_quad_gradient(td, im_orig);
|
||||
zarray_t *quads = apriltag_quad_thresh(td, im_orig);
|
||||
zarray_t *quads = apriltag_quad_thresh(td, im_orig, false);
|
||||
|
||||
zarray_t *detections = zarray_create(sizeof(apriltag_detection_t*));
|
||||
|
||||
@ -11956,4 +11956,228 @@ void imlib_find_apriltags(list_t *out, image_t *ptr, rectangle_t *roi, apriltag_
|
||||
fb_free(); // umm_init_x();
|
||||
}
|
||||
|
||||
void imlib_find_rects(list_t *out, image_t *ptr, rectangle_t *roi,
|
||||
uint32_t threshold)
|
||||
{
|
||||
// Frame Buffer Memory Usage...
|
||||
// -> GRAYSCALE Input Image = w*h*1
|
||||
// -> GRAYSCALE Threhsolded Image = w*h*1
|
||||
// -> UnionFind = w*h*4 (+w*h*2 for hash table)
|
||||
size_t resolution = roi->w * roi->h;
|
||||
size_t fb_alloc_need = resolution * (1 + 1 + 4 + 2); // read above...
|
||||
umm_init_x(((fb_avail() - fb_alloc_need) / resolution) * resolution);
|
||||
apriltag_detector_t *td = apriltag_detector_create();
|
||||
|
||||
uint8_t *grayscale_image = fb_alloc(roi->w * roi->h);
|
||||
|
||||
image_u8_t im;
|
||||
im.width = roi->w;
|
||||
im.height = roi->h;
|
||||
im.stride = roi->w;
|
||||
im.buf = grayscale_image;
|
||||
|
||||
switch(ptr->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) {
|
||||
uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(ptr, y);
|
||||
for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) {
|
||||
*(grayscale_image++) = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_GRAYSCALE: {
|
||||
for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) {
|
||||
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y);
|
||||
for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) {
|
||||
*(grayscale_image++) = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_BPP_RGB565: {
|
||||
for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) {
|
||||
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(ptr, y);
|
||||
for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) {
|
||||
*(grayscale_image++) = COLOR_RGB565_TO_GRAYSCALE(IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
memset(grayscale_image, 0, roi->w * roi->h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Detect quads according to requested image decimation
|
||||
// and blurring parameters.
|
||||
|
||||
// zarray_t *detections = apriltag_quad_gradient(td, &im, true);
|
||||
zarray_t *detections = apriltag_quad_thresh(td, &im, true);
|
||||
|
||||
td->nquads = zarray_size(detections);
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Decode tags from each quad.
|
||||
if (1) {
|
||||
for (int i = 0; i < zarray_size(detections); i++) {
|
||||
struct quad *quad_original;
|
||||
zarray_get_volatile(detections, i, &quad_original);
|
||||
|
||||
// refine edges is not dependent upon the tag family, thus
|
||||
// apply this optimization BEFORE the other work.
|
||||
//if (td->quad_decimate > 1 && td->refine_edges) {
|
||||
if (td->refine_edges) {
|
||||
refine_edges(td, &im, quad_original);
|
||||
}
|
||||
|
||||
// make sure the homographies are computed...
|
||||
if (quad_update_homographies(quad_original))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Reconcile detections--- don't report the same tag more
|
||||
// than once. (Allow non-overlapping duplicate detections.)
|
||||
if (1) {
|
||||
zarray_t *poly0 = g2d_polygon_create_zeros(4);
|
||||
zarray_t *poly1 = g2d_polygon_create_zeros(4);
|
||||
|
||||
for (int i0 = 0; i0 < zarray_size(detections); i0++) {
|
||||
|
||||
struct quad *det0;
|
||||
zarray_get_volatile(detections, i0, &det0);
|
||||
|
||||
for (int k = 0; k < 4; k++)
|
||||
zarray_set(poly0, k, det0->p[k], NULL);
|
||||
|
||||
for (int i1 = i0+1; i1 < zarray_size(detections); i1++) {
|
||||
|
||||
struct quad *det1;
|
||||
zarray_get_volatile(detections, i1, &det1);
|
||||
|
||||
for (int k = 0; k < 4; k++)
|
||||
zarray_set(poly1, k, det1->p[k], NULL);
|
||||
|
||||
if (g2d_polygon_overlaps_polygon(poly0, poly1)) {
|
||||
// the tags overlap. Delete one, keep the other.
|
||||
|
||||
int pref = 0; // 0 means undecided which one we'll keep.
|
||||
|
||||
// if we STILL don't prefer one detection over the other, then pick
|
||||
// any deterministic criterion.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pref = prefer_smaller(pref, det0->p[i][0], det1->p[i][0]);
|
||||
pref = prefer_smaller(pref, det0->p[i][1], det1->p[i][1]);
|
||||
}
|
||||
|
||||
if (pref == 0) {
|
||||
// at this point, we should only be undecided if the tag detections
|
||||
// are *exactly* the same. How would that happen?
|
||||
printf("uh oh, no preference for overlappingdetection\n");
|
||||
}
|
||||
|
||||
if (pref < 0) {
|
||||
// keep det0, destroy det1
|
||||
matd_destroy(det1->H);
|
||||
matd_destroy(det1->Hinv);
|
||||
zarray_remove_index(detections, i1, 1);
|
||||
i1--; // retry the same index
|
||||
goto retry1;
|
||||
} else {
|
||||
// keep det1, destroy det0
|
||||
matd_destroy(det0->H);
|
||||
matd_destroy(det0->Hinv);
|
||||
zarray_remove_index(detections, i0, 1);
|
||||
i0--; // retry the same index.
|
||||
goto retry0;
|
||||
}
|
||||
}
|
||||
|
||||
retry1: ;
|
||||
}
|
||||
|
||||
retry0: ;
|
||||
}
|
||||
|
||||
zarray_destroy(poly0);
|
||||
zarray_destroy(poly1);
|
||||
}
|
||||
|
||||
list_init(out, sizeof(find_rects_list_lnk_data_t));
|
||||
|
||||
const int r_diag_len = fast_roundf(fast_sqrtf((roi->w * roi->w) + (roi->h * roi->h))) * 2;
|
||||
int *theta_buffer = fb_alloc(sizeof(int) * r_diag_len);
|
||||
uint32_t *mag_buffer = fb_alloc(sizeof(uint32_t) * r_diag_len);
|
||||
point_t *point_buffer = fb_alloc(sizeof(point_t) * r_diag_len);
|
||||
|
||||
for (int i = 0, j = zarray_size(detections); i < j; i++) {
|
||||
struct quad *det;
|
||||
zarray_get_volatile(detections, i, &det);
|
||||
|
||||
line_t lines[4];
|
||||
lines[0].x1 = fast_roundf(det->p[0][0]) + roi->x; lines[0].y1 = fast_roundf(det->p[0][1]) + roi->y;
|
||||
lines[0].x2 = fast_roundf(det->p[1][0]) + roi->x; lines[0].y2 = fast_roundf(det->p[1][1]) + roi->y;
|
||||
lines[1].x1 = fast_roundf(det->p[1][0]) + roi->x; lines[1].y1 = fast_roundf(det->p[1][1]) + roi->y;
|
||||
lines[1].x2 = fast_roundf(det->p[2][0]) + roi->x; lines[1].y2 = fast_roundf(det->p[2][1]) + roi->y;
|
||||
lines[2].x1 = fast_roundf(det->p[2][0]) + roi->x; lines[2].y1 = fast_roundf(det->p[2][1]) + roi->y;
|
||||
lines[2].x2 = fast_roundf(det->p[3][0]) + roi->x; lines[2].y2 = fast_roundf(det->p[3][1]) + roi->y;
|
||||
lines[3].x1 = fast_roundf(det->p[3][0]) + roi->x; lines[3].y1 = fast_roundf(det->p[3][1]) + roi->y;
|
||||
lines[3].x2 = fast_roundf(det->p[0][0]) + roi->x; lines[3].y2 = fast_roundf(det->p[0][1]) + roi->y;
|
||||
|
||||
uint32_t magnitude = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if(!lb_clip_line(&lines[i], 0, 0, ptr->w, ptr->h)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t index = trace_line(ptr, &lines[i], theta_buffer, mag_buffer, point_buffer);
|
||||
|
||||
for (int j = 0; j < index; j++) {
|
||||
magnitude += mag_buffer[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (magnitude < threshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
find_rects_list_lnk_data_t lnk_data;
|
||||
rectangle_init(&(lnk_data.rect), fast_roundf(det->p[0][0]) + roi->x, fast_roundf(det->p[0][1]) + roi->y, 0, 0);
|
||||
|
||||
for (size_t k = 1, l = (sizeof(det->p) / sizeof(det->p[0])); k < l; k++) {
|
||||
rectangle_t temp;
|
||||
rectangle_init(&temp, fast_roundf(det->p[k][0]) + roi->x, fast_roundf(det->p[k][1]) + roi->y, 0, 0);
|
||||
rectangle_united(&(lnk_data.rect), &temp);
|
||||
}
|
||||
|
||||
// Add corners...
|
||||
lnk_data.corners[0].x = fast_roundf(det->p[3][0]) + roi->x; // top-left
|
||||
lnk_data.corners[0].y = fast_roundf(det->p[3][1]) + roi->y; // top-left
|
||||
lnk_data.corners[1].x = fast_roundf(det->p[2][0]) + roi->x; // top-right
|
||||
lnk_data.corners[1].y = fast_roundf(det->p[2][1]) + roi->y; // top-right
|
||||
lnk_data.corners[2].x = fast_roundf(det->p[1][0]) + roi->x; // bottom-right
|
||||
lnk_data.corners[2].y = fast_roundf(det->p[1][1]) + roi->y; // bottom-right
|
||||
lnk_data.corners[3].x = fast_roundf(det->p[0][0]) + roi->x; // bottom-left
|
||||
lnk_data.corners[3].y = fast_roundf(det->p[0][1]) + roi->y; // bottom-left
|
||||
|
||||
lnk_data.magnitude = magnitude;
|
||||
|
||||
list_push_back(out, &lnk_data);
|
||||
}
|
||||
|
||||
fb_free(); // point_buffer
|
||||
fb_free(); // mag_buffer
|
||||
fb_free(); // theta_buffer
|
||||
|
||||
zarray_destroy(detections);
|
||||
fb_free(); // grayscale_image;
|
||||
apriltag_detector_destroy(td);
|
||||
fb_free(); // umm_init_x();
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
@ -342,7 +342,7 @@ void imlib_find_lines(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int
|
||||
}
|
||||
}
|
||||
|
||||
static void pixel_magnitude(image_t *ptr, int x, int y, int *theta, uint32_t *mag)
|
||||
void pixel_magnitude(image_t *ptr, int x, int y, int *theta, uint32_t *mag)
|
||||
{
|
||||
switch (ptr->bpp) {
|
||||
case IMAGE_BPP_BINARY: {
|
||||
@ -518,7 +518,7 @@ static void pixel_magnitude(image_t *ptr, int x, int y, int *theta, uint32_t *ma
|
||||
|
||||
// http://www.brackeen.com/vga/source/djgpp20/lines.c.html
|
||||
// http://www.brackeen.com/vga/source/bc31/lines.c.html
|
||||
static size_t trace_line(image_t *ptr, line_t *l, int *theta_buffer, uint32_t *mag_buffer, point_t *point_buffer)
|
||||
size_t trace_line(image_t *ptr, line_t *l, int *theta_buffer, uint32_t *mag_buffer, point_t *point_buffer)
|
||||
{
|
||||
int dx = l->x2 - l->x1; // the horizontal distance of the line
|
||||
int dy = l->y2 - l->y1; // the vertical distance of the line
|
||||
|
||||
@ -898,10 +898,15 @@ typedef struct find_lines_list_lnk_data {
|
||||
|
||||
typedef struct find_circles_list_lnk_data {
|
||||
point_t p;
|
||||
int r;
|
||||
uint32_t magnitude;
|
||||
uint32_t r, magnitude;
|
||||
} find_circles_list_lnk_data_t;
|
||||
|
||||
typedef struct find_rects_list_lnk_data {
|
||||
point_t corners[4];
|
||||
rectangle_t rect;
|
||||
uint32_t magnitude;
|
||||
} find_rects_list_lnk_data_t;
|
||||
|
||||
typedef struct find_qrcodes_list_lnk_data {
|
||||
point_t corners[4];
|
||||
rectangle_t rect;
|
||||
@ -1149,6 +1154,8 @@ void imlib_find_blobs(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int
|
||||
bool (*threshold_cb)(void*,find_blobs_list_lnk_data_t*), void *threshold_cb_arg,
|
||||
bool (*merge_cb)(void*,find_blobs_list_lnk_data_t*,find_blobs_list_lnk_data_t*), void *merge_cb_arg);
|
||||
// Shape Detection
|
||||
void pixel_magnitude(image_t *ptr, int x, int y, int *theta, uint32_t *mag); // helper/internal
|
||||
size_t trace_line(image_t *ptr, line_t *l, int *theta_buffer, uint32_t *mag_buffer, point_t *point_buffer); // helper/internal
|
||||
void imlib_find_lines(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);
|
||||
void imlib_find_line_segments(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int x_stride, unsigned int y_stride,
|
||||
@ -1156,6 +1163,8 @@ void imlib_find_line_segments(list_t *out, image_t *ptr, rectangle_t *roi, unsig
|
||||
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);
|
||||
void imlib_find_rects(list_t *out, image_t *ptr, rectangle_t *roi,
|
||||
uint32_t threshold);
|
||||
// 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,
|
||||
|
||||
@ -2602,6 +2602,132 @@ static mp_obj_t py_image_find_circles(uint n_args, const mp_obj_t *args, mp_map_
|
||||
}
|
||||
#endif // OMV_ENABLE_FIND_CIRCLES
|
||||
|
||||
#ifdef OMV_ENABLE_FIND_RECTS
|
||||
// Rect Object //
|
||||
#define py_rect_obj_size 5
|
||||
typedef struct py_rect_obj {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t corners;
|
||||
mp_obj_t x, y, w, h, magnitude;
|
||||
} py_rect_obj_t;
|
||||
|
||||
static void py_rect_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind)
|
||||
{
|
||||
py_rect_obj_t *self = self_in;
|
||||
mp_printf(print,
|
||||
"{x:%d, y:%d, w:%d, h:%d, magnitude:%d}",
|
||||
mp_obj_get_int(self->x),
|
||||
mp_obj_get_int(self->y),
|
||||
mp_obj_get_int(self->w),
|
||||
mp_obj_get_int(self->h),
|
||||
mp_obj_get_int(self->magnitude));
|
||||
}
|
||||
|
||||
static mp_obj_t py_rect_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value)
|
||||
{
|
||||
if (value == MP_OBJ_SENTINEL) { // load
|
||||
py_rect_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_rect_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_rect_obj_size, index, false)) {
|
||||
case 0: return self->x;
|
||||
case 1: return self->y;
|
||||
case 2: return self->w;
|
||||
case 3: return self->h;
|
||||
case 4: return self->magnitude;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
}
|
||||
|
||||
mp_obj_t py_rect_corners(mp_obj_t self_in) { return ((py_rect_obj_t *) self_in)->corners; }
|
||||
mp_obj_t py_rect_rect(mp_obj_t self_in)
|
||||
{
|
||||
return mp_obj_new_tuple(4, (mp_obj_t []) {((py_rect_obj_t *) self_in)->x,
|
||||
((py_rect_obj_t *) self_in)->y,
|
||||
((py_rect_obj_t *) self_in)->w,
|
||||
((py_rect_obj_t *) self_in)->h});
|
||||
}
|
||||
|
||||
mp_obj_t py_rect_x(mp_obj_t self_in) { return ((py_rect_obj_t *) self_in)->x; }
|
||||
mp_obj_t py_rect_y(mp_obj_t self_in) { return ((py_rect_obj_t *) self_in)->y; }
|
||||
mp_obj_t py_rect_w(mp_obj_t self_in) { return ((py_rect_obj_t *) self_in)->w; }
|
||||
mp_obj_t py_rect_h(mp_obj_t self_in) { return ((py_rect_obj_t *) self_in)->h; }
|
||||
mp_obj_t py_rect_magnitude(mp_obj_t self_in) { return ((py_rect_obj_t *) self_in)->magnitude; }
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_rect_corners_obj, py_rect_corners);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_rect_rect_obj, py_rect_rect);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_rect_x_obj, py_rect_x);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_rect_y_obj, py_rect_y);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_rect_w_obj, py_rect_w);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_rect_h_obj, py_rect_h);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_rect_magnitude_obj, py_rect_magnitude);
|
||||
|
||||
STATIC const mp_rom_map_elem_t py_rect_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_corners), MP_ROM_PTR(&py_rect_corners_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&py_rect_rect_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&py_rect_x_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&py_rect_y_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_w), MP_ROM_PTR(&py_rect_w_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_h), MP_ROM_PTR(&py_rect_h_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_magnitude), MP_ROM_PTR(&py_rect_magnitude_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(py_rect_locals_dict, py_rect_locals_dict_table);
|
||||
|
||||
static const mp_obj_type_t py_rect_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_rect,
|
||||
.print = py_rect_print,
|
||||
.subscr = py_rect_subscr,
|
||||
.locals_dict = (mp_obj_t) &py_rect_locals_dict,
|
||||
};
|
||||
|
||||
static mp_obj_t py_image_find_rects(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);
|
||||
|
||||
list_t out;
|
||||
fb_alloc_mark();
|
||||
imlib_find_rects(&out, arg_img, &roi, py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_threshold), 1000));
|
||||
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_rects_list_lnk_data_t lnk_data;
|
||||
list_pop_front(&out, &lnk_data);
|
||||
|
||||
py_rect_obj_t *o = m_new_obj(py_rect_obj_t);
|
||||
o->base.type = &py_rect_type;
|
||||
o->corners = mp_obj_new_tuple(4, (mp_obj_t [])
|
||||
{mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_int(lnk_data.corners[0].x), mp_obj_new_int(lnk_data.corners[0].y)}),
|
||||
mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_int(lnk_data.corners[1].x), mp_obj_new_int(lnk_data.corners[1].y)}),
|
||||
mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_int(lnk_data.corners[2].x), mp_obj_new_int(lnk_data.corners[2].y)}),
|
||||
mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_int(lnk_data.corners[3].x), mp_obj_new_int(lnk_data.corners[3].y)})});
|
||||
o->x = mp_obj_new_int(lnk_data.rect.x);
|
||||
o->y = mp_obj_new_int(lnk_data.rect.y);
|
||||
o->w = mp_obj_new_int(lnk_data.rect.w);
|
||||
o->h = mp_obj_new_int(lnk_data.rect.h);
|
||||
o->magnitude = mp_obj_new_int(lnk_data.magnitude);
|
||||
|
||||
objects_list->items[i] = o;
|
||||
}
|
||||
|
||||
return objects_list;
|
||||
}
|
||||
#endif
|
||||
|
||||
// QRCode Object //
|
||||
#define py_qrcode_obj_size 10
|
||||
typedef struct py_qrcode_obj {
|
||||
@ -3708,6 +3834,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_line_segments_obj, 1, py_image_f
|
||||
#ifdef OMV_ENABLE_FIND_CIRCLES
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_circles_obj, 1, py_image_find_circles);
|
||||
#endif
|
||||
#ifdef OMV_ENABLE_FIND_RECTS
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_rects_obj, 1, py_image_find_rects);
|
||||
#endif
|
||||
/* Code Detection */
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_qrcodes_obj, 1, py_image_find_qrcodes);
|
||||
#ifdef OMV_ENABLE_APRILTAGS
|
||||
@ -3808,6 +3937,9 @@ static const mp_map_elem_t locals_dict_table[] = {
|
||||
{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
|
||||
#ifdef OMV_ENABLE_FIND_RECTS
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_find_rects), (mp_obj_t)&py_image_find_rects_obj},
|
||||
#endif
|
||||
/* Code Detection */
|
||||
{MP_OBJ_NEW_QSTR(MP_QSTR_find_qrcodes), (mp_obj_t)&py_image_find_qrcodes_obj},
|
||||
|
||||
@ -499,12 +499,26 @@ Q(circle)
|
||||
Q(r)
|
||||
// duplicate Q(magnitude)
|
||||
|
||||
// Find Rects
|
||||
Q(find_rects)
|
||||
// duplicate Q(roi)
|
||||
// duplicate Q(threshold)
|
||||
// Rect Object
|
||||
// duplicate Q(rect)
|
||||
Q(corners)
|
||||
// duplicate Q(rect)
|
||||
// duplicate Q(x)
|
||||
// duplicate Q(y)
|
||||
// duplicate Q(w)
|
||||
// duplicate Q(h)
|
||||
// duplicate Q(magnitude)
|
||||
|
||||
// Find QRCodes
|
||||
Q(find_qrcodes)
|
||||
// duplicate Q(roi)
|
||||
// QRCode Object
|
||||
Q(qrcode)
|
||||
Q(corners)
|
||||
// duplicate Q(corners)
|
||||
// duplicate Q(rect)
|
||||
// duplicate Q(x)
|
||||
// duplicate Q(y)
|
||||
|
||||
31
usr/examples/09-Feature-Detection/find_rects.py
Normal file
31
usr/examples/09-Feature-Detection/find_rects.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Find Rects Example
|
||||
#
|
||||
# This example shows off how to find rectangles in the image using the quad threshold
|
||||
# detection code from our April Tags code. The quad threshold detection algorithm
|
||||
# detects rectangles in an extremely robust way and is much better than Hough
|
||||
# Transform based methods. For example, it can still detect rectangles even when lens
|
||||
# distortion causes those rectangles to look bent. Rounded rectangles are no problem!
|
||||
# (But, given this the code will also detect small radius circles too)...
|
||||
|
||||
import sensor, image, time
|
||||
|
||||
sensor.reset()
|
||||
sensor.set_pixformat(sensor.RGB565) # grayscale is faster (160x120 max on OpenMV-M7)
|
||||
sensor.set_framesize(sensor.QQVGA)
|
||||
sensor.skip_frames(time = 2000)
|
||||
clock = time.clock()
|
||||
|
||||
while(True):
|
||||
clock.tick()
|
||||
img = sensor.snapshot()
|
||||
|
||||
# `threshold` below should be set to a high enough value to filter out noise
|
||||
# rectangles detected in the image which have low edge magnitudes. Rectangles
|
||||
# have larger edge magnitudes the larger and more contrasty they are...
|
||||
|
||||
for r in img.find_rects(threshold = 10000):
|
||||
img.draw_rectangle(r.rect(), color = (255, 0, 0))
|
||||
for p in r.corners(): img.draw_circle(p[0], p[1], 5, color = (0, 255, 0))
|
||||
print(r)
|
||||
|
||||
print("FPS %f" % clock.fps())
|
||||
Loading…
Reference in New Issue
Block a user