From bf447e5599c3eadcfad520c672c9a3368c01975e Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 17 Sep 2021 09:27:26 -0700 Subject: [PATCH] Add yuv image processing support --- src/omv/Makefile | 1 + src/omv/common/sensor.h | 3 + src/omv/common/sensor_utils.c | 8 +- src/omv/imlib/draw.c | 101 ++++++++++++----- src/omv/imlib/gif.c | 9 +- src/omv/imlib/imlib.c | 9 +- src/omv/imlib/imlib.h | 113 +++++++++++++++---- src/omv/imlib/jpeg.c | 77 ++++++++++++- src/omv/imlib/yuv.c | 127 +++++++++++++++++++++ src/omv/modules/py_fir.c | 2 +- src/omv/modules/py_helper.c | 20 +--- src/omv/modules/py_helper.h | 4 +- src/omv/modules/py_image.c | 149 ++++++++++++++++++------- src/omv/modules/py_sensor.c | 2 +- src/omv/ports/nrf/omv_portconfig.mk | 1 + src/omv/ports/rp2/omv_portconfig.cmake | 1 + src/omv/ports/stm32/modules/py_lcd.c | 3 +- src/omv/ports/stm32/modules/py_tv.c | 5 +- src/omv/ports/stm32/omv_portconfig.mk | 1 + src/omv/ports/stm32/sensor.c | 19 +++- src/omv/sensors/gc2145.c | 3 +- src/omv/sensors/mt9m114.c | 1 + src/omv/sensors/ov2640.c | 1 + src/omv/sensors/ov5640.c | 1 + src/omv/sensors/ov7670.c | 1 + src/omv/sensors/ov7725.c | 1 + src/omv/sensors/ov9650.c | 1 + 27 files changed, 530 insertions(+), 134 deletions(-) create mode 100644 src/omv/imlib/yuv.c diff --git a/src/omv/Makefile b/src/omv/Makefile index 542bc9a48..301b00b5c 100644 --- a/src/omv/Makefile +++ b/src/omv/Makefile @@ -91,6 +91,7 @@ SRCS += $(addprefix imlib/, \ stats.c \ template.c \ xyz_tab.c \ + yuv.c \ zbar.c \ ) diff --git a/src/omv/common/sensor.h b/src/omv/common/sensor.h index 264c3c141..57f676190 100644 --- a/src/omv/common/sensor.h +++ b/src/omv/common/sensor.h @@ -179,6 +179,8 @@ typedef enum { #define SENSOR_HW_FLAGS_BAYER_GBRG (SUBFORMAT_ID_GBRG) #define SENSOR_HW_FLAGS_BAYER_GRBG (SUBFORMAT_ID_GRBG) #define SENSOR_HW_FLAGS_BAYER_RGGB (SUBFORMAT_ID_RGGB) +#define SENSOR_HW_FLAGS_YUV422 (SUBFORMAT_ID_YUV422) +#define SENSOR_HW_FLAGS_YVU422 (SUBFORMAT_ID_YVU422) typedef void (*vsync_cb_t)(uint32_t vsync); typedef void (*frame_cb_t)(); @@ -203,6 +205,7 @@ typedef struct _sensor { uint32_t rgb_swap:1; // Byte-swap 2BPP RGB formats after capture. uint32_t yuv_swap:1; // Byte-swap 2BPP YUV formats after capture. uint32_t bayer:3; // Bayer/CFA pattern. + uint32_t yuv_order:1; // YUV/YVU order. } hw_flags; const uint16_t *color_palette; // Color palette used for color lookup. diff --git a/src/omv/common/sensor_utils.c b/src/omv/common/sensor_utils.c index 1b1799599..622b4ac0d 100644 --- a/src/omv/common/sensor_utils.c +++ b/src/omv/common/sensor_utils.c @@ -520,11 +520,11 @@ __weak int sensor_set_pixformat(pixformat_t pixformat) } // Some sensor drivers automatically switch to BAYER to reduce the frame size if it does not fit in RAM. - // If the current format is BAYER (1BPP), and the target format is RGB-565 (2BPP) and the frame does not + // If the current format is BAYER (1BPP), and the target format is color and (2BPP), and the frame does not // fit in RAM it will just be switched back again to BAYER, so we keep the current format unchanged. uint32_t size = framebuffer_get_buffer_size(); if ((sensor.pixformat == PIXFORMAT_BAYER) - && (pixformat == PIXFORMAT_RGB565) + && ((pixformat == PIXFORMAT_RGB565) || (pixformat == PIXFORMAT_YUV422)) && (MAIN_FB()->u * MAIN_FB()->v * 2 > size) && (MAIN_FB()->u * MAIN_FB()->v * 1 <= size)) { return 0; @@ -1033,7 +1033,7 @@ __weak int sensor_set_special_effect(sde_t sde) if (sensor.set_special_effect == NULL) { return SENSOR_ERROR_CTL_UNSUPPORTED; } - + // Call the sensor specific function. if (sensor.set_special_effect(&sensor, sde) != 0) { return SENSOR_ERROR_CTL_FAILED; @@ -1124,7 +1124,7 @@ __weak int sensor_auto_crop_framebuffer() return 0; } - if (sensor.pixformat == PIXFORMAT_RGB565) { + if ((sensor.pixformat == PIXFORMAT_RGB565) || (sensor.pixformat == PIXFORMAT_YUV422)) { // Switch to bayer for the quick 2x savings. sensor_set_pixformat(PIXFORMAT_BAYER); bpp = 1; diff --git a/src/omv/imlib/draw.c b/src/omv/imlib/draw.c index d012a8154..4a49c360d 100644 --- a/src/omv/imlib/draw.c +++ b/src/omv/imlib/draw.c @@ -2444,11 +2444,20 @@ void imlib_draw_row(int x_start, int x_end, int y_row, imlib_draw_row_data_t *da case PIXFORMAT_BAYER_ANY: { uint8_t *dst8 = (data->dst_row_override ? ((uint8_t *) data->dst_row_override) - : IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(data->dst_img, y_row)) + x_start; + : IMAGE_COMPUTE_BAYER_PIXEL_ROW_PTR(data->dst_img, y_row)) + x_start; uint8_t *src8 = ((uint8_t *) data->row_buffer[!data->toggle]) + x_start; unaligned_memcpy(dst8, src8, (x_end - x_start) * sizeof(uint8_t)); break; } + // Only yuv422 copying/cropping is supported. + case PIXFORMAT_YUV_ANY: { + uint16_t *dst16 = (data->dst_row_override + ? ((uint16_t *) data->dst_row_override) + : IMAGE_COMPUTE_YUV_PIXEL_ROW_PTR(data->dst_img, y_row)) + x_start; + uint16_t *src16 = ((uint16_t *) data->row_buffer[!data->toggle]) + x_start; + unaligned_memcpy(dst16, src16, (x_end - x_start) * sizeof(uint16_t)); + break; + } default: { break; } @@ -2673,7 +2682,7 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d // rgb_channel extracted / color_palette applied image image_t new_src_img; - if (((hint & IMAGE_HINT_EXTRACT_RGB_CHANNEL_FIRST) && (rgb_channel != -1) && IMAGE_IS_COLOR(src_img)) + if (((hint & IMAGE_HINT_EXTRACT_RGB_CHANNEL_FIRST) && (rgb_channel != -1) && src_img->is_color) || ((hint & IMAGE_HINT_APPLY_COLOR_PALETTE_FIRST) && color_palette)) { new_src_img.w = src_img_w; // same width as source image new_src_img.h = src_img_h; // same height as source image @@ -2687,11 +2696,10 @@ 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_bayer_or_jpeg = src_img->is_bayer || is_jpeg; - // Best format to convert bayer/jpeg image to. - int is_bayer_or_jpeg_pixfmt = (rgb_channel != -1) ? PIXFORMAT_RGB565 : - (color_palette ? PIXFORMAT_GRAYSCALE : - dst_img->pixfmt); + // Best format to convert yuv/bayer/jpeg image to. + int new_not_mutable_pixfmt = (rgb_channel != -1) ? PIXFORMAT_RGB565 : + (color_palette ? PIXFORMAT_GRAYSCALE : + dst_img->pixfmt); bool no_scaling_nearest_neighbor = (dst_delta_x == 1) && (dst_x_start == 0) && (src_x_start == 0) @@ -2707,21 +2715,23 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d // Do we need to convert the image? bool is_bayer_color_conversion = src_img->is_bayer && !dst_img->is_bayer; + bool is_yuv_color_conversion = src_img->is_yuv && !dst_img->is_yuv; + bool is_color_conversion = is_bayer_color_conversion || is_yuv_color_conversion; // Force a deep copy if we cannot use the image in-place. bool need_deep_copy = (dst_img->data == src_img->data) - && (is_scaling || (src_img_row_bytes < dst_img_row_bytes) || is_bayer_color_conversion); + && (is_scaling || (src_img_row_bytes < dst_img_row_bytes) || is_color_conversion); // Force a deep copy if we are scaling. - bool is_bayer_scaling = is_bayer_color_conversion && is_scaling; + bool is_color_conversion_scaling = is_color_conversion && is_scaling; // Make a deep copy of the source image. - if (need_deep_copy || is_bayer_scaling || is_jpeg) { + if (need_deep_copy || is_color_conversion_scaling || is_jpeg) { new_src_img.w = src_img->w; // same width as source image new_src_img.h = src_img->h; // same height as source image - if (is_bayer_or_jpeg) { - new_src_img.pixfmt = is_bayer_or_jpeg_pixfmt; + if (!src_img->is_mutable) { + new_src_img.pixfmt = new_not_mutable_pixfmt; size_t size = image_size(&new_src_img); new_src_img.data = fb_alloc(size, FB_ALLOC_NO_HINT); @@ -2729,7 +2739,9 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d case PIXFORMAT_BINARY: { if (src_img->is_bayer) { imlib_debayer_image(&new_src_img, src_img); - } else if (src_img->pixfmt == PIXFORMAT_JPEG) { + } else if (src_img->is_yuv) { + imlib_deyuv_image(&new_src_img, src_img); + } else if (is_jpeg) { jpeg_decompress_image_to_binary(&new_src_img, src_img); } break; @@ -2737,7 +2749,9 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d case PIXFORMAT_GRAYSCALE: { if (src_img->is_bayer) { imlib_debayer_image(&new_src_img, src_img); - } else if (src_img->pixfmt == PIXFORMAT_JPEG) { + } else if (src_img->is_yuv) { + imlib_deyuv_image(&new_src_img, src_img); + } else if (is_jpeg) { jpeg_decompress_image_to_grayscale(&new_src_img, src_img); } break; @@ -2745,12 +2759,15 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d case PIXFORMAT_RGB565: { if (src_img->is_bayer) { imlib_debayer_image(&new_src_img, src_img); - } else if (src_img->pixfmt == PIXFORMAT_JPEG) { + } else if (src_img->is_yuv) { + imlib_deyuv_image(&new_src_img, src_img); + } else if (is_jpeg) { jpeg_decompress_image_to_rgb565(&new_src_img, src_img); } break; } - case PIXFORMAT_BAYER_ANY: { + case PIXFORMAT_BAYER_ANY: + case PIXFORMAT_YUV_ANY: { memcpy(new_src_img.data, src_img->data, size); break; } @@ -2770,7 +2787,7 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d imlib_draw_row_data_t imlib_draw_row_data; imlib_draw_row_data.dst_img = dst_img; - imlib_draw_row_data.src_img_pixfmt = is_bayer_or_jpeg ? is_bayer_or_jpeg_pixfmt : src_img->pixfmt; + imlib_draw_row_data.src_img_pixfmt = (!src_img->is_mutable) ? new_not_mutable_pixfmt : src_img->pixfmt; imlib_draw_row_data.rgb_channel = rgb_channel; imlib_draw_row_data.alpha = alpha; imlib_draw_row_data.color_palette = color_palette; @@ -4538,7 +4555,9 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d } // while y break; } - case PIXFORMAT_RGB565: { + case PIXFORMAT_RGB565: + // Re-use RGB565 for yuv. + case PIXFORMAT_YUV_ANY: { while (y_not_done) { uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, next_src_y_index); // Must be called per loop to get the address of the temp buffer to blend with @@ -4622,16 +4641,44 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d } case PIXFORMAT_BAYER_ANY: { while (y_not_done) { - switch (is_bayer_or_jpeg_pixfmt) { - case PIXFORMAT_BINARY: - case PIXFORMAT_GRAYSCALE: - case PIXFORMAT_RGB565: { + switch (new_not_mutable_pixfmt) { + case PIXFORMAT_MUTABLE_ANY: { imlib_debayer_line(dst_x_start, dst_x_end, next_src_y_index, - imlib_draw_row_get_row_buffer(&imlib_draw_row_data), is_bayer_or_jpeg_pixfmt, src_img); + imlib_draw_row_get_row_buffer(&imlib_draw_row_data), + new_not_mutable_pixfmt, src_img); break; } - case PIXFORMAT_BAYER_ANY: { // Bayer images have the same shape as grayscale. - uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, next_src_y_index); + case PIXFORMAT_BAYER_ANY: { // Bayer images have the same shape as GRAYSCALE. + uint8_t *src_row_ptr = IMAGE_COMPUTE_BAYER_PIXEL_ROW_PTR(src_img, next_src_y_index); + imlib_draw_row_put_row_buffer(&imlib_draw_row_data, src_row_ptr); + break; + } + default : { + break; + } + } + + imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + + // Increment offsets + dst_y += dst_delta_y; + src_y_accum += src_y_frac; + next_src_y_index = src_y_accum >> 16; + y_not_done = ++y < dst_y_end; + } // while y + break; + } + case PIXFORMAT_YUV_ANY: { + while (y_not_done) { + switch (new_not_mutable_pixfmt) { + case PIXFORMAT_MUTABLE_ANY: { + imlib_deyuv_line(dst_x_start, dst_x_end, next_src_y_index, + imlib_draw_row_get_row_buffer(&imlib_draw_row_data), + new_not_mutable_pixfmt, src_img); + break; + } + case PIXFORMAT_YUV_ANY: { // YUV images have the same shape as RGB565. + uint16_t *src_row_ptr = IMAGE_COMPUTE_YUV_PIXEL_ROW_PTR(src_img, next_src_y_index); imlib_draw_row_put_row_buffer(&imlib_draw_row_data, src_row_ptr); break; } @@ -4743,7 +4790,9 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d } // while y break; } - case PIXFORMAT_RGB565: { + case PIXFORMAT_RGB565: + // Re-use RGB565 for yuv. + case PIXFORMAT_YUV_ANY: { while (y_not_done) { int src_y_index = next_src_y_index; uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index); diff --git a/src/omv/imlib/gif.c b/src/omv/imlib/gif.c index b5aeb590d..ade7d9e9f 100644 --- a/src/omv/imlib/gif.c +++ b/src/omv/imlib/gif.c @@ -88,13 +88,17 @@ void gif_add_frame(FIL *fp, image_t *img, uint16_t delay) write_byte(fp, (red<<5) | (green<<2) | blue); } } - } else if (IM_IS_BAYER(img)) { + } else if (img->is_bayer || img->is_yuv) { for (int y=0; yis_bayer) { + imlib_debayer_line(0, block_size, y, pixels, PIXFORMAT_RGB565, img); + } else { + imlib_deyuv_line(0, block_size, y, pixels, PIXFORMAT_RGB565, img); + } for (int x=0; xh; } - case PIXFORMAT_GRAYSCALE: { + case PIXFORMAT_GRAYSCALE: + case PIXFORMAT_BAYER_ANY: { // re-use return IMAGE_GRAYSCALE_LINE_LEN_BYTES(ptr) * ptr->h; } - case PIXFORMAT_RGB565: { + case PIXFORMAT_RGB565: + case PIXFORMAT_YUV_ANY: { // re-use return IMAGE_RGB565_LINE_LEN_BYTES(ptr) * ptr->h; } - case PIXFORMAT_BAYER_ANY: { - return ptr->w * ptr->h; - } case PIXFORMAT_JPEG: { return ptr->size; } diff --git a/src/omv/imlib/imlib.h b/src/omv/imlib/imlib.h index f50bd1a26..b909c674f 100644 --- a/src/omv/imlib/imlib.h +++ b/src/omv/imlib/imlib.h @@ -341,20 +341,22 @@ typedef enum { PIXFORMAT_ID_BINARY = 1, PIXFORMAT_ID_GRAY = 2, PIXFORMAT_ID_RGB565 = 3, - PIXFORMAT_ID_YUV422 = 4, - PIXFORMAT_ID_BAYER = 5, + PIXFORMAT_ID_BAYER = 4, + PIXFORMAT_ID_YUV422 = 5, PIXFORMAT_ID_JPEG = 6, /* Note: Update PIXFORMAT_IS_VALID when adding new formats */ } pixformat_id_t; // Pixel sub-format IDs. typedef enum { + SUBFORMAT_ID_GRAY8 = 0, + SUBFORMAT_ID_GRAY16 = 1, SUBFORMAT_ID_BGGR = 0, // !!! Note: Make sure bayer sub-formats don't !!! SUBFORMAT_ID_GBRG = 1, // !!! overflow the sensor.hw_flags.bayer field !!! SUBFORMAT_ID_GRBG = 2, SUBFORMAT_ID_RGGB = 3, - SUBFORMAT_ID_GRAY8 = 0, - SUBFORMAT_ID_GRAY16 = 1, + SUBFORMAT_ID_YUV422 = 0, + SUBFORMAT_ID_YVU422 = 1, /* Note: Update PIXFORMAT_IS_VALID when adding new formats */ } subformat_id_t; @@ -364,45 +366,58 @@ typedef enum { PIXFORMAT_BPP_GRAY8 = 1, PIXFORMAT_BPP_GRAY16 = 2, PIXFORMAT_BPP_RGB565 = 2, - PIXFORMAT_BPP_YUV422 = 2, PIXFORMAT_BPP_BAYER = 1, + PIXFORMAT_BPP_YUV422 = 2, /* Note: Update PIXFORMAT_IS_VALID when adding new formats */ } pixformat_bpp_t; // Pixel format flags. +#define PIXFORMAT_FLAGS_Y (1 << 28) // YUV format. #define PIXFORMAT_FLAGS_M (1 << 27) // Mutable format. #define PIXFORMAT_FLAGS_C (1 << 26) // Colored format. #define PIXFORMAT_FLAGS_J (1 << 25) // Compressed format (JPEG/PNG). #define PIXFORMAT_FLAGS_R (1 << 24) // RAW/Bayer format. +#define PIXFORMAT_FLAGS_CY (PIXFORMAT_FLAGS_C | PIXFORMAT_FLAGS_Y) #define PIXFORMAT_FLAGS_CM (PIXFORMAT_FLAGS_C | PIXFORMAT_FLAGS_M) #define PIXFORMAT_FLAGS_CR (PIXFORMAT_FLAGS_C | PIXFORMAT_FLAGS_R) +#define PIXFORMAT_FLAGS_CJ (PIXFORMAT_FLAGS_C | PIXFORMAT_FLAGS_J) #define IMLIB_IMAGE_MAX_SIZE(x) ((x) & 0xFFFFFFFF) // Each pixel format encodes flags, pixel format id and bpp as follows: -// 31......28 27 26 25 24 23..........16 15...........8 7.............0 -// MF CF JF RF +// 31......29 28 27 26 25 24 23..........16 15...........8 7.............0 +// 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_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_YUV422 = (PIXFORMAT_FLAGS_CM | (PIXFORMAT_ID_YUV422 << 16) | (0 << 8) | PIXFORMAT_BPP_YUV422 ), - 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_JPEG = (PIXFORMAT_FLAGS_J | (PIXFORMAT_ID_JPEG << 16) | (0 << 8) | 0 ), + 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_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_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_t; +#define PIXFORMAT_MUTABLE_ANY \ + PIXFORMAT_BINARY: \ + case PIXFORMAT_GRAYSCALE: \ + case PIXFORMAT_RGB565 \ + #define PIXFORMAT_BAYER_ANY \ PIXFORMAT_BAYER_BGGR: \ case PIXFORMAT_BAYER_GBRG: \ case PIXFORMAT_BAYER_GRBG: \ case PIXFORMAT_BAYER_RGGB \ +#define PIXFORMAT_YUV_ANY \ + PIXFORMAT_YUV422: \ + case PIXFORMAT_YVU422 \ + #define IMLIB_PIXFORMAT_IS_VALID(x) \ ((x == PIXFORMAT_BINARY) \ || (x == PIXFORMAT_GRAYSCALE) \ @@ -411,6 +426,8 @@ typedef enum { || (x == PIXFORMAT_BAYER_GBRG) \ || (x == PIXFORMAT_BAYER_GRBG) \ || (x == PIXFORMAT_BAYER_RGGB) \ + || (x == PIXFORMAT_YUV422) \ + || (x == PIXFORMAT_YVU422) \ || (x == PIXFORMAT_JPEG)) \ #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) @@ -425,7 +442,8 @@ struct { \ uint32_t is_compressed :1; \ uint32_t is_color :1; \ uint32_t is_mutable :1; \ - uint32_t /*reserved*/ :4; \ + uint32_t is_yuv :1; \ + uint32_t /*reserved*/ :3; \ }; \ uint32_t pixfmt; \ }; \ @@ -436,7 +454,8 @@ struct { \ struct { \ union { \ struct { \ - uint32_t /*reserved*/ :4; \ + uint32_t /*reserved*/ :3; \ + uint32_t is_yuv :1; \ uint32_t is_mutable :1; \ uint32_t is_color :1; \ uint32_t is_compressed :1; \ @@ -468,10 +487,6 @@ void image_copy(image_t *dst, image_t *src); size_t image_size(image_t *ptr); bool image_get_mask_pixel(image_t *ptr, int x, int y); -#define IMAGE_IS_MUTABLE(image) (image->is_mutable) -#define IMAGE_IS_COLOR(image) (image->is_color) -#define IMAGE_IS_MUTABLE_BAYER(image) (image->is_mutable || image->is_bayer) - #define IMAGE_BINARY_LINE_LEN(image) (((image)->w + UINT32_T_MASK) >> UINT32_T_SHIFT) #define IMAGE_BINARY_LINE_LEN_BYTES(image) (IMAGE_BINARY_LINE_LEN(image) * sizeof(uint32_t)) @@ -550,6 +565,40 @@ bool image_get_mask_pixel(image_t *ptr, int x, int y); ((uint16_t *) _image->data)[(_image->w * _y) + _x] = _v; \ }) +#define IMAGE_GET_YUV_PIXEL(image, x, y) \ +({ \ + __typeof__ (image) _image = (image); \ + __typeof__ (x) _x = (x); \ + __typeof__ (y) _y = (y); \ + ((uint16_t *) _image->data)[(_image->w * _y) + _x]; \ +}) + +#define IMAGE_PUT_YUV_PIXEL(image, x, y, v) \ +({ \ + __typeof__ (image) _image = (image); \ + __typeof__ (x) _x = (x); \ + __typeof__ (y) _y = (y); \ + __typeof__ (v) _v = (v); \ + ((uint16_t *) _image->data)[(_image->w * _y) + _x] = _v; \ +}) + +#define IMAGE_GET_BAYER_PIXEL(image, x, y) \ +({ \ + __typeof__ (image) _image = (image); \ + __typeof__ (x) _x = (x); \ + __typeof__ (y) _y = (y); \ + ((uint8_t *) _image->data)[(_image->w * _y) + _x]; \ +}) + +#define IMAGE_PUT_BAYER_PIXEL(image, x, y, v) \ +({ \ + __typeof__ (image) _image = (image); \ + __typeof__ (x) _x = (x); \ + __typeof__ (y) _y = (y); \ + __typeof__ (v) _v = (v); \ + ((uint8_t *) _image->data)[(_image->w * _y) + _x] = _v; \ +}) + // Fast Stuff // #define IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(image, y) \ @@ -634,6 +683,20 @@ bool image_get_mask_pixel(image_t *ptr, int x, int y); _row_ptr[_x] = _v; \ }) +#define IMAGE_COMPUTE_BAYER_PIXEL_ROW_PTR(image, y) \ +({ \ + __typeof__ (image) _image = (image); \ + __typeof__ (y) _y = (y); \ + ((uint8_t *) _image->data) + (_image->w * _y); \ +}) + +#define IMAGE_COMPUTE_YUV_PIXEL_ROW_PTR(image, y) \ +({ \ + __typeof__ (image) _image = (image); \ + __typeof__ (y) _y = (y); \ + ((uint16_t *) _image->data) + (_image->w * _y); \ +}) + // Old Image Macros - Will be refactor and removed. But, only after making sure through testing new macros work. // Image kernels @@ -1016,6 +1079,10 @@ void imlib_fill_image_from_float(image_t *img, int w, int h, float *data, float void imlib_debayer_line(int x_start, int x_end, int y_row, void *dst_row_ptr, pixformat_t pixfmt, image_t *src); void imlib_debayer_image(image_t *dst, image_t *src); +// YUV Image Processing +void imlib_deyuv_line(int x_start, int x_end, int y_row, void *dst_row_ptr, pixformat_t pixfmt, image_t *src); +void imlib_deyuv_image(image_t *dst, image_t *src); + /* Color space functions */ int8_t imlib_rgb565_to_l(uint16_t pixel); int8_t imlib_rgb565_to_a(uint16_t pixel); diff --git a/src/omv/imlib/jpeg.c b/src/omv/imlib/jpeg.c index b8c01c5a5..14518afe2 100644 --- a/src/omv/imlib/jpeg.c +++ b/src/omv/imlib/jpeg.c @@ -148,7 +148,9 @@ static void jpeg_get_mcu(image_t *src, int x_offset, int y_offset, int dx, int d } case PIXFORMAT_RGB565: { if ((dx != MCU_W) || (dy != MCU_H)) { // partial MCU, fill with 0's to start - memset(Y0, 0, JPEG_444_YCBCR_MCU_SIZE); + memset(Y0, 0, JPEG_444_GS_MCU_SIZE); + memset(CB, 0, JPEG_444_GS_MCU_SIZE); + memset(CR, 0, JPEG_444_GS_MCU_SIZE); } for (int y = y_offset, yy = y + dy, index = 0; y < yy; y++) { @@ -220,9 +222,79 @@ static void jpeg_get_mcu(image_t *src, int x_offset, int y_offset, int dx, int d } break; } + case PIXFORMAT_YUV_ANY: { + if ((dx != MCU_W) || (dy != MCU_H)) { // partial MCU, fill with 0's to start + memset(Y0, 0, JPEG_444_GS_MCU_SIZE); + memset(CB, 0, JPEG_444_GS_MCU_SIZE); + memset(CR, 0, JPEG_444_GS_MCU_SIZE); + } + + int shift = (src->pixfmt == PIXFORMAT_YUV422) ? 24 : 8; + + for (int y = y_offset, yy = y + dy, index = 0; y < yy; y++) { + uint32_t *rp = (uint32_t *) (IMAGE_COMPUTE_YUV_PIXEL_ROW_PTR(src, y) + x_offset); + + for (int x = 0, xx = dx - 1; x < xx; x += 2, index += 2) { + int pixels = *rp++; + + #if (OMV_HARDWARE_JPEG == 0) + pixels ^= 0x80808080; + #endif + + Y0[index] = pixels, Y0[index + 1] = pixels >> 16; + + int cb = pixels >> shift; + CB[index] = cb, CB[index + 1] = cb; + + int cr = pixels >> (32 - shift); + CR[index] = cr, CR[index + 1] = cr; + } + + if (dx & 1) { + int pixel = *((uint16_t *) rp); + + #if (OMV_HARDWARE_JPEG == 0) + pixel ^= 0x8080; + #endif + + Y0[index] = pixel; + + if (index % MCU_W) { + if (shift == 8) { + CR[index] = CR[index - 1]; + CB[index++] = pixel >> 8; + } else { + CB[index] = CB[index - 1]; + CR[index++] = pixel >> 8; + } + } else { + if (shift == 8) { + CB[index] = pixel >> 8; + #if (OMV_HARDWARE_JPEG == 0) + CR[index++] = 0; + #else + CR[index++] = 0x80; + #endif + } else { + #if (OMV_HARDWARE_JPEG == 0) + CB[index] = 0; + #else + CB[index] = 0x80; + #endif + CR[index++] = pixel >> 8; + } + } + } + + index += MCU_W - dx; + } + break; + } case PIXFORMAT_BAYER_ANY: { if ((dx != MCU_W) || (dy != MCU_H)) { // partial MCU, fill with 0's to start - memset(Y0, 0, JPEG_444_YCBCR_MCU_SIZE); + memset(Y0, 0, JPEG_444_GS_MCU_SIZE); + memset(CB, 0, JPEG_444_GS_MCU_SIZE); + memset(CR, 0, JPEG_444_GS_MCU_SIZE); } int src_w = src->w, w_limit = src_w - 1, w_limit_m_1 = w_limit - 1; @@ -987,6 +1059,7 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) break; case PIXFORMAT_RGB565: case PIXFORMAT_BAYER_ANY: + case PIXFORMAT_YUV_ANY: mcu_size = JPEG_444_YCBCR_MCU_SIZE; JPEG_Info.ColorSpace = JPEG_YCBCR_COLORSPACE; JPEG_Info.ChromaSubsampling = JPEG_444_SUBSAMPLING; diff --git a/src/omv/imlib/yuv.c b/src/omv/imlib/yuv.c new file mode 100644 index 000000000..1322d82b5 --- /dev/null +++ b/src/omv/imlib/yuv.c @@ -0,0 +1,127 @@ +/* + * 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. + * + * Deyuv Functions + */ +#include "imlib.h" + +void imlib_deyuv_line(int x_start, int x_end, int y_row, void *dst_row_ptr, pixformat_t pixfmt, image_t *src) +{ + int shift = (src->pixfmt == PIXFORMAT_YUV422) ? 16 : 0; + int src_w = src->w, w_limit = src_w - 1; + + uint16_t *rowptr_yuv = ((uint16_t *) src->data) + (y_row * src_w); + + // If the image is an odd width this will go for the last loop and we drop the last column. + for (int x = x_start; x < x_end; x += 2) { + int32_t row_yuv; // signed + + // keep pixels in bounds + if (x >= w_limit) { + if (src_w >= 2) { + uint32_t temp = *((uint32_t *) (rowptr_yuv + x - 1)); + row_yuv = ((temp & 0xff00) << 16) | (temp & 0xff0000) | (temp >> 16); + } else { + row_yuv = *((uint16_t *) (rowptr_yuv + x)); + row_yuv = ((row_yuv & 0xff) << 16) | 0x80000000; + } + } else { + row_yuv = *((uint32_t *) (rowptr_yuv + x)); + } + + int y0 = row_yuv & 0xff, y1 = (row_yuv >> 16) & 0xff; + + switch (pixfmt) { + case PIXFORMAT_BINARY: { + uint32_t *row_ptr_32 = (uint32_t *) dst_row_ptr; + IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr_32, x, (y0 >> 7)); + + if (x != w_limit) { + IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr_32, x + 1, (y1 >> 7)); + } + + break; + } + case PIXFORMAT_GRAYSCALE: { + uint8_t *row_ptr_8 = (uint8_t *) dst_row_ptr; + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr_8, x, y0); + + if (x != w_limit) { + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr_8, x + 1, y1); + } + + break; + } + case PIXFORMAT_RGB565: { + uint16_t *row_ptr_16 = (uint16_t *) dst_row_ptr; + + // R = Y + (1.40200 * U) + // G = Y - (0.34414 * V) - (0.71414 * U) + // B = Y + (1.77200 * V) + + // R = Y + ((179 * U) >> 7) + // G = Y - (((44 * V) - (91 * U)) >> 7) + // B = Y + ((227 * V) >> 7) + + row_yuv ^= 0x80008000; + + int u = (row_yuv << shift) >> 24; // signed bit extraction + int v = (row_yuv << (16 - shift)) >> 24; // signed bit extraction + + int ry = (179 * u) >> 7; + int gy = ((44 * v) + (91 * u)) >> 7; + int by = (227 * v) >> 7; + + int r0 = y0 + ry, g0 = y0 - gy, b0 = y0 + by; + r0 = IM_MIN(IM_MAX(r0, COLOR_R8_MIN), COLOR_R8_MAX); + g0 = IM_MIN(IM_MAX(g0, COLOR_G8_MIN), COLOR_G8_MAX); + b0 = IM_MIN(IM_MAX(b0, COLOR_B8_MIN), COLOR_B8_MAX); + int rgb565_0 = COLOR_R8_G8_B8_TO_RGB565(r0, g0, b0); + IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr_16, x, rgb565_0); + + if (x != w_limit) { + int r1 = y1 + ry, g1 = y1 - gy, b1 = y1 + by; + r1 = IM_MIN(IM_MAX(r1, COLOR_R8_MIN), COLOR_R8_MAX); + g1 = IM_MIN(IM_MAX(g1, COLOR_G8_MIN), COLOR_G8_MAX); + b1 = IM_MIN(IM_MAX(b1, COLOR_B8_MIN), COLOR_B8_MAX); + int rgb565_1 = COLOR_R8_G8_B8_TO_RGB565(r1, g1, b1); + IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr_16, x + 1, rgb565_1); + } + + break; + } + default: { + break; + } + } + } +} + +void imlib_deyuv_image(image_t *dst, image_t *src) +{ + for (int y = 0, src_w = src->w, src_h = src->h; y < src_h; y++) { + void *row_ptr = NULL; + + switch (dst->pixfmt) { + case PIXFORMAT_BINARY: { + row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(dst, y); + break; + } + case PIXFORMAT_GRAYSCALE: { + row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(dst, y); + break; + } + case PIXFORMAT_RGB565: { + row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(dst, y); + break; + } + } + + imlib_deyuv_line(0, src_w, y, row_ptr, dst->pixfmt, src); + } +} diff --git a/src/omv/modules/py_fir.c b/src/omv/modules/py_fir.c index 1e243f351..50568a6b8 100644 --- a/src/omv/modules/py_fir.c +++ b/src/omv/modules/py_fir.c @@ -1082,7 +1082,7 @@ mp_obj_t py_fir_snapshot(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) 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_bayer(copy_to_fb_obj); + arg_other = py_helper_arg_to_image_not_compressed(copy_to_fb_obj); } } diff --git a/src/omv/modules/py_helper.c b/src/omv/modules/py_helper.c index 3bef46f80..f86805b21 100644 --- a/src/omv/modules/py_helper.c +++ b/src/omv/modules/py_helper.c @@ -27,21 +27,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(py_func_unavailable_obj, 0, py_func_unavailable); image_t *py_helper_arg_to_image_mutable(const mp_obj_t arg) { image_t *arg_img = py_image_cobj(arg); - PY_ASSERT_TRUE_MSG(IMAGE_IS_MUTABLE(arg_img), "Image is not mutable!"); + PY_ASSERT_TRUE_MSG(arg_img->is_mutable, "Image is not mutable!"); return arg_img; } -image_t *py_helper_arg_to_image_mutable_bayer(const mp_obj_t arg) +image_t *py_helper_arg_to_image_not_compressed(const mp_obj_t arg) { image_t *arg_img = py_image_cobj(arg); - PY_ASSERT_TRUE_MSG(IMAGE_IS_MUTABLE_BAYER(arg_img), "Image is not mutable!"); - return arg_img; -} - -image_t *py_helper_arg_to_image_mutable_bayer_jpeg(const mp_obj_t arg) -{ - image_t *arg_img = py_image_cobj(arg); - PY_ASSERT_TRUE_MSG(IMLIB_PIXFORMAT_IS_VALID(arg_img->pixfmt), "Image is not mutable!"); + PY_ASSERT_FALSE_MSG(arg_img->is_compressed, "Image is compressed!"); return arg_img; } @@ -52,13 +45,6 @@ image_t *py_helper_arg_to_image_grayscale(const mp_obj_t arg) return arg_img; } -image_t *py_helper_arg_to_image_color(const mp_obj_t arg) -{ - image_t *arg_img = py_image_cobj(arg); - PY_ASSERT_TRUE_MSG(arg_img->pixfmt == PIXFORMAT_RGB565, "Image is not RGB565!"); - return arg_img; -} - image_t *py_helper_keyword_to_image_mutable(uint n_args, const mp_obj_t *args, uint arg_index, mp_map_t *kw_args, mp_obj_t kw, image_t *default_val) { diff --git a/src/omv/modules/py_helper.h b/src/omv/modules/py_helper.h index 10150ff87..134737bb5 100644 --- a/src/omv/modules/py_helper.h +++ b/src/omv/modules/py_helper.h @@ -13,10 +13,8 @@ #include "imlib.h" extern const mp_obj_fun_builtin_var_t py_func_unavailable_obj; image_t *py_helper_arg_to_image_mutable(const mp_obj_t arg); -image_t *py_helper_arg_to_image_mutable_bayer(const mp_obj_t arg); -image_t *py_helper_arg_to_image_mutable_bayer_jpeg(const mp_obj_t arg); +image_t *py_helper_arg_to_image_not_compressed(const mp_obj_t arg); image_t *py_helper_arg_to_image_grayscale(const mp_obj_t arg); -image_t *py_helper_arg_to_image_color(const mp_obj_t arg); image_t *py_helper_keyword_to_image_mutable(uint n_args, const mp_obj_t *args, uint arg_index, mp_map_t *kw_args, mp_obj_t kw, image_t *default_val); image_t *py_helper_keyword_to_image_mutable_mask(uint n_args, const mp_obj_t *args, uint arg_index, diff --git a/src/omv/modules/py_image.c b/src/omv/modules/py_image.c index a98c3afd2..b6333b5af 100644 --- a/src/omv/modules/py_image.c +++ b/src/omv/modules/py_image.c @@ -312,8 +312,8 @@ STATIC mp_obj_t py_image_it_iternext(mp_obj_t self_in) return row; } } - case PIXFORMAT_BAYER_ANY: - case PIXFORMAT_GRAYSCALE: { + case PIXFORMAT_GRAYSCALE: + case PIXFORMAT_BAYER_ANY: { if (self->cur >= img->h) { return MP_OBJ_STOP_ITERATION; } else { @@ -325,7 +325,8 @@ STATIC mp_obj_t py_image_it_iternext(mp_obj_t self_in) return row; } } - case PIXFORMAT_RGB565: { + case PIXFORMAT_RGB565: + case PIXFORMAT_YUV_ANY: { if (self->cur >= img->h) { return MP_OBJ_STOP_ITERATION; } else { @@ -373,8 +374,13 @@ static void py_image_print(const mp_print_t *print, mp_obj_t self, mp_print_kind (image->pixfmt == PIXFORMAT_BINARY) ? "binary" : (image->pixfmt == PIXFORMAT_GRAYSCALE) ? "grayscale" : (image->pixfmt == PIXFORMAT_RGB565) ? "rgb565" : + (image->pixfmt == PIXFORMAT_BAYER_BGGR) ? "bayer_bggr" : + (image->pixfmt == PIXFORMAT_BAYER_GBRG) ? "bayer_gbrg" : + (image->pixfmt == PIXFORMAT_BAYER_GRBG) ? "bayer_grbg" : + (image->pixfmt == PIXFORMAT_BAYER_RGGB) ? "bayer_rggb" : (image->pixfmt == PIXFORMAT_YUV422) ? "yuv422" : - (image->pixfmt == PIXFORMAT_JPEG) ? "jpeg" : "bayer", + (image->pixfmt == PIXFORMAT_YVU422) ? "yvu422" : + (image->pixfmt == PIXFORMAT_JPEG) ? "jpeg" : "unknown", image_size(image)); } } @@ -401,8 +407,8 @@ static mp_obj_t py_image_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value mp_uint_t i = mp_get_index(self->base.type, image->w * image->h, index, false); return mp_obj_new_int(IMAGE_GET_BINARY_PIXEL(image, i % image->w, i / image->w)); } - case PIXFORMAT_BAYER_ANY: - case PIXFORMAT_GRAYSCALE: { + case PIXFORMAT_GRAYSCALE: + case PIXFORMAT_BAYER_ANY: { if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(image->w * image->h, index, &slice)) { @@ -419,7 +425,8 @@ static mp_obj_t py_image_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value uint8_t p = IMAGE_GET_GRAYSCALE_PIXEL(image, i % image->w, i / image->w); return mp_obj_new_int(p); } - case PIXFORMAT_RGB565: { + case PIXFORMAT_RGB565: + case PIXFORMAT_YUV_ANY: { if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(image->w * image->h, index, &slice)) { @@ -428,17 +435,27 @@ static mp_obj_t py_image_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value mp_obj_tuple_t *result = mp_obj_new_tuple(slice.stop - slice.start, NULL); for (mp_uint_t i = 0; i < result->len; i++) { uint16_t p = IMAGE_GET_RGB565_PIXEL(image, (slice.start + i) % image->w, (slice.start + i) / image->w); - result->items[i] = mp_obj_new_tuple(3, (mp_obj_t []) {mp_obj_new_int(COLOR_RGB565_TO_R8(p)), - mp_obj_new_int(COLOR_RGB565_TO_G8(p)), - mp_obj_new_int(COLOR_RGB565_TO_B8(p))}); + if (image->is_yuv) { + result->items[i] = mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_int(p & 0xff), + mp_obj_new_int((p >> 8) & 0xff)}); + } else { + result->items[i] = mp_obj_new_tuple(3, (mp_obj_t []) {mp_obj_new_int(COLOR_RGB565_TO_R8(p)), + mp_obj_new_int(COLOR_RGB565_TO_G8(p)), + mp_obj_new_int(COLOR_RGB565_TO_B8(p))}); + } } return result; } mp_uint_t i = mp_get_index(self->base.type, image->w * image->h, index, false); uint16_t p = IMAGE_GET_RGB565_PIXEL(image, i % image->w, i / image->w); - return mp_obj_new_tuple(3, (mp_obj_t []) {mp_obj_new_int(COLOR_RGB565_TO_R8(p)), - mp_obj_new_int(COLOR_RGB565_TO_G8(p)), - mp_obj_new_int(COLOR_RGB565_TO_B8(p))}); + if (image->is_yuv) { + return mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_int(p & 0xff), + mp_obj_new_int((p >> 8) & 0xff)}); + } else { + return mp_obj_new_tuple(3, (mp_obj_t []) {mp_obj_new_int(COLOR_RGB565_TO_R8(p)), + mp_obj_new_int(COLOR_RGB565_TO_G8(p)), + mp_obj_new_int(COLOR_RGB565_TO_B8(p))}); + } } case PIXFORMAT_JPEG: { if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { @@ -486,8 +503,8 @@ static mp_obj_t py_image_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value IMAGE_PUT_BINARY_PIXEL(image, i % image->w, i / image->w, mp_obj_get_int(value)); return mp_const_none; } - case PIXFORMAT_BAYER_ANY: - case PIXFORMAT_GRAYSCALE: { + case PIXFORMAT_GRAYSCALE: + case PIXFORMAT_BAYER_ANY: { if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(image->w * image->h, index, &slice)) { @@ -515,7 +532,8 @@ static mp_obj_t py_image_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value IMAGE_PUT_GRAYSCALE_PIXEL(image, i % image->w, i / image->w, p); return mp_const_none; } - case PIXFORMAT_RGB565: { + case PIXFORMAT_RGB565: + case PIXFORMAT_YUV_ANY: { if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(image->w * image->h, index, &slice)) { @@ -528,16 +546,36 @@ static mp_obj_t py_image_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value PY_ASSERT_TRUE_MSG(value_l_len == (slice.stop - slice.start), "cannot grow or shrink image"); for (mp_uint_t i = 0; i < (slice.stop - slice.start); i++) { mp_obj_t *value_2; - mp_obj_get_array_fixed_n(value_l[i], 3, &value_2); - uint16_t p = COLOR_R8_G8_B8_TO_RGB565(mp_obj_get_int(value_2[0]), mp_obj_get_int(value_2[1]), mp_obj_get_int(value_2[2])); - IMAGE_PUT_RGB565_PIXEL(image, (slice.start + i) % image->w, (slice.start + i) / image->w, p); + uint16_t p; + if (image->is_yuv) { + mp_obj_get_array_fixed_n(value_l[i], 2, &value_2); + p = (mp_obj_get_int(value_2[0]) & 0xff) | + (mp_obj_get_int(value_2[1]) & 0xff) << 8; + } else { + mp_obj_get_array_fixed_n(value_l[i], 3, &value_2); + p = COLOR_R8_G8_B8_TO_RGB565(mp_obj_get_int(value_2[0]), + mp_obj_get_int(value_2[1]), + mp_obj_get_int(value_2[2])); + } + IMAGE_PUT_RGB565_PIXEL(image, (slice.start + i) % image->w, + (slice.start + i) / image->w, p); } } else { mp_obj_t *value_2; - mp_obj_get_array_fixed_n(value, 3, &value_2); - uint16_t p = COLOR_R8_G8_B8_TO_RGB565(mp_obj_get_int(value_2[0]), mp_obj_get_int(value_2[1]), mp_obj_get_int(value_2[2])); + uint16_t p; + if (image->is_yuv) { + mp_obj_get_array_fixed_n(value, 2, &value_2); + p = (mp_obj_get_int(value_2[0]) & 0xff) | + (mp_obj_get_int(value_2[1]) & 0xff) << 8; + } else { + mp_obj_get_array_fixed_n(value, 3, &value_2); + p = COLOR_R8_G8_B8_TO_RGB565(mp_obj_get_int(value_2[0]), + mp_obj_get_int(value_2[1]), + mp_obj_get_int(value_2[2])); + } for (mp_uint_t i = 0; i < (slice.stop - slice.start); i++) { - IMAGE_PUT_RGB565_PIXEL(image, (slice.start + i) % image->w, (slice.start + i) / image->w, p); + IMAGE_PUT_RGB565_PIXEL(image, (slice.start + i) % image->w, + (slice.start + i) / image->w, p); } } return mp_const_none; @@ -626,6 +664,8 @@ static mp_obj_t py_image_format(mp_obj_t img_obj) return mp_obj_new_int(PIXFORMAT_RGB565); case PIXFORMAT_BAYER_ANY: return mp_obj_new_int(PIXFORMAT_BAYER); + case PIXFORMAT_YUV_ANY: + return mp_obj_new_int(PIXFORMAT_YUV422); default: return mp_obj_new_int(PIXFORMAT_JPEG); } @@ -647,7 +687,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_bytearray_obj, py_image_bytearray); STATIC mp_obj_t py_image_get_pixel(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable_bayer(args[0]); + image_t *arg_img = py_helper_arg_to_image_not_compressed(args[0]); const mp_obj_t *arg_vec; uint offset = py_helper_consume_array(n_args, args, 1, 2, &arg_vec); @@ -707,7 +747,18 @@ STATIC mp_obj_t py_image_get_pixel(uint n_args, const mp_obj_t *args, mp_map_t * pixel_tuple[2] = mp_obj_new_int(COLOR_RGB565_TO_B8(pixel)); return mp_obj_new_tuple(3, pixel_tuple); } else { - return mp_obj_new_int(IMAGE_GET_GRAYSCALE_PIXEL(arg_img, arg_x, arg_y)); // Correct! + return mp_obj_new_int(IMAGE_GET_BAYER_PIXEL(arg_img, arg_x, arg_y)); + } + case PIXFORMAT_YUV_ANY: + if (arg_rgbtuple) { + uint16_t pixel; imlib_deyuv_line(arg_x, arg_x + 1, arg_y, &pixel, PIXFORMAT_RGB565, arg_img); + mp_obj_t pixel_tuple[3]; + pixel_tuple[0] = mp_obj_new_int(COLOR_RGB565_TO_R8(pixel)); + pixel_tuple[1] = mp_obj_new_int(COLOR_RGB565_TO_G8(pixel)); + pixel_tuple[2] = mp_obj_new_int(COLOR_RGB565_TO_B8(pixel)); + return mp_obj_new_tuple(3, pixel_tuple); + } else { + return mp_obj_new_int(IMAGE_GET_YUV_PIXEL(arg_img, arg_x, arg_y)); } default: return mp_const_none; } @@ -716,7 +767,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_get_pixel_obj, 2, py_image_get_pixel) STATIC mp_obj_t py_image_set_pixel(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable_bayer(args[0]); + image_t *arg_img = py_helper_arg_to_image_not_compressed(args[0]); const mp_obj_t *arg_vec; uint offset = py_helper_consume_array(n_args, args, 1, 2, &arg_vec); @@ -735,18 +786,16 @@ STATIC mp_obj_t py_image_set_pixel(uint n_args, const mp_obj_t *args, mp_map_t * IMAGE_PUT_BINARY_PIXEL(arg_img, arg_x, arg_y, arg_c); return args[0]; } - case PIXFORMAT_GRAYSCALE: { + case PIXFORMAT_GRAYSCALE: + case PIXFORMAT_BAYER_ANY: { // re-use IMAGE_PUT_GRAYSCALE_PIXEL(arg_img, arg_x, arg_y, arg_c); return args[0]; } - case PIXFORMAT_RGB565: { + case PIXFORMAT_RGB565: + case PIXFORMAT_YUV_ANY: { // re-use IMAGE_PUT_RGB565_PIXEL(arg_img, arg_x, arg_y, arg_c); return args[0]; } - case PIXFORMAT_BAYER_ANY: { - IMAGE_PUT_GRAYSCALE_PIXEL(arg_img, arg_x, arg_y, arg_c); // Correct! - return args[0]; - } default: return args[0]; } } @@ -861,7 +910,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_midpoint_pooled_obj, 3, py_image_midp 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) { - image_t *src_img = py_helper_arg_to_image_mutable_bayer_jpeg(args[0]); + image_t *src_img = py_image_cobj(args[0]); float arg_x_scale = 1.f; bool got_x_scale = py_helper_keyword_float_maybe(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_scale), &arg_x_scale); @@ -926,7 +975,7 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_pa if (mp_obj_is_integer(copy_obj)) { copy = mp_obj_get_int(copy_obj); } else { - arg_other = py_helper_arg_to_image_mutable_bayer(copy_obj); + arg_other = py_helper_arg_to_image_not_compressed(copy_obj); } } @@ -1004,6 +1053,24 @@ 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.is_yuv) { + if (((arg_x_scale != 1) && (arg_x_scale != -1)) || + ((arg_y_scale != 1) && (arg_y_scale != -1)) || + (arg_rgb_channel != -1) || + (arg_alpha != 256) || + (color_palette != NULL) || + (alpha_palette != NULL)) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Only YUV422 copying/cropping is supported!")); + } else { + if (arg_roi.x % 2) { + dst_img.pixfmt = (dst_img.pixfmt == PIXFORMAT_YUV422) ? PIXFORMAT_YVU422 : PIXFORMAT_YUV422; + } + hint &= ~(IMAGE_HINT_AREA | + IMAGE_HINT_BICUBIC | + IMAGE_HINT_BILINEAR | + 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) || @@ -1138,7 +1205,7 @@ static mp_obj_t py_image_compress(uint n_args, const mp_obj_t *args, mp_map_t *k { if (IM_IS_JPEG((image_t *) py_image_cobj(args[0]))) return args[0]; - image_t *arg_img = py_helper_arg_to_image_mutable_bayer(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!"); @@ -1158,7 +1225,7 @@ static mp_obj_t py_image_compress_for_ide(uint n_args, const mp_obj_t *args, mp_ { 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_mutable_bayer(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!"); @@ -1186,7 +1253,7 @@ static mp_obj_t py_image_compressed(uint n_args, const mp_obj_t *args, mp_map_t return py_image_from_struct(&out); } - arg_img = py_helper_arg_to_image_mutable_bayer(args[0]); + 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!"); @@ -1206,7 +1273,7 @@ static mp_obj_t py_image_compressed_for_ide(uint n_args, const mp_obj_t *args, m { 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_mutable_bayer(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!"); @@ -1258,7 +1325,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_image_flush_obj, py_image_flush); STATIC mp_obj_t py_image_clear(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable_bayer(args[0]); + image_t *arg_img = py_helper_arg_to_image_not_compressed(args[0]); image_t *arg_msk = py_helper_keyword_to_image_mutable_mask(n_args, args, 1, kw_args); @@ -1515,7 +1582,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_draw_edges_obj, 2, py_image_draw_edge STATIC mp_obj_t py_image_draw_image(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]); - image_t *arg_other = py_helper_arg_to_image_mutable_bayer(args[1]); + image_t *arg_other = py_helper_arg_to_image_not_compressed(args[1]); const mp_obj_t *arg_vec; uint offset = py_helper_consume_array(n_args, args, 2, 2, &arg_vec); @@ -5226,7 +5293,7 @@ static const mp_obj_type_t py_apriltag_type = { static mp_obj_t py_image_find_apriltags(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable_bayer_jpeg(args[0]); + image_t *arg_img = py_image_cobj(args[0]); rectangle_t roi; py_helper_keyword_rectangle_roi(arg_img, n_args, args, 1, kw_args, &roi); @@ -6905,8 +6972,8 @@ static const mp_rom_map_elem_t globals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_BINARY), MP_ROM_INT(PIXFORMAT_BINARY)}, /* 1BPP/BINARY*/ {MP_ROM_QSTR(MP_QSTR_GRAYSCALE), MP_ROM_INT(PIXFORMAT_GRAYSCALE)},/* 1BPP/GRAYSCALE*/ {MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(PIXFORMAT_RGB565)}, /* 2BPP/RGB565*/ - {MP_ROM_QSTR(MP_QSTR_YUV422), MP_ROM_INT(PIXFORMAT_YUV422)}, /* 2BPP/YUV422*/ {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_AREA), MP_ROM_INT(IMAGE_HINT_AREA)}, {MP_ROM_QSTR(MP_QSTR_BILINEAR), MP_ROM_INT(IMAGE_HINT_BILINEAR)}, diff --git a/src/omv/modules/py_sensor.c b/src/omv/modules/py_sensor.c index bc88953d1..55e66d4cf 100644 --- a/src/omv/modules/py_sensor.c +++ b/src/omv/modules/py_sensor.c @@ -991,8 +991,8 @@ STATIC const mp_map_elem_t globals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_BINARY), MP_OBJ_NEW_SMALL_INT(PIXFORMAT_BINARY)}, /* 1BPP/BINARY*/ { MP_OBJ_NEW_QSTR(MP_QSTR_GRAYSCALE), MP_OBJ_NEW_SMALL_INT(PIXFORMAT_GRAYSCALE)},/* 1BPP/GRAYSCALE*/ { MP_OBJ_NEW_QSTR(MP_QSTR_RGB565), MP_OBJ_NEW_SMALL_INT(PIXFORMAT_RGB565)}, /* 2BPP/RGB565*/ - { MP_OBJ_NEW_QSTR(MP_QSTR_YUV422), MP_OBJ_NEW_SMALL_INT(PIXFORMAT_YUV422)}, /* 2BPP/YUV422*/ { MP_OBJ_NEW_QSTR(MP_QSTR_BAYER), MP_OBJ_NEW_SMALL_INT(PIXFORMAT_BAYER)}, /* 1BPP/RAW*/ + { MP_OBJ_NEW_QSTR(MP_QSTR_YUV422), MP_OBJ_NEW_SMALL_INT(PIXFORMAT_YUV422)}, /* 2BPP/YUV422*/ { MP_OBJ_NEW_QSTR(MP_QSTR_JPEG), MP_OBJ_NEW_SMALL_INT(PIXFORMAT_JPEG)}, /* JPEG/COMPRESSED*/ { MP_OBJ_NEW_QSTR(MP_QSTR_OV2640), MP_OBJ_NEW_SMALL_INT(OV2640_ID)}, { MP_OBJ_NEW_QSTR(MP_QSTR_OV5640), MP_OBJ_NEW_SMALL_INT(OV5640_ID)}, diff --git a/src/omv/ports/nrf/omv_portconfig.mk b/src/omv/ports/nrf/omv_portconfig.mk index 1846e9ea5..1d49aae73 100644 --- a/src/omv/ports/nrf/omv_portconfig.mk +++ b/src/omv/ports/nrf/omv_portconfig.mk @@ -172,6 +172,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/imlib/, \ stats.o \ template.o \ xyz_tab.o \ + yuv.o \ zbar.o \ ) diff --git a/src/omv/ports/rp2/omv_portconfig.cmake b/src/omv/ports/rp2/omv_portconfig.cmake index b1a0c1181..493d35162 100644 --- a/src/omv/ports/rp2/omv_portconfig.cmake +++ b/src/omv/ports/rp2/omv_portconfig.cmake @@ -168,6 +168,7 @@ target_sources(${MICROPY_TARGET} PRIVATE ${TOP_DIR}/${OMV_DIR}/imlib/stats.c ${TOP_DIR}/${OMV_DIR}/imlib/template.c ${TOP_DIR}/${OMV_DIR}/imlib/xyz_tab.c + ${TOP_DIR}/${OMV_DIR}/imlib/yuv.c ${TOP_DIR}/${OMV_DIR}/imlib/zbar.c ${TOP_DIR}/${OMV_DIR}/ports/${PORT}/main.c diff --git a/src/omv/ports/stm32/modules/py_lcd.c b/src/omv/ports/stm32/modules/py_lcd.c index 80d3a0390..2684c7ac3 100644 --- a/src/omv/ports/stm32/modules/py_lcd.c +++ b/src/omv/ports/stm32/modules/py_lcd.c @@ -18,6 +18,7 @@ #include "py_lcd_cec.h" #include "py_lcd_touch.h" #include "py_helper.h" +#include "py_image.h" #include "extmod/machine_i2c.h" #include "omv_boardconfig.h" #include STM32_HAL_H @@ -1606,7 +1607,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_lcd_get_point_y_position_obj, py_lcd_get_poi STATIC mp_obj_t py_lcd_display(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable_bayer_jpeg(args[0]); + image_t *arg_img = py_image_cobj(args[0]); int arg_x_off = 0; int arg_y_off = 0; diff --git a/src/omv/ports/stm32/modules/py_tv.c b/src/omv/ports/stm32/modules/py_tv.c index 9567f9604..fa56e5bcf 100644 --- a/src/omv/ports/stm32/modules/py_tv.c +++ b/src/omv/ports/stm32/modules/py_tv.c @@ -15,6 +15,7 @@ #include "spi.h" #include "py_helper.h" +#include "py_image.h" #include "omv_boardconfig.h" #include STM32_HAL_H @@ -679,7 +680,7 @@ static void spi_tv_display(image_t *src_img, int dst_x_start, int dst_y_start, f const uint16_t *color_palette, const uint8_t *alpha_palette, image_hint_t hint) { - bool rgb565 = ((rgb_channel == -1) && IMAGE_IS_COLOR(src_img)) || color_palette; + bool rgb565 = ((rgb_channel == -1) && src_img->is_color) || color_palette; imlib_draw_row_callback_t cb = rgb565 ? spi_tv_draw_image_cb_rgb565 : spi_tv_draw_image_cb_grayscale; image_t dst_img; @@ -957,7 +958,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_tv_channel_obj, 0, 1, py_tv_channe STATIC mp_obj_t py_tv_display(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable_bayer_jpeg(args[0]); + image_t *arg_img = py_image_cobj(args[0]); int arg_x_off = 0; int arg_y_off = 0; diff --git a/src/omv/ports/stm32/omv_portconfig.mk b/src/omv/ports/stm32/omv_portconfig.mk index ea3d7dbeb..0ed201114 100644 --- a/src/omv/ports/stm32/omv_portconfig.mk +++ b/src/omv/ports/stm32/omv_portconfig.mk @@ -206,6 +206,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/imlib/, \ stats.o \ template.o \ xyz_tab.o \ + yuv.o \ zbar.o \ ) diff --git a/src/omv/ports/stm32/sensor.c b/src/omv/ports/stm32/sensor.c index 6cbfb7b36..8f35bddb3 100644 --- a/src/omv/ports/stm32/sensor.c +++ b/src/omv/ports/stm32/sensor.c @@ -627,7 +627,8 @@ void DCMI_DMAConvCpltUser(uint32_t addr) #if (OMV_ENABLE_SENSOR_MDMA == 1) mdma_memcpy(buffer, dst16, src16, sizeof(uint16_t), sensor.transpose); #else - if (sensor.hw_flags.rgb_swap) { + if ((sensor.pixformat == PIXFORMAT_RGB565 && sensor.hw_flags.rgb_swap) + || (sensor.pixformat == PIXFORMAT_YUV422 && sensor.hw_flags.yuv_swap)) { if (!sensor.transpose) { unaligned_memcpy_rev16(dst16, src16, MAIN_FB()->u); } else { @@ -668,7 +669,8 @@ static void mdma_config(MDMA_InitTypeDef *init, sensor_t *sensor, uint32_t bytes init->SourceBlockAddressOffset = 0; init->DestBlockAddressOffset = 0; - if ((sensor->pixformat == PIXFORMAT_RGB565) && sensor->hw_flags.rgb_swap) { + if ((sensor->pixformat == PIXFORMAT_RGB565 && sensor->hw_flags.rgb_swap) + || (sensor->pixformat == PIXFORMAT_YUV422 && sensor->hw_flags.yuv_swap)) { init->Endianness = MDMA_LITTLE_BYTE_ENDIANNESS_EXCHANGE; } else { init->Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; @@ -963,7 +965,6 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) #endif break; case PIXFORMAT_RGB565: - case PIXFORMAT_YUV422: MAIN_FB()->pixfmt = PIXFORMAT_RGB565; #if (OMV_ENABLE_SENSOR_MDMA == 1) // Flush data for MDMA @@ -978,6 +979,17 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) SCB_InvalidateDCache_by_Addr(buffer->data, w * h); #endif break; + case PIXFORMAT_YUV422: { + bool yuv_order = sensor->hw_flags.yuv_order == SENSOR_HW_FLAGS_YUV422; + int even = yuv_order ? PIXFORMAT_YUV422 : PIXFORMAT_YVU422; + int odd = yuv_order ? PIXFORMAT_YVU422 : PIXFORMAT_YUV422; + MAIN_FB()->pixfmt = (MAIN_FB()->x % 2) ? odd : even; + #if (OMV_ENABLE_SENSOR_MDMA == 1) + // Flush data for MDMA + SCB_InvalidateDCache_by_Addr(buffer->data, w * h * sizeof(uint16_t)); + #endif + break; + } case PIXFORMAT_JPEG: { int32_t size = 0; if (sensor->chip_id == OV5640_ID) { @@ -998,7 +1010,6 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) SCB_InvalidateDCache_by_Addr(buffer->data, size); #endif } - // Clean trailing data after 0xFFD9 at the end of the jpeg byte stream. MAIN_FB()->pixfmt = PIXFORMAT_JPEG; MAIN_FB()->size = jpeg_clean_trailing_bytes(size, buffer->data); diff --git a/src/omv/sensors/gc2145.c b/src/omv/sensors/gc2145.c index 5c852a470..e430bb332 100644 --- a/src/omv/sensors/gc2145.c +++ b/src/omv/sensors/gc2145.c @@ -748,7 +748,7 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) break; case PIXFORMAT_BAYER: // Make sure odd/even row are switched to work with our bayer conversion. - ret |= cambus_writeb(&sensor->bus, sensor->slv_addr, + ret |= cambus_writeb(&sensor->bus, sensor->slv_addr, REG_SYNC_MODE, REG_SYNC_MODE_DEF | REG_SYNC_MODE_ROW_SWITCH); ret |= cambus_writeb(&sensor->bus, sensor->slv_addr, REG_OUTPUT_FMT, REG_OUTPUT_SET_FMT(reg, REG_OUTPUT_FMT_BAYER)); @@ -881,6 +881,7 @@ int gc2145_init(sensor_t *sensor) sensor->hw_flags.jpege = 0; sensor->hw_flags.gs_bpp = 2; sensor->hw_flags.rgb_swap = 1; + sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; return 0; } diff --git a/src/omv/sensors/mt9m114.c b/src/omv/sensors/mt9m114.c index 92543d0ef..73af8e5ff 100644 --- a/src/omv/sensors/mt9m114.c +++ b/src/omv/sensors/mt9m114.c @@ -1013,6 +1013,7 @@ int mt9m114_init(sensor_t *sensor) sensor->hw_flags.gs_bpp = 2; sensor->hw_flags.rgb_swap = 0; sensor->hw_flags.bayer = SENSOR_HW_FLAGS_BAYER_GBRG; + sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; return 0; } diff --git a/src/omv/sensors/ov2640.c b/src/omv/sensors/ov2640.c index 516564ada..89738cf1a 100644 --- a/src/omv/sensors/ov2640.c +++ b/src/omv/sensors/ov2640.c @@ -857,6 +857,7 @@ int ov2640_init(sensor_t *sensor) sensor->hw_flags.jpeg_mode = 3; sensor->hw_flags.gs_bpp = 2; sensor->hw_flags.rgb_swap = 0; + sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; return 0; } diff --git a/src/omv/sensors/ov5640.c b/src/omv/sensors/ov5640.c index c34764570..2b6551c53 100644 --- a/src/omv/sensors/ov5640.c +++ b/src/omv/sensors/ov5640.c @@ -1383,6 +1383,7 @@ int ov5640_init(sensor_t *sensor) sensor->hw_flags.jpeg_mode = 4; sensor->hw_flags.gs_bpp = 1; sensor->hw_flags.rgb_swap = 0; + sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; return 0; } diff --git a/src/omv/sensors/ov7670.c b/src/omv/sensors/ov7670.c index ac9c78a39..7b1ef1674 100644 --- a/src/omv/sensors/ov7670.c +++ b/src/omv/sensors/ov7670.c @@ -454,6 +454,7 @@ int ov7670_init(sensor_t *sensor) sensor->hw_flags.jpege = 0; sensor->hw_flags.gs_bpp = 2; sensor->hw_flags.rgb_swap = 1; + sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; return 0; } diff --git a/src/omv/sensors/ov7725.c b/src/omv/sensors/ov7725.c index 9c105063e..33b37642c 100644 --- a/src/omv/sensors/ov7725.c +++ b/src/omv/sensors/ov7725.c @@ -624,6 +624,7 @@ int ov7725_init(sensor_t *sensor) sensor->hw_flags.jpege = 0; sensor->hw_flags.gs_bpp = 2; sensor->hw_flags.rgb_swap = 1; + sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; return 0; } diff --git a/src/omv/sensors/ov9650.c b/src/omv/sensors/ov9650.c index cabd7c8a8..37f68c0d8 100644 --- a/src/omv/sensors/ov9650.c +++ b/src/omv/sensors/ov9650.c @@ -540,6 +540,7 @@ int ov9650_init(sensor_t *sensor) sensor->hw_flags.jpege = 0; sensor->hw_flags.gs_bpp = 2; sensor->hw_flags.rgb_swap = 1; + sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; return 0; }