mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
modules/py_image: Add support to control jpeg subsampling.
This commit is contained in:
parent
31f58cc99a
commit
b037b2208a
@ -166,7 +166,7 @@ void framebuffer_update_jpeg_buffer() {
|
||||
.pixels = jpeg_framebuffer->pixels
|
||||
};
|
||||
// Note: lower quality saves USB bandwidth and results in a faster IDE FPS.
|
||||
bool overflow = jpeg_compress(src, &dst, jpeg_framebuffer->quality, false);
|
||||
bool overflow = jpeg_compress(src, &dst, jpeg_framebuffer->quality, false, JPEG_SUBSAMPLING_AUTO);
|
||||
|
||||
if (overflow) {
|
||||
// JPEG buffer overflowed, reduce JPEG quality for the next frame
|
||||
|
||||
@ -753,6 +753,13 @@ bool image_get_mask_pixel(image_t *ptr, int x, int y);
|
||||
#define JPEG_422_YCBCR_MCU_SIZE ((JPEG_444_GS_MCU_SIZE) * 4)
|
||||
#define JPEG_420_YCBCR_MCU_SIZE ((JPEG_444_GS_MCU_SIZE) * 6)
|
||||
|
||||
typedef enum jpeg_subsampling {
|
||||
JPEG_SUBSAMPLING_AUTO = 0,
|
||||
JPEG_SUBSAMPLING_444 = 0x11, // Chroma subsampling 4:4:4 (No subsampling)
|
||||
JPEG_SUBSAMPLING_422 = 0x21, // Chroma subsampling 4:2:2
|
||||
JPEG_SUBSAMPLING_420 = 0x22, // Chroma subsampling 4:2:0
|
||||
} jpeg_subsampling_t;
|
||||
|
||||
// Old Image Macros - Will be refactor and removed. But, only after making sure through testing new macros work.
|
||||
|
||||
// Image kernels
|
||||
@ -964,12 +971,6 @@ typedef enum template_match {
|
||||
SEARCH_DS, // Diamond search
|
||||
} template_match_t;
|
||||
|
||||
typedef enum jpeg_subsample {
|
||||
JPEG_SUBSAMPLE_1x1 = 0x11, // 1x1 chroma subsampling (No subsampling)
|
||||
JPEG_SUBSAMPLE_2x1 = 0x21, // 2x2 chroma subsampling
|
||||
JPEG_SUBSAMPLE_2x2 = 0x22, // 2x2 chroma subsampling
|
||||
} jpeg_subsample_t;
|
||||
|
||||
typedef enum corner_detector_type {
|
||||
CORNER_FAST,
|
||||
CORNER_AGAST
|
||||
@ -1180,7 +1181,7 @@ void imlib_hardware_jpeg_deinit();
|
||||
void jpeg_get_mcu(image_t *src, int x_offset, int y_offset, int dx, int dy,
|
||||
int8_t *Y0, int8_t *CB, int8_t *CR);
|
||||
void jpeg_decompress(image_t *dst, image_t *src);
|
||||
bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc);
|
||||
bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc, jpeg_subsampling_t subsampling);
|
||||
bool jpeg_is_valid(image_t *img);
|
||||
int jpeg_clean_trailing_bytes(int bpp, uint8_t *data);
|
||||
void jpeg_read_geometry(FIL *fp, image_t *img, const char *path, jpg_read_settings_t *rs);
|
||||
|
||||
@ -1509,7 +1509,7 @@ static void jpeg_init(int quality) {
|
||||
}
|
||||
}
|
||||
|
||||
static void jpeg_write_headers(jpeg_buf_t *jpeg_buf, int w, int h, int bpp, jpeg_subsample_t jpeg_subsample) {
|
||||
static void jpeg_write_headers(jpeg_buf_t *jpeg_buf, int w, int h, int bpp, jpeg_subsampling_t subsampling) {
|
||||
// Number of components (1 or 3)
|
||||
uint8_t nr_comp = (bpp == 1)? 1 : 3;
|
||||
|
||||
@ -1574,7 +1574,7 @@ static void jpeg_write_headers(jpeg_buf_t *jpeg_buf, int w, int h, int bpp, jpeg
|
||||
jpeg_put_bytes(jpeg_buf, m_sof0, sizeof(m_sof0));
|
||||
for (int i = 0; i < nr_comp; i++) {
|
||||
// Component ID, HV sampling, q table idx
|
||||
jpeg_put_bytes(jpeg_buf, (uint8_t [3]) {i + 1, (i == 0 && bpp == 2)? jpeg_subsample:0x11, (i > 0)}, 3);
|
||||
jpeg_put_bytes(jpeg_buf, (uint8_t [3]) {i + 1, (i == 0 && bpp == 2)? subsampling:0x11, (i > 0)}, 3);
|
||||
|
||||
}
|
||||
|
||||
@ -1613,7 +1613,7 @@ static void jpeg_write_headers(jpeg_buf_t *jpeg_buf, int w, int h, int bpp, jpeg
|
||||
jpeg_put_bytes(jpeg_buf, (uint8_t [3]) {0x00, 0x3F, 0x0}, 3);
|
||||
}
|
||||
|
||||
bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) {
|
||||
bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc, jpeg_subsampling_t subsampling) {
|
||||
#if (TIME_JPEG == 1)
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
#endif
|
||||
@ -1642,22 +1642,30 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) {
|
||||
// Initialize quantization tables
|
||||
jpeg_init(quality);
|
||||
|
||||
jpeg_subsample_t jpeg_subsample = JPEG_SUBSAMPLE_1x1;
|
||||
|
||||
if (src->is_color) {
|
||||
if (quality <= 35) {
|
||||
jpeg_subsample = JPEG_SUBSAMPLE_2x2;
|
||||
} else if (quality < 60) {
|
||||
jpeg_subsample = JPEG_SUBSAMPLE_2x1;
|
||||
if (subsampling == JPEG_SUBSAMPLING_AUTO) {
|
||||
if (quality <= 35) {
|
||||
subsampling = JPEG_SUBSAMPLING_420;
|
||||
} else if (quality < 60) {
|
||||
subsampling = JPEG_SUBSAMPLING_422;
|
||||
} else {
|
||||
subsampling = JPEG_SUBSAMPLING_444;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
subsampling = JPEG_SUBSAMPLING_444;
|
||||
}
|
||||
|
||||
jpeg_write_headers(&jpeg_buf, src->w, src->h, src->is_color ? 2 : 1, jpeg_subsample);
|
||||
jpeg_write_headers(&jpeg_buf, src->w, src->h, src->is_color ? 2 : 1, subsampling);
|
||||
|
||||
int DCY = 0, DCU = 0, DCV = 0;
|
||||
|
||||
switch (jpeg_subsample) {
|
||||
case JPEG_SUBSAMPLE_1x1: {
|
||||
switch (subsampling) {
|
||||
// Quiet GCC compiler warning (this is never reached)
|
||||
case JPEG_SUBSAMPLING_AUTO: {
|
||||
break;
|
||||
}
|
||||
case JPEG_SUBSAMPLING_444: {
|
||||
int8_t YDU[JPEG_444_GS_MCU_SIZE];
|
||||
int8_t UDU[JPEG_444_GS_MCU_SIZE];
|
||||
int8_t VDU[JPEG_444_GS_MCU_SIZE];
|
||||
@ -1683,7 +1691,7 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JPEG_SUBSAMPLE_2x1: {
|
||||
case JPEG_SUBSAMPLING_422: {
|
||||
// color only
|
||||
int8_t YDU[JPEG_444_GS_MCU_SIZE * 2];
|
||||
int8_t UDU[JPEG_444_GS_MCU_SIZE * 2];
|
||||
@ -1738,7 +1746,7 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JPEG_SUBSAMPLE_2x2: {
|
||||
case JPEG_SUBSAMPLING_420: {
|
||||
// color only
|
||||
int8_t YDU[JPEG_444_GS_MCU_SIZE * 4];
|
||||
int8_t UDU[JPEG_444_GS_MCU_SIZE * 4];
|
||||
@ -1972,7 +1980,7 @@ void jpeg_write(image_t *img, const char *path, int quality) {
|
||||
// When jpeg_compress needs more memory than in currently allocated it
|
||||
// will try to realloc. MP will detect that the pointer is outside of
|
||||
// the heap and return NULL which will cause an out of memory error.
|
||||
jpeg_compress(img, &out, quality, false);
|
||||
jpeg_compress(img, &out, quality, false, JPEG_SUBSAMPLING_AUTO);
|
||||
file_write(&fp, out.pixels, out.size);
|
||||
fb_free(); // frees alloc in jpeg_compress()
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ void mjpeg_write(FIL *fp, int width, int height, uint32_t *frames, uint32_t *byt
|
||||
// When jpeg_compress needs more memory than in currently allocated it
|
||||
// will try to realloc. MP will detect that the pointer is outside of
|
||||
// the heap and return NULL which will cause an out of memory error.
|
||||
jpeg_compress(&temp, &dst_img, quality, true);
|
||||
jpeg_compress(&temp, &dst_img, quality, true, JPEG_SUBSAMPLING_AUTO);
|
||||
} else {
|
||||
dst_img.size = img->size;
|
||||
dst_img.data = img->data;
|
||||
|
||||
@ -1025,6 +1025,9 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_pa
|
||||
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);
|
||||
|
||||
jpeg_subsampling_t subsampling =
|
||||
py_helper_keyword_int(n_args, args, 14, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_subsampling), JPEG_SUBSAMPLING_AUTO);
|
||||
|
||||
if (copy_obj) {
|
||||
if (mp_obj_is_integer(copy_obj)) {
|
||||
copy = mp_obj_get_int(copy_obj);
|
||||
@ -1111,7 +1114,7 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, const uint16_t *default_color_pa
|
||||
(hint & (~IMAGE_HINT_CENTER)) | IMAGE_HINT_BLACK_BACKGROUND, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (((dst_img.pixfmt == PIXFORMAT_JPEG) && jpeg_compress(&temp, &dst_img_tmp, arg_q, false))
|
||||
if (((dst_img.pixfmt == PIXFORMAT_JPEG) && jpeg_compress(&temp, &dst_img_tmp, arg_q, false, subsampling))
|
||||
|| ((dst_img.pixfmt == PIXFORMAT_PNG) && png_compress(&temp, &dst_img_tmp))) {
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Compression Failed!"));
|
||||
}
|
||||
@ -7246,6 +7249,10 @@ static const mp_rom_map_elem_t globals_dict_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR_ROTATE_90), MP_ROM_INT(IMAGE_HINT_VFLIP | IMAGE_HINT_TRANSPOSE)},
|
||||
{MP_ROM_QSTR(MP_QSTR_ROTATE_180), MP_ROM_INT(IMAGE_HINT_HMIRROR | IMAGE_HINT_VFLIP)},
|
||||
{MP_ROM_QSTR(MP_QSTR_ROTATE_270), MP_ROM_INT(IMAGE_HINT_HMIRROR | IMAGE_HINT_TRANSPOSE)},
|
||||
{MP_ROM_QSTR(MP_QSTR_JPEG_SUBSAMPLING_AUTO), MP_ROM_INT(JPEG_SUBSAMPLING_AUTO)},
|
||||
{MP_ROM_QSTR(MP_QSTR_JPEG_SUBSAMPLING_444), MP_ROM_INT(JPEG_SUBSAMPLING_444)},
|
||||
{MP_ROM_QSTR(MP_QSTR_JPEG_SUBSAMPLING_422), MP_ROM_INT(JPEG_SUBSAMPLING_422)},
|
||||
{MP_ROM_QSTR(MP_QSTR_JPEG_SUBSAMPLING_420), MP_ROM_INT(JPEG_SUBSAMPLING_420)},
|
||||
#ifdef IMLIB_FIND_TEMPLATE
|
||||
{MP_ROM_QSTR(MP_QSTR_SEARCH_EX), MP_ROM_INT(SEARCH_EX)},
|
||||
{MP_ROM_QSTR(MP_QSTR_SEARCH_DS), MP_ROM_INT(SEARCH_DS)},
|
||||
|
||||
@ -105,7 +105,7 @@ static void jpeg_compress_data_ready(JPEG_HandleTypeDef *hjpeg, uint8_t *pDataOu
|
||||
}
|
||||
}
|
||||
|
||||
bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) {
|
||||
bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc, jpeg_subsampling_t subsampling) {
|
||||
#if (TIME_JPEG == 1)
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
#endif
|
||||
@ -132,9 +132,17 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) {
|
||||
mcu_size = JPEG_444_YCBCR_MCU_SIZE;
|
||||
JPEG_Info.ColorSpace = JPEG_YCBCR_COLORSPACE;
|
||||
JPEG_Info.ChromaSubsampling = JPEG_444_SUBSAMPLING;
|
||||
if (quality < 60) {
|
||||
if (subsampling == JPEG_SUBSAMPLING_AUTO) {
|
||||
if (quality < 60) {
|
||||
mcu_size = JPEG_422_YCBCR_MCU_SIZE;
|
||||
JPEG_Info.ChromaSubsampling = JPEG_422_SUBSAMPLING;
|
||||
}
|
||||
} else if (subsampling == JPEG_SUBSAMPLING_422) {
|
||||
mcu_size = JPEG_422_YCBCR_MCU_SIZE;
|
||||
JPEG_Info.ChromaSubsampling = JPEG_422_SUBSAMPLING;
|
||||
} else if (subsampling == JPEG_SUBSAMPLING_420) {
|
||||
// not supported
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user