Merge pull request #114 from kwagyeman/master

New blob code.
This commit is contained in:
Ibrahim Abd Elkader 2016-04-10 01:22:16 +02:00
commit ddd855137e
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())