From bfd048e6f3c77cd7fbe66bfc5bebafb0220b4624 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 24 Mar 2017 12:45:27 -0400 Subject: [PATCH] Integrate ZBar 2/2 ZBar functionality integrated along with an example script. --- src/Makefile | 1 + src/omv/Makefile | 1 + src/omv/boards/OPENMV3/omv_boardconfig.h | 3 + src/omv/img/imlib.h | 32 +++++ src/omv/py/py_image.c | 156 +++++++++++++++++++++++ src/omv/py/qstrdefsomv.h | 32 +++++ usr/examples/16-Codes/find_barcodes.py | 65 ++++++++++ 7 files changed, 290 insertions(+) create mode 100644 usr/examples/16-Codes/find_barcodes.py diff --git a/src/Makefile b/src/Makefile index a6b72f99c..19a6ddfc4 100755 --- a/src/Makefile +++ b/src/Makefile @@ -138,6 +138,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\ blob.o \ qrcode.o \ apriltag.o \ + zbar.o \ fmath.o \ fsort.o \ fft.o \ diff --git a/src/omv/Makefile b/src/omv/Makefile index 6fcbbb32c..85a59d7b5 100644 --- a/src/omv/Makefile +++ b/src/omv/Makefile @@ -20,6 +20,7 @@ SRCS += $(addprefix img/, \ blob.c \ qrcode.c \ apriltag.c \ + zbar.c \ fmath.c \ fsort.c \ fft.c \ diff --git a/src/omv/boards/OPENMV3/omv_boardconfig.h b/src/omv/boards/OPENMV3/omv_boardconfig.h index 0d468211d..c8fb04e2e 100644 --- a/src/omv/boards/OPENMV3/omv_boardconfig.h +++ b/src/omv/boards/OPENMV3/omv_boardconfig.h @@ -27,6 +27,9 @@ // Enable AprilTags (64 KB). #define OMV_ENABLE_APRILTAGS +// Enable Barcodes (42 KB). +#define OMV_ENABLE_BARCODES + // Bootloader LED GPIO port/pin #define OMV_BOOTLDR_LED_PIN (GPIO_PIN_1) #define OMV_BOOTLDR_LED_PORT (GPIOC) diff --git a/src/omv/img/imlib.h b/src/omv/img/imlib.h index c4a906a8c..6137a1e54 100644 --- a/src/omv/img/imlib.h +++ b/src/omv/img/imlib.h @@ -920,6 +920,37 @@ typedef struct find_apriltags_list_lnk_data } find_apriltags_list_lnk_data_t; +typedef enum barcodes +{ + BARCODE_EAN2, + BARCODE_EAN5, + BARCODE_EAN8, + BARCODE_UPCE, + BARCODE_ISBN10, + BARCODE_UPCA, + BARCODE_EAN13, + BARCODE_ISBN13, + BARCODE_I25, + BARCODE_DATABAR, + BARCODE_DATABAR_EXP, + BARCODE_CODABAR, + BARCODE_CODE39, + BARCODE_PDF417, + BARCODE_CODE93, + BARCODE_CODE128 +} +barcodes_t; + +typedef struct find_barcodes_list_lnk_data +{ + rectangle_t rect; + size_t payload_len; + char *payload; + uint16_t type, rotation; + int quality; +} +find_barcodes_list_lnk_data_t; + /* Color space functions */ void imlib_rgb_to_lab(simple_color_t *rgb, simple_color_t *lab); void imlib_lab_to_rgb(simple_color_t *lab, simple_color_t *rgb); @@ -1104,5 +1135,6 @@ void imlib_find_blobs(list_t *out, image_t *ptr, rectangle_t *roi, unsigned int 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, float fx, float fy, float cx, float cy); +void imlib_find_barcodes(list_t *out, image_t *ptr, rectangle_t *roi); #endif //__IMLIB_H__ diff --git a/src/omv/py/py_image.c b/src/omv/py/py_image.c index 03f2f1086..70b307e84 100644 --- a/src/omv/py/py_image.c +++ b/src/omv/py/py_image.c @@ -2171,6 +2171,140 @@ static mp_obj_t py_image_find_apriltags(uint n_args, const mp_obj_t *args, mp_ma return objects_list; } +// BarCode Object // +#define py_barcode_obj_size 8 +typedef struct py_barcode_obj { + mp_obj_base_t base; + mp_obj_t x, y, w, h, payload, type, rotation, quality; +} py_barcode_obj_t; + +static void py_barcode_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) +{ + py_barcode_obj_t *self = self_in; + mp_printf(print, + "{x:%d, y:%d, w:%d, h:%d, payload:\"%s\", type:%d, rotation:%f, quality:%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_str_get_str(self->payload), + mp_obj_get_int(self->type), + (double) mp_obj_get_float(self->rotation), + mp_obj_get_int(self->quality)); +} + +static mp_obj_t py_barcode_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) +{ + if (value == MP_OBJ_SENTINEL) { // load + py_barcode_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_barcode_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_barcode_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->payload; + case 5: return self->type; + case 6: return self->rotation; + case 7: return self->quality; + } + } + return MP_OBJ_NULL; // op not supported +} + +mp_obj_t py_barcode_rect(mp_obj_t self_in) +{ + return mp_obj_new_tuple(4, (mp_obj_t []) {((py_barcode_obj_t *) self_in)->x, + ((py_barcode_obj_t *) self_in)->y, + ((py_barcode_obj_t *) self_in)->w, + ((py_barcode_obj_t *) self_in)->h}); +} + +mp_obj_t py_barcode_x(mp_obj_t self_in) { return ((py_barcode_obj_t *) self_in)->x; } +mp_obj_t py_barcode_y(mp_obj_t self_in) { return ((py_barcode_obj_t *) self_in)->y; } +mp_obj_t py_barcode_w(mp_obj_t self_in) { return ((py_barcode_obj_t *) self_in)->w; } +mp_obj_t py_barcode_h(mp_obj_t self_in) { return ((py_barcode_obj_t *) self_in)->h; } +mp_obj_t py_barcode_payload_fun(mp_obj_t self_in) { return ((py_barcode_obj_t *) self_in)->payload; } +mp_obj_t py_barcode_type_fun(mp_obj_t self_in) { return ((py_barcode_obj_t *) self_in)->type; } +mp_obj_t py_barcode_rotation_fun(mp_obj_t self_in) { return ((py_barcode_obj_t *) self_in)->rotation; } +mp_obj_t py_barcode_quality_fun(mp_obj_t self_in) { return ((py_barcode_obj_t *) self_in)->quality; } + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_rect_obj, py_barcode_rect); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_x_obj, py_barcode_x); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_y_obj, py_barcode_y); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_w_obj, py_barcode_w); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_h_obj, py_barcode_h); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_payload_fun_obj, py_barcode_payload_fun); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_type_fun_obj, py_barcode_type_fun); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_rotation_fun_obj, py_barcode_rotation_fun); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_barcode_quality_fun_obj, py_barcode_quality_fun); + +STATIC const mp_rom_map_elem_t py_barcode_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&py_barcode_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&py_barcode_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&py_barcode_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_w), MP_ROM_PTR(&py_barcode_w_obj) }, + { MP_ROM_QSTR(MP_QSTR_h), MP_ROM_PTR(&py_barcode_h_obj) }, + { MP_ROM_QSTR(MP_QSTR_payload), MP_ROM_PTR(&py_barcode_payload_fun_obj) }, + { MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&py_barcode_type_fun_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&py_barcode_rotation_fun_obj) }, + { MP_ROM_QSTR(MP_QSTR_quality), MP_ROM_PTR(&py_barcode_quality_fun_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(py_barcode_locals_dict, py_barcode_locals_dict_table); + +static const mp_obj_type_t py_barcode_type = { + { &mp_type_type }, + .name = MP_QSTR_barcode, + .print = py_barcode_print, + .subscr = py_barcode_subscr, + .locals_dict = (mp_obj_t) &py_barcode_locals_dict, +}; + +static mp_obj_t py_image_find_barcodes(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_barcodes(&out, arg_img, &roi); + 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_barcodes_list_lnk_data_t lnk_data; + list_pop_front(&out, &lnk_data); + + py_barcode_obj_t *o = m_new_obj(py_barcode_obj_t); + o->base.type = &py_barcode_type; + 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->payload = mp_obj_new_str(lnk_data.payload, lnk_data.payload_len, false); + o->type = mp_obj_new_int(lnk_data.type); + o->rotation = mp_obj_new_float((lnk_data.rotation * PI) / 180); + o->quality = mp_obj_new_int(lnk_data.quality); + + objects_list->items[i] = o; + xfree(lnk_data.payload); + } + + return objects_list; +} + static mp_obj_t py_image_midpoint_pool(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { image_t *arg_img = py_image_cobj(args[0]); @@ -2591,6 +2725,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_blobs_obj, 2, py_image_find_blob /* 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_apriltags_obj, 1, py_image_find_apriltags); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_find_barcodes_obj, 1, py_image_find_barcodes); /* Template Matching */ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_midpoint_pool_obj, 3, py_image_midpoint_pool); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_midpoint_pooled_obj, 3, py_image_midpoint_pooled); @@ -2668,6 +2803,9 @@ static const mp_map_elem_t locals_dict_table[] = { {MP_OBJ_NEW_QSTR(MP_QSTR_find_qrcodes), (mp_obj_t)&py_image_find_qrcodes_obj}, #ifdef OMV_ENABLE_APRILTAGS {MP_OBJ_NEW_QSTR(MP_QSTR_find_apriltags), (mp_obj_t)&py_image_find_apriltags_obj}, +#endif +#ifdef OMV_ENABLE_BARCODES + {MP_OBJ_NEW_QSTR(MP_QSTR_find_barcodes), (mp_obj_t)&py_image_find_barcodes_obj}, #endif /* Template Matching */ {MP_OBJ_NEW_QSTR(MP_QSTR_midpoint_pool), (mp_obj_t)&py_image_midpoint_pool_obj}, @@ -3050,6 +3188,24 @@ static const mp_map_elem_t globals_dict_table[] = { {MP_OBJ_NEW_QSTR(MP_QSTR_TAG36H11), MP_OBJ_NEW_SMALL_INT(TAG36H11)}, {MP_OBJ_NEW_QSTR(MP_QSTR_ARTOOLKIT), MP_OBJ_NEW_SMALL_INT(ARTOOLKIT)}, #endif +#ifdef OMV_ENABLE_BARCODES + {MP_OBJ_NEW_QSTR(MP_QSTR_EAN2), MP_OBJ_NEW_SMALL_INT(BARCODE_EAN2)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_EAN5), MP_OBJ_NEW_SMALL_INT(BARCODE_EAN5)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_EAN8), MP_OBJ_NEW_SMALL_INT(BARCODE_EAN8)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_UPCE), MP_OBJ_NEW_SMALL_INT(BARCODE_UPCE)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_ISBN10), MP_OBJ_NEW_SMALL_INT(BARCODE_ISBN10)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_UPCA), MP_OBJ_NEW_SMALL_INT(BARCODE_UPCA)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_EAN13), MP_OBJ_NEW_SMALL_INT(BARCODE_EAN13)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_ISBN13), MP_OBJ_NEW_SMALL_INT(BARCODE_ISBN13)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_I25), MP_OBJ_NEW_SMALL_INT(BARCODE_I25)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_DATABAR), MP_OBJ_NEW_SMALL_INT(BARCODE_DATABAR)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_DATABAR_EXP), MP_OBJ_NEW_SMALL_INT(BARCODE_DATABAR_EXP)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_CODABAR), MP_OBJ_NEW_SMALL_INT(BARCODE_CODABAR)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_CODE39), MP_OBJ_NEW_SMALL_INT(BARCODE_CODE39)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_PDF417), MP_OBJ_NEW_SMALL_INT(BARCODE_PDF417)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_CODE93), MP_OBJ_NEW_SMALL_INT(BARCODE_CODE93)}, + {MP_OBJ_NEW_QSTR(MP_QSTR_CODE128), MP_OBJ_NEW_SMALL_INT(BARCODE_CODE128)}, +#endif /* Color space functions */ {MP_OBJ_NEW_QSTR(MP_QSTR_rgb_to_lab), (mp_obj_t)&py_image_rgb_to_lab_obj}, diff --git a/src/omv/py/qstrdefsomv.h b/src/omv/py/qstrdefsomv.h index b0a1556be..21751f704 100644 --- a/src/omv/py/qstrdefsomv.h +++ b/src/omv/py/qstrdefsomv.h @@ -435,6 +435,7 @@ Q(fx) Q(fy) // duplicate Q(cx) // duplicate Q(cy) +// AprilTag Object Q(apriltag) // duplicate Q(rect) // duplicate Q(x) @@ -461,3 +462,34 @@ Q(TAG25H9) Q(TAG36H10) Q(TAG36H11) Q(ARTOOLKIT) + +// Find BarCodes +Q(find_barcodes) +// duplicate Q(roi) +// BarCode Object +Q(barcode) +// duplicate Q(rect) +// duplicate Q(x) +// duplicate Q(y) +// duplicate Q(w) +// duplicate Q(h) +// duplicate Q(payload) +// duplicate Q(type) +// duplicate Q(rotation) +// duplicate Q(quality) +Q(EAN2) +Q(EAN5) +Q(EAN8) +Q(UPCE) +Q(ISBN10) +Q(UPCA) +Q(EAN13) +Q(ISBN13) +Q(I25) +Q(DATABAR) +Q(DATABAR_EXP) +Q(CODABAR) +Q(CODE39) +Q(PDF417) +Q(CODE93) +Q(CODE128) diff --git a/usr/examples/16-Codes/find_barcodes.py b/usr/examples/16-Codes/find_barcodes.py new file mode 100644 index 000000000..c506497cd --- /dev/null +++ b/usr/examples/16-Codes/find_barcodes.py @@ -0,0 +1,65 @@ +# Barcode Example +# +# This example shows off how easy it is to detect bar codes using the +# OpenMV Cam M7. Barcode detection does not work on the M4 Camera. + +import sensor, image, time, math + +sensor.reset() +sensor.set_pixformat(sensor.GRAYSCALE) +sensor.set_framesize(sensor.VGA) # High Res! +sensor.set_windowing((640, 80)) # V Res of 80 == less work (40 for 2X the speed). +sensor.skip_frames(30) +sensor.set_auto_gain(False) # must turn this off to prevent image washout... +sensor.set_auto_whitebal(False) # must turn this off to prevent image washout... +clock = time.clock() + +# Barcode detection can run at the full 640x480 resolution of your OpenMV Cam's +# OV7725 camera module. Barcode detection will also work in RGB565 mode but at +# a lower resolution. That said, barcode detection requires a higher resolution +# to work well so it should always be run at 640x480 in grayscale... + +def barcode_name(code): + if(code.type() == image.EAN2): + return "EAN2" + if(code.type() == image.EAN5): + return "EAN5" + if(code.type() == image.EAN8): + return "EAN8" + if(code.type() == image.UPCE): + return "UPCE" + if(code.type() == image.ISBN10): + return "ISBN10" + if(code.type() == image.UPCA): + return "UPCA" + if(code.type() == image.EAN13): + return "EAN13" + if(code.type() == image.ISBN13): + return "ISBN13" + if(code.type() == image.I25): + return "I25" + if(code.type() == image.DATABAR): + return "DATABAR" + if(code.type() == image.DATABAR_EXP): + return "DATABAR_EXP" + if(code.type() == image.CODABAR): + return "CODABAR" + if(code.type() == image.CODE39): + return "CODE39" + if(code.type() == image.PDF417): + return "PDF417" + if(code.type() == image.CODE93): + return "CODE93" + if(code.type() == image.CODE128): + return "CODE128" + +while(True): + clock.tick() + img = sensor.snapshot() + codes = img.find_barcodes() + for code in codes: + img.draw_rectangle(code.rect()) + print_args = (barcode_name(code), code.payload(), (180 * code.rotation()) / math.pi, code.quality(), clock.fps()) + print("Barcode %s, Payload \"%s\", rotation %f (degrees), quality %d, FPS %f" % print_args) + if not codes: + print("FPS %f" % clock.fps())