mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Merge pull request #1557 from openmv/jpeg_hw_decoder
imlib: Add hardware JPEG decoder support.
This commit is contained in:
commit
08d5700150
@ -2734,7 +2734,7 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d
|
|||||||
if (!src_img->is_mutable) {
|
if (!src_img->is_mutable) {
|
||||||
new_src_img.pixfmt = new_not_mutable_pixfmt;
|
new_src_img.pixfmt = new_not_mutable_pixfmt;
|
||||||
size_t size = image_size(&new_src_img);
|
size_t size = image_size(&new_src_img);
|
||||||
new_src_img.data = fb_alloc(size, FB_ALLOC_NO_HINT);
|
new_src_img.data = fb_alloc(size, FB_ALLOC_CACHE_ALIGN);
|
||||||
|
|
||||||
switch (new_src_img.pixfmt) {
|
switch (new_src_img.pixfmt) {
|
||||||
case PIXFORMAT_BINARY: {
|
case PIXFORMAT_BINARY: {
|
||||||
|
|||||||
@ -13,14 +13,281 @@
|
|||||||
#include "py/obj.h"
|
#include "py/obj.h"
|
||||||
#include "py/nlr.h"
|
#include "py/nlr.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#define TIME_JPEG (1)
|
#define TIME_JPEG (0)
|
||||||
#if (TIME_JPEG == 1)
|
#if (TIME_JPEG == 1)
|
||||||
#include "py/mphal.h"
|
#include "py/mphal.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#endif
|
#endif
|
||||||
#include "imlib.h"
|
#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 FILE_HIGHWATER 1536
|
||||||
#define JPEG_FILE_BUF_SIZE 2048
|
#define JPEG_FILE_BUF_SIZE 2048
|
||||||
#define HUFF_TABLEN 273
|
#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();
|
mp_uint_t start = mp_hal_ticks_ms();
|
||||||
#endif
|
#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) {
|
if (JPEG_openRAM(&jpg, src->data, src->size, dst->data) == 0) {
|
||||||
// failed to parse the header
|
// failed to parse the header
|
||||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("JPEG decoder failed."));
|
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);
|
printf("time: %u ms\n", mp_hal_ticks_ms() - start);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user