在开发产品的时候,很多时候,我们都会用到图片解码,在本章中,我们将向大家介绍如何通过 ESP32-S3 来解码 BMP/JPG/JPEG/PNG/GIF 等图片,并在 SPILCD 上显示出来。

图片格式介绍

我们常用的图片格式有很多,一般最常用的有三种:JPEG(或JPG)、BMP、PNG和GIF。其中 JPEG(或 JPG)、PNG 和 BMP 是静态图片,而 GIF 则是可以实现动态图片。下面,我们简单介绍一下这三种图片格式。
BMP 编码简介
首先,我们来看看 BMP 图片格式。BMP(全称 Bitmap)是 Window操作系统中的标准图像文件格式,文件后缀名为“.bmp”,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP 文件所占用的空间很大,但是没有失真。BMP 文件的图像深度可选 lbit、4bit、8bit、16bit、24bit 及 32bit。BMP 文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。
典型的 BMP 图像文件由四部分组成:
①:位图头文件数据结构,它包含 BMP 图像文件的类型、显示内容等信息;
②:位图信息数据结构,它包含有 BMP 图像的宽、高、压缩方法,以及定义颜色等信息
③:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24 位的 BMP)就不需要调色板;
④:位图数据,这部分的内容根据 BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB,而其他的小于 24 位的使用调色板中颜色索引值。

JPEG 编码简介
JPEG 是 Joint Photographic Experts Group(联合图像专家组)的缩写,文件后辍名为“.jpg”或“.jpeg”,是最常用的图像文件格式,由一个软件开发联合会组织制定,同 BMP 格式不同,JPEG 是一种有损压缩格式,能够将图像压缩在很小的储存空间,图像中重复或不重要的资料会被丢失,因此容易造成图像数据的损伤(BMP 不会,但是 BMP 占用空间大)。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量明显降低,如果追求高品质图像,不宜采用过高压缩比例。但是JPEG压缩技术十分先进,它用有损压缩方式去除冗余的图像数据,在获得极高的压缩率的同时能展现十分丰富生动的图像,换句话说,就是可以用最少的磁盘空间得到较好的图像品质。而且JPEG是一种很灵活的格式,具有调节图像质量的功能,允许用不同的压缩比例对文件进行压缩,支持多种压缩级别,压缩比率通常在 10:1 到 40:1 之间,压缩比越大,品质就越低;相反地,压缩比越小,品质就越好。比如可以把 1.37Mb 的 BMP 位图文件压缩至20.3KB。当然也可以在图像质量和文件尺寸之间找到平衡点。JPEG格式压缩的主要是高频信息,对色彩的信息保留较好,适合应用于互联网,可减少图像的传输时间,可以支持 24bit 真彩色,
也普遍应用于需要连续色调的图像。
JPEG/JPG 的解码过程可以简单的概述为如下几个部分:
①:从文件头读出文件的相关信息。
JPEG 文件数据分为文件头和图像数据两大部分,其中文件头记录了图像的版本、长宽、采样因子、量化表、哈夫曼表等重要信息。所以解码前必须将文件头信息读出,以备图像数据解码过程之用。
②:从图像数据流读取一个最小编码单元(MCU),并提取出里边的各个颜色分量单元。
③:将颜色分量单元从数据流恢复成矩阵数据。使用文件头给出的哈夫曼表,对分割出来的颜色分量单元进行解码,把其恢复成 8×8 的数据矩阵。
④:8×8 的数据矩阵进一步解码。
此部分解码工作以 8×8 的数据矩阵为单位,其中包括相邻矩阵的直流系数差分解码、使用文件头给出的量化表反量化数据、反 Zig-zag 编码、隔行正负纠正、反向离散余弦变换等 5 个步骤,最终输出仍然是一个 8×8 的数据矩阵。
⑤:颜色系统 YCrCb 向 RGB 转换。
将一个 MCU 的各个颜色分量单元解码结果整合起来,将图像颜色系统从 YCrCb 向 RGB 转换。
⑥:排列整合各个 MCU 的解码数据。
不断读取数据流中的 MCU 并对其解码,直至读完所有 MCU 为止,将各 MCU 解码后的数据正确排列成完整的图像。JPEG的解码本身是比较复杂的,这里 FATFS的作者,提供了一个轻量级的 JPG/JPEG 解码库:TjpgDec,最少仅需 3KB 的 RAM 和 3.5KB 的 FLASH 即可实现JPG/JPEG 解码。

PNG 编码简介
PNG(Portable Network Graphics)是一种无损的位图图像格式,旨在替代 GIF 格式并增加一些 GIF 文件格式所不具备的特性。PNG 图像使用一种称为 DEFLATE 的无损数据压缩算法来减小文件大小,不会损失图像质量。这种压缩算法结合了 LZ77算法和哈夫曼编码,能够有效地压缩数据并减小文件大小。
在 PNG 编码过程中,预滤器编码格式被用来先对图像数据进行预处理,以便更好地利用Deflate 算法进行压缩。PNG 定义了五种不同的预滤器,分别是 None、Sub、Up、Average 和Paeth。这些预滤器根据图像像素的特性对每一行的像素进行编码,以更好地利用 Deflate 算法进行压缩。
PNG 还支持多种颜色模式,包括 8 位灰度图像、索引彩色图像和 24 位真彩色图像,并且可以支持 Alpha 通道透明度,这意味着可以在图像中创建半透明的效果。此外,PNG 还支持多层图像,可以将多个图像组合在一起,每个图像可以具有自己的透明度和颜色。
典型的 PNG 图像文件由以下几部分组成:
①:文件署名域(File Signature):这是文件的开头部分,由 8 个字节组成,用于标识该文件是一个 PNG 文件。其值固定为"89 50 4E 47 0D 0A 1A 0A"。
②:关键数据块(Critical Chunk):这是 PNG文件必须包含的数据块,包括 IHDR、IDAT、END等。IHDR块包含了图像的基本信息,如宽度、高度、像素格式等;IDAT块包含了图像的实际数据;IEND 块标记了图像数据的结束。
③:辅助数据块(Ancillary Chunk):这是可选的数据块,用于存储与图像相关的其他信息,如文本注释、时间戳等。这些数据块对于解码图像是可选的,但如果存在,解码器应当对其进行解析。

GIF 编码简介
GIF(Graphics Interchange Format)是 CompuServe 公司开发的图像文件存储格式,1987 年开发的 GIF 文件格式版本号是 GIF87a,1989 年进行了扩充,扩充后的版本号定义为 GIF89a。GIF图像文件以数据块(block)为单位来存储图像的相关信息。一个 GIF 文件由表示图形/图像的数据块、数据子块以及显示图形/图像的控制信息块组成,称为GIF数据流(DataStream)。数据流中的所有控制信息块和数据块都必须在文件头(Header)和文件结束块(Trailer)之间。
GIF 文件格式采用了 LZW(Lempel-ZivWalch)压缩算法来存储图像数据,定义了允许用户为图像设置背景的透明(transparency)属性。此外,GIF 文件格式可在一个文件中存放多幅彩色图形/图像。如果在 GIF 文件中存放有多幅图,它们可以像演幻灯片那样显示或者像动画那样演示。
一个 GIF 文件的结构可分为文件头(FileHeader)、GIF 数据流(GIFDataStream)和文件终结器(Trailer)三个部分。文件头包含 GIF 文件署名(Signature)和版本号(Version);GIF 数据流由控制标识符、图象块(ImageBlock)和其他的一些扩展块组成;文件终结器只有一个值为 0x3B 的字符(‘;’)表示文件结束。

libjpeg 简介

libjpeg 是一个完全用 C 语言编写的库,包含了广泛使用的 JPEG 解码、JPEG 编码和其他的JPEG 功能的实现。这个 IJG 库由组织(Independent JPEG Group(独立 JPEG 小组))提供并维护。libjepg,目前最新版本为 v9f,可以在 https://www.ijg.org/files/这个网站下载。libjpeg 具有稳定、兼容性强和解码速度较快等优点。
本章,我们使用 libjpeg 来实现 MJPEG 数据流的解码,MJPEG 数据流,其实就是一张张的JPEG 图片拼起来的图片视频流,只要能快速解码 JPEG 图片,就可以实现视频播放。
前面的图片显示实验我们使用了 TJPGD 实现 JPEG 解码,大家可能会问,为什么不直接用TJPGD 来解码呢?因为 TJPG 的特点是:占用资源少,但是解码速度慢。在 DNESP32S3 上,同样一张 320240的 JPG 图片,用 TJPGD来解码,需要 120 多毫秒,而用 libjpeg,则只需要 50ms左右即可完成解码,libjpeg 的解码速度明显比 TJPGD 快了不少,使得解码视频成为可能。实际上,经过优化后的 libjpeg,使用 DNESP32S3不超频的情况下,可以流畅播放 480272@ 10 帧的MJPEG 视频(带音频)。

支持 MJPEG 编码的 avi 格式视频:狸窝全能视频转换器

例子:ESP32中加载SD卡中PNG图片
1.配置SD卡文件系统,打开小齿轮(SDK Configureation editor)找到3rd Party Libraries,选中File sysytem on top of FatFS,修改下面为83(ASCII中的‘S’),下面是读取使用的缓冲区大小,另外也要进行打开png图片解码库。
在这里插入图片描述

因为LVGL要使用文件系统和初始化SD卡,所以LVGL要依赖fatfs和sd_card组件。打开lvgl–>env_support–>cmake–>esp.cmake文件,在第45行位置添加依赖(REQUIRES后面的内容)

 idf_component_register(SRCS ${SOURCES} ${EXAMPLE_SOURCES} ${DEMO_SOURCES}
      INCLUDE_DIRS ${LVGL_ROOT_DIR} ${LVGL_ROOT_DIR}/src ${LVGL_ROOT_DIR}/../
                   ${LVGL_ROOT_DIR}/examples ${LVGL_ROOT_DIR}/demos
      REQUIRES esp_timer
      fatfs
      )

打开lv_fs_fatfs.c文件(路径:lvgl/src/extra/libs/fsdrv/),将第230行的两个DIR修改为FF_DIR。
在这里插入图片描述
挂载sd卡,即可读出SD卡png图片
在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐