Upgrade Drawing Features

Heavily upgraded the drawing features onboard the OpenMV Cam. We now
have all the basic drawing methods folks expect along with all the
parameters you need. Finally! You can make big text fonts.

* Added line thickness support
* Added shape fills
* Added text scaling
* Added draw arrow.
This commit is contained in:
Kwabena W. Agyeman 2018-03-12 01:14:03 -04:00
parent 6339815ae3
commit fc74e70c32
17 changed files with 537 additions and 353 deletions

View File

@ -153,6 +153,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/, \
FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
binary.o \
blob.o \
draw.o \
qrcode.o \
apriltag.o \
dmtx.o \

View File

@ -24,6 +24,7 @@ SRCS += $(addprefix , \
SRCS += $(addprefix img/, \
binary.c \
blob.c \
draw.c \
qrcode.c \
apriltag.c \
dmtx.c \

205
src/omv/img/draw.c Normal file
View File

@ -0,0 +1,205 @@
/* This file is part of the OpenMV project.
* Copyright (c) 2013-2018 Ibrahim Abdelkader <iabdalkader@openmv.io> & Kwabena W. Agyeman <kwagyeman@openmv.io>
* This work is licensed under the MIT license, see the file LICENSE for details.
*/
#include "font.h"
#include "imlib.h"
// Get pixel (handles boundary check and image type check).
int imlib_get_pixel(image_t *img, int x, int y)
{
if ((0 <= x) && (x < img->w) && (0 <= y) && (y < img->h)) {
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
return IMAGE_GET_BINARY_PIXEL(img, x, y);
}
case IMAGE_BPP_GRAYSCALE: {
return IMAGE_GET_GRAYSCALE_PIXEL(img, x, y);
}
case IMAGE_BPP_RGB565: {
return IMAGE_GET_RGB565_PIXEL(img, x, y);
}
default: {
return -1;
}
}
}
return -1;
}
// Set pixel (handles boundary check and image type check).
void imlib_set_pixel(image_t *img, int x, int y, int p)
{
if ((0 <= x) && (x < img->w) && (0 <= y) && (y < img->h)) {
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
IMAGE_PUT_BINARY_PIXEL(img, x, y, p);
break;
}
case IMAGE_BPP_GRAYSCALE: {
IMAGE_PUT_GRAYSCALE_PIXEL(img, x, y, p);
break;
}
case IMAGE_BPP_RGB565: {
IMAGE_PUT_RGB565_PIXEL(img, x, y, p);
break;
}
default: {
break;
}
}
}
}
// https://stackoverflow.com/questions/1201200/fast-algorithm-for-drawing-filled-circles
static void point_fill(image_t *img, int cx, int cy, int r, int c)
{
for (int y = -r; y <= r; y++) {
for (int x = -r; x <= r; x++) {
if ((x * x) + (y * y) <= (r * r)) {
imlib_set_pixel(img, cx + x, cy + y, c);
}
}
}
}
// https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C
void imlib_draw_line(image_t *img, int x0, int y0, int x1, int y1, int c, int thickness)
{
int dx = abs(x1 - x0), sx = (x0 < x1) ? 1 : -1;
int dy = abs(y1 - y0), sy = (y0 < y1) ? 1 : -1;
int err = ((dx > dy) ? dx : -dy) / 2;
for (;;) {
point_fill(img, x0, y0, thickness - 1, c);
if ((x0 == x1) && (y0 == y1)) break;
int e2 = err;
if (e2 > -dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
static void xLine(image_t *img, int x1, int x2, int y, int c)
{
while (x1 <= x2) imlib_set_pixel(img, x1++, y, c);
}
static void yLine(image_t *img, int x, int y1, int y2, int c)
{
while (y1 <= y2) imlib_set_pixel(img, x, y1++, c);
}
void imlib_draw_rectangle(image_t *img, int rx, int ry, int rw, int rh, int c, int thickness, bool fill)
{
if (fill) {
for (int y = ry, yy = ry + rh; y < yy; y++) {
for (int x = rx, xx = rx + rw; x < xx; x++) {
imlib_set_pixel(img, x, y, c);
}
}
} else if (thickness > 0) {
for (int i = rx - ((thickness - 0) / 2), j = rx + rw + ((thickness - 1) / 2),
k = ry + rh - 1; i < j; i++) {
yLine(img, i, ry - ((thickness - 0) / 2), ry + ((thickness - 1) / 2), c);
yLine(img, i, k - ((thickness - 0) / 2), k + ((thickness - 1) / 2), c);
}
for (int i = ry - ((thickness - 0) / 2), j = ry + rh + ((thickness - 1) / 2),
k = rx + rw - 1; i < j; i++) {
xLine(img, rx - ((thickness - 0) / 2), rx + ((thickness - 1) / 2), i, c);
xLine(img, k - ((thickness - 0) / 2), k + ((thickness - 1) / 2), i, c);
}
}
}
// https://stackoverflow.com/questions/27755514/circle-with-thickness-drawing-algorithm
void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c, int thickness, bool fill)
{
if (fill) {
point_fill(img, cx, cy, r, c);
} else if (thickness > 0) {
thickness = IM_MIN(thickness, r);
int xo = r + ((thickness - 0) / 2);
int xi = r - ((thickness - 1) / 2);
int y = 0;
int erro = 1 - xo;
int erri = 1 - xi;
while(xo >= y) {
xLine(img, cx + xi, cx + xo, cy + y, c);
yLine(img, cx + y, cy + xi, cy + xo, c);
xLine(img, cx - xo, cx - xi, cy + y, c);
yLine(img, cx - y, cy + xi, cy + xo, c);
xLine(img, cx - xo, cx - xi, cy - y, c);
yLine(img, cx - y, cy - xo, cy - xi, c);
xLine(img, cx + xi, cx + xo, cy - y, c);
yLine(img, cx + y, cy - xo, cy - xi, c);
y++;
if (erro < 0) {
erro += 2 * y + 1;
} else {
xo--;
erro += 2 * (y - xo + 1);
}
if (y > (r - ((thickness - 1) / 2))) {
xi = y;
} else {
if (erri < 0) {
erri += 2 * y + 1;
} else {
xi--;
erri += 2 * (y - xi + 1);
}
}
}
}
}
void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c, int scale)
{
const int anchor = x_off;
for(char ch, last = '\0'; (ch = *str); str++, last = ch) {
if ((last == '\r') && (ch == '\n')) { // handle "\r\n" strings
continue;
}
if ((ch == '\n') || (ch == '\r')) { // handle '\n' or '\r' strings
x_off = anchor;
y_off += font[0].h * scale; // newline height == space height
continue;
}
if ((ch < ' ') || (ch > '~')) { // handle unknown characters
imlib_draw_rectangle(img,
x_off + ((scale * 3) / 2),
y_off + ((scale * 3) / 2),
(font[0].w * scale) - (((scale * 3) / 2) * 2),
(font[0].h * scale) - (((scale * 3) / 2) * 2),
c, scale, false);
continue;
}
const glyph_t *g = &font[ch - ' '];
for (int y = 0, yy = g->h * scale; y < yy; y++) {
for (int x = 0, xx = g->w * scale; x < xx; x++) {
if (g->data[y / scale] & (1 << (g->w - (x / scale)))) {
imlib_set_pixel(img, (x_off + x), (y_off + y), c);
}
}
}
x_off += g->w * scale;
}
}

View File

@ -117,7 +117,7 @@ void imlib_find_hog(image_t *src, rectangle_t *roi, int cell_size)
bin_t *bin = array_at(gds, i);
int x2 = l * cos_table[bin->d];
int y2 = l * sin_table[bin->d];
imlib_draw_line(src, (x1 - x2), (y1 + y2), (x1 + x2), (y1 - y2), bin->m);
imlib_draw_line(src, (x1 - x2), (y1 + y2), (x1 + x2), (y1 - y2), bin->m, 1);
}
hog_index += N_BINS;

View File

@ -519,139 +519,6 @@ void imlib_save_image(image_t *img, const char *path, rectangle_t *roi, int qual
}
}
void imlib_copy_image(image_t *dst, image_t *src, rectangle_t *roi)
{
if (IM_IS_JPEG(src)) {
dst->w = src->w;
dst->h = src->h;
dst->bpp = src->bpp;
dst->pixels = xalloc(src->bpp);
memcpy(dst->pixels, src->pixels, src->bpp);
} else {
rectangle_t rect;
if (!rectangle_subimg(src, roi, &rect)) ff_no_intersection(NULL);
dst->w = rect.w;
dst->h = rect.h;
dst->bpp = src->bpp;
dst->pixels = xalloc(rect.w * rect.h * src->bpp);
uint8_t *dst_pointer = dst->pixels;
for (int i = rect.y; i < (rect.y + rect.h); i++) {
int length = rect.w * src->bpp;
memcpy(dst_pointer,
src->pixels + (rect.x * src->bpp) + (i * src->w * src->bpp),
length);
dst_pointer += length;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Get pixel (handles boundary check and image type check).
int imlib_get_pixel(image_t *img, int x, int y)
{
return (IM_X_INSIDE(img, x) && IM_Y_INSIDE(img, y)) ?
( IM_IS_GS(img)
? IM_GET_GS_PIXEL(img, x, y)
: IM_GET_RGB565_PIXEL(img, x, y) )
: 0;
}
// Set pixel (handles boundary check and image type check).
void imlib_set_pixel(image_t *img, int x, int y, int p)
{
if (IM_X_INSIDE(img, x) && IM_Y_INSIDE(img, y)) {
if (IM_IS_GS(img)) {
IM_SET_GS_PIXEL(img, x, y, p);
} else {
IM_SET_RGB565_PIXEL(img, x, y, p);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void imlib_draw_line(image_t *img, int x0, int y0, int x1, int y1, int c)
{
int dx = abs(x1-x0);
int dy = abs(y1-y0);
int sx = x0<x1 ? 1 : -1;
int sy = y0<y1 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2;
for (;;) {
imlib_set_pixel(img, x0, y0, c);
if (x0==x1 && y0==y1) break;
int e2 = err;
if (e2 > -dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
void imlib_draw_rectangle(image_t *img, int rx, int ry, int rw, int rh, int c)
{
if (rw<=0 || rh<=0) {
return;
}
for (int i=rx, j=rx+rw, k=ry+rh-1; i<j; i++) {
imlib_set_pixel(img, i, ry, c);
imlib_set_pixel(img, i, k, c);
}
for (int i=ry+1, j=ry+rh-1, k=rx+rw-1; i<j; i++) {
imlib_set_pixel(img, rx, i, c);
imlib_set_pixel(img, k, i, c);
}
}
void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c)
{
int x = r, y = 0, radiusError = 1-x;
while (x>=y) {
imlib_set_pixel(img, x + cx, y + cy, c);
imlib_set_pixel(img, y + cx, x + cy, c);
imlib_set_pixel(img, -x + cx, y + cy, c);
imlib_set_pixel(img, -y + cx, x + cy, c);
imlib_set_pixel(img, -x + cx, -y + cy, c);
imlib_set_pixel(img, -y + cx, -x + cy, c);
imlib_set_pixel(img, x + cx, -y + cy, c);
imlib_set_pixel(img, y + cx, -x + cy, c);
y++;
if (radiusError<0) {
radiusError += 2 * y + 1;
} else {
x--;
radiusError += 2 * (y - x + 1);
}
}
}
void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c)
{
const int anchor = x_off;
for(char ch, last='\0'; (ch=*str); str++, last=ch) {
if (last=='\r' && ch=='\n') { // handle "\r\n" strings
continue;
}
if (ch=='\n' || ch=='\r') { // handle '\n' or '\r' strings
x_off = anchor;
y_off += font[0].h; // newline height == space height
continue;
}
if (ch<' ' || ch>'~') {
imlib_draw_rectangle(img,(x_off+1),(y_off+1),font[0].w-2,font[0].h-2,c);
continue;
}
const glyph_t *g = &font[ch-' '];
for (int y=0; y<g->h; y++) {
for (int x=0; x<g->w; x++) {
if (g->data[y] & (1<<(g->w-x))) {
imlib_set_pixel(img, (x_off+x), (y_off+y), c);
}
}
}
x_off += g->w;
}
}
////////////////////////////////////////////////////////////////////////////////
void imlib_histeq(image_t *img)

View File

@ -218,7 +218,7 @@ extern const uint8_t g826_table[256];
__typeof__ (r5) _r5 = (r5); \
__typeof__ (g6) _g6 = (g6); \
__typeof__ (b5) _b5 = (b5); \
(_r5 << 3) | (_g6 >> 3) | (_g6 << 13) | (_b5 << 8); \
(_r5 << 3) | (_g6 >> 3) | ((_g6 & 0x7) << 13) | (_b5 << 8); \
})
#define COLOR_R8_G8_B8_TO_RGB565(r8, g8, b8) COLOR_R5_G6_B5_TO_RGB565(COLOR_R8_TO_R5(r8), COLOR_G8_TO_G6(g8), COLOR_B8_TO_B5(b8))
@ -1107,7 +1107,6 @@ bool imlib_read_geometry(FIL *fp, image_t *img, const char *path, img_read_setti
void imlib_image_operation(image_t *img, const char *path, image_t *other, line_op_t op, void *data);
void imlib_load_image(image_t *img, const char *path);
void imlib_save_image(image_t *img, const char *path, rectangle_t *roi, int quality);
void imlib_copy_image(image_t *dst, image_t *src, rectangle_t *roi);
/* GIF functions */
void gif_open(FIL *fp, int width, int height, bool color, bool loop);
@ -1119,10 +1118,6 @@ void mjpeg_open(FIL *fp, int width, int height);
void mjpeg_add_frame(FIL *fp, uint32_t *frames, uint32_t *bytes, image_t *img, int quality);
void mjpeg_close(FIL *fp, uint32_t *frames, uint32_t *bytes, float fps);
/* Basic image functions */
int imlib_get_pixel(image_t *img, int x, int y);
void imlib_set_pixel(image_t *img, int x, int y, int p);
/* Point functions */
point_t *point_alloc(int16_t x, int16_t y);
bool point_equal(point_t *p1, point_t *p2);
@ -1136,12 +1131,6 @@ bool rectangle_subimg(image_t *img, rectangle_t *r, rectangle_t *r_out);
array_t *rectangle_merge(array_t *rectangles);
void rectangle_expand(rectangle_t *r, int x, int y);
/* Drawing functions */
void imlib_draw_line(image_t *img, int x0, int y0, int x1, int y1, int c);
void imlib_draw_rectangle(image_t *img, int rx, int ry, int rw, int rh, int c);
void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c);
void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c);
/* Image Morphing */
void imlib_morph(image_t *img, const int ksize, const int8_t *krn, const float m, const int b);
@ -1226,6 +1215,13 @@ void imlib_edge_canny(image_t *src, rectangle_t *roi, int low_thresh, int high_t
// HoG
void imlib_find_hog(image_t *src, rectangle_t *roi, int cell_size);
// Drawing Functions
int imlib_get_pixel(image_t *img, int x, int y);
void imlib_set_pixel(image_t *img, int x, int y, int p);
void imlib_draw_line(image_t *img, int x0, int y0, int x1, int y1, int c, int thickness);
void imlib_draw_rectangle(image_t *img, int rx, int ry, int rw, int rh, int c, int thickness, bool fill);
void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c, int thickness, bool fill);
void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c, int scale);
// Binary Functions
void imlib_binary(image_t *img, list_t *thresholds, bool invert, bool zero);
void imlib_invert(image_t *img);

View File

@ -181,9 +181,9 @@ int py_helper_keyword_color(image_t *img, uint n_args, const mp_obj_t *args, uin
} else {
mp_obj_t *arg_color;
mp_obj_get_array_fixed_n(kw_arg->value, 3, &arg_color);
default_val = COLOR_R5_G6_B5_TO_RGB565(COLOR_R8_TO_R5(mp_obj_get_int(arg_color[0])),
COLOR_G8_TO_G6(mp_obj_get_int(arg_color[1])),
COLOR_B8_TO_B5(mp_obj_get_int(arg_color[2])));
default_val = COLOR_R8_G8_B8_TO_RGB565(IM_MAX(IM_MIN(mp_obj_get_int(arg_color[0]), COLOR_R8_MAX), COLOR_R8_MIN),
IM_MAX(IM_MIN(mp_obj_get_int(arg_color[1]), COLOR_G8_MAX), COLOR_G8_MIN),
IM_MAX(IM_MIN(mp_obj_get_int(arg_color[2]), COLOR_B8_MAX), COLOR_B8_MIN));
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
default_val = COLOR_RGB565_TO_BINARY(default_val);
@ -204,9 +204,9 @@ int py_helper_keyword_color(image_t *img, uint n_args, const mp_obj_t *args, uin
} else {
mp_obj_t *arg_color;
mp_obj_get_array_fixed_n(args[arg_index], 3, &arg_color);
default_val = COLOR_R5_G6_B5_TO_RGB565(COLOR_R8_TO_R5(mp_obj_get_int(arg_color[0])),
COLOR_G8_TO_G6(mp_obj_get_int(arg_color[1])),
COLOR_B8_TO_B5(mp_obj_get_int(arg_color[2])));
default_val = COLOR_R8_G8_B8_TO_RGB565(IM_MAX(IM_MIN(mp_obj_get_int(arg_color[0]), COLOR_R8_MAX), COLOR_R8_MIN),
IM_MAX(IM_MIN(mp_obj_get_int(arg_color[1]), COLOR_G8_MAX), COLOR_G8_MIN),
IM_MAX(IM_MIN(mp_obj_get_int(arg_color[2]), COLOR_B8_MAX), COLOR_B8_MIN));
switch(img->bpp) {
case IMAGE_BPP_BINARY: {
default_val = COLOR_RGB565_TO_BINARY(default_val);

View File

@ -106,6 +106,12 @@ static const mp_obj_type_t py_kp_type = {
.unary_op = py_kp_unary_op,
};
py_kp_obj_t *py_kpts_obj(mp_obj_t kpts_obj)
{
PY_ASSERT_TYPE(kpts_obj, &py_kp_type);
return kpts_obj;
}
// LBP descriptor /////////////////////////////////////////////////////////////
typedef struct _py_lbp_obj_t {
@ -1012,9 +1018,11 @@ STATIC mp_obj_t py_image_draw_line(uint n_args, const mp_obj_t *args, mp_map_t *
int arg_y1 = mp_obj_get_int(arg_vec[3]);
int arg_c =
py_helper_keyword_color(arg_img, n_args, args, offset, kw_args, -1); // White.
py_helper_keyword_color(arg_img, n_args, args, offset + 0, kw_args, -1); // White.
int arg_thickness =
py_helper_keyword_int(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_thickness), 1);
imlib_draw_line(arg_img, arg_x0, arg_y0, arg_x1, arg_y1, arg_c);
imlib_draw_line(arg_img, arg_x0, arg_y0, arg_x1, arg_y1, arg_c, arg_thickness);
return args[0];
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_line_obj, 2, py_image_draw_line);
@ -1031,9 +1039,13 @@ STATIC mp_obj_t py_image_draw_rectangle(uint n_args, const mp_obj_t *args, mp_ma
int arg_rh = mp_obj_get_int(arg_vec[3]);
int arg_c =
py_helper_keyword_color(arg_img, n_args, args, offset, kw_args, -1); // White.
py_helper_keyword_color(arg_img, n_args, args, offset + 0, kw_args, -1); // White.
int arg_thickness =
py_helper_keyword_int(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_thickness), 1);
bool arg_fill =
py_helper_keyword_int(n_args, args, offset + 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_fill), false);
imlib_draw_rectangle(arg_img, arg_rx, arg_ry, arg_rw, arg_rh, arg_c);
imlib_draw_rectangle(arg_img, arg_rx, arg_ry, arg_rw, arg_rh, arg_c, arg_thickness, arg_fill);
return args[0];
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_rectangle_obj, 2, py_image_draw_rectangle);
@ -1049,9 +1061,13 @@ STATIC mp_obj_t py_image_draw_circle(uint n_args, const mp_obj_t *args, mp_map_t
int arg_cr = mp_obj_get_int(arg_vec[2]);
int arg_c =
py_helper_keyword_color(arg_img, n_args, args, offset, kw_args, -1); // White.
py_helper_keyword_color(arg_img, n_args, args, offset + 0, kw_args, -1); // White.
int arg_thickness =
py_helper_keyword_int(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_thickness), 1);
bool arg_fill =
py_helper_keyword_int(n_args, args, offset + 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_fill), false);
imlib_draw_circle(arg_img, arg_cx, arg_cy, arg_cr, arg_c);
imlib_draw_circle(arg_img, arg_cx, arg_cy, arg_cr, arg_c, arg_thickness, arg_fill);
return args[0];
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_circle_obj, 4, py_image_draw_circle);
@ -1067,9 +1083,11 @@ STATIC mp_obj_t py_image_draw_string(uint n_args, const mp_obj_t *args, mp_map_t
const char *arg_str = mp_obj_str_get_str(arg_vec[2]);
int arg_c =
py_helper_keyword_color(arg_img, n_args, args, offset, kw_args, -1); // White.
py_helper_keyword_color(arg_img, n_args, args, offset + 0, kw_args, -1); // White.
int arg_scale =
py_helper_keyword_int(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_scale), 1);
imlib_draw_string(arg_img, arg_x_off, arg_y_off, arg_str, arg_c);
imlib_draw_string(arg_img, arg_x_off, arg_y_off, arg_str, arg_c, arg_scale);
return args[0];
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_string_obj, 4, py_image_draw_string);
@ -1084,36 +1102,83 @@ STATIC mp_obj_t py_image_draw_cross(uint n_args, const mp_obj_t *args, mp_map_t
int arg_y = mp_obj_get_int(arg_vec[1]);
int arg_c =
py_helper_keyword_color(arg_img, n_args, args, offset, kw_args, -1); // White.
py_helper_keyword_color(arg_img, n_args, args, offset + 0, kw_args, -1); // White.
int arg_s =
py_helper_keyword_int(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_size), 5);
int arg_thickness =
py_helper_keyword_int(n_args, args, offset + 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_thickness), 1);
imlib_draw_line(arg_img, arg_x-arg_s, arg_y , arg_x+arg_s, arg_y , arg_c);
imlib_draw_line(arg_img, arg_x , arg_y-arg_s, arg_x , arg_y+arg_s, arg_c);
imlib_draw_line(arg_img, arg_x - arg_s, arg_y , arg_x + arg_s, arg_y , arg_c, arg_thickness);
imlib_draw_line(arg_img, arg_x , arg_y - arg_s, arg_x , arg_y + arg_s, arg_c, arg_thickness);
return args[0];
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_cross_obj, 2, py_image_draw_cross);
STATIC mp_obj_t py_image_draw_arrow(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
image_t *arg_img = py_helper_arg_to_image_mutable(args[0]);
const mp_obj_t *arg_vec;
uint offset = py_helper_consume_array(n_args, args, 1, 4, &arg_vec);
int arg_x0 = mp_obj_get_int(arg_vec[0]);
int arg_y0 = mp_obj_get_int(arg_vec[1]);
int arg_x1 = mp_obj_get_int(arg_vec[2]);
int arg_y1 = mp_obj_get_int(arg_vec[3]);
int arg_c =
py_helper_keyword_color(arg_img, n_args, args, offset + 0, kw_args, -1); // White.
int arg_s =
py_helper_keyword_int(n_args, args, offset + 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_size), 10);
int arg_thickness =
py_helper_keyword_int(n_args, args, offset + 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_thickness), 1);
// https://stackoverflow.com/questions/4165881/c-draw-a-line-in-gdi-with-a-filled-arrow-head-at-the-end
int dx = (arg_x1 - arg_x0);
int dy = (arg_y1 - arg_y0);
float length = fast_sqrtf((dx * dx) + (dy * dy));
float ux = dx / length;
float uy = dy / length;
float vx = -uy;
float vy = ux;
int a0x = arg_x1 - (arg_s * ux) + (arg_s * vx * 0.5);
int a0y = arg_y1 - (arg_s * uy) + (arg_s * vy * 0.5);
int a1x = arg_x1 - (arg_s * ux) - (arg_s * vx * 0.5);
int a1y = arg_y1 - (arg_s * uy) - (arg_s * vy * 0.5);
imlib_draw_line(arg_img, arg_x0, arg_y0, arg_x1, arg_y1, arg_c, arg_thickness);
imlib_draw_line(arg_img, arg_x1, arg_y1, a0x, a0y, arg_c, arg_thickness);
imlib_draw_line(arg_img, arg_x1, arg_y1, a1x, a1y, arg_c, arg_thickness);
return args[0];
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_arrow_obj, 2, py_image_draw_arrow);
STATIC mp_obj_t py_image_draw_keypoints(uint n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
image_t *arg_img = py_helper_arg_to_image_mutable(args[0]);
py_kp_obj_t *kpts_obj = (py_kp_obj_t*) args[1];
PY_ASSERT_TYPE(kpts_obj, &py_kp_type);
py_kp_obj_t *kpts_obj = py_kpts_obj(args[1]);
int arg_c =
py_helper_keyword_color(arg_img, n_args, args, 2, kw_args, -1); // White.
int arg_s =
py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_size), arg_img->w * 0.1f);
py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_size), arg_img->w / 10);
int arg_thickness =
py_helper_keyword_int(n_args, args, 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_thickness), 1);
bool arg_fill =
py_helper_keyword_int(n_args, args, 5, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_fill), false);
for (int i=0; i<array_length(kpts_obj->kpts); i++) {
for (int i = 0, ii = array_length(kpts_obj->kpts); i < ii; i++) {
kp_t *kp = array_at(kpts_obj->kpts, i);
int cx = kp->x;
int cy = kp->y;
int size = arg_s/2;
int size = arg_s / 2;
int si = sin_table[kp->angle] * size;
int co = cos_table[kp->angle] * size;
imlib_draw_line(arg_img, cx, cy, cx+co, cy+si, arg_c);
imlib_draw_circle(arg_img, cx, cy, size, arg_c);
imlib_draw_line(arg_img, cx, cy, cx + co, cy + si, arg_c, arg_thickness);
imlib_draw_circle(arg_img, cx, cy, size, arg_c, arg_thickness, arg_fill);
}
return args[0];
@ -4572,6 +4637,7 @@ static const mp_rom_map_elem_t locals_dict_table[] = {
{MP_ROM_QSTR(MP_QSTR_draw_circle), MP_ROM_PTR(&py_image_draw_circle_obj)},
{MP_ROM_QSTR(MP_QSTR_draw_string), MP_ROM_PTR(&py_image_draw_string_obj)},
{MP_ROM_QSTR(MP_QSTR_draw_cross), MP_ROM_PTR(&py_image_draw_cross_obj)},
{MP_ROM_QSTR(MP_QSTR_draw_arrow), MP_ROM_PTR(&py_image_draw_arrow_obj)},
{MP_ROM_QSTR(MP_QSTR_draw_keypoints), MP_ROM_PTR(&py_image_draw_keypoints_obj)},
/* Binary Methods */
{MP_ROM_QSTR(MP_QSTR_binary), MP_ROM_PTR(&py_image_binary_obj)},

View File

@ -38,15 +38,6 @@ Q(width)
Q(height)
Q(format)
Q(size)
Q(get_pixel)
Q(rgbtuple)
Q(set_pixel)
Q(draw_line)
Q(draw_rectangle)
Q(draw_circle)
Q(draw_string)
Q(draw_cross)
Q(draw_keypoints)
Q(morph)
Q(midpoint)
Q(mean)
@ -70,7 +61,6 @@ Q(find_edges)
Q(find_hog)
Q(cmp_lbp)
Q(quality)
Q(color)
Q(roi)
Q(offset)
Q(threshold)
@ -330,6 +320,55 @@ Q(CPUFREQ_216MHZ)
Q(get_frequency)
Q(set_frequency)
// Get Pixel
Q(get_pixel)
Q(rgbtuple)
// Set Pixel
Q(set_pixel)
Q(color)
// Draw Line
Q(draw_line)
// duplicate Q(color)
Q(thickness)
// Draw Rectangle
Q(draw_rectangle)
// duplicate Q(color)
// duplicate Q(thickness)
Q(fill)
// Draw Circle
Q(draw_circle)
// duplicate Q(color)
// duplicate Q(thickness)
// duplicate Q(fill)
// Draw String
Q(draw_string)
// duplicate Q(color)
// duplicate Q(scale)
// Draw Cross
Q(draw_cross)
// duplicate Q(color)
// duplicate Q(size)
// duplicate Q(thickness)
// Draw Arrow
Q(draw_arrow)
// duplicate Q(color)
// duplicate Q(size)
// duplicate Q(thickness)
// Draw Keypoints
Q(draw_keypoints)
// duplicate Q(color)
// duplicate Q(size)
// duplicate Q(thickness)
// duplicate Q(fill)
// Binary
Q(binary)
Q(invert)

View File

@ -0,0 +1,31 @@
# Arrow Drawing
#
# This example shows off drawing arrows on the OpenMV Cam.
import sensor, image, time, pyb
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # or GRAYSCALE...
sensor.set_framesize(sensor.QVGA) # or QQVGA...
sensor.skip_frames(time = 2000)
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot()
for i in range(10):
x0 = (pyb.rng() % (2*img.width())) - (img.width()//2)
y0 = (pyb.rng() % (2*img.height())) - (img.height()//2)
x1 = (pyb.rng() % (2*img.width())) - (img.width()//2)
y1 = (pyb.rng() % (2*img.height())) - (img.height()//2)
r = (pyb.rng() % 127) + 128
g = (pyb.rng() % 127) + 128
b = (pyb.rng() % 127) + 128
# If the first argument is a scaler then this method expects
# to see x0, y0, x1, and y1. Otherwise, it expects a (x0,y0,x1,y1) tuple.
img.draw_arrow(x0, y0, x1, y1, color = (r, g, b), size = 30, thickness = 2)
print(clock.fps())

View File

@ -0,0 +1,31 @@
# Circle Drawing
#
# This example shows off drawing circles on the OpenMV Cam.
import sensor, image, time, pyb
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # or GRAYSCALE...
sensor.set_framesize(sensor.QVGA) # or QQVGA...
sensor.skip_frames(time = 2000)
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot()
for i in range(10):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
radius = pyb.rng() % (max(img.height(), img.width())//2)
r = (pyb.rng() % 127) + 128
g = (pyb.rng() % 127) + 128
b = (pyb.rng() % 127) + 128
# If the first argument is a scaler then this method expects
# to see x, y, and radius. Otherwise, it expects a (x,y,radius) tuple.
img.draw_circle(x, y, radius, color = (r, g, b), thickness = 2, fill = False)
print(clock.fps())

View File

@ -1,55 +0,0 @@
# Color Drawing Example
#
# This example shows off your OpenMV Cam's built-in drawing capabilities. This
# example was originally a test but serves as good reference code. Please put
# your IDE into non-JPEG mode to see the best drawing quality.
import sensor, image, time
sensor.reset()
sensor.set_framesize(sensor.QVGA)
# All drawing functions use the same code to pass color.
# So we just need to test one function.
while(True):
# Test Draw Line (GRAYSCALE)
sensor.set_pixformat(sensor.GRAYSCALE)
for i in range(10):
img = sensor.snapshot()
for i in range(img.width()):
c = ((i * 255) + (img.width()/2)) / img.width()
img.draw_line([i, 0, i, img.height()-1], color = int(c))
sensor.snapshot()
time.sleep(1000)
# Test Draw Line (RGB565)
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for i in range(img.width()):
c = ((i * 255) + (img.width()/2)) / img.width()
img.draw_line([i, 0, i, img.height()-1], color = [int(c), 0, 0])
sensor.snapshot()
time.sleep(1000)
# Test Draw Line (RGB565)
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for i in range(img.width()):
c = ((i * 255) + (img.width()/2)) / img.width()
img.draw_line([i, 0, i, img.height()-1], color = [0, int(c), 0])
sensor.snapshot()
time.sleep(1000)
# Test Draw Line (RGB565)
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for i in range(img.width()):
c = ((i * 255) + (img.width()/2)) / img.width()
img.draw_line([i, 0, i, img.height()-1], color = [0, 0, int(c)])
sensor.snapshot()
time.sleep(1000)

View File

@ -1,118 +0,0 @@
# Crazy Drawing Example
#
# This example shows off your OpenMV Cam's built-in drawing capabilities. This
# example was originally a test but serves as good reference code. Please put
# your IDE into non-JPEG mode to see the best drawing quality.
import pyb, sensor, image, math
sensor.reset()
sensor.set_framesize(sensor.QVGA)
while(True):
# Test Set Pixel
sensor.set_pixformat(sensor.GRAYSCALE)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
img.set_pixel(x, y, 255)
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
img.set_pixel(x, y, (255, 255, 255))
# Test Draw Line
sensor.set_pixformat(sensor.GRAYSCALE)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x0 = (pyb.rng() % (2*img.width())) - (img.width()//2)
y0 = (pyb.rng() % (2*img.height())) - (img.height()//2)
x1 = (pyb.rng() % (2*img.width())) - (img.width()//2)
y1 = (pyb.rng() % (2*img.height())) - (img.height()//2)
img.draw_line([x0, y0, x1, y1])
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x0 = (pyb.rng() % (2*img.width())) - (img.width()//2)
y0 = (pyb.rng() % (2*img.height())) - (img.height()//2)
x1 = (pyb.rng() % (2*img.width())) - (img.width()//2)
y1 = (pyb.rng() % (2*img.height())) - (img.height()//2)
img.draw_line([x0, y0, x1, y1])
# Test Draw Rectangle
sensor.set_pixformat(sensor.GRAYSCALE)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
w = (pyb.rng() % img.width())
h = (pyb.rng() % img.height())
img.draw_rectangle([x, y, w, h])
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
w = (pyb.rng() % img.width())
h = (pyb.rng() % img.height())
img.draw_rectangle([x, y, w, h])
# Test Draw Circle
sensor.set_pixformat(sensor.GRAYSCALE)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
r = (pyb.rng() % (img.width() if (img.width() > img.height()) else img.height()))
img.draw_circle(x, y, r)
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
r = (pyb.rng() % (img.width() if (img.width() > img.height()) else img.height()))
img.draw_circle(x, y, r)
# Test Draw String
sensor.set_pixformat(sensor.GRAYSCALE)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
img.draw_string(x, y, "Hello\nWorld!")
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
img.draw_string(x, y, "Hello\nWorld!")
# Test Draw Cross
sensor.set_pixformat(sensor.GRAYSCALE)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
img.draw_cross(x, y)
sensor.set_pixformat(sensor.RGB565)
for i in range(10):
img = sensor.snapshot()
for j in range(100):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
img.draw_cross(x, y)

View File

@ -0,0 +1,29 @@
# Cross Drawing
#
# This example shows off drawing crosses on the OpenMV Cam.
import sensor, image, time, pyb
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # or GRAYSCALE...
sensor.set_framesize(sensor.QVGA) # or QQVGA...
sensor.skip_frames(time = 2000)
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot()
for i in range(10):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
r = (pyb.rng() % 127) + 128
g = (pyb.rng() % 127) + 128
b = (pyb.rng() % 127) + 128
# If the first argument is a scaler then this method expects
# to see x and y. Otherwise, it expects a (x,y) tuple.
img.draw_cross(x, y, color = (r, g, b), size = 10, thickness = 2)
print(clock.fps())

View File

@ -0,0 +1,31 @@
# Line Drawing
#
# This example shows off drawing lines on the OpenMV Cam.
import sensor, image, time, pyb
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # or GRAYSCALE...
sensor.set_framesize(sensor.QVGA) # or QQVGA...
sensor.skip_frames(time = 2000)
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot()
for i in range(10):
x0 = (pyb.rng() % (2*img.width())) - (img.width()//2)
y0 = (pyb.rng() % (2*img.height())) - (img.height()//2)
x1 = (pyb.rng() % (2*img.width())) - (img.width()//2)
y1 = (pyb.rng() % (2*img.height())) - (img.height()//2)
r = (pyb.rng() % 127) + 128
g = (pyb.rng() % 127) + 128
b = (pyb.rng() % 127) + 128
# If the first argument is a scaler then this method expects
# to see x0, y0, x1, and y1. Otherwise, it expects a (x0,y0,x1,y1) tuple.
img.draw_line(x0, y0, x1, y1, color = (r, g, b), thickness = 2)
print(clock.fps())

View File

@ -0,0 +1,31 @@
# Rectangle Drawing
#
# This example shows off drawing rectangles on the OpenMV Cam.
import sensor, image, time, pyb
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # or GRAYSCALE...
sensor.set_framesize(sensor.QVGA) # or QQVGA...
sensor.skip_frames(time = 2000)
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot()
for i in range(10):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
w = (pyb.rng() % (img.width()//2))
h = (pyb.rng() % (img.height()//2))
r = (pyb.rng() % 127) + 128
g = (pyb.rng() % 127) + 128
b = (pyb.rng() % 127) + 128
# If the first argument is a scaler then this method expects
# to see x, y, w, and h. Otherwise, it expects a (x,y,w,h) tuple.
img.draw_rectangle(x, y, w, h, color = (r, g, b), thickness = 2, fill = False)
print(clock.fps())

View File

@ -0,0 +1,29 @@
# Text Drawing
#
# This example shows off drawing text on the OpenMV Cam.
import sensor, image, time, pyb
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # or GRAYSCALE...
sensor.set_framesize(sensor.QVGA) # or QQVGA...
sensor.skip_frames(time = 2000)
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot()
for i in range(10):
x = (pyb.rng() % (2*img.width())) - (img.width()//2)
y = (pyb.rng() % (2*img.height())) - (img.height()//2)
r = (pyb.rng() % 127) + 128
g = (pyb.rng() % 127) + 128
b = (pyb.rng() % 127) + 128
# If the first argument is a scaler then this method expects
# to see x, y, and text. Otherwise, it expects a (x,y,text) tuple.
img.draw_string(x, y, "Hello World!", color = (r, g, b), scale = 2)
print(clock.fps())