mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
imlib: Add hardware JPEG decoder support.
This commit is contained in:
parent
b5b26ff390
commit
38c26cec92
@ -13,14 +13,281 @@
|
||||
#include "py/obj.h"
|
||||
#include "py/nlr.h"
|
||||
#include "py/runtime.h"
|
||||
#define TIME_JPEG (1)
|
||||
#define TIME_JPEG (0)
|
||||
#if (TIME_JPEG == 1)
|
||||
#include "py/mphal.h"
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include "imlib.h"
|
||||
|
||||
/* Defines and variables */
|
||||
static bool jpeg_is_progressive(image_t *img)
|
||||
{
|
||||
uint8_t *data = img->data;
|
||||
for (uint32_t i=0; i<img->size;) {
|
||||
uint8_t marker = data[i++];
|
||||
if (marker == 0xFF) {
|
||||
continue;
|
||||
} else if (marker == 0xD8) { //SOI
|
||||
continue;
|
||||
} else if (marker == 0xC0) { //SOF0/baseline
|
||||
return false;
|
||||
} else if (marker == 0xC2) { //SOF2/progressive
|
||||
return true;
|
||||
} else if (marker == 0xC2) { //EOI
|
||||
return false;
|
||||
} else if (marker == 0xDD) { //DRI 4 bytes payload.
|
||||
i += 4;
|
||||
} else if (marker >= 0xd0 && marker <= 0xd7) { //RSTn
|
||||
continue;
|
||||
} else if (i+2 < img->size) { // Other markers, variable size payload.
|
||||
i += __REV16(*((uint16_t*)(&data[i])));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0 //(OMV_HARDWARE_JPEG == 1)
|
||||
/* Hardware JPEG decoder */
|
||||
#include STM32_HAL_H
|
||||
#define JPEG_MCU_SIZE_444 (192)
|
||||
#define JPEG_MCU_SIZE_422 (256)
|
||||
#define JPEG_MCU_SIZE_420 (384)
|
||||
#define JPEG_MCU_SIZE_GRAY (64)
|
||||
#define debug_printf(...) //printf(__VA_ARGS__)
|
||||
|
||||
typedef struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint8_t *pixels;
|
||||
uint32_t bpp;
|
||||
uint32_t error;
|
||||
pixformat_t pixfmt;
|
||||
uint32_t mcu_count;
|
||||
uint32_t mcu_width;
|
||||
uint32_t mcu_height;
|
||||
uint32_t mcu_per_line;
|
||||
uint8_t *mcu_buffer[2];
|
||||
uint32_t mcu_buffer_size;
|
||||
} jpeg_state_t;
|
||||
|
||||
static jpeg_state_t *jpeg_state = NULL;
|
||||
static JPEG_HandleTypeDef hjpeg = {0};
|
||||
static DMA2D_HandleTypeDef hdma2d = {0};
|
||||
|
||||
static int dma2d_config(uint32_t pixfmt, uint32_t colorspace, uint32_t subsampling)
|
||||
{
|
||||
// Initial DMA2D configuration.
|
||||
hdma2d.Instance = DMA2D;
|
||||
HAL_DMA2D_DeInit(&hdma2d);
|
||||
hdma2d.XferCpltCallback = NULL;
|
||||
hdma2d.XferErrorCallback = NULL;
|
||||
|
||||
// Configure DMA2D output.
|
||||
hdma2d.Init.OutputOffset = 0;
|
||||
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; // Ignored in M2M mode.
|
||||
hdma2d.Init.RedBlueSwap = DMA2D_RB_REGULAR;
|
||||
hdma2d.Init.AlphaInverted = DMA2D_REGULAR_ALPHA;
|
||||
hdma2d.Init.BytesSwap = DMA2D_BYTES_REGULAR;
|
||||
hdma2d.Init.LineOffsetMode = DMA2D_LOM_PIXELS;
|
||||
|
||||
// Configure DMA2D input.
|
||||
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
|
||||
hdma2d.LayerCfg[1].InputAlpha = 0xFF;
|
||||
hdma2d.LayerCfg[1].InputOffset = 0;
|
||||
hdma2d.LayerCfg[1].RedBlueSwap = DMA2D_RB_REGULAR;
|
||||
hdma2d.LayerCfg[1].AlphaInverted = DMA2D_REGULAR_ALPHA;
|
||||
hdma2d.LayerCfg[1].ChromaSubSampling = subsampling;
|
||||
|
||||
if (pixfmt == PIXFORMAT_GRAYSCALE && colorspace == JPEG_GRAYSCALE_COLORSPACE) {
|
||||
hdma2d.Init.Mode = DMA2D_M2M; // GS -> GS, no PFC.
|
||||
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_L8;
|
||||
} else if (pixfmt == PIXFORMAT_RGB565 && colorspace == JPEG_YCBCR_COLORSPACE) {
|
||||
hdma2d.Init.Mode = DMA2D_M2M_PFC;
|
||||
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_YCBCR;
|
||||
} else { // Unsupported pixel format conversion
|
||||
return -1;
|
||||
}
|
||||
|
||||
// DMA2D initialization.
|
||||
HAL_DMA2D_Init(&hdma2d);
|
||||
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dma2d_copy_mcu(uint32_t x, uint32_t y, uint32_t mcu_width, uint32_t mcu_height, uint8_t *mcu_buffer)
|
||||
{
|
||||
// Wait for previous transfer.
|
||||
if (HAL_DMA2D_PollForTransfer(&hdma2d, 1000) != HAL_OK) {
|
||||
// TODO abort transfer and decoding, and set error flag.
|
||||
}
|
||||
|
||||
// Configure DMA2D output.
|
||||
hdma2d.Init.OutputOffset = jpeg_state->width - mcu_width;
|
||||
hdma2d.LayerCfg[1].InputOffset = jpeg_state->mcu_width - mcu_width;
|
||||
|
||||
// DMA2D initialization.
|
||||
HAL_DMA2D_Init(&hdma2d);
|
||||
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
|
||||
|
||||
uint8_t *dst = jpeg_state->pixels + (y * jpeg_state->width + x) * jpeg_state->bpp;
|
||||
|
||||
// Output size in pixels per line and number of lines
|
||||
HAL_DMA2D_Start(&hdma2d, (uint32_t) mcu_buffer, (uint32_t) dst, mcu_width, mcu_height);
|
||||
}
|
||||
|
||||
static void jpeg_info_ready_callback(JPEG_HandleTypeDef *hjpeg, JPEG_ConfTypeDef *jpeg_info)
|
||||
{
|
||||
uint32_t subsampling = DMA2D_NO_CSS;
|
||||
debug_printf("colorspace %ld subsampling %ld width %ld height %ld quality %ld\n",
|
||||
jpeg_info->ColorSpace, jpeg_info->ChromaSubsampling,
|
||||
jpeg_info->ImageWidth, jpeg_info->ImageHeight, jpeg_info->ImageQuality);
|
||||
|
||||
switch (jpeg_info->ChromaSubsampling) {
|
||||
case JPEG_420_SUBSAMPLING:
|
||||
subsampling = DMA2D_CSS_420;
|
||||
jpeg_state->mcu_width = 16;
|
||||
jpeg_state->mcu_height = 16;
|
||||
jpeg_state->mcu_buffer_size = JPEG_MCU_SIZE_420;
|
||||
break;
|
||||
case JPEG_422_SUBSAMPLING:
|
||||
subsampling = DMA2D_CSS_422;
|
||||
jpeg_state->mcu_width = 16;
|
||||
jpeg_state->mcu_height = 8;
|
||||
jpeg_state->mcu_buffer_size = JPEG_MCU_SIZE_422;
|
||||
break;
|
||||
case JPEG_444_SUBSAMPLING:
|
||||
subsampling = DMA2D_NO_CSS;
|
||||
jpeg_state->mcu_width = 8;
|
||||
jpeg_state->mcu_height = 8;
|
||||
if (jpeg_info->ColorSpace == JPEG_YCBCR_COLORSPACE) {
|
||||
jpeg_state->mcu_buffer_size = JPEG_MCU_SIZE_444;
|
||||
} else { // Grayscale
|
||||
jpeg_state->mcu_buffer_size = JPEG_MCU_SIZE_GRAY;
|
||||
}
|
||||
break;
|
||||
default: // unknown subsampling.
|
||||
goto config_error;
|
||||
}
|
||||
|
||||
if (dma2d_config(jpeg_state->pixfmt, jpeg_info->ColorSpace, subsampling) != 0) {
|
||||
goto config_error;
|
||||
}
|
||||
|
||||
// Allocate MCU buffer(s).
|
||||
jpeg_state->mcu_buffer[0] = fb_alloc(jpeg_state->mcu_buffer_size, FB_ALLOC_PREFER_SPEED | FB_ALLOC_CACHE_ALIGN);
|
||||
jpeg_state->mcu_buffer[1] = fb_alloc(jpeg_state->mcu_buffer_size, FB_ALLOC_PREFER_SPEED | FB_ALLOC_CACHE_ALIGN);
|
||||
jpeg_state->mcu_per_line = (jpeg_state->width / jpeg_state->mcu_width) + !!(jpeg_state->width % jpeg_state->mcu_width);
|
||||
|
||||
// Set JPEG output buffer.
|
||||
HAL_JPEG_ConfigOutputBuffer(hjpeg, jpeg_state->mcu_buffer[0], jpeg_state->mcu_buffer_size);
|
||||
return;
|
||||
|
||||
config_error:
|
||||
HAL_JPEG_Abort(hjpeg);
|
||||
jpeg_state->error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
static void jpeg_data_ready_callback(JPEG_HandleTypeDef *hjpeg, uint8_t *mcu_buffer, uint32_t length)
|
||||
{
|
||||
uint32_t mcu_width = jpeg_state->mcu_width;
|
||||
uint32_t mcu_height = jpeg_state->mcu_height;
|
||||
uint32_t x = (jpeg_state->mcu_count % jpeg_state->mcu_per_line) * mcu_width;
|
||||
uint32_t y = (jpeg_state->mcu_count / jpeg_state->mcu_per_line) * mcu_height;
|
||||
|
||||
if ((x + mcu_width) > jpeg_state->width) {
|
||||
mcu_width = jpeg_state->width - x;
|
||||
debug_printf("mcu: %lu mcu_perline %lu x:%lu y:%lu mcu_w:%lu mcu_cropped_w:%lu\n",
|
||||
jpeg_state->mcu_count % jpeg_state->mcu_per_line, jpeg_state->mcu_per_line,
|
||||
x, y, jpeg_state->mcu_width, mcu_width);
|
||||
}
|
||||
|
||||
if ((y + mcu_height) > jpeg_state->height) {
|
||||
mcu_height = jpeg_state->height - y;
|
||||
debug_printf("mcu: %ld mcu_perline %lu x:%lu y:%lu mcu_h:%lu mcu_cropped_h:%lu\n",
|
||||
jpeg_state->mcu_count % jpeg_state->mcu_per_line, jpeg_state->mcu_per_line,
|
||||
x, y, jpeg_state->mcu_height, mcu_height);
|
||||
}
|
||||
|
||||
// Flush MCU buffer cache for DMA2D.
|
||||
SCB_CleanDCache_by_Addr((uint32_t *) mcu_buffer, jpeg_state->mcu_buffer_size);
|
||||
|
||||
// Copy MCU to target frame buffer.
|
||||
dma2d_copy_mcu(x, y, mcu_width, mcu_height, mcu_buffer);
|
||||
|
||||
if (mcu_buffer == jpeg_state->mcu_buffer[0]) {
|
||||
HAL_JPEG_ConfigOutputBuffer(hjpeg, jpeg_state->mcu_buffer[1], jpeg_state->mcu_buffer_size);
|
||||
} else {
|
||||
HAL_JPEG_ConfigOutputBuffer(hjpeg, jpeg_state->mcu_buffer[0], jpeg_state->mcu_buffer_size);
|
||||
}
|
||||
jpeg_state->mcu_count++;
|
||||
}
|
||||
|
||||
void jpeg_decompress(image_t *dst, image_t *src)
|
||||
{
|
||||
hjpeg.Instance = JPEG;
|
||||
HAL_JPEG_DeInit(&hjpeg);
|
||||
HAL_JPEG_Init(&hjpeg);
|
||||
|
||||
// Register JPEG callbacks.
|
||||
HAL_JPEG_RegisterDataReadyCallback(&hjpeg, jpeg_data_ready_callback);
|
||||
HAL_JPEG_RegisterInfoReadyCallback(&hjpeg, jpeg_info_ready_callback);
|
||||
|
||||
// Supports decoding to Grayscale or RGB565 only.
|
||||
if (dst->pixfmt != PIXFORMAT_GRAYSCALE && dst->pixfmt != PIXFORMAT_RGB565) {
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Unsupported format."));
|
||||
}
|
||||
|
||||
// Supports decoding baseline JPEGs only.
|
||||
if (jpeg_is_progressive(src)) {
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Progressive JPEG is not supported."));
|
||||
}
|
||||
|
||||
// Set/reset JPEG state.
|
||||
jpeg_state_t jpeg_state_stk = {
|
||||
.width = dst->w,
|
||||
.height = dst->h,
|
||||
.pixels = dst->data,
|
||||
.bpp = dst->bpp,
|
||||
.error = false,
|
||||
.pixfmt = dst->pixfmt,
|
||||
.mcu_count = 0,
|
||||
.mcu_width = 0,
|
||||
.mcu_height = 0,
|
||||
.mcu_per_line = 0,
|
||||
.mcu_buffer = {NULL, NULL},
|
||||
.mcu_buffer_size = 0,
|
||||
};
|
||||
|
||||
jpeg_state = &jpeg_state_stk;
|
||||
|
||||
#if (TIME_JPEG == 1)
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
#endif
|
||||
|
||||
uint8_t buf[1024];
|
||||
// Start decoding JPEG headers.
|
||||
if (HAL_JPEG_Decode(&hjpeg, src->data, src->size, buf, sizeof(buf), 3000) != HAL_OK || jpeg_state->error) {
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("JPEG decoder failed"));
|
||||
}
|
||||
|
||||
if (jpeg_state->mcu_buffer[0] != NULL) {
|
||||
// Free MCU buffer(s).
|
||||
fb_free();
|
||||
fb_free();
|
||||
}
|
||||
|
||||
// Invalidate the dest image cache.
|
||||
SCB_InvalidateDCache_by_Addr(dst->pixels, image_size(dst));
|
||||
|
||||
#if (TIME_JPEG == 1)
|
||||
printf("time: %u ms\n", mp_hal_ticks_ms() - start);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
/* Software JPEG decoder */
|
||||
#define FILE_HIGHWATER 1536
|
||||
#define JPEG_FILE_BUF_SIZE 2048
|
||||
#define HUFF_TABLEN 273
|
||||
@ -2815,6 +3082,11 @@ void jpeg_decompress(image_t *dst, image_t *src)
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
#endif
|
||||
|
||||
// Supports decoding baseline JPEGs only.
|
||||
if (jpeg_is_progressive(src)) {
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Progressive JPEG is not supported."));
|
||||
}
|
||||
|
||||
if (JPEG_openRAM(&jpg, src->data, src->size, dst->data) == 0) {
|
||||
// failed to parse the header
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("JPEG decoder failed."));
|
||||
@ -2852,3 +3124,4 @@ void jpeg_decompress(image_t *dst, image_t *src)
|
||||
printf("time: %u ms\n", mp_hal_ticks_ms() - start);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user