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

版权声明:本文为博主原创文章,未经博主允许不得转载。欢迎联系我qq2488890051 https://blog.csdn.net/kangkanglhb88008/article/details/89504607

软件解码:

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格式,结果单片机就可以解码并且显示了

猜你喜欢

转载自blog.csdn.net/kangkanglhb88008/article/details/89504607