From f12a4659c87e8cea3236733b7f9bbc2fc4c72c60 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sun, 23 Jan 2022 19:16:07 -0800 Subject: [PATCH] Add PNG Support --- src/omv/Makefile | 1 + src/omv/boards/BORMIO/imlib_config.h | 4 + src/omv/boards/OPENMV3/imlib_config.h | 4 + src/omv/boards/OPENMV4/imlib_config.h | 4 + src/omv/boards/OPENMV4P/imlib_config.h | 4 + src/omv/boards/OPENMVPT/imlib_config.h | 4 + src/omv/boards/PORTENTA/imlib_config.h | 4 + src/omv/imlib/draw.c | 14 +- src/omv/imlib/framebuffer.c | 4 +- src/omv/imlib/imlib.c | 33 ++- src/omv/imlib/imlib.h | 35 ++- src/omv/imlib/jpeg.c | 10 + src/omv/imlib/png.c | 337 +++++++++++++++++++++++++ src/omv/modules/py_image.c | 266 ++++++++----------- src/omv/ports/nrf/omv_portconfig.mk | 1 + src/omv/ports/rp2/omv_portconfig.cmake | 1 + src/omv/ports/stm32/omv_portconfig.mk | 1 + 17 files changed, 559 insertions(+), 168 deletions(-) create mode 100644 src/omv/imlib/png.c diff --git a/src/omv/Makefile b/src/omv/Makefile index e52cd7daf..44c0d1f2c 100644 --- a/src/omv/Makefile +++ b/src/omv/Makefile @@ -73,6 +73,7 @@ SRCS += $(addprefix imlib/, \ jpegd.c \ jpeg.c \ lodepng.c \ + png.c \ kmeans.c \ lab_tab.c \ lbp.c \ diff --git a/src/omv/boards/BORMIO/imlib_config.h b/src/omv/boards/BORMIO/imlib_config.h index ab799f9d0..e94317ce1 100644 --- a/src/omv/boards/BORMIO/imlib_config.h +++ b/src/omv/boards/BORMIO/imlib_config.h @@ -149,4 +149,8 @@ // Enable STM32 DMA2D #define IMLIB_ENABLE_DMA2D +// Enable PNG encoder/decoder +#define IMLIB_ENABLE_PNG_ENCODER +#define IMLIB_ENABLE_PNG_DECODER + #endif //__IMLIB_CONFIG_H__ diff --git a/src/omv/boards/OPENMV3/imlib_config.h b/src/omv/boards/OPENMV3/imlib_config.h index ae7be5272..5deabd0c1 100644 --- a/src/omv/boards/OPENMV3/imlib_config.h +++ b/src/omv/boards/OPENMV3/imlib_config.h @@ -146,4 +146,8 @@ // Enable STM32 DMA2D #define IMLIB_ENABLE_DMA2D +// Enable PNG encoder/decoder +#define IMLIB_ENABLE_PNG_ENCODER +#define IMLIB_ENABLE_PNG_DECODER + #endif //__IMLIB_CONFIG_H__ diff --git a/src/omv/boards/OPENMV4/imlib_config.h b/src/omv/boards/OPENMV4/imlib_config.h index fd395d871..bdbd36541 100644 --- a/src/omv/boards/OPENMV4/imlib_config.h +++ b/src/omv/boards/OPENMV4/imlib_config.h @@ -149,4 +149,8 @@ // Enable STM32 DMA2D #define IMLIB_ENABLE_DMA2D +// Enable PNG encoder/decoder +#define IMLIB_ENABLE_PNG_ENCODER +#define IMLIB_ENABLE_PNG_DECODER + #endif //__IMLIB_CONFIG_H__ diff --git a/src/omv/boards/OPENMV4P/imlib_config.h b/src/omv/boards/OPENMV4P/imlib_config.h index 6b4cd2f48..933373141 100644 --- a/src/omv/boards/OPENMV4P/imlib_config.h +++ b/src/omv/boards/OPENMV4P/imlib_config.h @@ -149,4 +149,8 @@ // Enable STM32 DMA2D #define IMLIB_ENABLE_DMA2D +// Enable PNG encoder/decoder +#define IMLIB_ENABLE_PNG_ENCODER +#define IMLIB_ENABLE_PNG_DECODER + #endif //__IMLIB_CONFIG_H__ diff --git a/src/omv/boards/OPENMVPT/imlib_config.h b/src/omv/boards/OPENMVPT/imlib_config.h index 6b4cd2f48..933373141 100644 --- a/src/omv/boards/OPENMVPT/imlib_config.h +++ b/src/omv/boards/OPENMVPT/imlib_config.h @@ -149,4 +149,8 @@ // Enable STM32 DMA2D #define IMLIB_ENABLE_DMA2D +// Enable PNG encoder/decoder +#define IMLIB_ENABLE_PNG_ENCODER +#define IMLIB_ENABLE_PNG_DECODER + #endif //__IMLIB_CONFIG_H__ diff --git a/src/omv/boards/PORTENTA/imlib_config.h b/src/omv/boards/PORTENTA/imlib_config.h index 60d735dc8..a36cf85e5 100644 --- a/src/omv/boards/PORTENTA/imlib_config.h +++ b/src/omv/boards/PORTENTA/imlib_config.h @@ -149,4 +149,8 @@ // Enable STM32 DMA2D #define IMLIB_ENABLE_DMA2D +// Enable PNG encoder/decoder +#define IMLIB_ENABLE_PNG_ENCODER +#define IMLIB_ENABLE_PNG_DECODER + #endif //__IMLIB_CONFIG_H__ diff --git a/src/omv/imlib/draw.c b/src/omv/imlib/draw.c index 4a49c360d..e4bd891ae 100644 --- a/src/omv/imlib/draw.c +++ b/src/omv/imlib/draw.c @@ -2695,7 +2695,8 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d } // Special destination? - bool is_jpeg = (src_img->pixfmt == PIXFORMAT_JPEG); + bool is_jpeg = src_img->pixfmt == PIXFORMAT_JPEG; + bool is_png = src_img->pixfmt == PIXFORMAT_PNG; // Best format to convert yuv/bayer/jpeg image to. int new_not_mutable_pixfmt = (rgb_channel != -1) ? PIXFORMAT_RGB565 : (color_palette ? PIXFORMAT_GRAYSCALE : @@ -2726,7 +2727,7 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d bool is_color_conversion_scaling = is_color_conversion && is_scaling; // Make a deep copy of the source image. - if (need_deep_copy || is_color_conversion_scaling || is_jpeg) { + if (need_deep_copy || is_color_conversion_scaling || is_jpeg || is_png) { new_src_img.w = src_img->w; // same width as source image new_src_img.h = src_img->h; // same height as source image @@ -2743,6 +2744,8 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d imlib_deyuv_image(&new_src_img, src_img); } else if (is_jpeg) { jpeg_decompress_image_to_binary(&new_src_img, src_img); + } else if (is_png) { + png_decompress(&new_src_img, src_img); } break; } @@ -2753,6 +2756,8 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d imlib_deyuv_image(&new_src_img, src_img); } else if (is_jpeg) { jpeg_decompress_image_to_grayscale(&new_src_img, src_img); + } else if (is_png) { + png_decompress(&new_src_img, src_img); } break; } @@ -2763,6 +2768,8 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d imlib_deyuv_image(&new_src_img, src_img); } else if (is_jpeg) { jpeg_decompress_image_to_rgb565(&new_src_img, src_img); + } else if (is_png) { + png_decompress(&new_src_img, src_img); } break; } @@ -2772,6 +2779,9 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d break; } default : { + if (is_png) { + png_decompress(&new_src_img, src_img); + } break; } } diff --git a/src/omv/imlib/framebuffer.c b/src/omv/imlib/framebuffer.c index 7c5c15714..d16841c66 100644 --- a/src/omv/imlib/framebuffer.c +++ b/src/omv/imlib/framebuffer.c @@ -138,7 +138,7 @@ void framebuffer_update_jpeg_buffer() if (src->pixfmt != PIXFORMAT_INVALID && framebuffer->streaming_enabled && jpeg_framebuffer->enabled) { - if (src->pixfmt == PIXFORMAT_JPEG) { + if (src->is_compressed) { bool does_not_fit = false; if (mutex_try_lock_alternate(&jpeg_framebuffer->lock, MUTEX_TID_OMV)) { @@ -154,7 +154,7 @@ void framebuffer_update_jpeg_buffer() } if (does_not_fit) { - printf("Warning: JPEG too big! Trying framebuffer transfer using fallback method!\n"); + printf("Warning: JPEG/PNG too big! Trying framebuffer transfer using fallback method!\n"); int new_size = fb_encode_for_ide_new_size(src); fb_alloc_mark(); uint8_t *temp = fb_alloc(new_size, FB_ALLOC_NO_HINT); diff --git a/src/omv/imlib/imlib.c b/src/omv/imlib/imlib.c index f3dcdb204..61d1915e0 100644 --- a/src/omv/imlib/imlib.c +++ b/src/omv/imlib/imlib.c @@ -268,7 +268,7 @@ size_t image_size(image_t *ptr) case PIXFORMAT_YUV_ANY: { // re-use return IMAGE_RGB565_LINE_LEN_BYTES(ptr) * ptr->h; } - case PIXFORMAT_JPEG: { + case PIXFORMAT_COMPRESSED_ANY: { return ptr->size; } default: { @@ -506,6 +506,12 @@ static save_image_format_t imblib_parse_extension(image_t *img, const char *path && ((p[-4] == '.') || (p[-4] == '.'))) { // Will convert to JPG if not. return FORMAT_JPG; + } else if (((p[-1] == 'g') || (p[-1] == 'G')) + && ((p[-2] == 'n') || (p[-2] == 'N')) + && ((p[-3] == 'p') || (p[-3] == 'P')) + && ((p[-4] == '.') || (p[-4] == '.'))) { + // Will convert to PNG if not. + return FORMAT_PNG; } else if (((p[-1] == 'p') || (p[-1] == 'P')) && ((p[-2] == 'm') || (p[-2] == 'M')) && ((p[-3] == 'b') || (p[-3] == 'B')) @@ -547,8 +553,8 @@ static save_image_format_t imblib_parse_extension(image_t *img, const char *path bool imlib_read_geometry(FIL *fp, image_t *img, const char *path, img_read_settings_t *rs) { file_read_open(fp, path); - char magic[2]; - read_data(fp, &magic, 2); + char magic[4]; + read_data(fp, &magic, 4); file_close(fp); bool vflipped = false; @@ -570,6 +576,12 @@ bool imlib_read_geometry(FIL *fp, image_t *img, const char *path, img_read_setti // Do not use file_buffer_on() here. jpeg_read_geometry(fp, img, path, &rs->jpg_rs); file_buffer_on(fp); // REMEMBER TO TURN THIS OFF LATER! + } else if ((magic[0]==0x89) && (magic[1]==0x50) && (magic[2]==0x4E) && (magic[3]==0x47)) { // PNG + rs->format = FORMAT_PNG; + file_read_open(fp, path); + // Do not use file_buffer_on() here. + png_read_geometry(fp, img, path, &rs->png_rs); + file_buffer_on(fp); // REMEMBER TO TURN THIS OFF LATER! } else { ff_unsupported_format(NULL); } @@ -721,8 +733,8 @@ void imlib_load_image(image_t *img, const char *path) { FIL fp; file_read_open(&fp, path); - char magic[2]; - read_data(&fp, &magic, 2); + char magic[4]; + read_data(&fp, &magic, 4); file_close(&fp); if ((magic[0]=='P') @@ -733,6 +745,8 @@ void imlib_load_image(image_t *img, const char *path) bmp_read(img, path); } else if ((magic[0]==0xFF) && (magic[1]==0xD8)) { // JPEG jpeg_read(img, path); + } else if ((magic[0]==0x89) && (magic[1]==0x50) && (magic[2]==0x4E) && (magic[3]==0x47)) { // PNG + png_read(img, path); } else { ff_unsupported_format(NULL); } @@ -758,13 +772,20 @@ void imlib_save_image(image_t *img, const char *path, rectangle_t *roi, int qual case FORMAT_JPG: jpeg_write(img, path, quality); break; + case FORMAT_PNG: + png_write(img, path); + break; case FORMAT_DONT_CARE: // Path doesn't have an extension. if (IM_IS_JPEG(img)) { char *new_path = strcat(strcpy(fb_alloc(strlen(path)+5, FB_ALLOC_NO_HINT), path), ".jpg"); jpeg_write(img, new_path, quality); fb_free(); - } else if (IM_IS_BAYER(img)) { + } else if (img->pixfmt == PIXFORMAT_PNG) { + char *new_path = strcat(strcpy(fb_alloc(strlen(path)+5, FB_ALLOC_NO_HINT), path), ".png"); + png_write(img, new_path); + fb_free(); + }else if (IM_IS_BAYER(img)) { FIL fp; char *new_path = strcat(strcpy(fb_alloc(strlen(path)+5, FB_ALLOC_NO_HINT), path), ".raw"); file_write_open(&fp, new_path); diff --git a/src/omv/imlib/imlib.h b/src/omv/imlib/imlib.h index d57d4c1b7..8a54cb6c3 100644 --- a/src/omv/imlib/imlib.h +++ b/src/omv/imlib/imlib.h @@ -344,6 +344,8 @@ typedef enum { PIXFORMAT_ID_BAYER = 4, PIXFORMAT_ID_YUV422 = 5, PIXFORMAT_ID_JPEG = 6, + PIXFORMAT_ID_PNG = 7, + PIXFORMAT_ID_ARGB8 = 8, /* Note: Update PIXFORMAT_IS_VALID when adding new formats */ } pixformat_id_t; @@ -368,6 +370,7 @@ typedef enum { PIXFORMAT_BPP_RGB565 = 2, PIXFORMAT_BPP_BAYER = 1, PIXFORMAT_BPP_YUV422 = 2, + PIXFORMAT_BPP_ARGB8 = 4, /* Note: Update PIXFORMAT_IS_VALID when adding new formats */ } pixformat_bpp_t; @@ -388,25 +391,29 @@ typedef enum { // YF MF CF JF RF // NOTE: Bit 31-30 must Not be used for pixformat_t to be used as mp_int_t. typedef enum { - PIXFORMAT_INVALID = (0), + PIXFORMAT_INVALID = (0x00000000U), PIXFORMAT_BINARY = (PIXFORMAT_FLAGS_M | (PIXFORMAT_ID_BINARY << 16) | (0 << 8) | PIXFORMAT_BPP_BINARY ), PIXFORMAT_GRAYSCALE = (PIXFORMAT_FLAGS_M | (PIXFORMAT_ID_GRAY << 16) | (SUBFORMAT_ID_GRAY8 << 8) | PIXFORMAT_BPP_GRAY8 ), PIXFORMAT_RGB565 = (PIXFORMAT_FLAGS_CM | (PIXFORMAT_ID_RGB565 << 16) | (0 << 8) | PIXFORMAT_BPP_RGB565 ), + PIXFORMAT_ARGB8 = (PIXFORMAT_FLAGS_CM | (PIXFORMAT_ID_ARGB8 << 16) | (0 << 8) | PIXFORMAT_BPP_ARGB8 ), PIXFORMAT_BAYER = (PIXFORMAT_FLAGS_CR | (PIXFORMAT_ID_BAYER << 16) | (SUBFORMAT_ID_BGGR << 8) | PIXFORMAT_BPP_BAYER ), PIXFORMAT_BAYER_BGGR = (PIXFORMAT_FLAGS_CR | (PIXFORMAT_ID_BAYER << 16) | (SUBFORMAT_ID_BGGR << 8) | PIXFORMAT_BPP_BAYER ), PIXFORMAT_BAYER_GBRG = (PIXFORMAT_FLAGS_CR | (PIXFORMAT_ID_BAYER << 16) | (SUBFORMAT_ID_GBRG << 8) | PIXFORMAT_BPP_BAYER ), PIXFORMAT_BAYER_GRBG = (PIXFORMAT_FLAGS_CR | (PIXFORMAT_ID_BAYER << 16) | (SUBFORMAT_ID_GRBG << 8) | PIXFORMAT_BPP_BAYER ), PIXFORMAT_BAYER_RGGB = (PIXFORMAT_FLAGS_CR | (PIXFORMAT_ID_BAYER << 16) | (SUBFORMAT_ID_RGGB << 8) | PIXFORMAT_BPP_BAYER ), + PIXFORMAT_YUV = (PIXFORMAT_FLAGS_CY | (PIXFORMAT_ID_YUV422 << 16) | (SUBFORMAT_ID_YUV422 << 8) | PIXFORMAT_BPP_YUV422 ), PIXFORMAT_YUV422 = (PIXFORMAT_FLAGS_CY | (PIXFORMAT_ID_YUV422 << 16) | (SUBFORMAT_ID_YUV422 << 8) | PIXFORMAT_BPP_YUV422 ), PIXFORMAT_YVU422 = (PIXFORMAT_FLAGS_CY | (PIXFORMAT_ID_YUV422 << 16) | (SUBFORMAT_ID_YVU422 << 8) | PIXFORMAT_BPP_YUV422 ), PIXFORMAT_JPEG = (PIXFORMAT_FLAGS_CJ | (PIXFORMAT_ID_JPEG << 16) | (0 << 8) | 0 ), - PIXFORMAT_LAST = 0xFFFFFFFFU, + PIXFORMAT_PNG = (PIXFORMAT_FLAGS_CJ | (PIXFORMAT_ID_PNG << 16) | (0 << 8) | 0 ), + PIXFORMAT_LAST = (0xFFFFFFFFU), } pixformat_t; #define PIXFORMAT_MUTABLE_ANY \ PIXFORMAT_BINARY: \ case PIXFORMAT_GRAYSCALE: \ - case PIXFORMAT_RGB565 \ + case PIXFORMAT_RGB565: \ + case PIXFORMAT_ARGB8 \ #define PIXFORMAT_BAYER_ANY \ PIXFORMAT_BAYER_BGGR: \ @@ -418,17 +425,23 @@ typedef enum { PIXFORMAT_YUV422: \ case PIXFORMAT_YVU422 \ +#define PIXFORMAT_COMPRESSED_ANY \ + PIXFORMAT_JPEG: \ + case PIXFORMAT_PNG \ + #define IMLIB_PIXFORMAT_IS_VALID(x) \ ((x == PIXFORMAT_BINARY) \ || (x == PIXFORMAT_GRAYSCALE) \ || (x == PIXFORMAT_RGB565) \ + || (x == PIXFORMAT_ARGB8) \ || (x == PIXFORMAT_BAYER_BGGR) \ || (x == PIXFORMAT_BAYER_GBRG) \ || (x == PIXFORMAT_BAYER_GRBG) \ || (x == PIXFORMAT_BAYER_RGGB) \ || (x == PIXFORMAT_YUV422) \ || (x == PIXFORMAT_YVU422) \ - || (x == PIXFORMAT_JPEG)) \ + || (x == PIXFORMAT_JPEG) \ + || (x == PIXFORMAT_PNG)) \ #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) #define PIXFORMAT_STRUCT \ @@ -864,11 +877,18 @@ typedef struct jpg_read_settings { int32_t jpg_size; } jpg_read_settings_t; +typedef struct png_read_settings { + int32_t png_w; + int32_t png_h; + int32_t png_size; +} png_read_settings_t; + typedef enum save_image_format { FORMAT_DONT_CARE, FORMAT_BMP, FORMAT_PNM, FORMAT_JPG, + FORMAT_PNG, FORMAT_RAW, } save_image_format_t; @@ -877,6 +897,7 @@ typedef struct img_read_settings { bmp_read_settings_t bmp_rs; ppm_read_settings_t ppm_rs; jpg_read_settings_t jpg_rs; + png_read_settings_t png_rs; }; save_image_format_t format; } img_read_settings_t; @@ -1113,6 +1134,12 @@ void jpeg_read_geometry(FIL *fp, image_t *img, const char *path, jpg_read_settin void jpeg_read_pixels(FIL *fp, image_t *img); void jpeg_read(image_t *img, const char *path); void jpeg_write(image_t *img, const char *path, int quality); +void png_decompress(image_t *dst, image_t *src); +bool png_compress(image_t *src, image_t *dst); +void png_read_geometry(FIL *fp, image_t *img, const char *path, png_read_settings_t *rs); +void png_read_pixels(FIL *fp, image_t *img); +void png_read(image_t *img, const char *path); +void png_write(image_t *img, const char *path); bool imlib_read_geometry(FIL *fp, image_t *img, const char *path, img_read_settings_t *rs); void imlib_image_operation(image_t *img, const char *path, image_t *other, int scalar, line_op_t op, void *data); void imlib_load_image(image_t *img, const char *path); diff --git a/src/omv/imlib/jpeg.c b/src/omv/imlib/jpeg.c index 77327e560..c806a7766 100644 --- a/src/omv/imlib/jpeg.c +++ b/src/omv/imlib/jpeg.c @@ -1064,6 +1064,8 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) JPEG_Info.ColorSpace = JPEG_YCBCR_COLORSPACE; JPEG_Info.ChromaSubsampling = JPEG_444_SUBSAMPLING; break; + default: + break; } if (memcmp(&JPEG_Config, &JPEG_Info, sizeof(JPEG_ConfTypeDef))) { @@ -1089,6 +1091,10 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) dst->data = fb_alloc(dst->size, FB_ALLOC_PREFER_SIZE | FB_ALLOC_CACHE_ALIGN); } + if (src->is_compressed) { + return true; + } + // Compute size of the APP0 header with cache alignment padding. int app0_size = sizeof(JPEG_APP0); int app0_unalign_size = app0_size % __SCB_DCACHE_LINE_SIZE; @@ -1840,6 +1846,10 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) dst->size = IMLIB_IMAGE_MAX_SIZE(size); } + if (src->is_compressed) { + return true; + } + // JPEG buffer jpeg_buf_t jpeg_buf = { .idx = 0, diff --git a/src/omv/imlib/png.c b/src/omv/imlib/png.c new file mode 100644 index 000000000..c5176011e --- /dev/null +++ b/src/omv/imlib/png.c @@ -0,0 +1,337 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * PNG CODEC + */ +#include +#include "imlib.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "ff_wrapper.h" +#if defined(IMLIB_ENABLE_PNG_ENCODER) || defined(IMLIB_ENABLE_PNG_DECODER) +#include "lodepng.h" +#include "umm_malloc.h" + +#define TIME_PNG (0) + +void* lodepng_malloc(size_t size) +{ + return umm_malloc(size); +} + +void* lodepng_realloc(void* ptr, size_t new_size) +{ + return umm_realloc(ptr, new_size); +} + +void lodepng_free(void* ptr) +{ + return umm_free(ptr); +} + +unsigned lodepng_convert_cb(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h) +{ + unsigned error = 0; + unsigned numpixels = w * h; + + if (mode_in->colortype == LCT_CUSTOM) { + // Compression. + // Note: we're always encoding to 8 bits. + switch (mode_in->customfmt) { + case PIXFORMAT_RGB565: { + uint16_t *pixels = (uint16_t*) in; + if (mode_out->colortype == LCT_RGB) { + // RGB565 -> RGB888 + for (int i=0; i < numpixels; i++, out += 3) { + out[0] = COLOR_RGB565_TO_R8(pixels[i]); + out[1] = COLOR_RGB565_TO_G8(pixels[i]); + out[2] = COLOR_RGB565_TO_B8(pixels[i]); + } + } else if (mode_out->colortype == LCT_RGBA) { + // RGB565 -> RGBA888 + for (int i=0; i < numpixels; i++, out += 4) { + out[0] = COLOR_RGB565_TO_R8(pixels[i]); + out[1] = COLOR_RGB565_TO_G8(pixels[i]); + out[2] = COLOR_RGB565_TO_B8(pixels[i]); + out[3] = 255; + } + } else { + error = 56; // unsupported color mode conversion. + } + break; + } + case PIXFORMAT_YUV_ANY: + // YUV -> RGB888 + case PIXFORMAT_BAYER_ANY: + // BAYER -> RGB888 + default: + error = 56; // unsupported color mode conversion. + break; + } + } else if (mode_out->colortype == LCT_CUSTOM) { + // Decompression. + // NOTE: decode from 16 bits needs to be implemented. + switch (mode_out->customfmt) { + case PIXFORMAT_RGB565: { + uint16_t *pixels = (uint16_t*) out; + if (mode_in->colortype == LCT_RGB) { + // RGB888 -> RGB565 + for (int i=0; i < numpixels; i++, in += 3) { + pixels[i] = COLOR_R8_G8_B8_TO_RGB565(in[0], in[1], in[2]); + } + } else if (mode_in->colortype == LCT_RGBA) { + // RGBA888 -> RGB565 + for (int i=0; i < numpixels; i++, in += 4) { + pixels[i] = COLOR_R8_G8_B8_TO_RGB565(in[0], in[1], in[2]); + } + } else if (mode_in->colortype == LCT_GREY && mode_in->bitdepth == 8) { + // GRAYSCALE -> RGB565 + for (int i=0; i < numpixels; i++, in ++) { + pixels[i] = COLOR_R8_G8_B8_TO_RGB565(in[0], in[0], in[0]); + } + } else { + error = 56; // unsupported color mode conversion. + } + break; + } + default: + error = 56; // unsupported color mode conversion. + break; + } + + } else { + error = 56; // unsupported color mode conversion. + } + return error; +} + +#if defined(IMLIB_ENABLE_PNG_ENCODER) +bool png_compress(image_t *src, image_t *dst) +{ + #if (TIME_PNG==1) + mp_uint_t start = mp_hal_ticks_ms(); + #endif + + if (src->is_compressed) { + return true; + } + + umm_init_x(fb_avail()); + + LodePNGState state; + lodepng_state_init(&state); + // Invoked on custom formats. + state.lodepng_convert = &lodepng_convert_cb; + // Faster compression. + state.encoder.zlibsettings.windowsize = 1024; + + switch (src->pixfmt) { + case PIXFORMAT_BINARY: + state.info_raw.bitdepth = 1; + state.info_raw.colortype = LCT_GREY; + + state.encoder.auto_convert = false; + state.info_png.color.bitdepth = 8; + state.info_png.color.colortype = LCT_GREY; + break; + case PIXFORMAT_GRAYSCALE: + state.info_raw.bitdepth = 8; + state.info_raw.colortype = LCT_GREY; + + state.encoder.auto_convert = false; + state.info_png.color.bitdepth = 8; + state.info_png.color.colortype = LCT_GREY; + break; + case PIXFORMAT_RGB565: + state.info_raw.bitdepth = 16; + state.info_raw.colortype = LCT_CUSTOM; + state.info_raw.customfmt = PIXFORMAT_RGB565; + + state.encoder.auto_convert = false; + state.info_png.color.bitdepth = 8; + state.info_png.color.colortype = LCT_RGB; + break; + case PIXFORMAT_YUV_ANY: + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Input format is not supported")); + break; + case PIXFORMAT_BAYER_ANY: + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Input format is not supported")); + break; + } + + size_t png_size = 0; + uint8_t *png_data = NULL; + unsigned error = lodepng_encode(&png_data, &png_size, src->data, src->w, src->h, &state); + lodepng_state_cleanup(&state); + if (error) { + mp_raise_msg(&mp_type_RuntimeError, (mp_rom_error_text_t) lodepng_error_text(error)); + } + + if (dst->data == NULL) { + dst->data = png_data; + dst->size = png_size; + // fb_alloc() memory ill be free'd by called. + } else { + if (image_size(dst) <= png_size) { + dst->size = png_size; + memcpy(dst->data, png_data, png_size); + } else { + mp_raise_msg_varg(&mp_type_RuntimeError, + MP_ERROR_TEXT("Failed to compress image in place")); + } + // free fb_alloc() memory used for umm_init_x(). + fb_free(); // umm_init_x(); + } + + #if (TIME_PNG==1) + printf("time: %u ms\n", mp_hal_ticks_ms() - start); + #endif + + return false; +} +#endif // IMLIB_ENABLE_PNG_ENCODER + +#if defined(IMLIB_ENABLE_PNG_DECODER) +void png_decompress(image_t *dst, image_t *src) +{ + #if (TIME_PNG==1) + mp_uint_t start = mp_hal_ticks_ms(); + #endif + + umm_init_x(fb_avail()); + + LodePNGState state; + lodepng_state_init(&state); + // Invoked on custom formats. + state.lodepng_convert = &lodepng_convert_cb; + + switch (dst->pixfmt) { + case PIXFORMAT_BINARY: + state.info_raw.bitdepth = 1; + state.info_raw.colortype = LCT_GREY; + break; + case PIXFORMAT_GRAYSCALE: + state.info_raw.bitdepth = 8; + state.info_raw.colortype = LCT_GREY; + break; + case PIXFORMAT_RGB565: + state.info_raw.bitdepth = 16; + state.info_raw.colortype = LCT_CUSTOM; + state.info_raw.customfmt = PIXFORMAT_RGB565; + break; + } + + uint8_t *png_data = NULL; + uint32_t img_size = image_size(dst); + unsigned error = lodepng_decode(&png_data, (unsigned *) &dst->w, (unsigned *) &dst->h, &state, src->data, src->size); + lodepng_state_cleanup(&state); + if (error) { + mp_raise_msg(&mp_type_RuntimeError, (mp_rom_error_text_t) lodepng_error_text(error)); + } + + uint32_t new_img_size = image_size(dst); + if (new_img_size <= img_size) { + memcpy(dst->data, png_data, new_img_size); + } else { + mp_raise_msg_varg(&mp_type_RuntimeError, + MP_ERROR_TEXT("Failed to compress image in place")); + } + + // free fb_alloc() memory used for umm_init_x(). + fb_free(); // umm_init_x(); + + #if (TIME_PNG==1) + printf("time: %u ms\n", mp_hal_ticks_ms() - start); + #endif +} +#endif // IMLIB_ENABLE_PNG_DECODER +#endif // IMLIB_ENABLE_PNG_ENCODER || IMLIB_ENABLE_PNG_DECODER + + +#if !defined(IMLIB_ENABLE_PNG_ENCODER) +bool png_compress(image_t *src, image_t *dst) +{ + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("PNG encoder is not enabled")); +} +#endif + +#if !defined(IMLIB_ENABLE_PNG_DECODER) +void png_decompress(image_t *dst, image_t *src) +{ + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("PNG decoder is not enabled")); +} +#endif + +#if defined(IMLIB_ENABLE_IMAGE_FILE_IO) +// This function inits the geometry values of an image. +void png_read_geometry(FIL *fp, image_t *img, const char *path, png_read_settings_t *rs) +{ + uint32_t header; + file_seek(fp, 12); // start of IHDR + read_long(fp, &header); + if (header == 0x52444849) { // IHDR + uint32_t width, height; + read_long(fp, &width); + read_long(fp, &height); + width = __builtin_bswap32(width); + height = __builtin_bswap32(height); + + rs->png_w = width; + rs->png_h = height; + rs->png_size = IMLIB_IMAGE_MAX_SIZE(f_size(fp)); + + img->w = rs->png_w; + img->h = rs->png_h; + img->size = rs->png_size; + img->pixfmt = PIXFORMAT_PNG; + } else { + ff_file_corrupted(fp); + } +} + +// This function reads the pixel values of an image. +void png_read_pixels(FIL *fp, image_t *img) +{ + file_seek(fp, 0); + read_data(fp, img->pixels, img->size); +} + +void png_read(image_t *img, const char *path) +{ + FIL fp; + png_read_settings_t rs; + + file_read_open(&fp, path); + + // Do not use file_buffer_on() here. + png_read_geometry(&fp, img, path, &rs); + + if (!img->pixels) { + img->pixels = xalloc(img->size); + } + + png_read_pixels(&fp, img); + file_close(&fp); +} + +void png_write(image_t *img, const char *path) +{ + FIL fp; + file_write_open(&fp, path); + if (img->pixfmt == PIXFORMAT_PNG) { + write_data(&fp, img->pixels, img->size); + } else { + image_t out = { .w=img->w, .h=img->h, .pixfmt=PIXFORMAT_PNG, .size=0, .pixels=NULL }; // alloc in png compress + png_compress(img, &out); + write_data(&fp, out.pixels, out.size); + fb_free(); // frees alloc in png_compress() + } + file_close(&fp); +} +#endif //IMLIB_ENABLE_IMAGE_FILE_IO) diff --git a/src/omv/modules/py_image.c b/src/omv/modules/py_image.c index a32c191c3..30319b344 100644 --- a/src/omv/modules/py_image.c +++ b/src/omv/modules/py_image.c @@ -280,8 +280,8 @@ mp_obj_t py_image_unary_op(mp_unary_op_t op, mp_obj_t self_in) { switch (op) { case MP_UNARY_OP_LEN: { image_t *img = &self->_cobj; - if (img->pixfmt == PIXFORMAT_JPEG) { - // For JPEG images we create a 1D array. + if (img->is_compressed) { + // For JPEG/PNG images we create a 1D array. return mp_obj_new_int(img->size); } else { // For other formats, 2D array is created. @@ -338,7 +338,7 @@ STATIC mp_obj_t py_image_it_iternext(mp_obj_t self_in) return row; } } - default: {// JPEG + default: { // JPEG/PNG if (self->cur >= img->size) { return MP_OBJ_STOP_ITERATION; } else { @@ -362,7 +362,7 @@ STATIC mp_obj_t py_image_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) static void py_image_print(const mp_print_t *print, mp_obj_t self, mp_print_kind_t kind) { image_t *image = py_image_cobj(self); - if (image->pixfmt == PIXFORMAT_JPEG + if (image->is_compressed && image->pixels[0] == 0xFE && image->pixels[image->size-1] == 0xFE) { // print for ide. @@ -380,7 +380,8 @@ static void py_image_print(const mp_print_t *print, mp_obj_t self, mp_print_kind (image->pixfmt == PIXFORMAT_BAYER_RGGB) ? "bayer_rggb" : (image->pixfmt == PIXFORMAT_YUV422) ? "yuv422" : (image->pixfmt == PIXFORMAT_YVU422) ? "yvu422" : - (image->pixfmt == PIXFORMAT_JPEG) ? "jpeg" : "unknown", + (image->pixfmt == PIXFORMAT_JPEG) ? "jpeg" : + (image->pixfmt == PIXFORMAT_PNG) ? "png" : "unknown", image_size(image)); } } @@ -457,7 +458,8 @@ static mp_obj_t py_image_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value mp_obj_new_int(COLOR_RGB565_TO_B8(p))}); } } - case PIXFORMAT_JPEG: { + case PIXFORMAT_JPEG: + case PIXFORMAT_PNG: { if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(image->size, index, &slice)) { @@ -587,7 +589,8 @@ static mp_obj_t py_image_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value IMAGE_PUT_RGB565_PIXEL(image, i % image->w, i / image->w, p); return mp_const_none; } - case PIXFORMAT_JPEG: { + case PIXFORMAT_JPEG: + case PIXFORMAT_PNG: { if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(image->size, index, &slice)) { @@ -666,8 +669,12 @@ static mp_obj_t py_image_format(mp_obj_t img_obj) return mp_obj_new_int(PIXFORMAT_BAYER); case PIXFORMAT_YUV_ANY: return mp_obj_new_int(PIXFORMAT_YUV422); - default: + case PIXFORMAT_JPEG: return mp_obj_new_int(PIXFORMAT_JPEG); + case PIXFORMAT_PNG: + return mp_obj_new_int(PIXFORMAT_PNG); + default: + return mp_obj_new_int(PIXFORMAT_INVALID); } } STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_format_obj, py_image_format); @@ -908,7 +915,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_midpoint_pooled_obj, 3, py_image_midp #endif // IMLIB_ENABLE_MIDPOINT_POOLING static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_palette, bool copy_to_fb, - uint n_args, const mp_obj_t *args, mp_map_t *kw_args) + mp_obj_t copy_default, bool encode_for_ide_default, uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { image_t *src_img = py_image_cobj(args[0]); @@ -967,15 +974,22 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_pa } mp_obj_t copy_obj = py_helper_keyword_object(n_args, args, 11, kw_args, - MP_OBJ_NEW_QSTR(copy_to_fb ? MP_QSTR_copy_to_fb : MP_QSTR_copy), NULL); + MP_OBJ_NEW_QSTR(copy_to_fb ? MP_QSTR_copy_to_fb : MP_QSTR_copy), copy_default); bool copy = false; image_t *arg_other = copy_to_fb ? NULL : src_img; + int arg_q = py_helper_keyword_int(n_args, args, 12, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_quality), 90); + if ((arg_q < 1) || (100 < arg_q)) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("1 <= quality <= 100!")); + } + + bool arg_e = py_helper_keyword_int(n_args, args, 13, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_encode_for_ide), encode_for_ide_default); + if (copy_obj) { if (mp_obj_is_integer(copy_obj)) { copy = mp_obj_get_int(copy_obj); } else { - arg_other = py_helper_arg_to_image_not_compressed(copy_obj); + arg_other = py_image_cobj(copy_obj); } } @@ -986,11 +1000,13 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_pa image_t dst_img = { .w = fast_floorf(arg_roi.w * arg_x_scale), .h = fast_floorf(arg_roi.h * arg_y_scale), - .size = src_img->size, .pixfmt = (pixfmt == PIXFORMAT_INVALID) ? src_img->pixfmt : pixfmt, + .size = src_img->size, .pixels = NULL, }; + image_t dst_img_tmp = dst_img; + if (dst_img.is_bayer) { if (((arg_x_scale != 1) && (arg_x_scale != -1)) || ((arg_y_scale != 1) && (arg_y_scale != -1)) || @@ -1071,18 +1087,50 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_pa IMAGE_HINT_EXTRACT_RGB_CHANNEL_FIRST | IMAGE_HINT_APPLY_COLOR_PALETTE_FIRST); } - } else if (dst_img.pixfmt == PIXFORMAT_JPEG) { - if ((arg_x_scale != 1) || - (arg_y_scale != 1) || - (arg_roi.x != 0) || - (arg_roi.y != 0) || - (arg_roi.w != src_img->w) || - (arg_roi.h != src_img->h) || - (arg_rgb_channel != -1) || - (arg_alpha != 256) || - (color_palette != NULL) || - (alpha_palette != NULL)) { - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Only jpeg copying is supported!")); + } else if (dst_img.is_compressed) { + fb_alloc_mark(); + + bool simple = (arg_x_scale == 1) && + (arg_y_scale == 1) && + (arg_roi.x == 0) && + (arg_roi.y == 0) && + (arg_roi.w == src_img->w) && + (arg_roi.h == src_img->h) && + (arg_rgb_channel == -1) && + (arg_alpha == 256) && + (color_palette == NULL) && + (alpha_palette == NULL); + + if ((dst_img.pixfmt != src_img->pixfmt) || (!simple)) { + image_t temp; + memcpy(&temp, src_img, sizeof(image_t)); + + if (src_img->is_compressed || (!simple)) { + temp.w = dst_img.w; + temp.h = dst_img.h; + temp.pixfmt = PIXFORMAT_RGB565; // TODO PIXFORMAT_ARGB8888 + temp.size = 0; + temp.data = fb_alloc(image_size(&temp), FB_ALLOC_NO_HINT); + imlib_draw_image(&temp, src_img, 0, 0, arg_x_scale, arg_y_scale, &arg_roi, + arg_rgb_channel, arg_alpha, color_palette, alpha_palette, + (hint & (~IMAGE_HINT_CENTER)) | IMAGE_HINT_BLACK_BACKGROUND, NULL, NULL); + } + + if (((dst_img.pixfmt == PIXFORMAT_JPEG) && jpeg_compress(&temp, &dst_img_tmp, arg_q, false)) + || ((dst_img.pixfmt == PIXFORMAT_PNG) && png_compress(&temp, &dst_img_tmp))) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Compression Failed!")); + } + } else if (arg_e) { + dst_img_tmp.data = fb_alloc(image_size(&dst_img_tmp), FB_ALLOC_NO_HINT); + memcpy(dst_img_tmp.data, src_img->data, dst_img_tmp.size); + } else { + dst_img_tmp.data = src_img->data; + } + + if (arg_e) { + dst_img.size = fb_encode_for_ide_new_size(&dst_img_tmp); + } else { + dst_img.size = dst_img_tmp.size; } } @@ -1106,10 +1154,13 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_pa dst_img.data = xalloc(size); } - if (dst_img.pixfmt == PIXFORMAT_JPEG) { - if (dst_img.data != src_img->data) { - memcpy(dst_img.data, src_img->data, dst_img.size); + if (dst_img.is_compressed) { + if (arg_e) { + fb_encode_for_ide(dst_img.data, &dst_img_tmp); + } else if (dst_img.data != dst_img_tmp.data) { + memcpy(dst_img.data, dst_img_tmp.data, dst_img.size); } + fb_alloc_free_till_mark(); } else { fb_alloc_mark(); imlib_draw_image(&dst_img, src_img, 0, 0, arg_x_scale, arg_y_scale, &arg_roi, @@ -1127,170 +1178,73 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_pa static mp_obj_t py_image_to_bitmap(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - return py_image_to(PIXFORMAT_BINARY, NULL, false, n_args, args, kw_args); + return py_image_to(PIXFORMAT_BINARY, NULL, false, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_bitmap_obj, 1, py_image_to_bitmap); static mp_obj_t py_image_to_grayscale(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - return py_image_to(PIXFORMAT_GRAYSCALE, NULL, false, n_args, args, kw_args); + return py_image_to(PIXFORMAT_GRAYSCALE, NULL, false, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_grayscale_obj, 1, py_image_to_grayscale); static mp_obj_t py_image_to_rgb565(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - return py_image_to(PIXFORMAT_RGB565, NULL, false, n_args, args, kw_args); + return py_image_to(PIXFORMAT_RGB565, NULL, false, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_rgb565_obj, 1, py_image_to_rgb565); static mp_obj_t py_image_to_rainbow(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - return py_image_to(PIXFORMAT_RGB565, rainbow_table, false, n_args, args, kw_args); + return py_image_to(PIXFORMAT_RGB565, rainbow_table, false, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_rainbow_obj, 1, py_image_to_rainbow); +static mp_obj_t py_image_to_ironbow(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + return py_image_to(PIXFORMAT_RGB565, ironbow_table, false,NULL, false, n_args, args, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_ironbow_obj, 1, py_image_to_ironbow); + +static mp_obj_t py_image_to_jpeg(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + return py_image_to(PIXFORMAT_JPEG, NULL, false, NULL, false, n_args, args, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_jpeg_obj, 1, py_image_to_jpeg); + +static mp_obj_t py_image_to_png(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + return py_image_to(PIXFORMAT_PNG, NULL, false, NULL, false, n_args, args, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_png_obj, 1, py_image_to_png); + static mp_obj_t py_image_copy(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - return py_image_to(PIXFORMAT_INVALID, NULL, true, n_args, args, kw_args); + return py_image_to(PIXFORMAT_INVALID, NULL, true, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_copy_obj, 1, py_image_copy); static mp_obj_t py_image_crop(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - return py_image_to(PIXFORMAT_INVALID, NULL, false, n_args, args, kw_args); + return py_image_to(PIXFORMAT_INVALID, NULL, false, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_crop_obj, 1, py_image_crop); -static mp_obj_t py_image_jpeg_encode_for_ide(mp_obj_t img_obj) -{ - image_t *arg_img = py_image_cobj(img_obj); - PY_ASSERT_TRUE_MSG(arg_img->pixfmt == PIXFORMAT_JPEG, "Image format is not supported!"); - PY_ASSERT_TRUE_MSG(py_helper_is_equal_to_framebuffer(arg_img), "Can't compress in place!"); - - int new_size = fb_encode_for_ide_new_size(arg_img); - fb_alloc_mark(); - uint8_t *temp = fb_alloc(new_size, FB_ALLOC_NO_HINT); - fb_encode_for_ide(temp, arg_img); - - image_t out; - out.w = arg_img->w; - out.h = arg_img->h; - out.size = new_size; - py_helper_set_to_framebuffer(&out); - arg_img->size = new_size; - - memcpy(arg_img->data, temp, new_size); - fb_alloc_free_till_mark(); - - return img_obj; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_jpeg_encode_for_ide_obj, py_image_jpeg_encode_for_ide); - -static mp_obj_t py_image_jpeg_encoded_for_ide(mp_obj_t img_obj) -{ - image_t *arg_img = py_image_cobj(img_obj); - PY_ASSERT_TRUE_MSG(arg_img->pixfmt == PIXFORMAT_JPEG, "Image format is not supported!"); - - int new_size = fb_encode_for_ide_new_size(arg_img); - uint8_t *temp = xalloc(new_size); - fb_encode_for_ide(temp, arg_img); - arg_img->size = new_size; - - return py_image(arg_img->w, arg_img->h, arg_img->pixfmt, new_size, temp); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_jpeg_encoded_for_ide_obj, py_image_jpeg_encoded_for_ide); - -static mp_obj_t py_image_compress(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) -{ - if (IM_IS_JPEG((image_t *) py_image_cobj(args[0]))) return args[0]; - - image_t *arg_img = py_helper_arg_to_image_not_compressed(args[0]); - int arg_q = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_quality), 50); - PY_ASSERT_TRUE_MSG((1 <= arg_q) && (arg_q <= 100), "Error: 1 <= quality <= 100!"); - - fb_alloc_mark(); - image_t out = { .w=arg_img->w, .h=arg_img->h, .pixfmt=PIXFORMAT_JPEG, .size=0, .data=NULL }; // alloc in jpeg compress - PY_ASSERT_FALSE_MSG(jpeg_compress(arg_img, &out, arg_q, false), "Out of Memory!"); - PY_ASSERT_TRUE_MSG(out.size <= image_size(arg_img), "Can't compress in place!"); - memcpy(arg_img->data, out.data, out.size); - - arg_img->pixfmt = PIXFORMAT_JPEG; - arg_img->size = out.size; - fb_alloc_free_till_mark(); - py_helper_update_framebuffer(arg_img); - return args[0]; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_compress_obj, 1, py_image_compress); - static mp_obj_t py_image_compress_for_ide(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - if (IM_IS_JPEG((image_t *) py_image_cobj(args[0]))) return py_image_jpeg_encode_for_ide(args[0]); - - image_t *arg_img = py_helper_arg_to_image_not_compressed(args[0]); - int arg_q = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_quality), 50); - PY_ASSERT_TRUE_MSG((1 <= arg_q) && (arg_q <= 100), "Error: 1 <= quality <= 100!"); - - fb_alloc_mark(); - image_t out = { .w=arg_img->w, .h=arg_img->h, .pixfmt=PIXFORMAT_JPEG, .size=0, .data=NULL }; // alloc in jpeg compress - PY_ASSERT_FALSE_MSG(jpeg_compress(arg_img, &out, arg_q, false), "Out of Memory!"); - int new_size = fb_encode_for_ide_new_size(&out); - PY_ASSERT_TRUE_MSG(new_size <= image_size(arg_img), "Can't compress in place!"); - fb_encode_for_ide(arg_img->data, &out); - - arg_img->pixfmt = PIXFORMAT_JPEG; - arg_img->size = new_size; - fb_alloc_free_till_mark(); - py_helper_update_framebuffer(arg_img); - return args[0]; + return py_image_to(PIXFORMAT_JPEG, NULL, false, NULL, true, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_compress_for_ide_obj, 1, py_image_compress_for_ide); static mp_obj_t py_image_compressed(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_image_cobj(args[0]); - - if (IM_IS_JPEG(arg_img)) { - image_t out = { .w=arg_img->w, .h=arg_img->h, .pixfmt=PIXFORMAT_JPEG, .size=arg_img->size, .data=xalloc(arg_img->size) }; - memcpy(out.data, arg_img->data, arg_img->size); - return py_image_from_struct(&out); - } - - arg_img = py_helper_arg_to_image_not_compressed(args[0]); - int arg_q = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_quality), 50); - PY_ASSERT_TRUE_MSG((1 <= arg_q) && (arg_q <= 100), "Error: 1 <= quality <= 100!"); - - fb_alloc_mark(); - image_t out = { .w=arg_img->w, .h=arg_img->h, .pixfmt=PIXFORMAT_JPEG, .size=0, .data=NULL }; // alloc in jpeg compress - PY_ASSERT_FALSE_MSG(jpeg_compress(arg_img, &out, arg_q, false), "Out of Memory!"); - uint8_t *temp = xalloc(out.size); - memcpy(temp, out.data, out.size); - out.data = temp; - fb_alloc_free_till_mark(); - - return py_image_from_struct(&out); + return py_image_to(PIXFORMAT_JPEG, NULL, false, mp_const_true, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_compressed_obj, 1, py_image_compressed); static mp_obj_t py_image_compressed_for_ide(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - if (IM_IS_JPEG((image_t *) py_image_cobj(args[0]))) return py_image_jpeg_encoded_for_ide(args[0]); - - image_t *arg_img = py_helper_arg_to_image_not_compressed(args[0]); - int arg_q = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_quality), 50); - PY_ASSERT_TRUE_MSG((1 <= arg_q) && (arg_q <= 100), "Error: 1 <= quality <= 100!"); - - fb_alloc_mark(); - image_t out = { .w=arg_img->w, .h=arg_img->h, .pixfmt=PIXFORMAT_JPEG, .size=0, .data=NULL }; // alloc in jpeg compress - PY_ASSERT_FALSE_MSG(jpeg_compress(arg_img, &out, arg_q, false), "Out of Memory!"); - int new_size = fb_encode_for_ide_new_size(&out); - uint8_t *temp = xalloc(new_size); - fb_encode_for_ide(temp, &out); - - out.size = new_size; - out.data = temp; - fb_alloc_free_till_mark(); - - return py_image_from_struct(&out); + return py_image_to(PIXFORMAT_JPEG, NULL, false, mp_const_true, true, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_compressed_for_ide_obj, 1, py_image_compressed_for_ide); @@ -6049,12 +6003,15 @@ static const mp_rom_map_elem_t locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_to_grayscale), MP_ROM_PTR(&py_image_to_grayscale_obj)}, {MP_ROM_QSTR(MP_QSTR_to_rgb565), MP_ROM_PTR(&py_image_to_rgb565_obj)}, {MP_ROM_QSTR(MP_QSTR_to_rainbow), MP_ROM_PTR(&py_image_to_rainbow_obj)}, - {MP_ROM_QSTR(MP_QSTR_compress), MP_ROM_PTR(&py_image_compress_obj)}, + {MP_ROM_QSTR(MP_QSTR_to_ironbow), MP_ROM_PTR(&py_image_to_ironbow_obj)}, + {MP_ROM_QSTR(MP_QSTR_to_jpeg), MP_ROM_PTR(&py_image_to_jpeg_obj)}, + {MP_ROM_QSTR(MP_QSTR_to_png), MP_ROM_PTR(&py_image_to_png_obj)}, + {MP_ROM_QSTR(MP_QSTR_compress), MP_ROM_PTR(&py_image_to_jpeg_obj)}, {MP_ROM_QSTR(MP_QSTR_compress_for_ide), MP_ROM_PTR(&py_image_compress_for_ide_obj)}, {MP_ROM_QSTR(MP_QSTR_compressed), MP_ROM_PTR(&py_image_compressed_obj)}, {MP_ROM_QSTR(MP_QSTR_compressed_for_ide), MP_ROM_PTR(&py_image_compressed_for_ide_obj)}, - {MP_ROM_QSTR(MP_QSTR_jpeg_encode_for_ide), MP_ROM_PTR(&py_image_jpeg_encode_for_ide_obj)}, - {MP_ROM_QSTR(MP_QSTR_jpeg_encoded_for_ide),MP_ROM_PTR(&py_image_jpeg_encoded_for_ide_obj)}, + {MP_ROM_QSTR(MP_QSTR_jpeg_encode_for_ide), MP_ROM_PTR(&py_image_compress_for_ide_obj)}, + {MP_ROM_QSTR(MP_QSTR_jpeg_encoded_for_ide),MP_ROM_PTR(&py_image_compressed_for_ide_obj)}, {MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&py_image_copy_obj)}, {MP_ROM_QSTR(MP_QSTR_crop), MP_ROM_PTR(&py_image_crop_obj)}, {MP_ROM_QSTR(MP_QSTR_scale), MP_ROM_PTR(&py_image_crop_obj)}, @@ -6978,6 +6935,7 @@ static const mp_rom_map_elem_t globals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_BAYER), MP_ROM_INT(PIXFORMAT_BAYER)}, /* 1BPP/RAW*/ {MP_ROM_QSTR(MP_QSTR_YUV422), MP_ROM_INT(PIXFORMAT_YUV422)}, /* 2BPP/YUV422*/ {MP_ROM_QSTR(MP_QSTR_JPEG), MP_ROM_INT(PIXFORMAT_JPEG)}, /* JPEG/COMPRESSED*/ + {MP_ROM_QSTR(MP_QSTR_PNG), MP_ROM_INT(PIXFORMAT_PNG)}, /* PNG/COMPRESSED*/ {MP_ROM_QSTR(MP_QSTR_AREA), MP_ROM_INT(IMAGE_HINT_AREA)}, {MP_ROM_QSTR(MP_QSTR_BILINEAR), MP_ROM_INT(IMAGE_HINT_BILINEAR)}, {MP_ROM_QSTR(MP_QSTR_BICUBIC), MP_ROM_INT(IMAGE_HINT_BICUBIC)}, diff --git a/src/omv/ports/nrf/omv_portconfig.mk b/src/omv/ports/nrf/omv_portconfig.mk index dfbf28313..2babe46a6 100644 --- a/src/omv/ports/nrf/omv_portconfig.mk +++ b/src/omv/ports/nrf/omv_portconfig.mk @@ -152,6 +152,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/imlib/, \ jpegd.o \ jpeg.o \ lodepng.o \ + png.o \ kmeans.o \ lab_tab.o \ lbp.o \ diff --git a/src/omv/ports/rp2/omv_portconfig.cmake b/src/omv/ports/rp2/omv_portconfig.cmake index 9e7dcfac6..925888486 100644 --- a/src/omv/ports/rp2/omv_portconfig.cmake +++ b/src/omv/ports/rp2/omv_portconfig.cmake @@ -150,6 +150,7 @@ target_sources(${MICROPY_TARGET} PRIVATE ${TOP_DIR}/${OMV_DIR}/imlib/jpegd.c ${TOP_DIR}/${OMV_DIR}/imlib/jpeg.c ${TOP_DIR}/${OMV_DIR}/imlib/lodepng.c + ${TOP_DIR}/${OMV_DIR}/imlib/png.c ${TOP_DIR}/${OMV_DIR}/imlib/kmeans.c ${TOP_DIR}/${OMV_DIR}/imlib/lab_tab.c ${TOP_DIR}/${OMV_DIR}/imlib/lbp.c diff --git a/src/omv/ports/stm32/omv_portconfig.mk b/src/omv/ports/stm32/omv_portconfig.mk index b7d8223b8..3d96be181 100644 --- a/src/omv/ports/stm32/omv_portconfig.mk +++ b/src/omv/ports/stm32/omv_portconfig.mk @@ -190,6 +190,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/imlib/, \ jpegd.o \ jpeg.o \ lodepng.o \ + png.o \ kmeans.o \ lab_tab.o \ lbp.o \