From 783a78754ea60ba223ce0eaa8afbc74f8f673f6b Mon Sep 17 00:00:00 2001 From: Ibrahim Abd Elkader Date: Sun, 3 Jan 2021 00:38:44 +0200 Subject: [PATCH] Add new ImageIO Type. (#1084) * Add ImageIO type to support memory stream I/O. * Remove old image reader/writer and link imageio. * Enable ImageIO modules on all boards. * Check if stream is closed when calling read/write/seek --- src/omv/Makefile | 1 + src/omv/boards/NANO33/imlib_config.h | 7 + src/omv/boards/OPENMV1/imlib_config.h | 3 + src/omv/boards/OPENMV2/imlib_config.h | 3 + src/omv/boards/OPENMV3/imlib_config.h | 3 + src/omv/boards/OPENMV4/imlib_config.h | 3 + src/omv/boards/OPENMV4P/imlib_config.h | 3 + src/omv/boards/OPENMVPT/imlib_config.h | 3 + src/omv/boards/PORTENTA/imlib_config.h | 3 + src/omv/modules/py_image.c | 233 +--------------- src/omv/modules/py_imageio.c | 360 +++++++++++++++++++++++++ src/omv/modules/py_imageio.h | 14 + src/omv/modules/qstrdefsomv.h | 25 +- src/omv/ports/nrf/omv_portconfig.mk | 1 + src/omv/ports/stm32/omv_portconfig.mk | 1 + 15 files changed, 419 insertions(+), 244 deletions(-) create mode 100644 src/omv/modules/py_imageio.c create mode 100644 src/omv/modules/py_imageio.h diff --git a/src/omv/Makefile b/src/omv/Makefile index 9920d96f7..a4387bd00 100644 --- a/src/omv/Makefile +++ b/src/omv/Makefile @@ -40,6 +40,7 @@ SRCS += $(addprefix modules/, \ py_gif.c \ py_helper.c \ py_image.c \ + py_imageio.c \ py_mjpeg.c \ py_omv.c \ py_sensor.c \ diff --git a/src/omv/boards/NANO33/imlib_config.h b/src/omv/boards/NANO33/imlib_config.h index ca14e857a..6e682ed32 100644 --- a/src/omv/boards/NANO33/imlib_config.h +++ b/src/omv/boards/NANO33/imlib_config.h @@ -11,6 +11,13 @@ #ifndef __IMLIB_CONFIG_H__ #define __IMLIB_CONFIG_H__ +// Enable Image I/O +#define IMLIB_ENABLE_IMAGE_IO + +// Enable Image File I/O +// Not filesystem yet +//#define IMLIB_ENABLE_IMAGE_FILE_IO + // Enable LAB LUT //#define IMLIB_ENABLE_LAB_LUT diff --git a/src/omv/boards/OPENMV1/imlib_config.h b/src/omv/boards/OPENMV1/imlib_config.h index 6bcfe9b9f..3970abdcb 100644 --- a/src/omv/boards/OPENMV1/imlib_config.h +++ b/src/omv/boards/OPENMV1/imlib_config.h @@ -12,6 +12,9 @@ #define __IMLIB_CONFIG_H__ // Enable Image I/O +//#define IMLIB_ENABLE_IMAGE_IO + +// Enable Image File I/O //#define IMLIB_ENABLE_IMAGE_FILE_IO // Enable LAB LUT diff --git a/src/omv/boards/OPENMV2/imlib_config.h b/src/omv/boards/OPENMV2/imlib_config.h index 886e0cfad..2958541d5 100644 --- a/src/omv/boards/OPENMV2/imlib_config.h +++ b/src/omv/boards/OPENMV2/imlib_config.h @@ -12,6 +12,9 @@ #define __IMLIB_CONFIG_H__ // Enable Image I/O +#define IMLIB_ENABLE_IMAGE_IO + +// Enable Image File I/O #define IMLIB_ENABLE_IMAGE_FILE_IO // Enable LAB LUT diff --git a/src/omv/boards/OPENMV3/imlib_config.h b/src/omv/boards/OPENMV3/imlib_config.h index 2d936d0ae..070cd0934 100644 --- a/src/omv/boards/OPENMV3/imlib_config.h +++ b/src/omv/boards/OPENMV3/imlib_config.h @@ -12,6 +12,9 @@ #define __IMLIB_CONFIG_H__ // Enable Image I/O +#define IMLIB_ENABLE_IMAGE_IO + +// Enable Image File I/O #define IMLIB_ENABLE_IMAGE_FILE_IO // Enable LAB LUT diff --git a/src/omv/boards/OPENMV4/imlib_config.h b/src/omv/boards/OPENMV4/imlib_config.h index bae7fd5ec..85d9bde81 100644 --- a/src/omv/boards/OPENMV4/imlib_config.h +++ b/src/omv/boards/OPENMV4/imlib_config.h @@ -12,6 +12,9 @@ #define __IMLIB_CONFIG_H__ // Enable Image I/O +#define IMLIB_ENABLE_IMAGE_IO + +// Enable Image File I/O #define IMLIB_ENABLE_IMAGE_FILE_IO // Enable LAB LUT diff --git a/src/omv/boards/OPENMV4P/imlib_config.h b/src/omv/boards/OPENMV4P/imlib_config.h index 9a7d2cfb8..f7a60faa3 100644 --- a/src/omv/boards/OPENMV4P/imlib_config.h +++ b/src/omv/boards/OPENMV4P/imlib_config.h @@ -12,6 +12,9 @@ #define __IMLIB_CONFIG_H__ // Enable Image I/O +#define IMLIB_ENABLE_IMAGE_IO + +// Enable Image File I/O #define IMLIB_ENABLE_IMAGE_FILE_IO // Enable LAB LUT diff --git a/src/omv/boards/OPENMVPT/imlib_config.h b/src/omv/boards/OPENMVPT/imlib_config.h index a094bc704..61bcaa1a7 100644 --- a/src/omv/boards/OPENMVPT/imlib_config.h +++ b/src/omv/boards/OPENMVPT/imlib_config.h @@ -12,6 +12,9 @@ #define __IMLIB_CONFIG_H__ // Enable Image I/O +#define IMLIB_ENABLE_IMAGE_IO + +// Enable Image File I/O #define IMLIB_ENABLE_IMAGE_FILE_IO // Enable LAB LUT diff --git a/src/omv/boards/PORTENTA/imlib_config.h b/src/omv/boards/PORTENTA/imlib_config.h index 0eb7ee43f..b1db02196 100644 --- a/src/omv/boards/PORTENTA/imlib_config.h +++ b/src/omv/boards/PORTENTA/imlib_config.h @@ -12,6 +12,9 @@ #define __IMLIB_CONFIG_H__ // Enable Image I/O +#define IMLIB_ENABLE_IMAGE_IO + +// Enable Image File I/O #define IMLIB_ENABLE_IMAGE_FILE_IO // Enable LAB LUT diff --git a/src/omv/modules/py_image.c b/src/omv/modules/py_image.c index e38ddb36a..ad8fa3ab6 100644 --- a/src/omv/modules/py_image.c +++ b/src/omv/modules/py_image.c @@ -31,6 +31,9 @@ #include "py_helper.h" #include "py_image.h" #include "omv_boardconfig.h" +#if defined(IMLIB_ENABLE_IMAGE_IO) +#include "py_imageio.h" +#endif static const mp_obj_type_t py_cascade_type; static const mp_obj_type_t py_image_type; @@ -6622,228 +6625,6 @@ static const mp_obj_type_t py_image_type = { .locals_dict = (mp_obj_t) &locals_dict }; -#if defined(IMLIB_ENABLE_IMAGE_FILE_IO) -// ImageWriter Object // -typedef struct py_imagewriter_obj { - mp_obj_base_t base; - FIL fp; - uint32_t ms; -} py_imagewriter_obj_t; - -static void py_imagewriter_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) -{ - py_imagewriter_obj_t *self = self_in; - mp_printf(print, "{\"size\":%d}", f_size(&self->fp)); -} - -mp_obj_t py_imagewriter_size(mp_obj_t self_in) -{ - return mp_obj_new_int(f_size(&((py_imagewriter_obj_t *) self_in)->fp)); -} - -mp_obj_t py_imagewriter_add_frame(mp_obj_t self_in, mp_obj_t img_obj) -{ - // Don't use the file buffer here... - - FIL *fp = &((py_imagewriter_obj_t *) self_in)->fp; - PY_ASSERT_TYPE(img_obj, &py_image_type); - image_t *arg_img = &((py_image_obj_t *) img_obj)->_cobj; - - uint32_t ms = mp_hal_ticks_ms(); // Write out elapsed ms. - write_long(fp, ms - ((py_imagewriter_obj_t *) self_in)->ms); - ((py_imagewriter_obj_t *) self_in)->ms = ms; - - write_long(fp, arg_img->w); - write_long(fp, arg_img->h); - write_long(fp, arg_img->bpp); - - uint32_t size = image_size(arg_img); - - write_data(fp, arg_img->data, size); - if (size % 16) write_data(fp, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16 - (size % 16)); // Pad to multiple of 16 bytes. - return self_in; -} - -mp_obj_t py_imagewriter_close(mp_obj_t self_in) -{ - file_close(&((py_imagewriter_obj_t *) self_in)->fp); - return self_in; -} - -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_imagewriter_size_obj, py_imagewriter_size); -STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_imagewriter_add_frame_obj, py_imagewriter_add_frame); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_imagewriter_close_obj, py_imagewriter_close); - -STATIC const mp_rom_map_elem_t py_imagewriter_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&py_imagewriter_size_obj) }, - { MP_ROM_QSTR(MP_QSTR_add_frame), MP_ROM_PTR(&py_imagewriter_add_frame_obj) }, - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&py_imagewriter_close_obj) } -}; - -STATIC MP_DEFINE_CONST_DICT(py_imagewriter_locals_dict, py_imagewriter_locals_dict_table); - -static const mp_obj_type_t py_imagewriter_type = { - { &mp_type_type }, - .name = MP_QSTR_imagewriter, - .print = py_imagewriter_print, - .locals_dict = (mp_obj_t) &py_imagewriter_locals_dict -}; - -mp_obj_t py_image_imagewriter(mp_obj_t path) -{ - py_imagewriter_obj_t *obj = m_new_obj(py_imagewriter_obj_t); - obj->base.type = &py_imagewriter_type; - file_write_open(&obj->fp, mp_obj_str_get_str(path)); - - write_long(&obj->fp, *((uint32_t *) "OMV ")); // OpenMV - write_long(&obj->fp, *((uint32_t *) "IMG ")); // Image - write_long(&obj->fp, *((uint32_t *) "STR ")); // Stream - write_long(&obj->fp, *((uint32_t *) "V1.0")); // v1.0 - - obj->ms = mp_hal_ticks_ms(); - return obj; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_imagewriter_obj, py_image_imagewriter); - -// ImageReader Object // -typedef struct py_imagereader_obj { - mp_obj_base_t base; - FIL fp; - uint32_t ms; -} py_imagereader_obj_t; - -static void py_imagereader_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) -{ - py_imagereader_obj_t *self = self_in; - mp_printf(print, "{\"size\":%d}", f_size(&self->fp)); -} - -mp_obj_t py_imagereader_size(mp_obj_t self_in) -{ - return mp_obj_new_int(f_size(&((py_imagereader_obj_t *) self_in)->fp)); -} - -mp_obj_t py_imagereader_next_frame(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) -{ - // Don't use the file buffer here... - - mp_obj_t copy_to_fb_obj = py_helper_keyword_object(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_copy_to_fb), NULL); - bool copy_to_fb = true; - image_t *arg_other = NULL; - - if (copy_to_fb_obj) { - if (mp_obj_is_integer(copy_to_fb_obj)) { - copy_to_fb = mp_obj_get_int(copy_to_fb_obj); - } else { - arg_other = py_helper_arg_to_image_mutable(copy_to_fb_obj); - } - } - - if (copy_to_fb) { - fb_update_jpeg_buffer(); - } - - FIL *fp = &((py_imagereader_obj_t *) args[0])->fp; - - if (f_eof(fp)) { - if (!py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_loop), true)) { - return mp_const_none; - } - - file_seek(fp, 16); // skip past the header - - if (f_eof(fp)) { // empty file - return mp_const_none; - } - } - - uint32_t ms_tmp; - read_long(fp, &ms_tmp); - - uint32_t ms; // Wait for elapsed ms. - ms = 0; - if (!py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_pause), true)) { - for (ms = mp_hal_ticks_ms(); - ((ms - ((py_imagewriter_obj_t *) args[0])->ms) < ms_tmp); - ms = mp_hal_ticks_ms()) { - __WFI(); - } - } - ((py_imagewriter_obj_t *) args[0])->ms = ms; - - image_t image = {0}; - - read_long(fp, (uint32_t *) &image.w); - read_long(fp, (uint32_t *) &image.h); - read_long(fp, (uint32_t *) &image.bpp); - - uint32_t size = image_size(&image); - - if (copy_to_fb) { - py_helper_set_to_framebuffer(&image); - } else if (arg_other) { - PY_ASSERT_TRUE_MSG((size <= image_size(arg_other)), "The new image won't fit in the target frame buffer!"); - image.data = arg_other->data; - } else { - image.data = xalloc(size); - } - - char ignore[15]; - read_data(fp, image.data, size); - if (size % 16) read_data(fp, ignore, 16 - (size % 16)); // Read in to multiple of 16 bytes. - py_helper_update_framebuffer(&image); - - if (arg_other) { - arg_other->w = image.w; - arg_other->h = image.h; - arg_other->bpp = image.bpp; - } - - return py_image_from_struct(&image); -} - -mp_obj_t py_imagereader_close(mp_obj_t self_in) -{ - file_close(&((py_imagereader_obj_t *) self_in)->fp); - return self_in; -} - -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_imagereader_size_obj, py_imagereader_size); -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_imagereader_next_frame_obj, 1, py_imagereader_next_frame); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_imagereader_close_obj, py_imagereader_close); - -STATIC const mp_rom_map_elem_t py_imagereader_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&py_imagereader_size_obj) }, - { MP_ROM_QSTR(MP_QSTR_next_frame), MP_ROM_PTR(&py_imagereader_next_frame_obj) }, - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&py_imagereader_close_obj) } -}; - -STATIC MP_DEFINE_CONST_DICT(py_imagereader_locals_dict, py_imagereader_locals_dict_table); - -static const mp_obj_type_t py_imagereader_type = { - { &mp_type_type }, - .name = MP_QSTR_imagereader, - .print = py_imagereader_print, - .locals_dict = (mp_obj_t) &py_imagereader_locals_dict -}; - -mp_obj_t py_image_imagereader(mp_obj_t path) -{ - py_imagereader_obj_t *obj = m_new_obj(py_imagereader_obj_t); - obj->base.type = &py_imagereader_type; - file_read_open(&obj->fp, mp_obj_str_get_str(path)); - - read_long_expect(&obj->fp, *((uint32_t *) "OMV ")); // OpenMV - read_long_expect(&obj->fp, *((uint32_t *) "IMG ")); // Image - read_long_expect(&obj->fp, *((uint32_t *) "STR ")); // Stream - read_long_expect(&obj->fp, *((uint32_t *) "V1.0")); // v1.0 - - obj->ms = mp_hal_ticks_ms(); - return obj; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_imagereader_obj, py_image_imagereader); -#endif //IMLIB_ENABLE_IMAGE_FILE_IO - mp_obj_t py_image_binary_to_grayscale(mp_obj_t arg) { int8_t b = mp_obj_get_int(arg) & 1; @@ -7521,12 +7302,10 @@ static const mp_rom_map_elem_t globals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_CODE93), MP_ROM_INT(BARCODE_CODE93)}, {MP_ROM_QSTR(MP_QSTR_CODE128), MP_ROM_INT(BARCODE_CODE128)}, #endif - #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) - {MP_ROM_QSTR(MP_QSTR_ImageWriter), MP_ROM_PTR(&py_image_imagewriter_obj)}, - {MP_ROM_QSTR(MP_QSTR_ImageReader), MP_ROM_PTR(&py_image_imagereader_obj)}, + #if defined(IMLIB_ENABLE_IMAGE_IO) + {MP_ROM_QSTR(MP_QSTR_ImageIO), MP_ROM_PTR(&py_imageio_type) }, #else - {MP_ROM_QSTR(MP_QSTR_ImageWriter), MP_ROM_PTR(&py_func_unavailable_obj)}, - {MP_ROM_QSTR(MP_QSTR_ImageReader), MP_ROM_PTR(&py_func_unavailable_obj)}, + {MP_ROM_QSTR(MP_QSTR_ImageIO), MP_ROM_PTR(&py_func_unavailable_obj)}, #endif {MP_ROM_QSTR(MP_QSTR_binary_to_grayscale), MP_ROM_PTR(&py_image_binary_to_grayscale_obj)}, {MP_ROM_QSTR(MP_QSTR_binary_to_rgb), MP_ROM_PTR(&py_image_binary_to_rgb_obj)}, diff --git a/src/omv/modules/py_imageio.c b/src/omv/modules/py_imageio.c new file mode 100644 index 000000000..f27c08645 --- /dev/null +++ b/src/omv/modules/py_imageio.c @@ -0,0 +1,360 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2019 Ibrahim Abdelkader + * Copyright (c) 2013-2019 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * Image I/O Python module. + */ +#include "imlib_config.h" +#if defined(IMLIB_ENABLE_IMAGE_IO) + +#include +#include "py/obj.h" +#include "py/nlr.h" +#include "py/mphal.h" +#include "py/runtime.h" + +#include "py_assert.h" +#include "py_helper.h" +#include "py_image.h" +#include "py_imageio.h" +#include "fb_alloc.h" +#include "framebuffer.h" +#include "common.h" +#if defined(IMLIB_ENABLE_IMAGE_FILE_IO) +#include "ff_wrapper.h" +#endif +#include "omv_boardconfig.h" + +typedef enum image_io_stream_type { + IMAGE_IO_FILE_STREAM, + IMAGE_IO_MEMORY_STREAM, + IMAGE_IO_FILE_READ = 'r', + IMAGE_IO_FILE_WRITE = 'w', +} image_io_stream_type_t ; + +typedef struct _py_imageio_obj { + mp_obj_base_t base; + bool closed; + union { + #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) + struct{ + FIL fp; + uint32_t ms; + uint32_t mode; + }; + #endif + struct { + uint32_t w; + uint32_t h; + uint32_t bpp; + uint32_t f_size; + uint32_t f_count; + uint32_t offset; + uint8_t *buffer; + }; + }; + image_io_stream_type_t type; +} py_imageio_obj_t; + +static void py_imageio_print(const mp_print_t *print, mp_obj_t self, mp_print_kind_t kind) +{ + py_imageio_obj_t *stream = MP_OBJ_TO_PTR(self); + if (0) { + #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) + } else if (stream->type == IMAGE_IO_FILE_STREAM) { + mp_printf(print, "{\"type\": file stream \"size\":%d}", f_size(&stream->fp)); + #endif + } else if (stream->type == IMAGE_IO_MEMORY_STREAM) { + mp_printf(print, "{\"type\": memory stream \"size\":%d}", stream->f_size * stream->f_count); + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Invalid image stream")); + } +} + +mp_obj_t py_imageio_size(mp_obj_t self) +{ + py_imageio_obj_t *stream = MP_OBJ_TO_PTR(self); + if (0) { + #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) + } else if (stream->type == IMAGE_IO_FILE_STREAM) { + return mp_obj_new_int(f_size(&stream->fp)); + #endif + } else if (stream->type == IMAGE_IO_MEMORY_STREAM) { + return mp_obj_new_int(stream->f_size * stream->f_count); + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Invalid image stream")); + } + return mp_obj_new_int(-1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_imageio_size_obj, py_imageio_size); + +mp_obj_t py_imageio_write(mp_obj_t self, mp_obj_t img_obj) +{ + py_imageio_obj_t *stream = MP_OBJ_TO_PTR(self); + PY_ASSERT_TRUE_MSG((stream->closed == false), "Stream closed"); + image_t *image = py_image_cobj(img_obj); + + if (0) { + #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) + } else if (stream->type == IMAGE_IO_FILE_STREAM) { + PY_ASSERT_TRUE_MSG((stream->mode == IMAGE_IO_FILE_WRITE), "Invalid stream direction"); + + FIL *fp = &stream->fp; + uint32_t ms = mp_hal_ticks_ms(); // Write out elapsed ms. + write_long(fp, ms - stream->ms); + stream->ms = ms; + + write_long(fp, image->w); + write_long(fp, image->h); + write_long(fp, image->bpp); + uint32_t size = image_size(image); + + write_data(fp, image->data, size); + if (size % 16) { + // Pad to multiple of 16 bytes. + write_data(fp, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16 - (size % 16)); + } + #endif + } else if (stream->type == IMAGE_IO_MEMORY_STREAM) { + if (stream->offset == stream->f_count) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("EOF")); + } + uint32_t size = image_size(image); + PY_ASSERT_TRUE_MSG((size == stream->f_size), "Invalid frame size"); + memcpy(stream->buffer + (stream->offset * size), image->data, size); + stream->offset += 1; + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Invalid image stream")); + } + + return self; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_imageio_write_obj, py_imageio_write); + +mp_obj_t py_imageio_read(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + image_t image = {0}; + mp_obj_t copy_to_fb_obj; + py_imageio_obj_t *stream = MP_OBJ_TO_PTR(args[0]); + PY_ASSERT_TRUE_MSG((stream->closed == false), "Stream closed"); + + bool copy_to_fb = true; + image_t *arg_other = NULL; + copy_to_fb_obj = py_helper_keyword_object(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_copy_to_fb), NULL); + + if (copy_to_fb_obj) { + if (mp_obj_is_integer(copy_to_fb_obj)) { + copy_to_fb = mp_obj_get_int(copy_to_fb_obj); + } else { + arg_other = py_helper_arg_to_image_mutable(copy_to_fb_obj); + } + } + + if (copy_to_fb) { + fb_update_jpeg_buffer(); + } + + if (0) { + #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) + } else if (stream->type == IMAGE_IO_FILE_STREAM) { + PY_ASSERT_TRUE_MSG((stream->mode == IMAGE_IO_FILE_READ), "Invalid stream direction"); + + FIL *fp = &stream->fp; + if (f_eof(fp)) { + if (!py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_loop), true)) { + return mp_const_none; + } + + file_seek(fp, 16); // skip past the header + + if (f_eof(fp)) { // empty file + return mp_const_none; + } + } + + uint32_t ms = 0, ms_tmp; + read_long(fp, &ms_tmp); + + if (!py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_pause), true)) { + for (ms = mp_hal_ticks_ms(); + ((ms - stream->ms) < ms_tmp); + ms = mp_hal_ticks_ms()) { + __WFI(); + } + } + stream->ms = ms; + read_long(fp, (uint32_t *) &image.w); + read_long(fp, (uint32_t *) &image.h); + read_long(fp, (uint32_t *) &image.bpp); + uint32_t size = image_size(&image); + + if (copy_to_fb) { + py_helper_set_to_framebuffer(&image); + } else if (arg_other) { + PY_ASSERT_TRUE_MSG((size <= image_size(arg_other)), "The new image won't fit in the target frame buffer!"); + image.data = arg_other->data; + } else { + image.data = xalloc(size); + } + + char ignore[15]; + read_data(fp, image.data, size); + if (size % 16) { + // Read in to multiple of 16 bytes. + read_data(fp, ignore, 16 - (size % 16)); + } + py_helper_update_framebuffer(&image); + + if (arg_other) { + arg_other->w = image.w; + arg_other->h = image.h; + arg_other->bpp = image.bpp; + } + #endif + } else if (stream->type == IMAGE_IO_MEMORY_STREAM) { + if (stream->offset == stream->f_count) { + return mp_const_none; + } + image.w = stream->w; + image.h = stream->h; + image.bpp = stream->bpp; + uint32_t size = stream->f_size; + + if (copy_to_fb) { + py_helper_set_to_framebuffer(&image); + } else if (arg_other) { + PY_ASSERT_TRUE_MSG((size <= image_size(arg_other)), "The new image won't fit in the target frame buffer!"); + image.data = arg_other->data; + } else { + image.data = xalloc(size); + } + memcpy(image.data, stream->buffer + stream->offset * size, size); + stream->offset += 1; + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Invalid image stream")); + } + + return py_image_from_struct(&image); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_imageio_read_obj, 1, py_imageio_read); + +mp_obj_t py_imageio_seek(mp_obj_t self, mp_obj_t offs) +{ + py_imageio_obj_t *stream = MP_OBJ_TO_PTR(self); + PY_ASSERT_TRUE_MSG((stream->closed == false), "Stream closed"); + + if (0) { + #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) + } else if (stream->type == IMAGE_IO_FILE_STREAM) { + // TODO not implemented for file stream. + #endif + } else if (stream->type == IMAGE_IO_MEMORY_STREAM) { + uint32_t offset = mp_obj_get_int(offs); + if (offset < 0 || offset > stream->f_count) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Invalid stream offset")); + } + stream->offset = offset; + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Invalid image stream")); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(py_imageio_seek_obj, py_imageio_seek); + +mp_obj_t py_imageio_close(mp_obj_t self) +{ + py_imageio_obj_t *stream = MP_OBJ_TO_PTR(self); + PY_ASSERT_TRUE_MSG((stream->closed == false), "Stream closed"); + + if (0) { + #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) + } else if (stream->type == IMAGE_IO_FILE_STREAM) { + file_close(&stream->fp); + #endif + } else if (stream->type == IMAGE_IO_MEMORY_STREAM) { + fb_alloc_free_till_mark_past_mark_permanent(); + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Invalid image stream")); + } + stream->closed = true; + return self; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_imageio_close_obj, py_imageio_close); + +mp_obj_t py_imageio_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) +{ + // check arguments + mp_arg_check_num(n_args, n_kw, 2, 2, false); + + py_imageio_obj_t *stream = NULL; + stream = m_new_obj(py_imageio_obj_t); + stream->base.type = &py_imageio_type; + stream->closed = false; + + if (0) { + #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) + } else if (mp_obj_is_str(args[0])) { // File I/O + stream->type = IMAGE_IO_FILE_STREAM; + stream->mode = mp_obj_str_get_str(args[1])[0]; + if (stream->mode == IMAGE_IO_FILE_READ) { + file_read_open(&stream->fp, mp_obj_str_get_str(args[0])); + read_long_expect(&stream->fp, *((uint32_t *) "OMV ")); // OpenMV + read_long_expect(&stream->fp, *((uint32_t *) "IMG ")); // Image + read_long_expect(&stream->fp, *((uint32_t *) "STR ")); // Stream + read_long_expect(&stream->fp, *((uint32_t *) "V1.0")); // v1.0 + } else if (stream->mode == IMAGE_IO_FILE_WRITE) { + file_write_open(&stream->fp, mp_obj_str_get_str(args[0])); + write_long(&stream->fp, *((uint32_t *) "OMV ")); // OpenMV + write_long(&stream->fp, *((uint32_t *) "IMG ")); // Image + write_long(&stream->fp, *((uint32_t *) "STR ")); // Stream + write_long(&stream->fp, *((uint32_t *) "V1.0")); // v1.0 + } else { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid stream mode, expected 'r' or 'w'")); + } + stream->ms = mp_hal_ticks_ms(); + #endif + } else if (mp_obj_is_type(args[0], &mp_type_tuple)) { // Memory Stream I/O + mp_obj_t *image_info; + mp_obj_get_array_fixed_n(args[0], 3, &image_info); + + stream->type = IMAGE_IO_MEMORY_STREAM; + stream->f_count = mp_obj_get_int(args[1]); + stream->w = mp_obj_get_int(image_info[0]); + stream->h = mp_obj_get_int(image_info[1]); + stream->bpp = mp_obj_get_int(image_info[2]); + image_t image = {.w = stream->w, .h = stream->h, .bpp = stream->bpp, .pixels = NULL}; + stream->f_size = image_size(&image); + stream->offset = 0; + + fb_alloc_mark(); + stream->buffer = fb_alloc(stream->f_size * stream->f_count, FB_ALLOC_NO_HINT); + fb_alloc_mark_permanent(); + } else { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid stream type")); + } + + return MP_OBJ_FROM_PTR(stream); +} + +STATIC const mp_rom_map_elem_t py_imageio_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&py_imageio_size_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&py_imageio_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&py_imageio_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&py_imageio_seek_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&py_imageio_close_obj) } +}; +STATIC MP_DEFINE_CONST_DICT(py_imageio_locals_dict, py_imageio_locals_dict_table); + +const mp_obj_type_t py_imageio_type = { + { &mp_type_type }, + .name = MP_QSTR_ImageIO, + .print = py_imageio_print, + .make_new = py_imageio_make_new, + .locals_dict = (mp_obj_dict_t*)&py_imageio_locals_dict, +}; +#endif //IMLIB_ENABLE_IMAGE_IO diff --git a/src/omv/modules/py_imageio.h b/src/omv/modules/py_imageio.h new file mode 100644 index 000000000..423140abf --- /dev/null +++ b/src/omv/modules/py_imageio.h @@ -0,0 +1,14 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2019 Ibrahim Abdelkader + * Copyright (c) 2013-2019 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * ImageIO module. + */ +#ifndef __PY_IMAGE_IO_H__ +#define __PY_IMAGE_IO_H__ +extern const mp_obj_type_t py_imageio_type; +#endif // __PY_IMAGE_IO_H__ diff --git a/src/omv/modules/qstrdefsomv.h b/src/omv/modules/qstrdefsomv.h index 785d8cc22..ce3a36045 100644 --- a/src/omv/modules/qstrdefsomv.h +++ b/src/omv/modules/qstrdefsomv.h @@ -1164,24 +1164,15 @@ Q(displacement) // duplicate Q(scale) Q(response) -// Image Writer -Q(ImageWriter) -// Image Writer Object -Q(imagewriter) -// duplicate Q(size) -// duplicate Q(add_frame) -// duplicate Q(close) - -// Image Reader -Q(ImageReader) -// Image Reader Object -Q(imagereader) -// duplicate Q(size) -Q(next_frame) -// duplicate Q(copy_to_fb) -// duplicate Q(loop) -// duplicate Q(close) +// Image Stream +Q(imageio) +Q(ImageIO) +Q(size) +Q(read) +Q(write) +Q(close) Q(pause) +Q(seek) // LCD Module Q(lcd) diff --git a/src/omv/ports/nrf/omv_portconfig.mk b/src/omv/ports/nrf/omv_portconfig.mk index 9d01cda66..f5984f717 100644 --- a/src/omv/ports/nrf/omv_portconfig.mk +++ b/src/omv/ports/nrf/omv_portconfig.mk @@ -128,6 +128,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/modules/, \ py_gif.o \ py_helper.o \ py_image.o \ + py_imageio.o \ py_mjpeg.o \ py_omv.o \ py_sensor.o \ diff --git a/src/omv/ports/stm32/omv_portconfig.mk b/src/omv/ports/stm32/omv_portconfig.mk index 1a22f268b..07a01ca70 100644 --- a/src/omv/ports/stm32/omv_portconfig.mk +++ b/src/omv/ports/stm32/omv_portconfig.mk @@ -141,6 +141,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/modules/, \ py_gif.o \ py_helper.o \ py_image.o \ + py_imageio.o \ py_mjpeg.o \ py_omv.o \ py_sensor.o \