JPEG图片解码能力——STM32F769I 图像处理能力评测之三
硬件平台:STM32F769IDISCOVERY
软件开发平台:Keil 5.1
测试方法:
1.采用SD卡,并把测试的图片存放到根目录中。SD卡的容量为8G,Class 4。选用3种色彩丰富程度差别较大的图片,并把每种图片裁剪成800X480、480X320、320X240大小,作为基准测试图片。
2.配置系统时钟为200MHZ,SD卡时钟为25MHZ,SDRAM的时钟为100MHZ,使用FATFS文件系统。关闭LCD层0的显示,开启LCD层1的显示,使用RGB888显示。
3.选用STM32自带的JPEG硬件编解码器、TjpgDEC解码库和LIBJPEG解码库,分别对图片进行测试,比较三者的解码能力。
LIBJPEG解码库简介:
ibjpeg是一个完全用C语言编写的库,包含了被广泛使用的JPEG解码、JPEG编码和其他的JPEG功能的实现。目前最新的版本为9b。官网地址: http://www.ijg.org/。libjpeg具有稳定、兼容性强和解码速度较快等优点,但是使用这个库文件进行JPEG解码,消耗内存较大,对于内存不足的处理器来说,是一个很大的负担。在我们下载的STM32CUBEF7资源包里就有LIBJPEG库文件,因此不用去下载。库文件的存放路径如下图所示:
直接将整个文件夹复制到工程文件夹,然后添加文件,修改相应的配置参数即可使用。
TjpgDEC解码库简介:
TJpgDec是一个通用的JPEG图像解压缩器模块,针对小型嵌入式系统进行了高度优化。它使用非常低的内存消耗,使其可以被并入微小的微控制器,如AVR,8051,PIC,Z80,Cortex-M0等。支持输出RGB888or RGB565,支持图片缩放。官网地址:http://elm-chan.org/fsw/tjpgd/00index.html。目前版本为R0.01B,作者基本上没有进行更新。
JPEG硬件编解码器简介:
这个是STM32自带的硬件解码器,目前只存在于STM32F7X7、 STM32F7X8和STM32F7X9这几个系列产品中。它提供了一个快速和简单的硬件压缩器和解压缩器。JPEG编解码器可以解码ISO / IEC 10918-1规范中定义的JPEG流,它可以选择性地解析JPEG头,并相应地更新JPEG编解码器寄存器的量化表和霍夫曼表。
JPEG编解码块主要特性:
1.支持8位通道像素采样
2.对每个像素数据进行编解码只需一个时钟周期
3.支持JPEG图片头信息生成和解析,可以使能/禁止对JPEG图片头信息处理。
4.拥有四个可编程量化表和完全可编程的霍夫曼表。
5. 完全可编程的最小编码单元。
6.支持编码/解码。
7.数据传送支持DMA、中断和轮询。
JPEG编解码器框图如下所示:
从图中可以看出,JPEG编解码器拥有两个FIFO,一个为输入一个为输出,并且支持FIFO阈值中断。可以通过检测FIFO阈值中断是否产生,及时将数据送进FIFO或者从FIFO读出数据,从而使JPEG编解码操作维持连续性,使JPEG编解码器的性能得到最大的发挥。其中有一点需要注意,解码器输出的数据不是RGB数据,不能直接往LCD里送数据,需要进行数据转换算法,换算成RGB数据(就是这个问题,整整折腾了我一天的时间)。
下面是我选用的测试图片,按照图片色彩丰富程度从高到低进行排列:
好了,现在我们开始进行测试。为了保证测试的一致性,我们选用同一块开发板,并使系统运行时的工作参数保持相同。解码时间的计算以毫秒为单位,配置系统滴答定时器每隔1ms产生中断,提供时间计数。当图片文件打开后,开始时间计数,到LCD显示图片完成后,停止时间计数。计算二者的时间差,就是图片的解码时间。
JPEG硬件解码器分别使用DMA、IT和轮询的方式对图片进行解码。同时对TjpgdDEC和LIBJPEG进行优化处理,使解码时间尽量最短,因为我们比较的是三种解码方式工作在最优模式下的解码速度(个人能力有限,已经尽最大努力优化TjpgDEC和LIBJPEG,以下的时间数据仅供参考。)。经过三天的移植和测试,最后的测试数据如下图所示:
图片像素800X480
图片质量
硬件JPEG 中断方式
硬件JPEG DMA方式
硬件JPEG轮询方式
TjpgDEC解码
LIBJPEG解码
低
89ms
62ms
87ms
238ms
148ms
中
151ms
121ms
148ms
316ms
213ms
高
189ms
158ms
185ms
413ms
271ms
图片像素480X320
图片质量
硬件JPEG 中断方式
硬件JPEG DMA方式
硬件JPEG轮询方式
TjpgDEC解码
LIBJPEG解码
低
39ms
28ms
37ms
102ms
64ms
中
80ms
69ms
78ms
151ms
107ms
高
97ms
85ms
95ms
196ms
133ms
图片像素320X240
图片质量
硬件JPEG 中断方式
硬件JPEG DMA方式
硬件JPEG轮询方式
TjpgDEC解码
LIBJPEG解码
低
21ms
15ms
20ms
55ms
34ms
中
54ms
48ms
54ms
93ms
70ms
高
63ms
57ms
62ms
116ms
82ms
从上述三张数据表中可以看出,使用DMA方式的JPEG硬件解码速度是最快的,而采用TjpgDEC库解码是速度最慢的。其中中断方式的JPEG硬件解码要和使用轮询方式的JPEG硬件解码二者的速度差不多,都比TjpgDEC解码和LIBJPEG解码来的快。但是使用轮询方式的JPEG硬件解码会占用整个CPU,不如中断来的灵活。当解码的图片越来越小时,LIBJPEG解码和JPEG硬件解码在速度上不会差太多.因此,当解码较大的图片时,JPEG硬件解码的优势就发挥出来了。
最后奉上几张800X600的图片在开发板上的显示:
关于JPEG硬件编解码器使用的一些注意:
1.添加固件库文件stm32f7xx_hal_jpeg.c和stm32f7xx_hal_jpeg.h到项目工程。同时添加jpeg_utils.c、jpeg_utils.h和jpeg_utils_conf_template.h到项目工程,这三个是编解码数据格式转换相关的文件。
2.打开jpeg_utils_conf_template.h文件,修改第51行和52行的内容如下图所示:
修改第72行和73行的内容,如果使用解码器,将73行的内容修改为:#define USE_JPEG_ENCODER 0;如果使用编码器,将72行的内容修改为:#defineUSE_JPEG_DECODER 0。然后修改第75行的内容,这个是配置像素数据转换完成后的格式,根据屏幕像素的格式修改相应的值,比如我这里是RGB888,因此修改为:#define JPEG_RGB_FORMAT JPEG_RGB888。最后修改第76行的内容,这个是配置是否需要交换RB,如果使能,那么经过转换后,像素的格式为BGR888。我这里不需要这个功能,设置为:#defineJPEG_SWAP_RB 0。
保存并关闭文件,将文件名修改为jpeg_utils_conf.h。
3. 打开stm32f7xx_hal_conf.h文件,修改第96行如下图所示:
4.进行初始化,包括使能JPEG模组时钟,使能DMA2模组时钟,调用初始化函数对JPEG进行初始化,配置JPEG中断和DMA2中断。最后不要忘了,如果是进行解码操作,一定要调用JPEG_InitColorTables函数,初始化JPEG查找表。这个函数只能在初始化的时候调用,并且只能调用一次,后面不允许掉用,无论你有多少张JPEG图片要解码,都只在初始化的时候调用一次就够了。
5.使用轮询方式解码图片时,会完全占用CPU,直到解码完成后者解码出错才会退出。采用DMA和中断,不会一直占用CPU。解码过程中会回调以下个函数:
HAL_JPEG_InfoReadyCallback:指示JPEG头解析完成,可以查看图片的宽度和高度,以及像素数据的组成方式。在这一步中通常调用JPEG_GetDecodeColorConvertFunc函数来设定转换完成后,数据格式转换的函数。
HAL_JPEG_GetDataCallback:这个是请求数据输入的函数。参数NbDecodedData是指示解码器需要采集数据的字节数。
HAL_JPEG_DataReadyCallback:解码器数据转换好的通知函数。注意如果是一次性将JPEG图片数据传送到解码器,那么这里出来的是全部的解码数据,否则为部分解码数据。参数pDataOut为数据存放区,参数OutDataLength为输出数据的字节数量。通常在里调用函数进行数据转换操作。
HAL_JPEG_DecodeCpltCallback:编解码完成的指示函数。
HAL_JPEG_ErrorCallback:编解码错误的指示函数。
关于我编写程序的使用注意事项:
1.我已经将TjpgDEC和LIBJPEG都移植到工程项目中,编译完成后只会选用一种方式解码图片。要用其他的方式解码图片,需修改JpegDecode.h文件,如下图所示:
2.硬件JPEG、TjpgDEC和LIBJPEG的解码函数都存放在JpegDecode.c中。并且测试图片要存在放在SD卡的根目录中。