Add find_rects() using AprilTag's quad detector.

It's awesome.
This commit is contained in:
Kwabena W. Agyeman 2017-07-13 01:24:23 -04:00
parent d7bbf3a5d9
commit a4e556e7e3
7 changed files with 423 additions and 10 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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},

View File

@ -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)

View 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())