您当前的位置:首页 > 计算机 > 文件格式与编码

JPEG原理分析 & 文件格式分析 & 解码原理 & JPEG解码器的调试

时间:02-25来源:作者:点击数:

1 JPEG原理

1.1 JPEG简述

JPEG(Joint Photographic Experts Group)是联合图像专家组的英文缩写。 该组织从1986年正式开始制订静止数字图像的压缩编码标准,该标准于1992年正式通过,称为JPEG标准。

JPEG是第一个数字图像压缩的国际标准,它不仅适于静止图像的压缩,对 于电视图像序列的帧内压缩也常采用JPEG算法,因此JPEG是一个适用范围广泛的通用标准。


1.2 JPEG编码过程

在这里插入图片描述
1.2.0 RGB to YUV

为了减少各分量之间的相关性,减少数据的冗余,通常会把RGB颜色空间转换成YUV来进行各分量的编码。

在这里插入图片描述

1.2.1 Level Offset 零电平偏置下移

该步骤的作用是,图像内容平均亮度较高,将0电平移到中间,平均亮度降低, 便于DCT变换量化后直流的系数大大降低,也就降低了数据量。

将灰度级2 n 2^n2n的像素值,全部减去2 n − 1 2^{n-1}2n−1,数据形式由无符号数变为有符号数(补码),单极性数据变为双极性数据。


1.2.2 8x8 DCT变换

该步骤主要是用于去除图像数据之间的相关性,便于量化过程去除图像数据的空间冗余。

将图像分为8×8的像块;对于宽(高)不是8的整数倍的图像,使用图像边缘像素填充,以不改变频谱分布。然后对每一个子块进行DCT(Discrete Cosine Transform,离散余弦变换)。

DCT变换:

在这里插入图片描述

其中,C是8x8的DCT变换二维核矩阵,f ( x , y ) f(x,y)f(x,y)是原始的数据。由于DCT变换是一个正交变换,故CT = C−1

变换核矩阵如下所示:

在这里插入图片描述

需要特别强调的是,DCT是一种无损变换,也无法对图像进行压缩,这样做的目的是在为下一步的量化做准备。


1.2.3 Uniform scalar quantization 均匀标量量化

量化器主要是利用人眼视觉特性设计而成的矩阵量化DCT系数,减少视觉冗余。

将DCT变换后的临时结果,除以各自量化步长并四舍五入后取整,得到量化系数。

JPEG系统分别规定了亮度分量和色度分量的量化表,色度分量相应的量化步长比亮度分量大。

在量化步骤中,JPEG采用了中平型(Midtread)的均匀量化器。

量化是编码流程中唯一会引入误差也是唯一会带来压缩的步骤。Y、UV各一张表,共两张表。


1.2.4 直流:DPCM + VLC 可变长熵编码(采用Huffman)

8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:

  • 系数的数值比较大
  • 相邻8×8图像块的DC系数值变化不大,冗余

根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:

在这里插入图片描述

DPCM是对于DC系数处理时所需要用到的预测编码方法。此方法在之前的实验中已经有详细说明。

对DPCM后算出的DIFF差值使用Huffman编码。

将其分成类别,类似于指数的Golomb编码(只不过Golomb是一元码+定长码),也就是类别ID使用规范哈夫曼编码,类内索引使用定长码(自然码)。

所以,DC系数会产生一张长度为16的Huffman码表。

在这里插入图片描述

1.2.5 交流:ZigZag Scan + Run Length Encoding+VLC

对于量化后的数据,我们将其分为两路进行处理。一路是AC通路,一路是DC通路。ZigZag Scan+RLE是用于AC通路的,这是因为AC分量出现较多的0。JPEG采用对0系数的游程长度编码。而对非0值,则要保存所需数和实际值。

在编码之前,需要把二维的变换系数矩阵转换为一维序列,由于量化之后右下角高频系数大部分为零,采用ZigZag Scan读取可以制造较长的零游程,提高编码效率。在扫描中,如果后续的系数全部为零,则用“EOB”表示块结束。

在这里插入图片描述

在扫描后,采用RLE进行编码。

1.2.5.1 RLE编码的过程:
  1. RLE编码。在JPEG编码中,RLE的含义就同其原有的意义略有不同。在JPEG编码中,假设RLE编码之后得到了一个(M,N)的数据对,其中M是两个非零AC系数之间连续的0的个数(即,行程长度),N是下一个非零的AC系数的值。采用这样的方式进行表示,是因为AC系数当中有大量的0,而采用Zigzag扫描也会使得AC系数中有很多连续的0的存在,这样就很适合RLE编码。

例:例如,现有一个字符串,如下所示:

57,45,0,0,0,0,23,0,-30,-8,0,0,1,000…

经过RLE之后,将呈现出以下的形式:

(0,57) ; (0,45) ; (4,23) ; (1,-30) ; (0,-8) ; (2,1) ; (0,0)

注意,如果AC系数之间连续0的个数超过16,则用一个扩展字节(15,0)来表示16连续的0。

  1. Huffman编码。
    在进行了RLE后,仍然需要进行Huffman编码。对于任何一个RLE的数据对,如(0,57),都可以表示成(RRRR,SSSS)的形式。其中前面的0-16采用自然码RRRR,后面的SSSS则是与DC一致的Huffman分组的编码方式,存储索引。放到码流里的是其组内编码。
    在这里插入图片描述

所以,最后总共有4张Huffman码表(亮度DC,亮度AC,色度DC,色度AC)。

那么,这些码表如何存储?源数据又放在哪里?针对这些未解之谜,接下来我们就分析JPEG的存储结构。


1.3 一个JPEG编码的示例

某个图象的一个8*8方块的亮度值:

在这里插入图片描述

Level Offset 后:

在这里插入图片描述

DCT变换后:

在这里插入图片描述

量化后:

在这里插入图片描述

其中,参照的量化表是:

在这里插入图片描述

随后,对于这个8*8方块的亮度量化后的数据分别进行AC和DC两路的编码。


2 JPEG文件格式的解析

由于后面需要导出码表等操作,势必需要掌握jpeg的存储格式。因此,我们针对实验所用的testrgb-2x2.jpg,作一个完整解析。


2.0 文件概览

在这里插入图片描述

JPEG以segment组成。每个segment都有一个名字,为其segment marker.我们逐一对每个marker进行分析。

整个文件以SOI开始,EOI结束。中间包含了APP0字段,两个DQT字段,一个SOF0字段,四个DHT字段,然后包含所有的ImageData数据。每个字段的作用都会在下面详细解释。


2.1 SOI(Start of Image)

图像以SOI(Start of Image)标志图像开始,内容为固定值FFD8

在这里插入图片描述

2.2 APP0

接下来是APP0字段。APP0字段是应用程序保留标记0。

该字段以FFE0开启,后面包含信息:

在这里插入图片描述

2.3 DQT[0]

DQT就是DCT后的两张量化表,一张AC,一张DC。每张量化表都由FFDB字段开始。随后的4个字节说明了该字段的长度,然后存放的就是量化表。

量化表中的第一个字节被分成了高四位和低四位来用。高四位表示了该量化表的精度,0:8位;1:16位;低四位表示了量化表ID,取值范围为0~3;接下来是所有的表项,数量为(64×(精度+1))字节,里面都是量化的系数。量化表中的数据按照Z字形保存量化表内8x8的数据

在这里插入图片描述

该jpeg文件存放了0和1两张码表。

2.4 SOF0[0]

SOF0[0]为帧图像开始marker.以FFC0为开始标记

在这里插入图片描述

后两个字节标注数据长度;然后一个字节标注了每个颜色分量每个像素的位数(8),然后表明了行数和每行的采样点数,然后附上了三个components体信息。每个component都是一个颜色分量,内含颜色索引ID、Sample factor(高四位水平因子、低四位垂直因子)、和采用的量化表号。

2.5 DHT[0] (Define Huffman Table)

DHT是存放Huffman码表的地方。该Marker以FFC4作为开始标记。然后是字段长度,类型(AC/DC),索引(Index),位表(bit table),值表(value table)。

在这里插入图片描述

表的内容如课件所示。

在这里插入图片描述
彩蛋:课件上的数据与分析的图像是一张图像。

一共有4张DHT,对应AC/DC的Y/UV。

2.6 ImageData[0]-SOS

在大量的图像数据开始前,还有一个SOS字段。该字段表明了扫描开始,说明了数据是如何组织的。该字段以FFDA开始,然后表明了字段的长度,然后说明了颜色分量数,该与SOF字段中的数据应该是保持一致的。然后针对于每一个颜色分量信息,给出了每个分量的DC/AC使用的哈夫曼表编号。

在这里插入图片描述
这中间有一些课件未提及的数据?
在这里插入图片描述

然后是)谱选择开始、谱选择结束和谱选择固定值003F00。然后就是正式的图像数据了。

3 程序架构和结构体分析

从main入口开始,我们观察到程序设定了两个模式,一个跑分模式(benchmark_mode)和一个转换图像模式(convert_one_image)。由于我们这次不涉及到跑分模式,我们只对转换图像模式(convert_one_image)进行分析。

我们跳入函数convert_one_image,查看里面干了什么。首先,将程序加载到内存中然后就关闭;然后解码jpeg(这是最主要的工作);然后获取图像大小;然后获取每个通道的内存地址;拥有了获取的这些信息后,才可以以想要的输出方式存储。然后系统对于解码后的文件进行了写出,以用户选择的模式进行写出。

在该函数中,设置了这些变量:

  FILE *fp; //打开的文件
  unsigned int length_of_file; //文件长度
  unsigned int width, height; //宽、高
  unsigned char *buf; //存储的buffer
  struct jdec_private *jdec; //一个结构体指针
  unsigned char *components[3]; //三个通道的指针

可以看到,后面的处理都是针对于jdec这个指针指向的jdec_private结构体进行处理的:

 jdec = tinyjpeg_init(); //该函数用于初始化jdec结构体
 tinyjpeg_parse_header(jdec, buf, length_of_file)//解析JPEG文件头
 tinyjpeg_get_size(jdec, &width, &height); // 计算图像宽高
 tinyjpeg_decode(jdec, output_format);// 解码实际数据
 tinyjpeg_get_components(jdec, components); //获得每个通道的数据

所以,我们有必要对于jdec_private这个结构体进行一个分析。

3.1 结构体:jdec_private

jdec_private是每一个JPEG的码流中的一小块的结构体,包含了所有完整的内容的定义。

在jedc_private 中,定义了指向三个components的指针和三个components结构体(下面详述其意义);定义了图像的宽高;码流长度、始末指针;还有三张量化表(最终只用到两张,Y一张,UV一张);以及DC\AC各四张哈夫曼表(实际各用两张)。

3.2 结构体:components

components主要用于单个颜色通道的DCT变换后的值的存储;以及指明使用了哪个huffman_table和量化table。

3.3 结构体:huffman_table

huffman_table这个结构体主要用于存储所有的Huffman表。huffman表分为快速的查找表和慢速表。

4 解码过程更详细的解释

tinyjpeg_parse_header中,解析了JPEG的文件头。在读完SOI后,调用parse_JFIF对于每个Marker进行解析。这个解析过程持续到遇到了sos Marker,也就是扫描行开始。

  while (!sos_marker_found)

4.1 DQT解码

我们输入的JPEG文件需要先解码DQT,也就是量化表。在这一步里,构建起了对应的编号的量化表。通过信息的剥离,在parse_DQT()首先找到了对应的需要创建的是哪张量化表,然后就开始调用build_quantization_table开始创建了。

static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
  int qi;  //该参数记录了采用的量化表号码
  float *table; //指针,指向量化表开始
  const unsigned char *dqt_block_end;//指针,指向量化表结束
#if TRACE
  fprintf(p_trace,"> DQT marker\n");
  fflush(p_trace);
#endif
  dqt_block_end = stream + be16_to_cpu(stream);
  stream += 2;	/* Skip length */

  while (stream < dqt_block_end)
   {
     qi = *stream++; //读入stream中的一个字节,该字节是流中的量化表系数,并赋值给qi
#if SANITY_CHECK
     if (qi>>4)
       snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported\n");
     if (qi>4)
       snprintf(error_string, sizeof(error_string),"No more 4 quantization table is supported (got %d)\n", qi);
#endif
     table = priv->Q_tables[qi]; 
     build_quantization_table(table, stream); //开始构建量化表
     stream += 64;
   }
#if TRACE
  fprintf(p_trace,"< DQT marker\n");
  fflush(p_trace);
#endif
  return 0;
}

我们进入build_quantization_table进行创建量化表的查看:

static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
  /* Taken from libjpeg. Copyright Independent JPEG Group's LLM idct.
   * For float AA&N IDCT method, divisors are equal to quantization
   * coefficients scaled by scalefactor[row]*scalefactor[col], where
   *   scalefactor[0] = 1
   *   scalefactor[k] = cos(k*PI/16) * sqrt(2)    for k=1..7
   * We apply a further scale factor of 8.
   * What's actually stored is 1/divisor so that the inner loop can
   * use a multiplication rather than a division.
   */
  int i, j;
  static const double aanscalefactor[8] = {
     1.0, 1.387039845, 1.306562965, 1.175875602,
     1.0, 0.785694958, 0.541196100, 0.275899379
  };
  const unsigned char *zz = zigzag;

  for (i=0; i<8; i++) {
     for (j=0; j<8; j++) {
       *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
     }
   }

}

从中我们可以看出,量化表的建立传入了两个参数,一个是正式的要写入的量化表qtable,另一个是参考表(reftable),是从流中读取的数据。

对于这个数据,首先程序设定了一个比例因子, scalefactor[0] = 1

;scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1…7,且存的是倒数,这是便于进行乘法运算的。

这里主要是因为,在编码的时候,进行了人眼视觉特性设计而成的矩阵量化DCT系数,解码的时候需要进行反操作。

且采用了zigzag扫描:

static const unsigned char zigzag[64] = 
{
   0,  1,  5,  6, 14, 15, 27, 28,
   2,  4,  7, 13, 16, 26, 29, 42,
   3,  8, 12, 17, 25, 30, 41, 43,
   9, 11, 18, 24, 31, 40, 44, 53,
  10, 19, 23, 32, 39, 45, 52, 54,
  20, 22, 33, 38, 46, 51, 55, 60,
  21, 34, 37, 47, 50, 56, 59, 61,
  35, 36, 48, 49, 57, 58, 62, 63
};

因此,我们如果想要输出量化表,其实在这一步后面把ref_table表输出就可以了。码流里直接读出来的量化表是真正的量化表。后来计算出来的量化表,乘了比例因子,是为了辅助浮点dct ,idct 运算用的。如果是浮点运算,在编码端要乘比例因子,所以在解码端也要乘。具体的代码添加见下面。

4.2 SOF解码

接下来该JPEG头中是SOF,故解析了SOF,帧图像开始marker。这段解析较为简单,主要读出了图像的通道数量、图像的宽和高、每个通道的ID、水平垂直采样因子和使用的量化表等。

4.3 DHT解码

接下来就到了Huffman码表的解析。

length = be16_to_cpu(stream) - 2; // 表长(可能包含多张表) stream += 2; /* Skip length */
while (length>0) { // 是否还有表
index = *stream++;
/* We need to calculate the number of bytes 'vals' will takes */
huff_bits[0] = 0; count = 0;
for (i=1; i<17; i++) {
huff_bits[i] = *stream++; count += huff_bits[i];
}
if(index&0xf0)//AC 表
// (index&0xf), Huffman 表序号
build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]); else // DC 表
build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);
length -= 1; length -= 16; length -= count; stream += count;
}

5 修改程序输出为YUV

想要修改程序的输出,我们需要先对程序进行一个逻辑解读。从main入口开始,我们观察到程序设定了两个模式,一个跑分模式(benchmark_mode)和一个转换图像模式(convert_one_image)。由于我们要做的事情是修改程序输出为YUV,在这里我们忽略benchmark_mode

我们跳入函数convert_one_image,查看里面干了什么。首先,将程序加载到内存中然后就关闭;然后解码jpeg(这是最主要的工作);然后获取图像大小;然后获取每个通道的内存地址;拥有了获取的这些信息后,才可以以想要的输出方式存储。

我们想要的存储方式是TINYJPEG_FMT_YUV420P,对应的处理函数是write_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);
}

因此,想要完成这个任务非常容易,只需要将所有内容写入到一个文件中,输出一个.yuv文件即可。

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

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

将所有内容全部写入一个文件中,再次运行程序,已成功生成了我们所需的文件,打开进行查看:

在这里插入图片描述

至此,修改程序输出为YUV已完成。



6 TRACE输出所有的量化矩阵和所有的Huffman码表

6.1 TRACE:输出TXT

我们想要在程序执行的过程中获得一些调试信息时候,可以使用之前打开的TRACE文件进行输出。TRACE可以在代码中的任意地方进行条件编译。比如下面这段:

#if TRACE
  fprintf(p_trace,"< DQT marker\n");
  fflush(p_trace);
#endif

也就是说,当TRACE被设定为1,程序的if条件成立,就会执行这段宏内的条件编译内容,完成对于trace文件的写入。

至于TRACE和输出文件的设定,是在cpp头宏定义的:

#define	 TRACE 1																																																																																											 
#define  TRACEFILE "trace_jpeg.txt"//add by nxn

所以,我们想关闭TraceFile的输出,设置TRACE=0即可。

6.2 输出量化矩阵

在4.1小节我们已经说明,量化矩阵会在哪里产生,也就是build_quantization_table里。现在我们在该函数末尾加上一个TRACE,写上输出它的代码:

#if TRACE
  const unsigned char* anotherzz = zigzag;
  for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
          fprintf(p_trace, "%-6d", ref_table[*anotherzz++]);
          if (j == 7) {
              fprintf(p_trace, "\n");
          }
      }
  }
#endif

输出结果:

在这里插入图片描述

6.3 输出Huffman码表

默认的代码中已经输出了所有的Huffman码表。在3.3中我们可以看出,AC和DC的Huffman码表都是在build_huffman_table函数中完成建立的。因此我们进入这两个函数,然后对他们进行查看,并输出码表。函数中已经写好:

#if TRACE
     fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
	 fflush(p_trace);
    #endif
在这里插入图片描述

我们可以在TRACE文件里看到所有码表内容。完整的输出可以在附录中查询到。

7 输出DC/AC图像

首先采用类似TRACEFILE文件的方式添加宏定义:

#define  snprintf _snprintf//add by nxn
#define	 TRACE 1																																																																																											 1//add by nxn
#define  TRACEFILE "trace_jpeg.txt"//add by nxn
#define  OUTPUTACDC 1
#define  OUTPUTACFILE "output_ac.yuv"
#define  OUTPUTDCFILE "output_dc.yuv"

然后添加文件读写:

FILE *p_trace;//add by nxn
FILE* output_ac;
FILE* output_dc;
# if OUTPUTACDC
  output_ac = fopen(OUTPUTACFILE, "wb");
  output_dc = fopen(OUTPUTDCFILE, "wb");
# endif

图像的解码在解出jpeg头后的tinyjpeg_decode函数中完成。因此,我们跳转进该函数,首先添加需要开的buffer:

    unsigned char* dcImgBuff;
    unsigned char* acImgBuff;
    unsigned char* uvBuff = 128;
    int count = 0;

在循环里:




     for (x=0; x < priv->width; x+=xstride_by_mcu)
     {
	        decode_MCU(priv);
# if OUTPUTACDC
            dcImgBuff = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4 + 0.5);  // DCT[0]为DC系数;DC系数范围-512~512;变换到0~255
            acImgBuff = (unsigned char)(priv->component_infos->DCT[1] + 128);   // 选取DCT[1]作为AC的observation;+128便于观察
            fwrite(&dcImgBuff, 1, 1, output_dc);
            fwrite(&acImgBuff, 1, 1, output_ac);
            count++;
# endif
	        convert_to_pixfmt(priv);
	        priv->plane[0] += bytes_per_mcu[0];
	        priv->plane[1] += bytes_per_mcu[1];
	        priv->plane[2] += bytes_per_mcu[2];
	        if (priv->restarts_to_go>0)
	        {
	             priv->restarts_to_go--;
	            if (priv->restarts_to_go == 0)
	            {
	                priv->stream -= (priv->nbits_in_reservoir/8);
	                resync(priv);
	                 if (find_next_rst_marker(priv) < 0)
		                return -1;
	            }
	        }
     }
   }
#if TRACE
  fprintf(p_trace,"Input file size: %d\n", priv->stream_length+2);
  fprintf(p_trace,"Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2);
  fflush(p_trace);
#endif

  return 0;
}

我们通过计数器,可以看到最后计数下来是4096个像素:

在这里插入图片描述

因此,在我们的打开图像时候应该选择64*64的4:2:0的图像。

可以看到效果:

在这里插入图片描述

8 概率统计

使用python对图像进行统计

import numpy as np
import matplotlib.pyplot as plt
import collections
import pandas as pd
from collections import Counter


fraw = open("output_ac.yuv", "rb")
frec = open("output_dc.yuv", "rb")
raw = []
rec = []


i = 0
while i < 64 * 64:
    i += 1
    buf1 = fraw.read(1)
    buf2 = frec.read(1)
    if buf1:
        buf1 = int.from_bytes(buf1, byteorder='big')
        buf2 = int.from_bytes(buf2, byteorder='big')
        raw.append(buf1)
        rec.append(buf2)




a = Counter(raw)
b = Counter(rec)
x=[]
y=[]
x_dc=[]
y_dc=[]
for i in a:
    x.append(i);
    y.append(a[i])
    x_dc.append(i)
    y_dc.append(b[i])
    # x是值,y是计数


plt.rcParams['font.sans-serif'] = ['Songti SC']  # 指定默认字体
fig,rgb = plt.subplots()
rgb.bar(x,y)
rgb.bar(x_dc,y_dc)



rgb.set_xlabel('值')
rgb.set_ylabel('出现频率')
rgb.set_title('AC/DC系数统计图')
rgb.legend()
plt.show()

在这里插入图片描述

附录

trace_jpeg.txt

> Unknown marker e0
> DQT marker
2     1     1     2     2     4     5     6     
1     1     1     2     3     6     6     6     
1     1     2     2     4     6     7     6     
1     2     2     3     5     9     8     6     
2     2     4     6     7     11    10    8     
2     4     6     6     8     10    11    9     
5     6     8     9     10    12    12    10    
7     9     10    10    11    10    10    10    
< DQT marker
> DQT marker
2     2     2     5     10    10    10    10    
2     2     3     7     10    10    10    10    
2     3     6     10    10    10    10    10    
5     7     10    10    10    10    10    10    
10    10    10    10    10    10    10    10    
10    10    10    10    10    10    10    10    
10    10    10    10    10    10    10    10    
10    10    10    10    10    10    10    10    
< DQT marker
> SOF marker
> SOF marker
Size:1024x1024 nr_components:3 (????)  precision:8
Component:1  factor:2x2  Quantization table:0
Component:2  factor:1x1  Quantization table:1
Component:3  factor:1x1  Quantization table:1
< SOF marker
> DHT marker (length=27)
Huffman table DC[0] length=10
val=04 code=00000000 codesize=02
val=05 code=00000001 codesize=02
val=06 code=00000002 codesize=02
val=03 code=00000006 codesize=03
val=07 code=0000000e codesize=04
val=02 code=0000001e codesize=05
val=01 code=0000003e codesize=06
val=00 code=0000007e codesize=07
val=09 code=000000fe codesize=08
val=08 code=000001fe codesize=09
< DHT marker
> DHT marker (length=60)
Huffman table AC[0] length=43
val=00 code=00000000 codesize=02
val=01 code=00000002 codesize=03
val=03 code=00000003 codesize=03
val=02 code=00000008 codesize=04
val=04 code=00000009 codesize=04
val=05 code=0000000a codesize=04
val=11 code=0000000b codesize=04
val=21 code=0000000c codesize=04
val=22 code=0000001a codesize=05
val=31 code=0000001b codesize=05
val=61 code=0000001c codesize=05
val=06 code=0000003a codesize=06
val=12 code=0000003b codesize=06
val=a1 code=0000003c codesize=06
val=32 code=0000007a codesize=07
val=41 code=0000007b codesize=07
val=62 code=0000007c codesize=07
val=13 code=000000fa codesize=08
val=51 code=000000fb codesize=08
val=23 code=000001f8 codesize=09
val=42 code=000001f9 codesize=09
val=71 code=000001fa codesize=09
val=81 code=000001fb codesize=09
val=91 code=000001fc codesize=09
val=15 code=000003fa codesize=10
val=52 code=000003fb codesize=10
val=63 code=000003fc codesize=10
val=07 code=000007fa codesize=11
val=14 code=000007fb codesize=11
val=33 code=000007fc codesize=11
val=53 code=000007fd codesize=11
val=16 code=00000ffc codesize=12
val=43 code=00000ffd codesize=12
val=08 code=00001ffc codesize=13
val=b1 code=00001ffd codesize=13
val=34 code=00003ffc codesize=14
val=c1 code=00003ffd codesize=14
val=24 code=00007ffc codesize=15
val=d1 code=0000fffa codesize=16
val=09 code=0000fffb codesize=16
val=72 code=0000fffc codesize=16
val=f0 code=0000fffd codesize=16
val=a2 code=0000fffe codesize=16
< DHT marker
> DHT marker (length=28)
Huffman table DC[1] length=11
val=05 code=00000000 codesize=02
val=06 code=00000001 codesize=02
val=07 code=00000002 codesize=02
val=04 code=00000006 codesize=03
val=03 code=0000000e codesize=04
val=02 code=0000001e codesize=05
val=00 code=0000007c codesize=07
val=01 code=0000007d codesize=07
val=0a code=0000007e codesize=07
val=08 code=000000fe codesize=08
val=09 code=000001fe codesize=09
< DHT marker
> DHT marker (length=43)
Huffman table AC[1] length=26
val=00 code=00000000 codesize=02
val=01 code=00000002 codesize=03
val=03 code=00000003 codesize=03
val=04 code=00000004 codesize=03
val=05 code=00000005 codesize=03
val=02 code=0000000c codesize=04
val=21 code=0000000d codesize=04
val=31 code=0000000e codesize=04
val=61 code=0000001e codesize=05
val=11 code=0000003e codesize=06
val=41 code=000000fc codesize=08
val=06 code=000001fa codesize=09
val=12 code=000001fb codesize=09
val=13 code=000001fc codesize=09
val=15 code=000001fd codesize=09
val=14 code=000003fc codesize=10
val=22 code=000003fd codesize=10
val=51 code=000003fe codesize=10
val=07 code=000007fe codesize=11
val=32 code=00000ffe codesize=12
val=23 code=00003ffc codesize=14
val=71 code=00003ffd codesize=14
val=08 code=00007ffc codesize=15
val=16 code=00007ffd codesize=15
val=33 code=00007ffe codesize=15
val=81 code=0000fffe codesize=16
< DHT marker
> SOS marker
ComponentId:1  tableAC:0 tableDC:0
ComponentId:2  tableAC:1 tableDC:1
ComponentId:3  tableAC:1 tableDC:1
< SOS marker
Use decode 2x2 sampling
Input file size: 111269
Input bytes actually read: 111268

参考资料

  1. 现代电视原理课件
  2. 《数据压缩》课件
  3. JPEG编解码原理及C++调试
  4. 实验提供的所有资料
  5. C条件编译
  6. 非常感谢老师的讲解!
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门