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
|
// Enable Find_Circles
|
||||||
#define OMV_ENABLE_FIND_CIRCLES
|
#define OMV_ENABLE_FIND_CIRCLES
|
||||||
|
|
||||||
|
// Enable Find_Rects
|
||||||
|
#define OMV_ENABLE_FIND_RECTS
|
||||||
|
|
||||||
// Enable AprilTags (64 KB).
|
// Enable AprilTags (64 KB).
|
||||||
#define OMV_ENABLE_APRILTAGS
|
#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
|
// 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;
|
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.
|
// Ensure that the black border is inside the white border.
|
||||||
if (dot < 0)
|
if ((!overrideMode) && (dot < 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// we now sort the points according to theta. This is a prepatory
|
// 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;
|
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.
|
// 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;
|
struct quad quad;
|
||||||
memset(&quad, 0, sizeof(struct 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);
|
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.
|
// and blurring parameters.
|
||||||
|
|
||||||
// zarray_t *quads = apriltag_quad_gradient(td, im_orig);
|
// 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*));
|
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();
|
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
|
#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) {
|
switch (ptr->bpp) {
|
||||||
case IMAGE_BPP_BINARY: {
|
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/djgpp20/lines.c.html
|
||||||
// http://www.brackeen.com/vga/source/bc31/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 dx = l->x2 - l->x1; // the horizontal distance of the line
|
||||||
int dy = l->y2 - l->y1; // the vertical 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 {
|
typedef struct find_circles_list_lnk_data {
|
||||||
point_t p;
|
point_t p;
|
||||||
int r;
|
uint32_t r, magnitude;
|
||||||
uint32_t magnitude;
|
|
||||||
} find_circles_list_lnk_data_t;
|
} 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 {
|
typedef struct find_qrcodes_list_lnk_data {
|
||||||
point_t corners[4];
|
point_t corners[4];
|
||||||
rectangle_t rect;
|
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 (*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);
|
bool (*merge_cb)(void*,find_blobs_list_lnk_data_t*,find_blobs_list_lnk_data_t*), void *merge_cb_arg);
|
||||||
// Shape Detection
|
// 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,
|
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);
|
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,
|
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);
|
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,
|
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);
|
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
|
// 1/2D Bar Codes
|
||||||
void imlib_find_qrcodes(list_t *out, image_t *ptr, rectangle_t *roi);
|
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,
|
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
|
#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 //
|
// QRCode Object //
|
||||||
#define py_qrcode_obj_size 10
|
#define py_qrcode_obj_size 10
|
||||||
typedef struct py_qrcode_obj {
|
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
|
#ifdef OMV_ENABLE_FIND_CIRCLES
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_circles_obj, 1, py_image_find_circles);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_circles_obj, 1, py_image_find_circles);
|
||||||
#endif
|
#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 */
|
/* Code Detection */
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_qrcodes_obj, 1, py_image_find_qrcodes);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_qrcodes_obj, 1, py_image_find_qrcodes);
|
||||||
#ifdef OMV_ENABLE_APRILTAGS
|
#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},
|
{MP_OBJ_NEW_QSTR(MP_QSTR_find_line_segments), (mp_obj_t)&py_image_find_line_segments_obj},
|
||||||
#ifdef OMV_ENABLE_FIND_CIRCLES
|
#ifdef OMV_ENABLE_FIND_CIRCLES
|
||||||
{MP_OBJ_NEW_QSTR(MP_QSTR_find_circles), (mp_obj_t)&py_image_find_circles_obj},
|
{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
|
#endif
|
||||||
/* Code Detection */
|
/* Code Detection */
|
||||||
{MP_OBJ_NEW_QSTR(MP_QSTR_find_qrcodes), (mp_obj_t)&py_image_find_qrcodes_obj},
|
{MP_OBJ_NEW_QSTR(MP_QSTR_find_qrcodes), (mp_obj_t)&py_image_find_qrcodes_obj},
|
||||||
|
|||||||
@ -499,12 +499,26 @@ Q(circle)
|
|||||||
Q(r)
|
Q(r)
|
||||||
// duplicate Q(magnitude)
|
// 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
|
// Find QRCodes
|
||||||
Q(find_qrcodes)
|
Q(find_qrcodes)
|
||||||
// duplicate Q(roi)
|
// duplicate Q(roi)
|
||||||
// QRCode Object
|
// QRCode Object
|
||||||
Q(qrcode)
|
Q(qrcode)
|
||||||
Q(corners)
|
// duplicate Q(corners)
|
||||||
// duplicate Q(rect)
|
// duplicate Q(rect)
|
||||||
// duplicate Q(x)
|
// duplicate Q(x)
|
||||||
// duplicate Q(y)
|
// 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