数据压缩第九次作业:JPEG原理分析及JPEG解码器的调试

实验目的:
掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。
主要设备:
安装Windows和Visual Studio软件的个人计算机
实验内容:
一、JPEG:
1.基本定义:
JPEG(Joint Photographic Experts Group)是JPEG标准的产物,该标准由国际标准化组织(ISO)制订,是面向连续色调静止图像的一种压缩标准。 [1] JPEG格式是最常用的图像文件格式,后缀名为.jpg或.jpeg。
在这里插入图片描述
2.JPEG编码基本原理:
如上图所示,JPEG的编码基本原理如上图所示:
(1)Level offset(零电平偏置):
将输入的图像的所有像素点的值-128,使其原先范围由0~255变为-128~127
(2)8✖️8DCT变换:
将零电平偏置之后的图像分成88像素的块来处理,不足88的,则取边缘像素补齐,对每个块做DCT变换,直流系数在每个块的左上角,越右下角的分量频率越高。
(3)Uniform scalar quantization(量化):
使用根据人眼视觉特性设计的量化的量化矩阵对DCT变换之后的结果进行量化,从而进而减少视觉冗余。
(4)DCT变换后DC系数值较大,且相邻块变化不大,利用这个特性对其进行DPCM,对相邻块的DC差值huffman编码
(5)对AC系数做之字形扫描,再进行游程编码和huffman编码
3.JPEG文件格式:
(1)Segment 的组织形式
JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
(a)均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的Segment length(包含表示 Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);
(b) 采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
©Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
(2)JPEG的Segment Marker
(备注:此部分截图中的表格均为三列格式:分别为:“symbol(符号)”,“Code Assignment(0xFF+Marker)(标记代码)”,“Description(说明)”)
(a)Start Of Frame markers, non hierarchical Huffman coding在这里插入图片描述
(b)Start Of Frame markers, hierarchical Huffman coding
在这里插入图片描述
©Start Of Frame markers, non-hierarchical arithmetic coding
在这里插入图片描述
(d)Start Of Frame markers, hierarchical arithmetic coding
在这里插入图片描述
(e)Huffman table specification
在这里插入图片描述
(f)arithmetic coding conditioning specification
在这里插入图片描述
(g)Restart interval termination
在这里插入图片描述
(h)Other marker
在这里插入图片描述
(i)Reserved markers在这里插入图片描述
(3)部分Segment marker介绍:
(a)SOI:START OF IMAGE,图像开始,标记代码两字节,固定值0xFFD8
(b)EOI:END OF IMAGE,图像结束,标记代码两字节,固定值0xFFD9
(c)APP0应用程序保留标记0:
标记代码:2字节,固定值0xFFE0
在这里插入图片描述
(d)DQT定义量化表:标记代码2字节,固定值0xFFDB
包含9个具体字段:
数据长度:2字节,字段1和多个字段2的总长度
量化表:数据长度-2字节
a)精度及量化表ID:1字节
高4位:精度,只有两个可选值 0:8位,1:16位
b)表项:(64*(精度+1))字节
(e)SOF0:帧图像开始:标记代码2字节,固定值0xFFC0
包含9个具体字段:
数据长度:2字节,整个部分6个字段的总长度
精度:1字节,代表每个数据样本的位数,通常为8位
图像高度:2字节,单位像素
图像宽度:2字节,单位像素
颜色分量数:1字节,3个数值可选:
1:灰度图,3:YCrCb或者YIQ,4:CMYK
而JFIF中使用TCrCb,故这里颜色分量数恒为3
颜色分量信息:通常为9字节(颜色分量数✖️3字节)
ea)颜色分量ID,1字节
eb)水平/垂直采样因子,1字节,高4位:水平采样因子;第四位,垂直采样因子
ec)量化表,1字节,当前分量使用的量化表ID
(f)DHT:定义huffman表,标记代码2字节,0xFFC4
在这里插入图片描述
(g)SOS:扫描开始,标记代码2字节,固定值0xFFDA
在这里插入图片描述
3.JPEG解码基本原理
(1)读取文件
(2)解析segment marker
(a)解析 SOI
(b)解析 APP0
检查标识“JFIF”及版本并得到一些参数
© 解析 DQT
得到量化表长度(可能包含多张量化表)
得到量化表的精度
得到及检查量化表的序号(只能是 0 —— 3)
得到量化表内容(64 个数据)
(d)解析 SOF0
得到每个 sample 的比特数、长宽、颜色分量数
得到每个颜色分量的 ID、水平采样因子、垂直采样因子、使用的量化表 序号(与 DQT 中序号对应)
(e) 解析 DHT
得到 Huffman 表的类型(AC、DC)、序号
依据数据重建 Huffman 表 3.2.6 解析 SOS
得到解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT 中序号对应)
(3)依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8 宏块的个数
(4) 对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解 码)
(a)对每个宏块进行 Huffman 解码,得到 DCT 系数
(b)对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr
©遇到 Segment Marker RST 时,清空之前的 DC DCT 系数
(5)解析到 EOI,解码结束
(6) 将 Y、Cb、Cr 转化为需要的色彩空间并保存。
二、具体实验步骤
1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
在本次实验中,我们主要采用本图片,test.jpg进行操作:(嗯,看着有点晕其实)
在这里插入图片描述
首先在命令行中按照主程序中的设置添加参数,先运行一下代码:
在这里插入图片描述
在这里插入图片描述
而后我们便可以发现文件夹中生成了三个名为test的新文件,即:test.u,test.v,test.y,分别存储的是test.jpg图片转化为yuv文件之后的三个分量。
所以,我们需要对于原来的代码进行修改:
首先,我们找到write_yuv这个函数,从代码注释中,我们可以看出,这段就是使之输出test.u,test.v,test.y的相关代码:
在这里插入图片描述
所以,我们对本段代码进行如下添加,使之可以输出YUV文件:

static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
   
  FILE *F;
  char temp[1024];

  snprintf(temp, 1024, "%s.Y", filename);
  F = fopen(temp, "wb");
  fwrite(components[0], width, height, F);
  fclose(F);
  snprintf(temp, 1024, "%s.U", filename);
  F = fopen(temp, "wb");
  fwrite(components[1], width*height/4, 1, F);
  fclose(F);
  snprintf(temp, 1024, "%s.V", filename);
  F = fopen(temp, "wb");
  fwrite(components[2], width*height/4, 1, F);
  fclose(F);

  snprintf(temp, 1024, "%s.YUV", filename);
  F = fopen(temp, "wb");
  fwrite(components[0], width, height, F);
  fwrite(components[1], width * height / 4, 1, F);
  fwrite(components[2], width * height / 4, 1, F);
  fclose(F);
}

其中,需要注意的是,最开始输入命令行的时候可能出现报错:
“错误 D8016 “/ZI”和“/Gy-”命令行选项不兼容 ”
在查找了一下之后,发现需要做以下操作即可解决:
在这里插入图片描述
在这里插入图片描述
最后运行程序,可以看到,程序生成了一个test.yuv的文件,使用pyuv打开可以看到其和原图JPG形式的图片没有差别:
在这里插入图片描述
至此,本实验第一步完成。
2. 程序调试:
(1)程序运行的整体框架:(本部分的大部分介绍都以注释的形式,写在代码中)
首先介绍三个比较重要的结构体:
(a)struct huffman_table

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];
};

从这个结构体中,我们可以看出,其包含三个部分:其中lookup为short int型,用来加速查表,如果查找失败的话则就要使用慢速查找表。code_size为码字的长度。定义这个结构体的目的就是用来快速查找,从而加速程序的运行速度。
(b)struct component

struct component 
{
   
  unsigned int Hfactor;
  unsigned int Vfactor;
  float *Q_table;  /* Pointer to the quantisation table to use */
  struct huffman_table *AC_table;
  struct huffman_table *DC_table;
  short int previous_DC; /* Previous DC coefficient */
  short int DCT[64];  /* DCT coef */
#if SANITY_CHECK
  unsigned int cid;
#endif
};

这个结构体中,首先定义的Hfactor和Vfactor分别对应horizontal和vertical即水平和垂直方向的采样信息。而后定义Q_table则是对应此次DCT变换所对应的量化表,DCT[64]则代表这个8✖️8的宏块所存储的DCT系数,previousDC则指的是前一个直流系数。
这部分的主要用途就是用来存储每一个MCU的信息,用来进行DCT的变换。
(c)struct jdec_private

struct jdec_private
{
   
  /* Public variables */
  uint8_t *components[COMPONENTS];
  unsigned int width, height; /* Size of the image */
  unsigned int flags;

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

  const unsigned char *stream; /* Pointer to the current 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];

};

此部分的结构体则是包含整合了以上两个结构体中的内容,在解码过程中,我们会频繁使用到这个结构体。在这个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值