天天看点

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

实验5_JPEG

  • 一、JPEG是什么?
    • 1、JPEG简介
    • 2、JPEG优缺点
    • 3、JPEG4种运行模式
  • 二、JPEG编码
    • 1、Level offset(零偏置)
    • 2、8×8DCT
    • 3、量化
    • 4、DCT直流值和AC交流系数的编码
    • 5、熵编码
  • 三、JPEG文件格式分析
    • 1、JPEG格式说明
    • 2、JPEG主要数据段说明
      • 1)SOI ->Start of Image
      • 2)APP0 ->Application 0(保留标记)
      • 3)APPx -> APPn->Application,x=1~15(可任选)
      • 4)DQT ->Define Quantization Table,定义量化表
      • 5)SOF0 ->Start of Frame
      • 6)DHT ->Difine Huffman Table
      • 7)DRI ->Define Restart Interval
      • 8)SOS ->Start of Scan
      • 9)EOI ->End of Image
  • 四、实验解码代码分析
    • 1、jedc_private结构体
    • 2、compontent结构体
    • 3、build_huffman_table函数
    • 4、build_quantization_table函数
    • 5、生成YUV图像
    • 6、输出量化矩阵
    • 7、输出Huffman码表
    • 8、结果分析
      • 1)生成文件
      • 2)YUV图像
      • 3)量化表
      • 4)huffman表
  • 鸣谢

一、JPEG是什么?

1、JPEG简介

JPEG( Joint Photographic Experts Group)即联合图像专家组,是用于连续色调静态图像压缩的一种标准,文件后缀名为.jpg或.jpeg,是最常用的图像文件格式。其主要是采用预测编码(DPCM)、离散余弦变换(DCT)以及熵编码的联合编码方式,以去除冗余的图像和彩色数据,属于有损压缩格式,它能够将图像压缩在很小的储存空间,一定程度上会造成图像数据的损伤。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量降低,如果追求高品质图像,则不宜采用过高的压缩比例。

然而,JPEG压缩技术十分先进,它可以用有损压缩方式去除冗余的图像数据,换句话说,就是可以用较少的磁盘空间得到较好的图像品质。而且JPEG是一种很灵活的格式,具有调节图像质量的功能,它允许用不同的压缩比例对文件进行压缩,支持多种压缩级别,压缩比率通常在10;1到40;1,压缩比越大,图像品质就越低;相反地,压缩比越小,图像品质就越高。同一幅图像,用JPEG格式存储的文件是其他类型文件的1/10~1/20,通常只有几十KB,质量损失较小,基本无法看出。JPEG格式压缩的主要是高频信息,对色彩的信息保留较好,适合应用于互联网;它可减少图像的传输时间,支持24位真彩色;也普遍应用于需要连续色调的图像中。

JPEG格式可分为标准JPEG、渐进式JPEG及JPEG2000三种格式。

  1. 标准JPEG格式;此类型在网页下载时只能由上而下依序显示图像,直到图像资料全部下载完毕,才能看到图像全貌。
  2. 渐进式JPEG;此类型在网页下载时,先呈现出图像的粗略外观后,再慢慢地呈现出完整的内容,而且存成渐进式JPG格式的文档比存成标准JPG格式的文档要来得小,所以如果要在网页上使用图像,可以多用这种格式。
  3. JPEG2000;它是新一代的影像压缩法,压缩品质更高,并可改善在无线传输时,常因信号不稳造成马赛克现象及位置错乱的情况,改善传输的品质。

2、JPEG优缺点

优点:

1)它支持极高的压缩率,因此JPEG图像的下载速度大大加快。

2)它能够轻松地处理16.8M颜色,可以很好地再现全彩色的图像。

3)在对图像的压缩处理过程中,该图像格式可以允许自由地在最小文件尺寸(最低图像质量)和最大文件尺寸(最高图像质量)之间选择。

4)该格式的文件尺寸相对较小,下载速度快,有利于在带宽并不“富裕”的情况下传输。

缺点:

1)并非所有的浏览器都支持将各种JPEG图像插入网页。

2)压缩时,可能使图像的质量受到损失,因此不适宜用该格式来显示高清晰度的图像。

3、JPEG4种运行模式

在JPEG算法中,共包含4种运行模式,其中一种是基于DPCM的无损压缩算法,另外3种是基于DCT的有损压缩算法。其要点如下:

1)无损压缩编码模式。采用预测法和哈夫曼编码(或算术编码)以保证重建图像与原图像完全相同(设均方误差为零),可见无失真。

2)基于DCT的顺序编码模式。根据DCT变换原理,按从上到下、从左到右的顺序对图像数据进行压缩编码。当信息传送到接收端时,首先按照上述规律进行解码,从而还原图像。在此过程中存在信息丢失,因此这是一种有损图像压缩编码。

3)基于DCT的累进编码模式。它也是以DCT变换为基础的,但是其扫描过程不同。它通过多次扫描的方法来对一幅图像进行数据压缩。其描述过程采取由粗到细逐步累加的方式进行。图像还原时,在屏幕上首先看到的是图像的大致情况,而后逐步地细化,直到全部还原出来为止。

4)基于DCT的分层编码模式。这种模式是以图像分辨率为基准进行图像编码的。它首先是从低分辨率开始,逐步提高分辨率,直至与原图像的分辨率相同为止。图像重建时也是如此。可见其效果与基于DCT累进编码模式相似,但其处理起来更复杂,所获得的压缩比也更高一些。

二、JPEG编码

基本(baseline) JPEG编码器如下图:

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

下面按步骤简单的介绍一下各部分:

1、Level offset(零偏置)

对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数

➢ 对于n=8,即将0-255的值域,通过减去128,转换为值域在-128-127之间的值

◼目的:使像素的绝对值出现3位10进制的概率大大减少

2、8×8DCT

对每个单独的彩色图像分量,把整个分量图像分成8×8的图像块,如图所示,并作为两维离散余弦变换DCT的输入

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

DCT变换使用下式计算

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

逆变换使用下式计算

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

其中

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

3、量化

为了达到压缩的目的,DCT系数需作量化,量化表针对的设计。例如,利用人眼的视觉特性,对在图象中占有较大能量的低频成分,赋予较小的量化间隔和较少的比特表示,以获得较高的压缩比。

JPEG的量化采用线性均匀量化器,量化公式为:

Cq(u,v)=Integer(Round(C(u,c)/Q(u,v))
           

其中Q(u,v)是量化器步长,它是量化表的元素,量化元素随DCT系数的不同和彩色分量的不同有不同的值。量化表的大小也为8X8,和64个变换系数一一对应。在JPEG算法中,对于8X8的亮度信息和色度信息,分别给出了缺省的量化表。这个量化表在实验的基础上,结合人眼的视觉特性而获得的

4、DCT直流值和AC交流系数的编码

DC系数差分编码与AC系数游程编码,64个变换系数中,DC系数是处于左上角的,它实际上等于64个图像采样值的平均值。相邻的8×8子块之间的DC系数有较强的相关性。JPEG对于量化后的DC系数采用差分编码,二相邻块的DC系数的差值为DCi-(DCi-1)。其余63个AC系数量化后通常出现较多零值,JPEG算法采用游程编码,并建议在8×8矩阵中按照“Z”形的次序进行,可增加零的连续次数。

系数编码后都采用统一的格式表示,包含二个符号的内容:第一符号占一个字节,对于DC系数而言,它的高4位总为零;对于AC系数而言,它表示到下一个非零系数前,所包含的连续为零的系数个数。第一字节的低4位表示DC差值的幅值编码所需的比特数,或表示AC系数中下一个非零幅值编码所需的比特数。第二个符号字节表示DC差值的幅值,或下一个非零AC系数的幅值。

还需要指出,由于第一字节中只有4位表示游程长度,量大值为15。当游程长度大于15时,可以插入一个至多个(10)字节,直至剩下的零AC系数个数小于15为止。因此,63个AC系数表示为由二个符号对组成的序列,其中也可能插入10个字节,快结束字节以全零表示。

l DC值:量化后,坐标u=v=0时的取值。它是整个块能量的主要部分,它有两个特点:

– 该值比较大

– 相邻的两个图像块之间的DC值变化不大

l 对于量化后的AC系数,它是一个稀疏矩阵:矩阵中许多位置上的值为零。

– 采用RLE编码

– 用EOB(特殊的码字表示块的结束

l 例如下面的量化后的亮度快,按Z字形排列:

下标:0 1 2 3 4 5 6 7 8 9—30 31 32—63

系数:12 5 -2 0 2 0 0 0 1 0 -1 0

5、熵编码

对于上面给出的码序列,再进行统计特性的熵编码。这仅对于序列中每个符号对中的第一个字节进行,第二个幅值字节不作编码,仍然直接传送。

JPEG建议使用二种熵编码方法:哈夫曼编码和自适应二进制算术编码。对于哈夫曼编码,JPEG提供了针对DC系数、AC系数使用的哈夫曼表(包括对于图像的亮度值与色度值二种情况),在编码和解码时使用。JPEG解码器能够同时存储最多4套不同的熵编码表。

三、JPEG文件格式分析

1、JPEG格式说明

JPEG文件可以看作由多条数据段拼接成的文件,每条数据段包括两个部分:标记码和数据流。

标记码:由两个字节构成,其前一个字节为0xFF(通常只有一个0xFF,可以多个连续的0xFF),后一个字节则根据不同意义有不同数值。

数据流:记录了关于JPEG文件的相应信息(有些数据段无数据流)。

常用的数据段有SOI、APPx、DQT、SOF0、DHT、DRI、SOS、EOI等(SOI等只是数据段名,例如SOI是标记码为0xFFD8的数据段名,DHT是标记码为0xFFC4的数据段名)。

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

2、JPEG主要数据段说明

1)SOI ->Start of Image

含义:图像开始

标记码:0xFFD8 占2字节

数据流:无

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

2)APP0 ->Application 0(保留标记)

含义:应用程序保留标记0

标记码:0xFFE0——占2字节

数据流:

数据段长度——占2字节

标识符——5字节(固定值0x4A46494600=“JFIF0”)

版本号——2字节

密度单位——1字节(0->无单位 1->点数/英寸 2->点数/厘米)

X方向像素密度——2字节

Y方向像素密度——2字节

缩略图水平像素数目——1字节

缩略图垂直像素数目——1字节

缩略图RGB位图——长度为是3的倍数

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

3)APPx -> APPn->Application,x=1~15(可任选)

含义:应用程序保留标记x

标记码:0xFFE1~0xFFF——2字节(手机照片通常包含APP1,内容由地点,时间等)

数据流:

数据段长度——占2字节(不同数据段数据格式不同)

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

4)DQT ->Define Quantization Table,定义量化表

含义:定义量化表

标记码:0xFFDB——占2字节

数据流:

数据段长度——占2字节

量化表长度——占2字节

量化表ID——占1字节

量化表内容——占64字节

(量化表可以有多个,不超过4个)

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

量化表1

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

量化表2

5)SOF0 ->Start of Frame

含义:帧图像起始

标记码:0xFFC0——占2字节

数据流:

数据段长度——占2字节

精度——1字节

图像高度——2字节

图像宽度——2字节

颜色分量数——1字节(1-灰度图 3-YCrCb或YIQ 4-CMYK)

颜色分量信息——9字节

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

6)DHT ->Difine Huffman Table

含义:定义哈夫曼表

标记码:0xFFC4——占2字节

数据流:

数据段长度——占2字节

霍夫曼表长度——占2字节

表ID和表类型——1字节

不同位数的码字数量——16字节

编码内容——占16个不同位数的码字数量之和字节

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

哈夫曼表1

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

哈夫曼表2

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

哈夫曼表3

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

哈夫曼表4

7)DRI ->Define Restart Interval

含义:定义差分编码累计复位的间隔

标记码:0xFFDD——占2字节

数据流:

数据段长度——占2字节(长度固定为4)

MCU块的单元中的重新开始间隔——占2字节

8)SOS ->Start of Scan

含义:定义差分编码累计复位的间隔

标记码:0xFFDA——占2字节

数据流:

数据段长度——占2字节

颜色分量数——1字节(1-灰度图是 3-YCrCb或YIQ 4-CMYK)

颜色分量ID——1字节

直流/交流系数表号——1字节

压缩图像数据——3字节(固定值0x003F00)

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

9)EOI ->End of Image

含义:图像结束

标记码:0xFFD9 占2字节

数据流:无

四、实验解码代码分析

1、jedc_private结构体

jdec_private 结构体,其用于指示解码过程中的所有信息,量化表,霍夫曼码表以及图像数据;

struct jdec_private
{
  //公共变量
  uint8_t *components[COMPONENTS];
  unsigned int width, height;	// 图像大小 
  unsigned int flags;

  // 私有变量
  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];		//量化表
  struct huffman_table HTDC[HUFFMAN_TABLES];	//DC huffman表
  struct huffman_table HTAC[HUFFMAN_TABLES];	// AC huffman表
  int default_huffman_table_initialized;
  int restart_interval;
  int restarts_to_go;				//在此重新启动间隔中留下的mcu。
  int last_rst_marker_seen;			//RST标记每次递增
  //IDCT之后用于存储每个组件的临时空间
  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、compontent结构体

Component 结构体,其用于参与霍夫曼解码,反量化,IDCT 以及彩色空间变换,Hfactor和 Vfactor 用于说明水平与垂直的采样情况。

struct component 
{
  unsigned int Hfactor;
  unsigned int Vfactor;
  float *Q_table;		// 指向要使用的量化表的指针。
  struct huffman_table *AC_table;
  struct huffman_table *DC_table;
  short int previous_DC;	// 前直流系数 
  short int DCT[64];		// DCT 系数
#if SANITY_CHECK
  unsigned int cid;
#endif
};
           

3、build_huffman_table函数

如名可知,是为了构建霍夫曼表:

static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
  unsigned int i, j, code, code_size, val, nbits;
  unsigned char huffsize[HUFFMAN_BITS_SIZE+1], *hz;
  unsigned int huffcode[HUFFMAN_BITS_SIZE+1], *hc;
  int next_free_entry;

  /*
   * 构建临时数组
   *   huffsize[X] => numbers of bits to write vals[X]
   */
  hz = huffsize;
  for (i=1; i<=16; i++)
   {
     for (j=1; j<=bits[i]; j++)
       *hz++ = i;
   }
  *hz = 0;

  memset(table->lookup, 0xff, sizeof(table->lookup));
  for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++)
    table->slowtable[i][0] = 0;

  /* 构建临时数组
   *   huffcode[X] => code used to write vals[X]
   */
  code = 0;
  hc = huffcode;
  hz = huffsize;
  nbits = *hz;
  while (*hz)
   {
     while (*hz == nbits)
      {
	*hc++ = code++;
	hz++;
      }
     code <<= 1;
     nbits++;
   }

  /*
   * 建立查找表
   */
  next_free_entry = -1;
  for (i=0; huffsize[i]; i++)
   {
     val = vals[i];
     code = huffcode[i];
     code_size = huffsize[i];
	#if TRACE
     fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
	 fflush(p_trace);
    #endif
     table->code_size[val] = code_size;
     if (code_size <= HUFFMAN_HASH_NBITS)
      {
	/*
	 * 可以将Val放在查找表中,因此可以填充此表的所有值
	 * 值为val的列 
	 */
	int repeat = 1UL<<(HUFFMAN_HASH_NBITS - code_size);
	code <<= HUFFMAN_HASH_NBITS - code_size;
	while ( repeat-- )
	  table->lookup[code++] = val;

      }
     else
      {
	/* Perhaps sorting the array will be an optimization */
	uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1];
	while(slowtable[0])
	  slowtable+=2;
	slowtable[0] = code;
	slowtable[1] = val;
	slowtable[2] = 0;
	/* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */
      }

   }
}
           

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

  //输出量化表
#if TRACE
  const unsigned char* zhi = zigzag;
  for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
          fprintf(p_trace, "%-10d", ref_table[*zhi++]);
          if (j == 7) {
              fprintf(p_trace, "\n");
          }
      }
  }
#endif

}
           

5、生成YUV图像

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按照全部像素Y数据块、U数据块、V数据块依次存放,且为4:2:0采样,所以写入方式为YYYY…,U…,V…;按顺序写入即可

6、输出量化矩阵

量化表是在build_huffman_table函数中建立的,因此在其中加入以下输出代码即可输出量化表:

//输出量化表
#if TRACE
  const unsigned char* zhi = zigzag;
  for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
          fprintf(p_trace, "%-10d", ref_table[*zhi++]);
          if (j == 7) {
              fprintf(p_trace, "\n");
          }
      }
  }
#endif

           

7、输出Huffman码表

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
           

8、结果分析

1)生成文件

由下图可见,一共生成5个文件,其中output.Y、output.U、output.V是原本生成的Y、U、V分开的数据,因为不方便查看,所以需要增加代码使其生成.YUV文件以便查看,output.YUV即是生成的.YUV文件;trace_jpeg.txt文件包含了如huffman码表,量化表等内容。

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

2)YUV图像

输出的YUV文件,与原图像相同,实验结果正确

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

3)量化表

增加代码,输出量化表

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

4)huffman表

由表可知,huffman表是分直流与交流分量,分别有不同的码表

实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢
实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢
实验5_JPEG解码一、JPEG是什么?二、JPEG编码三、JPEG文件格式分析四、实验解码代码分析鸣谢

鸣谢

本文参考CSDN博主「翱翔菜鸟」的原创文章

原文链接:JPEG文件格式分析——经典干货!!!https://blog.csdn.net/hunnanlitong/article/details/106323065

继续阅读