New blob code.

Blob tracking has now been updated to work without requiring prior
segmentation of the image. You can still run it on a segmented image,
but, that is not needed anymore.

Use the copy color feature of the OpenMV IDE to get a color in the
image. Once you have that you can then pass the color to find_blobs which
will output a tuple of lists of blobs for each color. By default, all
blobs less than 1/1000th of the image are filtered out, however, you can
add a custom filter function which gets the image and the blob about to
be added to the list and you can decide to filter it or not.

For marker tracking, we now have a function called find markers which
basically merges all the blobs found by find blobs into one list of
blobs. Each new blob will have a color code value which will tell you
what colors are part of that blob. We support tracking up to 30 unique
colors this way.
This commit is contained in:
Kwabena W. Agyeman 2016-04-09 11:42:27 -04:00
parent 50fe3e96bc
commit d1ff36602a
5 changed files with 415 additions and 166 deletions

View File

@ -3,136 +3,355 @@
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Blob count.
* Blob and color code/marker detection code...
*
*/
#include "xalloc.h"
#include <mp.h>
#include "mdefs.h"
#include "fb_alloc.h"
#include "imlib.h"
#include <arm_math.h>
blob_t *blob_alloc(int x, int y, int w, int h, int id, int c)
ALWAYS_INLINE static uint8_t *init_mask(rectangle_t *roi)
{
blob_t *blob = xalloc(sizeof(*blob));
blob->x = x;
blob->y = y;
blob->w = w;
blob->h = h;
blob->id = id;
blob->c = c;
return blob;
return fb_alloc0(((roi->w+7)/8)*roi->h);
}
void blob_add_point(blob_t *blob, int px, int py)
ALWAYS_INLINE static void deinit_mask()
{
/* expand blob */
if (px < blob->x) {
blob->x = px;
}
fb_free();
}
if (py < blob->y) {
blob->y = py;
}
ALWAYS_INLINE static void set_mask_pixel(rectangle_t *roi, uint8_t *mask, int x, int y)
{
mask[(((roi->w+7)/8)*y)+(x/8)] |= (1 << (x%8));
}
if (px > blob->w) {
blob->w = px;
}
ALWAYS_INLINE static bool get_not_mask_pixel(rectangle_t *roi, uint8_t *mask, int x, int y)
{
return !((mask[(((roi->w+7)/8)*y)+(x/8)] >> (x%8)) & 1);
}
if (py > blob->h) {
blob->h = py;
typedef struct stack_queue {
int head_p, tail_p, size;
point_t *data_p;
} stack_queue_t;
ALWAYS_INLINE static stack_queue_t *init_stack_queue(rectangle_t *roi)
{
stack_queue_t *sq = fb_alloc(sizeof(stack_queue_t));
sq->head_p = 0;
sq->tail_p = 0;
// The size here is the perimeter in pixels around the roi. It's the perimeter
// around the roi vs the roi perimeter so that we can't run out of space while
// executing the wildfire algorithm for new points. Additionally, this also
// takes care of the pointer comparison issue since it will never get full.
sq->size = (((roi->w+2)*2)-2)+(((roi->h+2)*2)-2);
sq->data_p = fb_alloc(sq->size*sizeof(point_t));
return sq;
}
ALWAYS_INLINE static void deinit_stack_queue()
{
fb_free();
fb_free();
}
ALWAYS_INLINE static void stack_queue_push(stack_queue_t *sq, int x, int y)
{
sq->data_p[sq->head_p] = (point_t) {.x = x, .y = y};
sq->head_p = (sq->head_p + 1) % sq->size;
}
ALWAYS_INLINE static point_t stack_queue_pop(stack_queue_t *sq)
{
point_t p = sq->data_p[sq->tail_p];
sq->tail_p = (sq->tail_p + 1) % sq->size;
return p;
}
ALWAYS_INLINE static bool stack_queue_not_empty(stack_queue_t *sq)
{
return sq->head_p != sq->tail_p;
}
ALWAYS_INLINE static bool threshold_gs(image_t *img, int x, int y, simple_color_t l_thresholds, simple_color_t h_thresholds, bool invert)
{
int pixel = IM_GET_GS_PIXEL(img, x, y);
return invert ^
((l_thresholds.G <= pixel) &&
(pixel <= h_thresholds.G));
}
ALWAYS_INLINE static bool threshold_rgb565(image_t *img, int x, int y, simple_color_t l_thresholds, simple_color_t h_thresholds, bool invert)
{
int pixel = IM_GET_RGB565_PIXEL(img, x, y);
const int lab_l = IM_RGB5652L(pixel);
const int lab_a = IM_RGB5652A(pixel);
const int lab_b = IM_RGB5652B(pixel);
return invert ^
((l_thresholds.L <= lab_l) &&
(lab_l <= h_thresholds.L) &&
(l_thresholds.A <= lab_a) &&
(lab_a <= h_thresholds.A) &&
(l_thresholds.B <= lab_b) &&
(lab_b <= h_thresholds.B));
}
ALWAYS_INLINE static bool threshold(image_t *img, int x, int y, simple_color_t l_thresholds, simple_color_t h_thresholds, bool invert)
{
if (IM_IS_GS(img)) {
return threshold_gs(img, x, y, l_thresholds, h_thresholds, invert);
} else {
return threshold_rgb565(img, x, y, l_thresholds, h_thresholds, invert);
}
}
array_t *imlib_count_blobs(struct image *image)
mp_obj_t imlib_find_blobs(mp_obj_t img_obj, image_t *img, int num_thresholds, simple_color_t *l_thresholds, simple_color_t *h_thresholds, bool invert, rectangle_t *r, mp_obj_t filtering_fn)
{
array_t *blobs;
blob_t *blob;
// We're using a modified wildfire algorithm below where instead of using a
// the stack we use a queue along with a burn mask to filter out already
// visited pixels. For each color blob in the image, where a color blob is
// an area of connected pixels that all are within a threshold, the algorithm
// computes the bounding box around all those pixels, number of pixels in the
// blob, centroid, and blob orientation. The algorithm then returns a list
// of blobs for each set of thresholds passed in. That is, this function
// returns a tuple of lists of blobs.
array_alloc(&blobs, xfree);
uint8_t *pixels = (uint8_t*) image->pixels;
rectangle_t rect;
if (!rectangle_subimg(img, r, &rect)) {
return mp_const_none;
}
// points array
int p_size = 100;
point_t *points = xalloc(p_size*sizeof*points);
mp_obj_t blob_lists[num_thresholds];
for (int y=0; y<image->h; y++) {
for (int x=0; x<image->w; x++) {
blob = NULL;
uint8_t label = pixels[y*image->w+x];
uint8_t *mask = init_mask(&rect);
stack_queue_t *sq = init_stack_queue(&rect);
if (label) {
int w,e;
int p_idx =0;
int p_max = 1;
// set initial point
points[0].x=x;
points[0].y=y;
// alloc new blob
blob = blob_alloc(image->w, image->h, 0, 0, label, 0);
while(p_idx<p_max) {
point_t *p=&points[p_idx++];
blob_add_point(blob, p->x, p->y);
// scan west
for (w=p->x-1; w>=0 && pixels[p->y*image->w+w]==label; w--) {
blob_add_point(blob, w, p->y);
}
// scan east
for (e=p->x+1; e<image->w && pixels[p->y*image->w+e]==label; e++) {
blob_add_point(blob, e, p->y);
}
// scan north and south rows, add a point only if it's the last
// point or the last connected point in a row, this saves some memory
// and other points on this segment will still be reachable from this one.
// add points on north row
for (int i=w+1; i<e; i++) {
pixels[p->y*image->w+i]=0;
if ((p->y-1) > 0 && pixels[(p->y-1)*image->w+i]==label) {
if (i==(e-1) || (pixels[(p->y-1)*image->w+i+1]!=label)) {
points[p_max].x = i;
points[p_max].y = p->y-1;
if (++p_max == p_size) {
p_size +=100;
points = xrealloc(points, p_size*sizeof*points);
for (int n = 0; n < num_thresholds; n++) {
blob_lists[n] = mp_obj_new_list(4, NULL); // 4 is just the intial list size guess
mp_obj_list_set_len(blob_lists[n], 0);
for (int i = 0; i < rect.h; i++) {
for (int j = 0; j < rect.w; j++) {
int x = (rect.x + j); // in img
int y = (rect.y + i); // in img
if (get_not_mask_pixel(&rect, mask, j, i) // in roi
&& threshold(img, x, y, l_thresholds[n], h_thresholds[n], invert)) { // in img
int blob_x1 = x;
int blob_y1 = y;
int blob_x2 = x;
int blob_y2 = y;
int blob_pixels = 1;
int blob_cx = x;
int blob_cy = y;
int blob_a = x*x; // equal to (x-mx)^2
int blob_b = x*y; // equal to (x-mx)*(y-my)
int blob_c = y*y; // equal to (y-my)^2
set_mask_pixel(&rect, mask, j, i); // in roi
stack_queue_push(sq, x, y); // in img
do {
point_t p = stack_queue_pop(sq);
for (int a = -1; a <= 1; a++) {
for (int b = -1; b <= 1; b++) {
int c = (p.x + b); // in img
int d = (p.y + a); // in img
int e = (c - rect.x); // in roi
int f = (d - rect.y); // in roi
if (IM_X_INSIDE(&rect, e) // in roi
&& IM_Y_INSIDE(&rect, f) // in roi
&& get_not_mask_pixel(&rect, mask, e, f) // in roi
&& threshold(img, c, d, l_thresholds[n], h_thresholds[n], invert)) { // in img
blob_x1 = IM_MIN(blob_x1, c);
blob_y1 = IM_MIN(blob_y1, d);
blob_x2 = IM_MAX(blob_x2, c);
blob_y2 = IM_MAX(blob_y2, d);
blob_pixels += 1;
blob_cx += c;
blob_cy += d;
blob_a += c*c;
blob_b += c*d;
blob_c += d*d;
set_mask_pixel(&rect, mask, e, f); // in roi
stack_queue_push(sq, c, d); // in img
}
}
}
}
// add points on south row
for (int i=w+1; i<e; i++) {
if ((p->y+1) < image->h && pixels[(p->y+1)*image->w+i]==label) {
if (i==(e-1) || (pixels[(p->y+1)*image->w+i+1]!=label)) {
points[p_max].x = i;
points[p_max].y = p->y+1;
if (++p_max == p_size) {
p_size +=100;
points = xrealloc(points, p_size*sizeof*points);
}
}
} while(stack_queue_not_empty(sq));
int mx = (blob_cx/blob_pixels); // x centroid
int my = (blob_cy/blob_pixels); // y centroid
// The below equations were derived by translating the orientation
// calculation from a double pass algorithm to single pass.
blob_a -= (mx*blob_cx)+(mx*blob_cx);
blob_a += blob_pixels*mx*mx;
blob_b -= (mx*blob_cy)+(my*blob_cx);
blob_b += blob_pixels*mx*my;
blob_c -= (my*blob_cy)+(my*blob_cy);
blob_c += blob_pixels*my*my;
// Compute the final blob orientation from a, b, and c sums.
float o = ((blob_a!=blob_c)?fast_atan2f(blob_b,blob_a-blob_c):0.0)/2.0;
mp_obj_t blob_tuple[10];
blob_tuple[0] = mp_obj_new_int(blob_x1);
blob_tuple[1] = mp_obj_new_int(blob_y1);
blob_tuple[2] = mp_obj_new_int(blob_x2-blob_x1+1);
blob_tuple[3] = mp_obj_new_int(blob_y2-blob_y1+1);
blob_tuple[4] = mp_obj_new_int(blob_pixels);
blob_tuple[5] = mp_obj_new_int(mx);
blob_tuple[6] = mp_obj_new_int(my);
blob_tuple[7] = mp_obj_new_float(o);
blob_tuple[8] = mp_obj_new_int(1<<n);
blob_tuple[9] = mp_obj_new_int(1);
mp_obj_t blob_tuple_obj = mp_obj_new_tuple(10, blob_tuple);
if (filtering_fn != MP_OBJ_NULL) {
if (mp_obj_is_true(mp_call_function_2(filtering_fn, img_obj, blob_tuple_obj))) {
mp_obj_list_append(blob_lists[n], blob_tuple_obj);
} else {
mp_obj_tuple_del(blob_tuple_obj);
}
}
}
if (blob) {
blob->w = blob->w - blob->x;
blob->h = blob->h - blob->y;
// discard small blobs
if (blob->w > 10 && blob->h > 10) {
array_push_back(blobs, blob);
} else {
xfree(blob);
if (blob_pixels >= ((img->w*img->h)/1000)) {
mp_obj_list_append(blob_lists[n], blob_tuple_obj);
} else {
mp_obj_tuple_del(blob_tuple_obj);
}
}
}
}
}
}
xfree(points);
return blobs;
deinit_stack_queue();
deinit_mask();
return mp_obj_new_tuple(num_thresholds, blob_lists);
}
mp_obj_t imlib_find_markers(mp_obj_t img_obj, mp_obj_t blob_lists_obj, int margin, mp_obj_t filtering_fn)
{
// After you have a list of blobs this function will merge blobs from the
// different colors lists that intersect into one blob. The new merged big
// blob will have a bounding box that surronds all the merged blobs, pixels will
// include all the blobs, and centroids/orientations are averaged. Additionally,
// the new blob will have an extra code value with a bit set for each color
// that was merged into the blob along with the number of blobs merged. The
// color code provides a nice and easy user controllable way to get an idea
// of what colors are in a merged blob.
mp_uint_t blob_l_len;
mp_obj_t *blob_l;
mp_obj_get_array(blob_lists_obj, &blob_l_len, &blob_l);
if (!blob_l_len) return mp_const_none;
mp_uint_t blob_lists_len[blob_l_len];
mp_obj_t *blob_lists[blob_l_len];
rectangle_t rect; // reusing mask from above - so we need a fake rect obj.
rect.x = 0;
rect.y = 0;
rect.w = 0;
rect.h = blob_l_len;
for (mp_uint_t i = 0; i < blob_l_len; i++) {
mp_obj_get_array(blob_l[i], &blob_lists_len[i], &blob_lists[i]);
rect.w = IM_MAX(rect.w, blob_lists_len[i]); // find longest list
}
if (!rect.w) return mp_const_none;
uint8_t *mask = init_mask(&rect);
mp_obj_t out = mp_obj_new_list(4, NULL); // 4 is just the intial list size guess
mp_obj_list_set_len(out, 0);
for (mp_uint_t i = 0; i < blob_l_len; i++) {
for (mp_uint_t j = 0; j < blob_lists_len[i]; j++) {
if (get_not_mask_pixel(&rect, mask, j, i)) {
set_mask_pixel(&rect, mask, j, i);
mp_obj_t *temp0;
mp_obj_get_array_fixed_n(blob_lists[i][j], 10, &temp0);
int blob_x = mp_obj_get_int(temp0[0]); // rect x
int blob_y = mp_obj_get_int(temp0[1]); // rect y
int blob_w = mp_obj_get_int(temp0[2]); // rect w
int blob_h = mp_obj_get_int(temp0[3]); // rect h
int blob_pixels = mp_obj_get_int(temp0[4]); // pixels
int blob_cx = mp_obj_get_int(temp0[5]); // centroid x
int blob_cy = mp_obj_get_int(temp0[6]); // centroid y
float blob_rotation = mp_obj_get_float(temp0[7]); // rotation
int blob_code = mp_obj_get_int(temp0[8]); // code bit
int blob_count = mp_obj_get_int(temp0[9]); // blob count
for (mp_uint_t a = 0; a < blob_l_len; a++) {
for (mp_uint_t b = 0; b < blob_lists_len[a]; b++) {
if (get_not_mask_pixel(&rect, mask, b, a)) {
mp_obj_t *temp1;
mp_obj_get_array_fixed_n(blob_lists[a][b], 10, &temp1);
rectangle_t t0;
t0.x = blob_x - margin;
t0.y = blob_y - margin;
t0.w = blob_w + (2*margin);
t0.h = blob_h + (2*margin);
rectangle_t t1;
t1.x = mp_obj_get_int(temp1[0]) - margin;
t1.y = mp_obj_get_int(temp1[1]) - margin;
t1.w = mp_obj_get_int(temp1[2]) + (2*margin);
t1.h = mp_obj_get_int(temp1[3]) + (2*margin);
if (rectangle_intersects(&t0, &t1)) {
set_mask_pixel(&rect, mask, b, a);
// Compute bounding rect...
blob_x = IM_MIN(blob_x, t1.x);
blob_y = IM_MIN(blob_y, t1.y);
int x2_0 = t0.x+t0.w-1;
int x2_1 = t1.x+t1.w-1;
int x2 = IM_MAX(x2_0, x2_1);
blob_w = x2-blob_x+1;
int y2_0 = t0.y+t0.h-1;
int y2_1 = t1.y+t1.h-1;
int y2 = IM_MAX(y2_0, y2_1);
blob_h = y2-blob_y+1;
// Update tracking info...
blob_pixels += mp_obj_get_int(temp1[4]);
blob_cx += mp_obj_get_int(temp1[5]);
blob_cy += mp_obj_get_int(temp1[6]);
blob_rotation += mp_obj_get_float(temp1[7]);
blob_code |= mp_obj_get_int(temp1[8]);
blob_count += mp_obj_get_int(temp1[9]);
}
}
}
}
blob_cx /= blob_count;
blob_cy /= blob_count;
blob_rotation /= blob_count;
// Build output object.
mp_obj_t blob_tuple[10];
blob_tuple[0] = mp_obj_new_int(blob_x);
blob_tuple[1] = mp_obj_new_int(blob_y);
blob_tuple[2] = mp_obj_new_int(blob_w);
blob_tuple[3] = mp_obj_new_int(blob_h);
blob_tuple[4] = mp_obj_new_int(blob_pixels);
blob_tuple[5] = mp_obj_new_int(blob_cx);
blob_tuple[6] = mp_obj_new_int(blob_cy);
blob_tuple[7] = mp_obj_new_float(blob_rotation);
blob_tuple[8] = mp_obj_new_int(blob_code);
blob_tuple[9] = mp_obj_new_int(blob_count);
mp_obj_t blob_tuple_obj = mp_obj_new_tuple(10, blob_tuple);
if (filtering_fn != MP_OBJ_NULL) {
if (mp_obj_is_true(mp_call_function_2(filtering_fn, img_obj, blob_tuple_obj))) {
mp_obj_list_append(out, blob_tuple_obj);
} else {
mp_obj_tuple_del(blob_tuple_obj);
}
} else {
mp_obj_list_append(out, blob_tuple_obj);
}
}
}
}
deinit_mask();
return out;
}

View File

@ -13,6 +13,7 @@
#include <ff.h>
#include "array.h"
#include "fmath.h"
#include "obj.h"
#define IM_SWAP16(x) __REV16(x) // Swap bottom two chars in short.
#define IM_SWAP32(x) __REV32(x) // Swap bottom two shorts in long.
@ -231,15 +232,6 @@ typedef struct statistics {
int8_t l_upper_q, a_upper_q, b_upper_q;
} statistics_t;
typedef struct blob {
int x;
int y;
int w;
int h;
int c;
int id;
} blob_t;
typedef struct color {
union {
uint8_t vec[3];
@ -466,6 +458,10 @@ void imlib_mean_filter(image_t *img, const int ksize);
void imlib_mode_filter(image_t *img, const int ksize);
void imlib_median_filter(image_t *img, const int ksize, const int percentile);
/* Color Tracking */
mp_obj_t imlib_find_blobs(mp_obj_t img_obj, image_t *img, int num_thresholds, simple_color_t *l_thresholds, simple_color_t *h_thresholds, bool invert, rectangle_t *r, mp_obj_t filtering_fn);
mp_obj_t imlib_find_markers(mp_obj_t img_obj, mp_obj_t blob_lists_obj, int margin, mp_obj_t filtering_fn);
/* Clustering functions */
array_t *cluster_kmeans(array_t *points, int k);
@ -474,7 +470,6 @@ int imlib_image_mean(struct image *src);
void imlib_histeq(struct image *src);
void imlib_threshold(image_t *src, image_t *dst, color_t *color, int color_size, int threshold);
void imlib_rainbow(image_t *src, struct image *dst);
array_t *imlib_count_blobs(struct image *image);
/* Integral image functions */
void imlib_integral_image_alloc(struct integral_image *sum, int w, int h);

View File

@ -752,6 +752,72 @@ static mp_obj_t py_image_median(uint n_args, const mp_obj_t *args, mp_map_t *kw_
return mp_const_none;
}
static mp_obj_t py_image_find_blobs(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");
mp_uint_t arg_t_len;
mp_obj_t *arg_t;
mp_obj_get_array(args[1], &arg_t_len, &arg_t);
if (!arg_t_len) return mp_const_none;
simple_color_t l_t[arg_t_len], u_t[arg_t_len];
if (IM_IS_GS(arg_img)) {
for (int i=0; i<arg_t_len; i++) {
mp_obj_t *temp;
mp_obj_get_array_fixed_n(arg_t[i], 2, &temp);
int lo = mp_obj_get_int(temp[0]);
int hi = mp_obj_get_int(temp[1]);
// Swap ranges if they are wrong.
l_t[i].G = IM_MIN(lo, hi);
u_t[i].G = IM_MAX(lo, hi);
}
} else {
for (int i=0; i<arg_t_len; i++) {
mp_obj_t *temp;
mp_obj_get_array_fixed_n(arg_t[i], 6, &temp);
int l_lo = mp_obj_get_int(temp[0]);
int l_hi = mp_obj_get_int(temp[1]);
int a_lo = mp_obj_get_int(temp[2]);
int a_hi = mp_obj_get_int(temp[3]);
int b_lo = mp_obj_get_int(temp[4]);
int b_hi = mp_obj_get_int(temp[5]);
// Swap ranges if they are wrong.
l_t[i].L = IM_MIN(l_lo, l_hi);
u_t[i].L = IM_MAX(l_lo, l_hi);
l_t[i].A = IM_MIN(a_lo, a_hi);
u_t[i].A = IM_MAX(a_lo, a_hi);
l_t[i].B = IM_MIN(b_lo, b_hi);
u_t[i].B = IM_MAX(b_lo, b_hi);
}
}
rectangle_t arg_r;
py_helper_lookup_rectangle(kw_args, arg_img, &arg_r);
mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_feature_filter), MP_MAP_LOOKUP);
mp_obj_t kw_val = (kw_arg != NULL) ? kw_arg->value : MP_OBJ_NULL;
int arg_invert = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_invert), 0);
return imlib_find_blobs(args[0], arg_img, arg_t_len, l_t, u_t, arg_invert ? 1 : 0, &arg_r, kw_val);
}
static mp_obj_t py_image_find_markers(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");
int margin = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_margin), 2);
mp_map_elem_t *kw_arg = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_feature_filter), MP_MAP_LOOKUP);
mp_obj_t kw_val = (kw_arg != NULL) ? kw_arg->value : MP_OBJ_NULL;
return imlib_find_markers(args[0], args[1], margin, kw_val);
}
static mp_obj_t py_image_scale(mp_obj_t image_obj, mp_obj_t size_obj)
{
int w,h;
@ -986,30 +1052,6 @@ static mp_obj_t py_image_compress(mp_obj_t image_obj, mp_obj_t quality)
return py_image_from_struct(&cimage);
}
static mp_obj_t py_image_find_blobs(mp_obj_t image_obj)
{
// Get image pointer
image_t *image = py_image_cobj(image_obj);
// Run blob detector
array_t *blobs = imlib_count_blobs(image);
// Add detected blobs to a new Python list
mp_obj_t objects_list = mp_obj_new_list(0, NULL);
if (array_length(blobs)) {
for (int j=0; j<array_length(blobs); j++) {
blob_t *r = array_at(blobs, j);
mp_obj_t blob[6] = {
mp_obj_new_int(r->x), mp_obj_new_int(r->y), mp_obj_new_int(r->w),
mp_obj_new_int(r->h), mp_obj_new_int(r->c), mp_obj_new_int(r->id)
};
mp_obj_list_append(objects_list, mp_obj_new_tuple(6, blob));
}
}
array_free(blobs);
return objects_list;
}
static mp_obj_t py_image_find_features(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
rectangle_t roi;
@ -1232,6 +1274,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_midpoint_obj, 2, py_image_midpoint);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_mean_obj, py_image_mean);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_mode_obj, py_image_mode);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_median_obj, 2, py_image_median);
/* Color Tracking */
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_blobs_obj, 2, py_image_find_blobs);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_markers_obj, 2, py_image_find_markers);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_scale_obj, py_image_scale);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_scaled_obj, py_image_scaled);
@ -1243,7 +1288,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(py_image_threshold_obj, py_image_threshold);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_rainbow_obj, py_image_rainbow);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_image_compress_obj, py_image_compress);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_find_blobs_obj, py_image_find_blobs);
STATIC MP_DEFINE_CONST_FUN_OBJ_3(py_image_find_template_obj, py_image_find_template);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_features_obj, 2, py_image_find_features);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_keypoints_obj, 1, py_image_find_keypoints);
@ -1294,6 +1338,9 @@ static const mp_map_elem_t locals_dict_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR_mean), (mp_obj_t)&py_image_mean_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_mode), (mp_obj_t)&py_image_mode_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_median), (mp_obj_t)&py_image_median_obj},
/* Color Tracking */
{MP_OBJ_NEW_QSTR(MP_QSTR_find_blobs), (mp_obj_t)&py_image_find_blobs_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_find_markers), (mp_obj_t)&py_image_find_markers_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_scale), (mp_obj_t)&py_image_scale_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_scaled), (mp_obj_t)&py_image_scaled_obj},
@ -1305,7 +1352,6 @@ static const mp_map_elem_t locals_dict_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR_rainbow), (mp_obj_t)&py_image_rainbow_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_compress), (mp_obj_t)&py_image_compress_obj},
/* objects/feature detection */
{MP_OBJ_NEW_QSTR(MP_QSTR_find_blobs), (mp_obj_t)&py_image_find_blobs_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_find_template), (mp_obj_t)&py_image_find_template_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_find_features), (mp_obj_t)&py_image_find_features_obj},
{MP_OBJ_NEW_QSTR(MP_QSTR_find_keypoints), (mp_obj_t)&py_image_find_keypoints_obj},

View File

@ -58,6 +58,8 @@ Q(midpoint)
Q(mean)
Q(mode)
Q(median)
Q(find_blobs)
Q(find_markers)
Q(kp_desc)
Q(lbp_desc)
Q(Cascade)
@ -69,7 +71,6 @@ Q(compress)
Q(rainbow)
Q(histeq)
Q(threshold)
Q(find_blobs)
Q(find_template)
Q(find_features)
Q(find_keypoints)
@ -82,6 +83,8 @@ Q(mul)
Q(add)
Q(bias)
Q(percentile)
Q(feature_filter)
Q(margin)
// Lcd Module
Q(lcd)

View File

@ -1,45 +1,31 @@
import sensor, time, pyb
led_r = pyb.LED(1)
led_g = pyb.LED(2)
led_b = pyb.LED(3)
sensor.reset()
sensor.set_contrast(2)
sensor.set_framesize(sensor.QCIF)
sensor.set_framesize(sensor.QVGA)
sensor.set_pixformat(sensor.RGB565)
# Finds a red blob.
COLOR1 = ( 50, 55, 73, 82, 47, 63)
# Select an aera of the image and click copy color to get
# new color tracking parameters for something in the image.
clock = time.clock()
while (True):
clock.tick()
# Take snapshot
image = sensor.snapshot()
# Threshold image with RGB
binary = image.threshold([(255, 0, 0),
(0, 255, 0),
(0, 0, 255)], 80)
# Image closing
binary.dilate(3)
binary.erode(3)
# Detect blobs in image
blobs = binary.find_blobs()
blob_l = image.find_blobs([COLOR1])
led_r.off()
led_g.off()
led_b.off()
# Draw rectangles around detected blobs
for r in blobs:
if r[5]==1:
led_r.on()
if r[5]==2:
led_g.on()
if r[5]==3:
led_b.on()
image.draw_rectangle(r[0:4])
time.sleep(50)
for blobs in blob_l:
for r in blobs:
if r[8]==1:
led_r.on()
image.draw_rectangle(r[0:4])
print(clock.fps())