数据压缩原理与应用 实验五 JPEG 原理分析及 JPEG 解码器的调试

一、基本原理

1.JPEG文件格式介绍
1.1 SOI(Start of Image)图像开始

标记代码:固定值0xFFD8(2字节)

1.2 APP0(Application)应用程序保留标记
  • 标记代码 :固定值0xFFE0(2字节)
  • 包含9个具体字段:
序号内容字节数取值
1数据长度2字节①~⑨9个字段的总长度
2标识符5字节固定值0x4A46494600,即字符串“JFIF0”
3版本号2字节一般是0x0102,表示JFIF的版本号1.2
4X和Y的密度单位1字节只有三个值可选 0:无单位;1:点数/英寸;2:点数/厘米
5X方向像素密度2字节取值范围未知
6Y方向像素密度2字节取值范围未知
7缩略图水平像素数目1字节取值范围未知
8缩略图垂直像素数目1字节取值范围未知
9缩略图RGB位图长度可能是3的倍数缩略图RGB位图数据
1.3 DQT(DefineQuantization Table)定义量化表
  • 标记代码:固定值0xFFDB(2字节)
  • 包含6个具体字段:
序号内容分层字节数取值
1数据长度2字节字段①和多个字段②的总长度
2.1量化表精度及量化表ID1字节高4位:精度,只有两个可选值 0:8位;1:16位 低4位:量化表ID,取值范围为0~3
2.2量化表表项1字节(64×(精度+1))字节

* 本标记段中,字段②可以重复出现,表示多个量化表,但最多只能出现4次

1.4 SOF0(Start of Frame)帧图像开始
  • 标记代码:固定值0xFFC0(2字节)
  • 包含6个具体字段:
序号内容分层字节数取值
1数据长度2字节①~⑥六个字段的总长度
2精度1字节通常是8位,一般软件都不支持 12位和16位
3图像高度2字节图像高度(单位:像素)
4图像宽度2字节图像宽度(单位:像素)
5颜色分量数1字节只有3个数值可选-1:灰度图;3:YCrCb或YIQ;4:CMYK 而JFIF中使用YCrCb,故这里颜色分量数恒为3
6.1颜色分量信息颜色分量ID1字节只有3个数值可选-1:灰度图;3:YCrCb或YIQ;4:CMYK ,而JFIF中使用YCrCb,故这里颜色分量数恒为3
6.2颜色分量信息水平/垂直采样因子1字节高4位:水平采样因子/低4位:垂直采样因子
6.3颜色分量信息量化表1字节当前分量使用的量化表的ID
1.5 DHT(Define Huffman Table)定义哈夫曼表
  • 标记代码:固定值0xFFC4(2字节)
  • 包含2个具体字段:
序号内容字节数
1数据长度2字节
2Huffman表数据长度2字节

- 表ID和表类型 1字节
- 高4位:只有两个值可选(0:DC直流;1:AC交流)+低4位:哈夫曼表ID
- 不同位数的码字数量 16字节
- 编码内容:16个不同位数的码字数量之和(字节)
- 本标记段中,字段②可以重复出现(一般4次),也可以只出现1次。

1.6 SOS(Start of Scan)-扫描开始 12字节
  • 标记代码:固定值0xFFDA(2字节)
  • 包含4个具体字段:
序号内容分层字节数取值
1数据长度2字节①~④两个字段的总长度
2颜色分量数1字节应该和SOF中的字段⑤的值相同- 1:灰度图是;3: YCrCb或YIQ;4:CMYK
3.1颜色分量信息颜色分量ID1字节
3.2颜色分量信息直流/交流系数表号1字节高4位:直流分量使用的哈夫曼树编号+低4位:交流分量使用的哈夫曼树编号
4.1压缩图像数据谱选择开始1字节固定值0x00
4.2压缩图像数据谱选择结束1字节固定值0x3F
4.3压缩图像数据谱选择1字节在基本JPEG中总为00

1.7 EOI(End of Image)图像结束

标记代码:固定值0xFFD9(2字节)

2.JPEG编码原理

JPEG编码原理

2.1 零偏置-Level Offset
  • 目的:使像素的绝对值出现3位10进制的概率大大减少
  • 对于灰度级是 2n 的像素,通过减去 2n1 ,将无符号的整数值变成有符号数
    • eg:对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值
2.2 8x8DCT变换
  • 对每个单独的彩色图像分量,把整个分量图像分成8×8的图像块,如图所示,并作为两维离散余弦变换DCT的输入
    DCT
2.3 量化
  • 中平型均匀量化器
    这里写图片描述
  • 量化步距是按照系数所在的位置、颜色分量来确定
  • 因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值
  • 根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化
  • 如果原始图象中细节丰富,则去掉的数据较多,量化后的系数与量化前差别大。反之,细节少的原始图象在压缩时去掉的数据少些
2.4 DC系数的差分编码
  • 8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:
    • 系数的数值比较大
    • 相邻8×8图像块的DC系数值变化不大:冗余
  • 根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:

    • DIFFk=DCkDCk1
  • 对DIFF用Huffman编码:分成类别,类似指数Golomb编码

    • 类别ID:一元码编码
    • 类内索引:采用定长码
2.5 AC系数的Z字扫描
  • 由于经DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出EOB (End of Block)即可。
    Z字扫描
2.6 AC系数的游程编码
  • 在JPEG和MPEG编码中规定为:(run, level)
    • 表示连续run个0,后面跟值为level的系数
    • 如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7表示为(1, 2), (2, 3) ,…
  • 编码:
    • Run: 最多15个,用4位表示RRRR
    • Level:类似DC
      -分成16个类别,用4位表示SSSS表示类别号
      -类内索引
    • 对(RRRR, SSSS)联合用Huffman编码
    • 对类内索引用定长码编码

二、JPEG解码流程

1. 读取文件
2. 解析 Segment Marker
  • 1.解析 SOI
  • 2.解析 APP0
    • 检查标识“JFIF”及版本
    • 得到一些参数
  • 3.解析 DQT
    • 得到量化表长度(可能包含多张量化表)
    • 得到量化表的精度
    • 得到及检查量化表的序号(只能是 0 —— 3)
    • 得到量化表内容(64 个数据)
  • 4.解析 SOF0
    • 得到每个 sample 的比特数、长宽、颜色分量数
    • 得到每个颜色分量的 ID、水平采样因子、垂直采样因子、使用的量表序号(与 DQT 中序号对应)
  • 5.解析 DHT
    • 得到 Huffman 表的类型(AC、DC) 、序号
    • 依据数据重建 Huffman 表
  • 6.解析 SOS
    • 得到解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与DHT中序号对应)
3. 依据每个分量的水平、 垂直采样因子计算MCU的大小个 , 并得到每个MCU中8*8宏块的个数
4. 对每个MCU解码(依照各分量水平、垂直采样因子对MCU中每个分量宏块解码)
  • 对每个宏块进行 Huffman 解码,得到 DCT 系数
  • 对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr
  • 遇到 Segment Marker RST 时,清空之前的 DC DCT 系数
5. 解析到EOI,解码结束
6. 将Y、Cb、Cr转化为需要的色彩空间并保存

三、代码分析

1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
  • tinyjpeg.h中增加YUV类型:
enum tinyjpeg_fmt {
   TINYJPEG_FMT_GREY = 1,
   TINYJPEG_FMT_BGR24,
   TINYJPEG_FMT_RGB24,
   TINYJPEG_FMT_YUV420P,
   //Add by ts
   TINYJPEG_FMT_YUV,
   //End 
};
  • loadjpeg.c中判断输出格式时增加YUV类型:
 switch (output_format)
   {
    case TINYJPEG_FMT_RGB24:
    case TINYJPEG_FMT_BGR24:
      write_tga(outfilename, output_format, width, height, components);
      break;
    case TINYJPEG_FMT_YUV420P:
      write_yuv(outfilename, width, height, components);
      break;
      //Add by ts
    case TINYJPEG_FMT_YUV:
        write_YUV(outfilename, width, height, components);
        break;
    //End
    case TINYJPEG_FMT_GREY:
      write_pgm(outfilename, width, height, components);
      break;
   }
  • loadjpeg.c的main函数中增加相应代码
input_filename = argv[current_argument];
  if (strcmp(argv[current_argument+1],"yuv420p")==0)
    output_format = TINYJPEG_FMT_YUV420P;
  else if (strcmp(argv[current_argument+1],"rgb24")==0)
    output_format = TINYJPEG_FMT_RGB24;
  else if (strcmp(argv[current_argument+1],"bgr24")==0)
    output_format = TINYJPEG_FMT_BGR24;
  else if (strcmp(argv[current_argument+1],"grey")==0)
    output_format = TINYJPEG_FMT_GREY;
  //Add by ts
  else if (strcmp(argv[current_argument+1],"yuv")==0)
    output_format = TINYJPEG_FMT_YUV;
  //End
  else
    exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");
  output_filename = argv[current_argument+2];
  • 在tinyjpeg.c增加case TINYJPEG_FMT_YUV项
 decode_mcu_table = decode_mcu_3comp_table;//
  switch (pixfmt) {
     case TINYJPEG_FMT_YUV420P:
       ……;
     case TINYJPEG_FMT_RGB24:
       ……;
     case TINYJPEG_FMT_BGR24:
       ……;
     case TINYJPEG_FMT_GREY:
       ……;

    //Add by ts
     case TINYJPEG_FMT_YUV:
       colorspace_array_conv = convert_colorspace_yuv420p;
       if (priv->components[0] == NULL)
     priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
       if (priv->components[1] == NULL)
     priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4);
       if (priv->components[2] == NULL)
     priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4);
       bytes_per_blocklines[0] = priv->width;
       bytes_per_blocklines[1] = priv->width/4;
       bytes_per_blocklines[2] = priv->width/4;
       bytes_per_mcu[0] = 8;
       bytes_per_mcu[1] = 4;
       bytes_per_mcu[2] = 4;
       break;
    //End
     default:
2.程序调试过程中,应做到:
  • 2.1 理解程序设计的整体框架
  • 2.2 理解以下三个结构体的设计目的
/* 存放DC系数、AC系数的哈夫曼码表 */
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];
};
/* 存放8*8数据块组成的MCU数据值 */
struct component 
{
  unsigned int Hfactor;//水平采样
  unsigned int Vfactor;//垂直采样
  float *Q_table;       /* Pointer to the quantisation table to use */
  struct huffman_table *AC_table;//交流huffman结构体
  struct huffman_table *DC_table;//直流huffman结构体
  short int previous_DC;    /* Previous DC coefficient */
  short int DCT[64];        /* DCT coef */
#if SANITY_CHECK//SANITY_CHECK为1
  unsigned int cid;//???
#endif
};
/* 包含struct huffman_table和struct component,是一个综合的结构体 */
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 YUV*/
  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.3 理解在视音频编解码调试中TRACE的目的和含义
    • 会打开和关闭TRACE
    • 会根据自己的要求修改TRACE

四、实验结果

1.原始图片

这里写图片描述

2.TXT输出结果

量化表

DC0

AC0

DC1

AC1

3.输出的DC、AC图像
图像Freq
DCDC_tableDC_freq
ACAC_tableAC_freq
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据压缩是现代计算最重要的领域和工具之一。从获取数据到CD-ROM,从编码理论到图像处理,现代计算的许多层面都依赖于数据压缩。本书对数据压缩的许多不同类型和方示提供了全面的参考。内容包括详尽而有益的分类、最常用方法的描述、方法使用和获益的比较以及“如何”应用的讨论。全书的介绍沿数据的压缩领域的主干来组织、游程编码、统计方法、基于字典的方法、图像压缩、音频压缩和视频压缩。 数据压缩是现代计算最重要的领域和工具之一。从获取数据到CD-ROM,从编码理论到图像处理,现代计算的许多层面都依赖于数据压缩。本书对数据压缩的许多不同类型和方示提供了全面的参考。内容包括详尽而有益的分类、最常用方法的描述、方法使用和获益的比较以及“如何”应用的讨论。全书的介绍沿数据的压缩领域的主干来组织、游程编码、统计方法、基于字典的方法、图像压缩、音频压缩和视频压缩。该书的主要主题为:视频压缩、小波方法、音频压缩、用于JPEG和JBIG的QM编码器、图像变换、用于压缩简单图像的EIDAC方法、前缀图像压缩、ACB和FHM曲线压缩和边缘破碎法。 本书为所有的计算机科学家、计算机工程师、电气工程师、信号/图像处理工程师,以及其他需要一部压缩方法大全的科学家们,提供了一本十分宝贵的参考和指南。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值