【数据压缩】JPEG编码原理和码流分析

本文介绍了一个JPEG解码实验,详细解析了JPEG文件的结构及解码流程,包括文件头解析、量化表、Huffman码表的建立等关键步骤,并展示了实验结果。

一、实验目的

掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。

二、实验原理

JPEG编码原理

JPEG编码包含以下几个主要内容:

  1. 8*8分块。
  2. 正向离散余弦变换(FDCT)
  3. 量化(quantization)
  4. Z字形编码(zigzag scan)
  5. 使用差分脉冲编码调制(DPCM)对直流系数(DC)进行编码
  6. 使用行程长度编码(RLE)对交流系数(AC)进行编码。
  7. 熵编码。

编码框图如图所示,分别对应着上述过程。
在这里插入图片描述

JPEG格式文件解析

图像开始SOI标记(0xFFD8)和图像结束EOI标记(0xFFD9)

FFD8: SOI,Start of Image,图像开始,所有的 JPEG 文件必须以 SOI 开始。
FFD9: End of Image,图像结束,JPEG 文件必须以 EOI 结束。
在这里插入图片描述

FFDB: DQT,Define Quantization Table,定义量化表

第一个FFDB定义第一张量化表

00 43 00 + qt_table(64字节)

第二个字节43表示这一部分长67字节,第三个字节表示索引号0,qt_table长64字节对应量化表的8*8个值

第二个FFDB定义第二章量化表,不同的是上张表的索引号为 00,这张表的索引号为 01,在后面的SOF0 的部分中我们将会知道上张表对应亮度量化表,这张表对应色度量化表,对这张图来说就这两张量化表。

在这里插入图片描述

FFC4: DHT,Define Huffman Table,定义 Huffman 树表
FFC4:标记代码,2 字节,代表定义 Huffman 表。
数据长度: 2 字节,这里是1F, 31 字节的长度(包括长度自身)
Huffman 表 ID 号和类型:1 字节,高 4 位为表的类型,0:DC 直流;1:AC 交流 可以看出这里是直流表;低四位为 Huffman 表 ID。 这里是00,可以看出这张表是直流 DC 的第 0 张表,在后面的扫描开始的部分中我们可以获右为亮度的直流系数表。
不同长度 Huffman 的码字数量:固定为 16 个字节,每个字节代表从长度为 1到长度为 16 的 码 字 的个 数 , 以 表 中 的 分析, 这 16 个字节之后的1+5+1+1+1+1+1+1=12 个字节对应的就是每个符字对应的权值,这些权值的含义即为 DC 系数经 DPCM 编码后幅度值的位长。
通过上面的码长与码字个数的关系来生成相应码长的码字,再对应上之后的权值即位长根据解码得到的位长来读取之后相应长度的码字,再查上面这张可变长二进制编码表,就可以得到直流系数的幅度值,注意这个幅度值是经过 DPCM 差分编码得到的。

在这里插入图片描述

** FFDA: 标记代码 SOS,Start of Scan**
数据长度:2 字节,这里是00 0C,长度为 12 字节。
颜色分量数:1 字节 应该和 SOF 中的颜色分量数相同
颜色分量信息:每个分量对应 3 个字节,第一个字节是颜色分量 ID,1,2,3
对应 YUV,第二个字节高位为直流分量使用的哈夫曼树编号,这里 Y 的直流分量用的是 DC 的第 0 张表,低四位代表交流分量使用的哈夫曼树编号,这里 Y的交流分量用的是 AC 的第 0 张表,而两个色度信号的直流分量都用的是 DC
的第 1 张表,交流分量用的是 AC 的第 1 张表.压缩图像数据
a)谱选择开始 1 字节 固定值 0x00
b)谱选择结束 1 字节 固定值 0x3F
c)谱选择 1 字节 在基本 JPEG 中总为 00
在这里插入图片描述

三、代码调试与分析

阅读JPEG解码相关代码,分析主要解码部分如下:

tinyjpeg_parse_header 文件头解析

int convert_one_image(const char *infilename, const char *outfilename, int output_format) //读取JPEG文件,开始解码,存储结果
{
   
   
  FILE *fp;
  unsigned int length_of_file; //文件大小
  unsigned int width, height;  //图像宽高
  unsigned char *buf;  
  struct jdec_private *jdec;
  unsigned char *components[3];

  /* Load the Jpeg into memory 将JPEG文件读入缓冲区*/
  fp = fopen(infilename, "rb");
  if (fp == NULL)
    exitmessage("Cannot open filename\n");
  length_of_file = filesize(fp);
  buf = (unsigned char *)malloc(length_of_file + 4);
  if (buf == NULL)
    exitmessage("Not enough memory for loading file\n");
  fread(buf, length_of_file, 1, fp);
  fclose(fp);

  /* Decompress it */
  jdec = tinyjpeg_init();  //初始化
  if (jdec == NULL)
    exitmessage("Not enough memory to alloc the structure need for decompressing\n");

  if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)  //解析JEPG文件头信息
    exitmessage(tinyjpeg_get_errorstring(jdec));

  /* Get the size of the image 获得图像宽高信息*/
  tinyjpeg_get_size(jdec, &width, &height);

  snprintf(error_string, sizeof(error_string),"Decoding JPEG image...\n");
  if (tinyjpeg_decode(jdec, output_format) < 0)  //解码实际数据
    exitmessage(tinyjpeg_get_errorstring(jdec));

  /* 
   * Get address for each plane (not only max 3 planes is supported), and
   * depending of the output mode, only some components will be filled 
   * RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane
   */
  tinyjpeg_get_components(jdec, components);

  /* Save it 根据所需格式保存输出文件*/
  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;
    case TINYJPEG_FMT_GREY:
      write_pgm(outfilename, width, height, components);
      break;
   }

  /* Only called this if the buffers were allocated by tinyjpeg_decode() */
  tinyjpeg_free(jdec);
  /* else called just free(jdec); */

  free(buf);
  return 0;
}

parse_JFIF 解析marker标识

int convert_one_image(const char *infilename, const char *outfilename, int output_format) //读取JPEG文件,开始解码,存储结果
{
   
   
  FILE *fp;
  unsigned int length_of_file; //文件大小
  unsigned int width, height;  //图像宽高
  unsigned char *buf;  
  struct jdec_private *jdec;
  unsigned char *components[3];

  /* Load the Jpeg into memory 将JPEG文件读入缓冲区*/
  fp = fopen(infilename, "rb");
  if (fp == NULL)
    exitmessage("Cannot open filename\n");
  length_of_file = filesize(fp);
  buf = (unsigned char *)malloc(length_of_file + 4);
  if (buf == NULL)
    exitmessage("Not enough memory for loading file\n");
  fread(buf, length_of_file, 1, fp);
  fclose(fp);

  /* Decompress it */
  jdec = tinyjpeg_init();  //初始化
  if (jdec == NULL)
    exitmessage("Not enough memory to alloc the structure need for decompressing\n");

  if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)  //解析JEPG文件头信息
    exitmessage(tinyjpeg_get_errorstring(jdec));

  /* Get the size of the image 获得图像宽高信息*/
  tinyjpeg_get_size(jdec, &width, &height);

  snprintf(error_string, sizeof(error_string),"Decoding JPEG image...\n");
  if (tinyjpeg_decode(jdec, output_format) < 0)  //解码实际数据
    exitmessage(tinyjpeg_get_errorstring(jdec));

  /* 
   * Get address for each plane (not only max 3 planes is supported), and
   * depending of the output mode, only some components will be filled 
   * RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane
   */
  tinyjpeg_get_components(jdec, components);

  /* Save it 根据所需格式保存输出文件*/
  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;
    case TINYJPEG_FMT_GREY:
      write_pgm(outfilename, width, height, components);
      break;
   }

  /* Only called this if the buffers were allocated by tinyjpeg_decode() */
  tinyjpeg_free(jdec);
  /* else called just free(jdec); */

  free(buf);
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值