本帖最后由 SJZhu 于 2024-10-8 15:57 编辑
JPEG和ARGB8888是两种不同的图像数据表示方式,主要用于不同的用途和情境。它们之间的区别和联系如下: 区别:数据格式类型:
- JPEG:是一种压缩的图像格式,采用有损压缩算法,主要用于存储和传输数字图像。JPEG文件通常以.jpg或.jpeg为扩展名。
- ARGB8888:是一种不压缩的像素格式,表示每个像素的颜色信息。它是一种32位的格式,每个通道(Alpha、Red、Green、Blue)各占8位。
用途:
- JPEG:由于支持高效压缩且文件大小较小,JPEG非常适合用于互联网图片、数码相片以及需要快速传输的场合。
- ARGB8888:适用于需要高精度和完全质量的图像处理场合,比如图形渲染和图像编辑,因为它能完整保留每个像素的颜色信息。
压缩方式:
- JPEG:使用有损压缩,可以大幅减小文件体积,但会导致一定的图像质量损失。
- ARGB8888:不进行压缩,保留完整的图像信息,因此占用更多存储空间。
渲染性能:
- JPEG:需要解码过程,通常较适于图片展示而不是实时图形操作。
- ARGB8888:直接存储每个像素的数据,适合用于对图像进行实时操作和精细化处理。
联系:图像表示:
- 两者都是用于图像的表示方式,一个用于压缩存储,一个用于直接表示颜色数据。它们可以在图像处理过程中相互转换。
互操作性:
- 一幅图像可以从ARGB8888格式编码为JPEG以便储存或传输,也可以从JPEG解码回ARGB8888进行编辑和处理。
常见使用场景:
- 图像文件通常在存储时使用JPEG格式以节省空间,而在实际处理或编辑时会被解码为诸如ARGB8888等不压缩格式。
JPG格式的文件,可以通过下面的Python脚本转换为16进制:
from PIL import Image
import binascii
import re
def jpg_to_hex(file_path):
# 打开图像
with open(file_path, 'rb') as image_file:
# 读取文件的二进制数据
binary_data = image_file.read()
# 将二进制数据转换为十六进制
hex_data = binascii.hexlify(binary_data)
#return hex_data
# 转换为字符串
hex_string = hex_data.decode('utf-8')
return hex_string
# 示例用法
file_path = 'example.jpg'
hex_representation = jpg_to_hex(file_path)
#print(hex_representation)
split_str = [hex_representation[i:i+2] for i in range(0, len(hex_representation), 2)]
print(split_str)
JPEG解码流程:
在图形化配置界面,首先使能JPEG模块,然后使能HPDMA模块:
LTDC显示还是使用上一个工程的配置方式:
主函数通过调用MX_JPEG_Init -> HAL_JPEG_Init(&hjpeg)来初始化硬件JPEG模块
LTDC模块初始化代码:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] LTDC Initialization Function
* @param None
* @retval None
*/
static void MX_LTDC_Init(void)
{
/* USER CODE BEGIN LTDC_Init 0 */
/* USER CODE END LTDC_Init 0 */
LTDC_LayerCfgTypeDef pLayerCfg = {0};
/* USER CODE BEGIN LTDC_Init 1 */
/* USER CODE END LTDC_Init 1 */
hltdc.Instance = LTDC;
hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
hltdc.Init.HorizontalSync = 3;
hltdc.Init.VerticalSync = 3;
hltdc.Init.AccumulatedHBP = 11;
hltdc.Init.AccumulatedVBP = 11;
hltdc.Init.AccumulatedActiveW = 811;
hltdc.Init.AccumulatedActiveH = 491;
hltdc.Init.TotalWidth = 819;
hltdc.Init.TotalHeigh = 499;
hltdc.Init.Backcolor.Blue = 0;
hltdc.Init.Backcolor.Green = 0;
hltdc.Init.Backcolor.Red = 0;
if (HAL_LTDC_Init(&hltdc) != HAL_OK)
{
Error_Handler();
}
pLayerCfg.WindowX0 = 0;
pLayerCfg.WindowX1 = 320;
pLayerCfg.WindowY0 = 0;
pLayerCfg.WindowY1 = 240;
pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888;
pLayerCfg.Alpha = 127;
pLayerCfg.Alpha0 = 0;
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
pLayerCfg.FBStartAdress = (uint32_t)&image_data_Cloud_320_240;
pLayerCfg.ImageWidth = 320;
pLayerCfg.ImageHeight = 240;
pLayerCfg.Backcolor.Blue = 0;
pLayerCfg.Backcolor.Green = 0;
pLayerCfg.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN LTDC_Init 2 */
/* USER CODE END LTDC_Init 2 */
}
主函数代码:
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t toBottom = 0;
uint32_t toTop = 0;
/* STM32H7RSxx HAL library initialization:
- Systick timer is configured by default as source of time base, but user
can eventually implement his proper time base source (a general purpose
timer for example or other time source), keeping in mind that Time base
duration should be kept 1ms since PPP_TIMEOUT_VALUEs are defined and
handled in milliseconds basis.
- Set NVIC Group Priority to 4
- Low Level Initialization
*/
MPU_Config();
/* USER CODE END 1 */
/* Enable the CPU Cache */
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
/* Update SystemCoreClock variable according to RCC registers values. */
SystemCoreClockUpdate();
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* Initialize LD1 and LD2*/
BSP_LED_Init(LD1);
BSP_LED_Init(LD2);
BSP_LED_Init(LD3);
BSP_LED_Init(LD4);
/* USER CODE END Init */
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_HPDMA1_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
MX_JPEG_Init();
MX_LTDC_Init();
/* USER CODE BEGIN 2 */
tickstart = HAL_GetTick();
JPEG_Decode_DMA(&hjpeg, (uint32_t)image_320_240_jpg, IMAGE_320_240_JPG_SIZE, (uint32_t) rawBuffer);
while((Jpeg_HWDecodingEnd == 0) && ((HAL_GetTick() - tickstart) < 5000))
{
}
if (Jpeg_HWDecodingEnd != 0)
{
/* Test ends properly */
BSP_LED_On(LD1);
}
else
{
/* Test error */
BSP_LED_On(LD2);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while(1){
HAL_LTDC_SetAddress(&hltdc, (uint32_t)rawBuffer, 0);
HAL_LTDC_Reload( &hltdc,LTDC_RELOAD_IMMEDIATE);
BSP_LED_Toggle(LD3);
BSP_LED_Toggle(LD4);
HAL_Delay(1000);
HAL_LTDC_SetAddress(&hltdc, (uint32_t)&image_data_Cloud_320_240, 0);
HAL_LTDC_Reload( &hltdc,LTDC_RELOAD_IMMEDIATE);
BSP_LED_Toggle(LD3);
BSP_LED_Toggle(LD4);
HAL_Delay(1000);
}
}
此外烧录到板子后发现解码的图片没有正常显示,可能是LTDC配置参数与解码结果不兼容,目前还在调试中。解码后的数据在调试界面看到也有数据。
【更新10月8日】之所以出现上述视频中的解码图片不能正常显示的问题,在于JPEG硬件解码输出的图片格式为YCbCr,而LTDC控制器所需要的输入图片格式为ARGB8888.因此,结合相关资料【AN4996】,利用DMA2D模块完成图片格式的转换,进而成功的在TFT屏幕上显示出解码后的JPEG图片。
首先在图形化配置界面使能DMA2D:
这里发现一个问题,DMA2D选择像素格式转换模式,然而InputColorMode无法选择“DMA2D_INPUT_YCBCR”,只能手动修改代码。
static void MX_DMA2D_Init(void)
{
/* USER CODE BEGIN DMA2D_Init 0 */
/* USER CODE END DMA2D_Init 0 */
/* USER CODE BEGIN DMA2D_Init 1 */
/* USER CODE END DMA2D_Init 1 */
hdma2d.Instance = DMA2D;
hdma2d.Init.Mode = DMA2D_M2M_PFC;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_ARGB8888;
hdma2d.Init.OutputOffset = 0;
hdma2d.LayerCfg[1].InputOffset = 0;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_YCBCR;
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0;
hdma2d.LayerCfg[1].AlphaInverted = DMA2D_REGULAR_ALPHA;
hdma2d.LayerCfg[1].RedBlueSwap = DMA2D_RB_REGULAR;
hdma2d.LayerCfg[1].ChromaSubSampling = DMA2D_NO_CSS;
if (HAL_DMA2D_Init(&hdma2d) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMA2D_ConfigLayer(&hdma2d, 1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN DMA2D_Init 2 */
/* USER CODE END DMA2D_Init 2 */
}
硬件解码完成后,通过调用如下API来获取解码后的图片信息,包括Width与Height。
HAL_JPEG_GetInfo(&hjpeg, &MyJPEGInfo);
接下来是重头戏,即完成像素颜色格式转换:
DMA2D_Copy_YCbCr_To_RGB((uint32_t *)rawBuffer, /* JEPG解码后的数据 */
(uint32_t *)rawBuffer_DMA2D_ARGB8888, /* 这里是显存地址 */
0 ,
0,
MyJPEGInfo.ImageWidth,
MyJPEGInfo.ImageHeight,
LTDC_PIXEL_FORMAT_ARGB8888,
MyJPEGInfo.ChromaSubsampling);
而这个函数 DMA2D_Copy_YCbCr_To_RGB的具体实现如下(参考:安富莱的例程):
/*
*********************************************************************************************************
* 函 数 名: DMA2D_Copy_YCbCr_To_RGB
* 功能说明: YCbCr转RGB输出
* 形 参: pSrc: 数据源地址
* pDst: 数据目的地址
* x: X轴首地址
* y: Y轴首地址
* xsize: 目的区域的X轴大小,即每行像素数
* ysize: 目的区域的Y轴大小,即行数
* PixelFormat: 目标区颜色格式
* ChromaSampling : YCbCr Chroma sampling : 4:2:0, 4:2:2 or 4:4:4
* 返 回 值: 无
*********************************************************************************************************
*/
void DMA2D_Copy_YCbCr_To_RGB(uint32_t *pSrc,
uint32_t *pDst,
uint16_t x,
uint16_t y,
uint16_t xsize,
uint16_t ysize,
uint32_t PixelFormat,
uint32_t ChromaSampling)
{
uint32_t cssMode = DMA2D_CSS_420;
uint32_t inputLineOffset = 0;
uint32_t destination = 0;
/* 处理输入行偏移 */
if(ChromaSampling == JPEG_420_SUBSAMPLING)
{
cssMode = DMA2D_CSS_420;
inputLineOffset = xsize % 16;
if(inputLineOffset != 0)
{
inputLineOffset = 16 - inputLineOffset;
}
}
else if(ChromaSampling == JPEG_444_SUBSAMPLING)
{
cssMode = DMA2D_NO_CSS;
inputLineOffset = xsize % 8;
if(inputLineOffset != 0)
{
inputLineOffset = 8 - inputLineOffset;
}
}
else if(ChromaSampling == JPEG_422_SUBSAMPLING)
{
cssMode = DMA2D_CSS_422;
inputLineOffset = xsize % 16;
if(inputLineOffset != 0)
{
inputLineOffset = 16 - inputLineOffset;
}
}
/* 输出地址,特别注意末尾乘以2的对RGB565,如果输出格式是ARGB8888,需要乘以4 */
destination = (uint32_t)pDst + ((y * 320) + x) * 4;
/* DMA2D采用存储器到存储器模式,并且执行FPC颜色格式转换, 这种模式是前景层作为DMA2D输入 */
DMA2D->CR = 0x00010000UL | (1 << 9);
DMA2D->OOR = 320 - xsize;
/* 输出格式 */
DMA2D->OPFCCR = PixelFormat
| (DMA2D_REGULAR_ALPHA << 20)
| (DMA2D_RB_REGULAR << 21);
/* 前景层输入格式 */
DMA2D->FGPFCCR = DMA2D_INPUT_YCBCR
| (DMA2D_REPLACE_ALPHA << 16)
| (DMA2D_REGULAR_ALPHA << 20)
| (DMA2D_RB_REGULAR << 21)
| (0xFFU << 24)
| (cssMode << 18);
DMA2D->FGOR = inputLineOffset;
DMA2D->NLR = (uint32_t)(xsize << 16) | (uint16_t)ysize;
DMA2D->OMAR = (uint32_t)destination;
DMA2D->FGMAR = (uint32_t)pSrc;
/* 启动传输 */
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {}
}
接下来主循环中显示这个图片即可:
while(1){
HAL_LTDC_SetAddress(&hltdc, (uint32_t)rawBuffer_DMA2D_ARGB8888, 0);
HAL_LTDC_Reload( &hltdc,LTDC_RELOAD_IMMEDIATE);
BSP_LED_Toggle(LD3);
BSP_LED_Toggle(LD4);
HAL_Delay(1000);
}
最终解码出的照片如下所示:
参考:
1. 整理了一个STM32H7的寄存器方式YCbCr转RGB函数并DMA2D到显示屏,支持采样比4:4:4,4:2:2和4:2:0https://www.armbbs.cn/forum.php?mod=viewthread&tid=93536
2. https://www.st.com/content/ccc/resource/training/technical/product_training/group0/d7/07/45/76/c2/f9/41/4f/STM32H7-Peripheral-JPEG_codec_JPEG/files/STM32H7-Peripheral-JPEG_codec_JPEG.pdf/_jcr_content/translations/en.STM32H7-Peripheral-JPEG_codec_JPEG.pdf#:~:text=The%20JPEG%20codec%20integrated%20inside%20STM32%20products%20is%20an%20hardware
3. How to use the JPEG codec peripheral on STM32 MCUs https://www.st.com/content/ccc/resource/technical/document/application_note/group0/a5/9d/22/46/61/6d/4a/ab/DM00356635/files/DM00356635.pdf/jcr:content/translations/en.DM00356635.pdf#:~:text=Using%20the%20STM32H743/53/45/55/47/57/50xx%20and%20STM32H7Rx/7Sx%20devices%20for%20JPEG%20decoding%20operations,
|