软件解码:

JPEG/JPG 的解码过程可以简单的概述为如下几个部分:
1 、从文件头读出文件的相关信息。
JPEG 文件数据分为文件头和图像数据两大部分,其中文件头记录了图像的版本、
长宽、采样因子、量化表、哈夫曼表等重要信息。所以解码前必须将文件头信息读出,
以备
图像数据解码过程之用。
2 、从图像数据流读取一个最小编码单元(MCU)  ,并提取出里边的各个颜色分量单元。
3 、将颜色分量单元从数据流恢复成矩阵数据。
使用文件头给出的哈夫曼表,对分割出来的颜色分量单元进行解码,把其恢复成 8
×8 的数据矩阵。
ALIENTEK  阿波罗 STM32H743  开发板教程
737
STM32 H7  开发指南( ( 寄存器 版) )
4 、8 ×8  的数据矩阵进一步解码。
此部分解码工作以 8×8 的数据矩阵为单位, 其中包括相邻矩阵的直流系数差分
解码、使用文件头给出的量化表反量化数据、反 Zig- zag 编码、隔行正负纠正、反向离
散余弦变换等 5 个步骤, 最终输出仍然是一个 8×8 的数据矩阵。
5 、颜色系统 YCrCb 向 向 RGB  转换。
将一个 MCU 的各个颜色分量单元解码结果整合起来,将图像颜色系统从 YCrCb
向 RGB 转换。
6 、排列整合各个 MCU  的解码数据。
不断读取数据流中的 MCU 并对其解码,直至读完所有 MCU 为止,将各 MCU 解
码后的数据正确排列成完整的图像。

至于具体解码细节我不怎么明白,是不是解码出一部分像素进行显示,然后再解码下一部分进行接着显示。。。我猜测应该是这样才对,不可能一次性解码出整张图片放在内存ram里,然后才显示,这样对ram的空间要求太大,不符合嵌入式平台的要求

第二个点就是这个解码库自己定义了外层显示函数,我们只需要实现它给定的底层画点画线等函数接口就行了

ai_load_picfile(pname,0,0,lcddev.width,lcddev.height,1);//显示图片,pname为图片文件的全路径

//图片显示物理层接口
//在移植的时候,必须由用户自己实现这几个函数
typedef struct
{
u16(*read_point)(u16,u16); //u16 read_point(u16 x,u16 y)读点函数
void(*draw_point)(u16,u16,u16); //void draw_point(u16 x,u16 y,u16 color)画点函数
void(*fill)(u16,u16,u16,u16,u16);
///void fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)单色填充函数
void(*draw_hline)(u16,u16,u16,u16);
//void draw_hline(u16 x0,u16 y0,u16 len,u16 color) 画水平线函数
void(*fillcolor)(u16,u16,u16,u16,u16*); 
//void piclib_fill_color(u16 x,u16 y,u16 width,u16 height,u16 *color) 颜色填充
}_pic_phy;

硬件解码:

跟软件解码时候的函数接口都是一模一样的,外层显示函数,底层用户自己实现的液晶画点函数接口等等,都是一样的

不同点就在于:硬件解码时候,调用的函数的功能是配置JPEG硬件(正点原子的stm32h743程序只能对  BMP/JPG/JPEG/GIF进行解码(png图片无法解码),JPG/JPEG 图片可以用硬解码,bmp(好像本来就是位图,其实不用解码直接显示即可),gif格式图片,只能用软件解码方式(我们用代码自己做了个切换),明显显示大尺寸400*500像素gif动态图时候速度很慢了,明显一帧帧刷新,典型的卡成ppt,而且一帧图片还是从上往下慢慢刷出来,这也证明了我上面的猜测,解码一张图是一部分进行然后马上显示这一部分,接着再解码下一部分,然后进行显示......所以解码速度慢了,效果就是图片从上往下刷出来的)程序中识别的格式的代码是这样写的

//文件名传递         
    temp=f_typetell((u8*)filename);    //得到文件的类型
    switch(temp)
    {                                              
        case T_BMP:
            res=stdbmp_decode(filename);                 //解码bmp            
            break;
        case T_JPG:
        case T_JPEG:
            res=jpg_decode(filename,fast);                //解码JPG/JPEG            
            break;
        case T_GIF:
            res=gif_decode(filename,x,y,width,height);    //解码gif        
            break;
        default:
             res=PIC_FORMAT_ERR;                          //非图片格式!!!  所以png图片也无法解码
            break;
    }                                                 
    return res;寄存器相关设置了(就跟驱动普通外设比如定时器这些是一样的方法了),比如:

//初始化硬件JPEG解码器
//tjpeg:jpeg编解码控制结构体
void JPEG_Decode_Init(jpeg_codec_typedef *tjpeg)

    u8 i;
    tjpeg->inbuf_read_ptr=0;
    tjpeg->inbuf_write_ptr=0;
    tjpeg->indma_pause=0;
    tjpeg->outbuf_read_ptr=0;
    tjpeg->outbuf_write_ptr=0;    
    tjpeg->outdma_pause=0;        
    tjpeg->state=JPEG_STATE_NOHEADER;    //图片解码结束标志
    for(i=0;i<JPEG_DMA_INBUF_NB;i++)
    {
        tjpeg->inbuf[i].sta=0;
        tjpeg->inbuf[i].size=0;
    }
    for(i=0;i<JPEG_DMA_OUTBUF_NB;i++)
    {
        tjpeg->outbuf[i].sta=0;
        tjpeg->outbuf[i].size=0;
    }        
    MDMA_Channel6->CCR=0;        //MDMA通道6禁止
    MDMA_Channel7->CCR=0;        //MDMA通道7禁止
    MDMA_Channel6->CIFCR=0X1F;    //中断标志清零 
    MDMA_Channel7->CIFCR=0X1F;    //中断标志清零
    
    JPEG->CONFR1|=1<<3;            //硬件JPEG解码模式
    JPEG->CONFR0&=~(1<<0);        //停止JPEG编解码进程 
    JPEG->CR&=~(0X3F<<1);        //关闭所有中断 
    JPEG->CR|=1<<13;            //清空输入fifo
    JPEG->CR|=1<<14;            //清空输出fifo
    JPEG->CR|=1<<6;                //使能Jpeg Header解码完成中断
    JPEG->CR|=1<<5;                //使能解码完成中断
    JPEG->CFR=3<<5;                //清空标志   
    JPEG->CONFR0|=1<<0;            //使能JPEG编解码进程 
}

---------------------------------------------------------------------------------------------------------------------------------------
一张图片要想使用硬件解码,图片的宽度必须为16的整数倍而且图片的分辨率得小于液晶屏的分辨率,代码:

if(picinfo.ImgWidth<=lcddev.width&&picinfo.ImgHeight<=lcddev.height&&        //满足分辨率小于等于屏幕分辨率
                       picinfo.ImgWidth<=picinfo.S_Width&&picinfo.ImgHeight<=picinfo.S_Height    //满足图片宽度为16的整数倍
                       &&(picinfo.ImgWidth%16)==0)                                                //则可以硬件解码
                    { 
                        res=hjpgd_decode((u8*)filename);//采用硬解码JPG/JPEG
                    }else res=jpg_decode(filename,fast);//采用软件解码JPG/JPEG

上面的代码也说明了如果宽度不是16倍数,图片分辨率也大于了屏幕分辨率,那么就是调用了软解码,也就是说软件解码更灵活,什么尺寸都能解码成功,应该是软解码内部做了一些缩放,采样因子等处理,而硬件解码比较死板固定,没有这个功能。

所以我们的图片最好是事先在电脑上编辑为16倍数的宽度,这样才能提高解码速度。

硬件解码方式是图片全部数据解码完成后,一次性显示的,而软件解码则是一部分解码一部分显示再一部分解码一部分接着显示的

关于两种解码速度的比较:

软解码明显是从上往下中等速度刷出来的图片(都是针对800*480的图片),而硬解码则是瞬间显示出了图片,明显快很多,据原子教程说,软解码这样800*480图片需要

上图,是我们使用 4.3 寸 800*480 分辨率的 MCU 屏做的测试,可以看出,对于同一张
图片(图片分辨率:800*480),硬件 JPEG 解码,只需要 57.2ms,软件 JPEG 解码,则需要
530ms!硬件 JPEG 解码速度是软件 JPEG 解码的 9.3 倍!!可见,硬件 JPEG 解码大大提高
了对 JPG/JPEG 图片的解码能力。都是指的是从开始解码到显示完成。

STM32H7 的硬件
JPEG 解码性能可以在最快 20ms 内完成一张 800*480 的 JPEG 图片解码(读数据+解码
+YUV→RGB 转换,但是不包括显示)。这里不包括显示是需要20ms,对比上面的,显示出来一个800*480图片大概要30ms吧

那么也就是说图片不断硬件解码显示最快能达到20帧吧

-----------------------------------------------------------------------------------------------------------------------------------------

刚刚在测试的时候遇到个问题,在sd卡里明明放了一个90.jpg 380*560图片,不知道为什么显示不出来,按照程序,这个宽度不是16的倍数,那么会自动调用软件解码然后显示出来,也不至于不显示呀,然后我debug断点调试,发现

case T_JPEG:
            if(fast)                                    //可能需要硬件解码
            {
                res=jpg_get_size(filename,&picinfo.ImgWidth,&picinfo.ImgHeight);

这个函数这儿返回错误值了,我继续跟踪进去,发现

return JDR_FMT3;    /* Unsuppoted JPEG standard (may be progressive JPEG) */

运行到这儿了,返回的,然后我百度搜了一下这个错误,说是

应该是用photoshop存JPEG的时候,不小心把大量图片存成progressive JPEG了.
在网上查到在progressive的jpeg做decompress时,会占用大量的内存,在嵌入系统中,会导致out-of-memory

所以我的解决办法是:重新用电脑画图软件打开它保存一下(应该是进行了新的格式转换吧,比如图层信息啥的删除掉),还是保存为.jpg格式,结果单片机就可以解码并且显示了

stm32h743单片机嵌入式学习笔记6-压缩图片解码原理相关推荐

  1. stm32h743单片机嵌入式学习笔记7-FPU

    * FPU 即浮点运算单元(Float Point Unit).浮点运算,对于定点 CPU(没有 FPU 的 CPU) 来说必须要按照 IEEE-754 标准的算法来完成运算,是相当耗费时间的.而对于 ...

  2. stm32h743单片机嵌入式学习笔记4-文件系统FATFS

    FAT32 文件系统 就是在格式化的时候 选择 FAT32 方式格式化. 然后在 SD 卡存储区会产生一个类似于 目录 的文件系统. 这样存入 SD 卡中的所有 文件 文件夹 都会在这个目录中找到. ...

  3. 嵌入式学习笔记——基于Cortex-M的单片机介绍

    基于Cortex-M的单片机介绍 前言 1生产厂商及其产品线 1.1ARM单片机的产品线 1.2命名规则 作业1 2习单片机的资料准备 2.1STM32开发所需手册 2.1.1芯片的数据手册 芯片基本 ...

  4. 嵌入式学习笔记--jlink 工具RTT使用笔记

    嵌入式学习笔记–jlink 工具RTT 使用笔记 最近有调试一个ethcat板卡的需求,板卡设计的很简洁,没有多余的调试串口,正准备飞线到单片机针脚上的时候发现了RTT 的这个工具,记录一下使用的方法 ...

  5. 嵌入式学习笔记——ADC模数转换器

    ADC模数转换器 前言 ADC介绍 ADC概述 ADC的数量 ADC的特性 ADC框图 芯片外部框图 芯片内部框图 转换部分框图 状态输出部分 条件触发框图 寄存器介绍 编程思路 模式选择 规则通道的 ...

  6. cortex_m3_stm32嵌入式学习笔记(十五):待机唤醒实验(WK_UP外部中断)

    cortex_m3_stm32嵌入式学习笔记(十五):待机唤醒实验(WK_UP外部中断) https://blog.csdn.net/qq_16255321/article/details/43086 ...

  7. 嵌入式学习笔记——STM32的USART通信概述

    文章目录 前言 常用通信协议分类及其特征介绍 通信协议 通信协议分类 1.同步异步通信 2.全双工/半双工/单工 3.现场总线/板级总线 4. 串行/并行通信 5. 有线通信.无线通信 STM32通信 ...

  8. 嵌入式学习笔记——使用寄存器编程操作GPIO

    使用寄存器编程操作GPIO 前言 GPIO相关的寄存器 GPIO 端口模式寄存器 (GPIOx_MODER) (x = A..I) 位操作 GPIO 端口输出类型寄存器 (GPIOx_OTYPER) ...

  9. 嵌入式学习笔记——寄存器实现控制LED小灯

    文章目录 前言 GPIO通用输出模式 初始化LED小灯的GPIO 原理图 初始化代码 初始化的效果 功能函数封装 直接分开宏定义两个 使用条件运算符 封装函数实现简单的功能 KEIL MDK一些技巧 ...

最新文章

  1. 人和计算机在时间管理方面的相似性
  2. Eclipse设置字体大小等!
  3. Flutter之基本数据类型测试
  4. data.frame类型数据如何将第一列值替换为行号
  5. LeetCode 198 打家劫舍
  6. 并发编程-信号量的使用方法和其实现原理
  7. 使用windows 10 安装中文版语言
  8. [转载] python多重继承初始化_关于python多重继承初始化问题
  9. 宿主程序Crash与Lua是动态库还是静态库有关?
  10. 阿里云因 bug 禁用内部 IP 导致链路不通,造成大规模故障
  11. Atititt java redis jedis 使用 Redis体系 Redis 命令 Redis 命令Redis 键(key)Redis 字符串(String)Redis 哈希(Hash)R
  12. java8接口可以实现方法目的_Java8 collector接口的定制实现
  13. JS 模拟鼠标自动点击【浏览器内】
  14. obsidian标题自动添加序号
  15. 如何调整plt.plot()线的粗细,linewidth
  16. 解决WH1000XM3连接电脑无法使用麦克风问题
  17. 互联网协议 传输层及概述
  18. Windows Setup could not set the display language
  19. 高中英语试验修订本(第一册)上(人教版)
  20. English:现在分词和过去分词的用法

热门文章

  1. 运动控制:台达驱动器下载参数失败
  2. 【数据库】SQL Server知识点总结
  3. UTC时间格式转换yyyy-MM-dd'T'HH:mm:ss.SSSXXX
  4. Kafka Producer重试参数retries设置取舍
  5. Jest 学习01 - Jest 介绍、快速体验、vscode 智能提示、配置、监视模式、Babel 配置
  6. 关于深度学习人工智能模型的探讨(六)(3)
  7. pr / twixtor 补帧
  8. 面试题记录-- 对于软件测试的理解,测试的核心,测试策略
  9. 基于GEC6818的简易自动售货机的设计
  10. unity第三天:物体的碰撞器与触发器