实验五-JPEG原理分析及JPEG解码器的调试

本文详细介绍了JPEG格式的组成和编码原理,包括SOI、APP0、DQT、SOF0等标记的分析,以及DCT、量化和熵编码等步骤。此外,还详述了JPEG解码的过程,包括结构体定义、解码步骤和实验调试,最后通过实验总结加深了对JPEG编解码的理解。
摘要由CSDN通过智能技术生成

JPEG原理分析及JPEG解码器的调试

一、实验简介

JPEG( Joint Photographic Experts Group)是用于连续色调静态图像压缩的一种标准,文件后缀名为.jpg或.jpeg,是最常用的图像文件格式。
主要采用预测编码(DPCM)、离散余弦变换(DCT)以及熵编码的联合编码方式,以去除冗余的图像和彩色数据,属于有损压缩格式。
所谓有损压缩,就是把原始数据中不重要的部分去掉,以便可以用更小的体积保存。

1、JPEG格式分析

具体对照分析方法参见PNG格式分析

(1)SOI

SOI ,Start of Image,图像开始。标记代码2字节,固定值0xFFD8
在这里插入图片描述

(2)APP0

APP0,Application,应用程序保留标记0
在这里插入图片描述
标记代码 2字节 FF E0
数据长度 2字节 00 10
标识符 5字节 4A 46 49 46 00 ,即字符串JFIF0
版本号 2字节 01 01
X和Y的密度单位 1字节 00-无单位
X方向像素密度 2字节 00
Y方向像素密度 2字节 01
缩略图水平像素数目 1字节 00
缩略图垂直像素数目 1字节 01

(3)DQT

DQT,Define Quantization Table,定义量化表
在这里插入图片描述
标记代码 2字节 FF DB
数据长度 2字节 00 43
在这里插入图片描述

(4)SOF0

SOF0,Start of Frame,帧图像开始
在这里插入图片描述

在这里插入图片描述

(5)DHT

DHT,Define Huffman Table,定义哈夫曼表
在这里插入图片描述
在这里插入图片描述

(6)SOS

SOS,Start of Scan,扫描开始 12字节
在这里插入图片描述
在这里插入图片描述

(7)EOI

EOI,End of Image,图像结束 。标记代码2字节 固定值0xFFD9
在这里插入图片描述

2、JPEG编码原理

在这里插入图片描述
JPEG压缩编码算法的主要计算步骤如下:

(1) level offset
  • 将输入图片做一个零偏置电平下移,将原数值从无符号数转换为有符号数,将值域往下做搬移,从而提高编码效率。
  • 对于n=8,即将0~255的值域,通过减去128转换为值域在-128~127之间的值
(2) 8*8 DCT
  • 将原始图像分为8*8的小块, 对于图片宽高不为8的倍数的图片,进行边缘填充处理。
  • 对每个8x8块的每行进行变换,然后每列进行变换。得到的是一个8x8的变换系数矩阵。其中(0,0)位置的元素就是直流分量,矩阵中的其他元素根据其位置表示不同频率的交流分量。

DCT(离散余弦变换)具有很强的"能量集中"特性,大多数的自然信号(包括声音和图像)的能量都集中在DCT变换后的低频部分,当信号具有接近马尔科夫过程(Markov
processes)的统计特性时,离散余弦变换的去相关性接近于K-L变换(Karhunen-Loève
变换–它具有最优的去相关性)的性能,从而提高编码效率

(3) 量化(quantization)。
  • 量化就是用像素值÷量化表对应值所得的结果。
  • 量化表左上角的值较小,右上角的值较大,这样就起到了保持低频分量,抑制高频分量的目的。由于人眼对低频分量的敏感程度远高于高频分量,因此我们需要对低频分量细量化,对高频部分粗量化。通过量化可以达到通低频减高频的效果

在这里插入图片描述
左边那个量化表,最右下角的高频÷16,这样原先DCT后[-127,127]的范围就变成了[-7,7],固然减少了码字(从8位减至4位)。

  • JPEG使用的颜色是YUV格式。我们提到过,Y分量代表了亮度信息,UV分量代表了色差信息。由于人对亮度的敏感程度远高于色度,我们可以对Y采用细量化,对UV采用粗量化,从而进一步提高压缩比。所以量化表通常有两张,一张是针对Y的;一张是针对UV的。
(4) 编码

编码信息分两类,一类是使用差分脉冲编码调制(DPCM)对直流系数(DC)进行编码。一类是使用行程长度编码(RLE)对交流系数(AC)进行编码。

  • 使用差分脉冲编码调制(DPCM)对直流系数(DC)进行编码
    每个8 * 8格子F中的[0,0]位置上元素,代表8 * 8个子块的平均值,JPEG中对F[0,0]单独编码,由于两个相邻的8×8子块的DC系数相差很小,具有冗余。所以对它们采用差分编码DPCM,可以提高压缩比,也就是说对相邻的子块DC系数的差值进行编码。
  • 使用行程长度编码(RLE)对交流系数(AC)进行编码
    另一类是8×8块的其它63个子块,即交流(AC)系数,采用行程编码(游程编码Run-length encode,RLE)。为了保证低频分量先出现,高频分量后出现,以增加行程中连续“0”的个数,这63个元素采用了“之”字型(zigzag scan)。的排列方法,如下图所示。
    在这里插入图片描述
(5) 熵编码(Huffman编码)

为了进一步提高压缩比,需要对RLE编码结果再进行熵编码,我们选用Huffman编码根据使用频率来最大化节省字符(编码)的存储空间,

操作步骤大致如下图:
将符号按照概率由大到小排队,编码时从最小概率的两个符号开始,可选其中一个支路为0,另一支路为1,然后将已编码的两支路的概率合并,并重新按照概率从大到小的顺序排队。多次重复使用上述方法直至合并概率归一时为止。一般若将新合并后的支路排到等概率的最上支路,将有利于缩短码长方差,且编出的码更接近于等长码
在这里插入图片描述

3、JPEG解码原理

JPEG解码为编码的逆过程,解码大致流程如下:

(1)读入文件的相关信息

(2)初步了解图像数据流的结构

(3)颜色分量单元的内部解码

(4)直流系数的差分编码

(5)反量化 & 反Zig-zag编码

(6)反离散余弦变换

二、实验步骤

1、定义结构体、参数等

(1)struct huffman_table:

存储Huffman码表

struct huffman_table
{
   
  /* Fast look up table, using HUFFMAN_HASH_NBITS bits we can have directly the symbol,
   * if the symbol is <0, then we need to look into the tree table */
  short int lookup[HUFFMAN_HASH_SIZE];
  /* code size: give the number of bits of a symbol is encoded */
  unsigned char code_size[HUFFMAN_HASH_SIZE];
  /* some place to store value that is not encoded in the lookup table 
   * FIXME: Calculate if 256 value is enough to store all values
   */
  uint16_t slowtable[16-HUFFMAN_HASH_NBITS][256];
};
(2)struct component:

储存当前8×8像块中有关解码的信息

struct component 
{
   
  unsigned int Hfactor; // 水平采样因子
  unsigned int Vfactor; // 垂直采样因子
  float* Q_table;   // 指向该8×8块使用的量化表
  struct huffman_table *AC_table;   // 指向该块使用的AC Huffman表
  struct huffman_table *DC_table;   // 指向该块使用的DC Huffman表
  short int previous_DC;    // 前一个块的直流DCT系数
  short int DCT[64];    // DCT系数数组
    
#if SANITY_CHECK
  unsigned int cid;
#endif
};

(3)struct jdec_private:

JPEG数据流的结构体,包含图片宽高、Huffman码表等信息

/* tinyjpeg-internal.h */

struct jdec_private
{
   
  /* Public variables */
  uint8_t *components[COMPONENTS];  /* 分别指向YUV三个分量的三个指针 */
  unsigned int width, height;	/* 图像宽高 */
  unsigned int flags;

  /* Private variables */
  const unsigned char *stream_begin, *stream_end;
  unsigned int stream_length;

  const unsigned char *stream;	/* 指向当前数据流的指针 */
  unsigned int reservoir, nbits_in_reservoir;

  struct component component_infos[COMPONENTS];
  float Q_tables[COMPONENTS][64];		/* quantization tables */
  struct huffman_table HTDC[HUFFMAN_TABLES];	/* DC huffman tables */
  struct huffman_table HTAC[HUFFMAN_TABLES];	/* AC huffman tables */
  int default_huffman_table_initialized;
  int restart_interval;
  int restarts_to_go;				/* MCUs left in this restart interval */
  int last_rst_marker_seen;			/* Rst marker is incremented each time */

  /* Temp space used after the IDCT to store each components */
  uint8_t Y[64*4], Cr[64], Cb[64];

  jmp_buf jump_state;
  /* Internal Pointer use for colorspace conversion, do not modify it !!! */
  uint8_t *plane[COMPONENTS];
};

2、逐步调试JPEG解码器程序,将输入的JPEG文件进行解码

具体程序如下:

 /* 读取JPEG文件,进行解码,并存储结果 */
int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{
   
  FILE *fp;
  unsigned int length_of_file;
  unsigned int width, height;
  unsigned char *buf;
  struct jdec_private *jdec;
  unsigned char *components[3];

  /* 把jpeg读入缓冲区 */
  fp = fopen(infilename, "rb");
  if (fp == NULL)
    exitmessage("Cannot open filename\n");
  length_of_file = filesize(fp)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值