欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 21:56:00
...

一、JPEG原理分析

编码部分

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

1.零偏置(Level Offset)

对于灰度级是 2n\ 2^n的像素,通过减去  2n1\ 2^{n-1},将无符号的整数值变成有符号数。
例:对于 n=8 ,即将 0~255 的值域,通过减去 128 ,转换为值域在[128 ~127] 内。

2.DCT变换

DCT变换的全称是离散余弦变换(DCT),能够将空域的信号转换到频域上。
图像的存储过程中,高频部分往往占用了很大的空间,而低频部分却占用的不多,因此,在图像处理的时候,牺牲部分高频,既可以提高利用率,也能保存下图像的大部分细节。
DCT变换本身是无损的且具有对称性。对原始图像进行离散余弦变换,变换后DCT系数能量主要集中在左上角,其余大部分系数接近于零。数据压缩实验五:JPEG原理分析及JPEG解码器的调试

3.量化

uniform scalar quantization
采用中平型均匀量化器
 l(k)=[θ(k)/Q(k)+0.5],k=0,1,2\ l(k) = [θ(k)/Q(k)+0.5],k=0,1,2,……
θ(k):DCT系数
Q(k):量化步长
数据压缩实验五:JPEG原理分析及JPEG解码器的调试
量化表的确定:
因为人眼对亮度信号比对色差信号更敏感,因此使用了两
种量化表:亮度量化值和色差量化值
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对
低频分量采取较细的量化,对高频分量采取较粗的量化数据压缩实验五:JPEG原理分析及JPEG解码器的调试
建议基本量化表:
数据压缩实验五:JPEG原理分析及JPEG解码器的调试

4.量化后系数的编码

1)DC系数差分编码
8 × 8 图像块经过 DCT 变换之后得到的 DC 直流系数有两个特点:
a.系数的数值比较大
b.相邻 8 × 8 图像块的 DC 系数值变化不大:冗余
根据这个特点, JPEG 算法使用了差分脉冲调制编码( DPCM技术),对相邻图像块之间量化 DC 系数的差值 DIFF进行编码: DIFFk=DCkDCk1\ DIFF_k = DC_k-DC_{k-1}
对差值DIFF进行Huffman编码

2)AC系数
a.由于经 DCT 变换后,系数大多数集中在左上角,即低频
分量区,因此采用 Z 字形按频率的高低顺序读出,可以出
现很多连零的机会。使用游程编码。尤其在最后,如果都是零,给出 EOB (End of Block) 即可。
数据压缩实验五:JPEG原理分析及JPEG解码器的调试
b.AC系数编码
游程编码
在 JPEG 和 MPEG 编码中规定为:(run,level)
表示连续 run 个 0 ,后面跟值为 level 的系数。
如:−3 0 −3 −2 −6 2 −4 1 −4 1 1 5 1 2 −1 1 −1 2 0 0 0 0 0 −1 −1
EOB
表示为 (0,3) ; (1,-3) ; (0,-2) ; (0,-6) ; (0,2) ; (0,-4) ; (0,1) ; (0,-4) ;(0,1) (0,1) ; (0,5) ; ( 0,1);(0,2) ; (0,-1) ; (0,1) ; (0,-1) ; (0,2) ; (5,-1) ;(0,-1) ; EOB

Run: 最多 15 个,用 4 位表示 Z
Level :类似 DC

  • 分成 16 个类别,用 4 位表示表示类别号 C
  • 类内索引
    对 (Z, C)联合用 Huffman 编码
    对类内索引用定长码编码

解码部分

与编码相反:
解码 Huffman 数据
解码 DC 差值
重构量化后的系数
DCT 逆变换
丢弃填充的行 列
反 0 偏置
对丢失的 CbCr 分量差值(下采样的逆过程)
YCbCr ——> RGB

二、JPEG文件格式

JPEG文件大体上可以分成两个部分:标记码(Tag)和压缩数据。
标记码由两个字节构成,前一个字节是固定值0xFF ,后一个字节则根据不同意义有不同数值。
在每个标记码之前还可以添加数目不限的无意义的0xFF填充,也就说连续的多个0xFF可以被理解为一个θxFF ,并表示一个标记码的开始。
在一个完整的两字节的标记码后,就是该标记码对应的压缩数据流,记录了关于文件的诸种信息。
数据压缩实验五:JPEG原理分析及JPEG解码器的调试
1.图像起始SOI和EOI
一定以 0xFFD8 开始,表示图像开始 SOI (Start of Image) 标记
一定以 0xFFD9 结束,表示图像结束 EOI(End of Image) 标记

2.APP0
固定值0xFFE0

名称 字节数 描述
APPO长度(length) 2 9个字段的总长度,即不包括标记代码,但包括本字段
标识符(identifier) 5 固定值 0x4A46494600,即字符串“JFIF0”
版本号(version) 2 一般是 0x0102,表示 JFIF 的版本号 1.2,可能会有其他数值代表其他版本
X和Y的密度单位 1 units=0: 无单位; units=1: 点数/英寸; units=2:点数/厘米
X方向像素密度(X density) 2
Y方向像素密度(Y density) 2
缩略图水平像素数目(thumbnail horizontal pixels) 1
缩略图垂直像素数目(thumbnail vertical pixels) 1
缩略图RGB位图(thumbnail RGB bi tmap) 3的倍数 缩略图 RGB 位图数据

3.APPn
n取1-15(任选)
固定值0xFFE1-0XFFFF

名称 字节数 描述
APPn长度(length) 2 2 个字段的总长度,即不包括标记代码,但包括本字段
详细信息(application specific information) 数据长度-2

4.一个或多个量化表DQT
固定值0xFFDB

名称 字节数 描述
数据长度(length) 2 2 个字段的总长度,即不包括标记代码,但包括本字段
量化表(quantization table) 数据长度-2 精度及量化表id(1字节)高 4 位:精度,只有两个可选值 0:8位;1:16位; 低 4 位:量化表 ID,取值范围为 0~3
表项(16*(精度+1)字节)

5.SOF帧开始
固定值0xFFC0

名称 字节数 描述
帧开始长度(start of frame length) 2 六个字段的总长度,即不包括标记代码,但包括本字段
精度(precision) 1 每个数据样本的位数,通常是 8 位,一般软件都不支持 12 位和 16 位
图像高度(image height) 2 图像高度(单位:像素),如果不支持 DNL 就必须 > 0
图像宽度(image width) 2 图像宽度(单位:像素),如果不支持 DNL 就必须 > 0
颜色分量数(number of color components) 1 只有 3 个数值可选
1:灰度图;3:YCrCb 或 YIQ;4:CMYK;
而 JFIF 中使用 YCrCb,故这里颜色分量数恒为 3
对每个颜色分量(for each component) 颜色分量数*3 《颜色分量信息表格》

对颜色分量:

  • ID
  • 垂直方向的样本因子(vertical sample factor)
  • 水平方向的样本因子(hori zontal sample factor)
  • 量化表号(quantization table#)

6.DHT霍夫曼表
固定值0xFFC4
数据压缩实验五:JPEG原理分析及JPEG解码器的调试
7.SOS扫描开始
固定值0xFFDA
数据压缩实验五:JPEG原理分析及JPEG解码器的调试

三、JPEG解码器调试

1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
数据压缩实验五:JPEG原理分析及JPEG解码器的调试
2. 程序调试过程中,应做到:

理解程序设计的整体框架

loadjpeg.c

主要部分main、load_multiple_times和convert_one_image
以及不同的写出文件(tga、yuv、pgm)的函数

main函数

int main(int argc, char *argv[])
{
  int output_format = TINYJPEG_FMT_YUV420P;
  char *output_filename, *input_filename;

  clock_t start_time, finish_time;
  unsigned int duration;
  int current_argument;
  int benchmark_mode = 0;

  if (argc < 3)//如果输入的参数小于3,显示使用指南
    usage();

  current_argument = 1;//目前进行到的argument
  while (1)
   {
     if (strcmp(argv[current_argument], "--benchmark")==0)//调试界面中的可选项[option]
       benchmark_mode = 1;
     else
       break;
     current_argument++;//指向下一串字符
   }

  if (argc < current_argument+2)//参数不合要求
    usage();//再次显示使用指南

  input_filename = argv[current_argument];//输入文件名
  //if判断要求哪种format,选择不同的输出形式
  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;
  else
    exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");//format错误
  output_filename = argv[current_argument+2];//输出文件名
  start_time = clock();

  if (benchmark_mode)
    load_multiple_times(input_filename, output_filename, output_format);
  else
    convert_one_image(input_filename, output_filename, output_format);
    
 finish_time = clock();
 duration = finish_time - start_time;
 snprintf(error_string, sizeof(error_string),"Decoding finished in %u ticks\n", duration);
#if TRACE
  fclose(p_trace);
#endif
  return 0;
}

load_multiple_times

/**
 * Load one jpeg image, and try to decompress 1000 times, and save the result.
 * This is mainly used for benchmarking the decoder, or to test if between each
 * called of the library the DCT is corrected reset (a bug was found).
 */
int load_multiple_times(const char *filename, const char *outfilename, int output_format)
{
  FILE *fp;
  int cout, length_of_file;//定义计数和输入文件长度
  unsigned int width, height;//定义宽和高
  unsigned char *buf;//定义一个buf的缓冲区
  struct jdec_private *jdec;//定义一个jdec_private类型的jdec
  unsigned char *components[4];//定义指针数组

  jdec = tinyjpeg_init();
  count = 0;

  /* Load the Jpeg into memory */
  fp = fopen(filename, "rb");
  if (fp == NULL)
    exitmessage("Cannot open filename\n");
  length_of_file = filesize(fp);
  buf = (unsigned char *)malloc(length_of_file + 4);
  fread(buf, length_of_file, 1, fp);
  fclose(fp);

  while (count<1000)//图像解码1000次
   {
     if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)
       exitmessage(tinyjpeg_get_errorstring(jdec));

     tinyjpeg_decode(jdec, output_format);

     count++;
   }

  /* 
   * 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);
  tinyjpeg_get_size(jdec, &width, &height);

  /* 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;
   }

  free(buf);
  tinyjpeg_free(jdec);
  return 0;
}

convert_one_image

int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{
  FILE *fp;
  unsigned int length_of_file;//定义输入文件长度
  unsigned int width, height;//定义宽和高
  unsigned char *buf;//定义一个buf的缓冲区
  struct jdec_private *jdec;//定义一个jdec_private类型的jdec
  unsigned char *components[3];//定义指针数组

  /* Load the Jpeg into memory */
  //打开文件之后把图片的基本信息写入
  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)//解压缩JPEG图像的头信息
    exitmessage(tinyjpeg_get_errorstring(jdec));

  /* Get the size of the image */
  tinyjpeg_get_size(jdec, &width, &height);

  DCT_0_mat = (short int*)malloc(width * height / 64 * sizeof(short int));
  DCT_1_mat = (short int*)malloc(width * height / 64 * sizeof(short int));
  DC_pos = 0;
  AC_pos = 0;


  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);
      write_DC_AC(width, height);
      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;
}

tinyjpeg.c

1.4个函数处理数据流
fill_ nbits:数据放入存储库。将任何0xff,0x00转换为0xff。
get_ nbits:从流中读取nbits,并写入结果,nbits从流中移除,寄存器被自动填满。
look_ nbits:从流中读取nbits,而不将其标记为已读。
Skip_nbits:从流中读取nbits,但不返回结果。
流:当前指针在JPEG数据(每字节读取字节)
reservoir:包含信息位的寄存器。只有nbits_ in_ reservoir
有效。

#define fill_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \
   while (nbits_in_reservoir<nbits_wanted) \
    { \
      unsigned char c; \
      if (stream >= priv->stream_end) \
        longjmp(priv->jump_state, -EIO); \
      c = *stream++; \
      reservoir <<= 8; \
      if (c == 0xff && *stream == 0x00) \
        stream++; \
      reservoir |= c; \
      nbits_in_reservoir+=8; \
    } \
}  while(0);

/* Signed version !!!! */
#define get_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) do { \
   fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \
   result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \
   nbits_in_reservoir -= (nbits_wanted);  \
   reservoir &= ((1U<<nbits_in_reservoir)-1); \
   if ((unsigned int)result < (1UL<<((nbits_wanted)-1))) \
       result += (0xFFFFFFFFUL<<(nbits_wanted))+1; \
}  while(0);

#define look_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) do { \
   fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \
   result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \
}  while(0);

/* To speed up the decoding, we assume that the reservoir have enough bit 
 * slow version:
 * #define skip_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \
 *   fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \
 *   nbits_in_reservoir -= (nbits_wanted); \
 *   reservoir &= ((1U<<nbits_in_reservoir)-1); \
 * }  while(0);
 */
#define skip_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \
   nbits_in_reservoir -= (nbits_wanted); \
   reservoir &= ((1U<<nbits_in_reservoir)-1); \
}  while(0);

2.创建霍夫曼表

/**
 *
 * Decode a single block that contains the DCT coefficients.
 * The table coefficients is already dezigzaged at the end of the operation.
 *
 */
static void process_Huffman_data_unit(struct jdec_private *priv, int component)
{
  unsigned char j;
  unsigned int huff_code;
  unsigned char size_val, count_0;

  struct component *c = &priv->component_infos[component];
  short int DCT[64];


  /* Initialize the DCT coef table */
  memset(DCT, 0, sizeof(DCT));

  /* DC coefficient decoding */
  huff_code = get_next_huffman_code(priv, c->DC_table);
  //trace("+ %x\n", huff_code);
  if (huff_code) {
     get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]);
     DCT[0] += c->previous_DC;
     c->previous_DC = DCT[0];
  } else {
     DCT[0] = c->previous_DC;
  }

  /* AC coefficient decoding */
  j = 1;
  while (j<64)
   {
     huff_code = get_next_huffman_code(priv, c->AC_table);
     //trace("- %x\n", huff_code);

     size_val = huff_code & 0xF;
     count_0 = huff_code >> 4;

     if (size_val == 0)
      { /* RLE */
	if (count_0 == 0)
	  break;	/* EOB found, go out */
	else if (count_0 == 0xF)
	  j += 16;	/* skip 16 zeros */
      }
     else
      {
	j += count_0;	/* skip count_0 zeroes */
	if (__unlikely(j >= 64))
	 {
	   snprintf(error_string, sizeof(error_string), "Bad huffman data (buffer overflow)");
	   break;
	 }
	get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]);
	j++;
      }
   }

  for (j = 0; j < 64; j++)
    c->DCT[j] = DCT[zigzag[j]];
}

/*
 * Takes two array of bits, and build the huffman table for size, and code
 * 
 * lookup will return the symbol if the code is less or equal than HUFFMAN_HASH_NBITS.
 * code_size will be used to known how many bits this symbol is encoded.
 * slowtable will be used when the first lookup didn't give the result.
 */
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;

  /*
   * Build a temp array 
   *   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;

  /* Build a temp array
   *   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++;
   }

  /*
   * Build the lookup table, and the slowtable if needed.
   */
  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)
      {
	/*
	 * Good: val can be put in the lookup table, so fill all value of this
	 * column with value 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 */
      }

   }
}

static void build_default_huffman_tables(struct jdec_private *priv)
{
  if (   (priv->flags & TINYJPEG_FLAGS_MJPEG_TABLE) 
      && priv->default_huffman_table_initialized)
    return;

  build_huffman_table(bits_dc_luminance, val_dc_luminance, &priv->HTDC[0]);
  build_huffman_table(bits_ac_luminance, val_ac_luminance, &priv->HTAC[0]);

  build_huffman_table(bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1]);
  build_huffman_table(bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1]);

  priv->default_huffman_table_initialized = 1;
}

3.建立量化表


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

}

4.解析DQT

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++;
#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;
}

5.解析SOF

static int parse_SOF(struct jdec_private *priv, const unsigned char *stream)
{
  int i, width, height, nr_components, cid, sampling_factor;
  int Q_table;
  struct component *c;
#if TRACE
  fprintf(p_trace,"> SOF marker\n");
  fflush(p_trace);
#endif
  print_SOF(stream);

  height = be16_to_cpu(stream+3);
  width  = be16_to_cpu(stream+5);
  nr_components = stream[7];
#if SANITY_CHECK
  if (stream[2] != 8)
    snprintf(error_string, sizeof(error_string),"Precision other than 8 is not supported\n");
  if (width>JPEG_MAX_WIDTH || height>JPEG_MAX_HEIGHT)
    snprintf(error_string, sizeof(error_string),"Width and Height (%dx%d) seems suspicious\n", width, height);
  if (nr_components != 3)
    snprintf(error_string, sizeof(error_string),"We only support YUV images\n");
  if (height%16)
    snprintf(error_string, sizeof(error_string),"Height need to be a multiple of 16 (current height is %d)\n", height);
  if (width%16)
    snprintf(error_string, sizeof(error_string),"Width need to be a multiple of 16 (current Width is %d)\n", width);
#endif
  stream += 8;
  for (i=0; i<nr_components; i++) {
     cid = *stream++;
     sampling_factor = *stream++;
     Q_table = *stream++;
     c = &priv->component_infos[i];
#if SANITY_CHECK
     c->cid = cid;
     if (Q_table >= COMPONENTS)
       snprintf(error_string, sizeof(error_string),"Bad Quantization table index (got %d, max allowed %d)\n", Q_table, COMPONENTS-1);
#endif
     c->Vfactor = sampling_factor&0xf;
     c->Hfactor = sampling_factor>>4;
     c->Q_table = priv->Q_tables[Q_table];
#if TRACE
     fprintf(p_trace,"Component:%d  factor:%dx%d  Quantization table:%d\n",
           cid, c->Hfactor, c->Hfactor, Q_table );
	 fflush(p_trace);
#endif

  }
  priv->width = width;
  priv->height = height;
#if TRACE
  fprintf(p_trace,"< SOF marker\n");
  fflush(p_trace);
#endif

  return 0;
}

6.解析SOS

static int parse_SOS(struct jdec_private *priv, const unsigned char *stream)
{
  unsigned int i, cid, table;
  unsigned int nr_components = stream[2];
#if TRACE
  fprintf(p_trace,"> SOS marker\n");
  fflush(p_trace);
#endif

#if SANITY_CHECK
  if (nr_components != 3)
    snprintf(error_string, sizeof(error_string),"We only support YCbCr image\n");
#endif

  stream += 3;
  for (i=0;i<nr_components;i++) {
     cid = *stream++;
     table = *stream++;
#if SANITY_CHECK
     if ((table&0xf)>=4)
	snprintf(error_string, sizeof(error_string),"We do not support more than 2 AC Huffman table\n");
     if ((table>>4)>=4)
	snprintf(error_string, sizeof(error_string),"We do not support more than 2 DC Huffman table\n");
     if (cid != priv->component_infos[i].cid)
        snprintf(error_string, sizeof(error_string),"SOS cid order (%d:%d) isn't compatible with the SOF marker (%d:%d)\n",
	      i, cid, i, priv->component_infos[i].cid);
#if TRACE
     fprintf(p_trace,"ComponentId:%d  tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4);
	 fflush(p_trace);
#endif
#endif
     priv->component_infos[i].AC_table = &priv->HTAC[table&0xf];
     priv->component_infos[i].DC_table = &priv->HTDC[table>>4];
  }
  priv->stream = stream+3;
#if TRACE
  fprintf(p_trace,"< SOS marker\n");
  fflush(p_trace);
#endif
  return 0;
}

7.解析DHT

static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{
  unsigned int count, i;
  unsigned char huff_bits[17];
  int length, index;

  length = be16_to_cpu(stream) - 2;
  stream += 2;	/* Skip length */
#if TRACE
  fprintf(p_trace,"> DHT marker (length=%d)\n", length);
  fflush(p_trace);
#endif

  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 SANITY_CHECK
     if (count >= HUFFMAN_BITS_SIZE)
       snprintf(error_string, sizeof(error_string),"No more than %d bytes is allowed to describe a huffman table", HUFFMAN_BITS_SIZE);
     if ( (index &0xf) >= HUFFMAN_TABLES)
       snprintf(error_string, sizeof(error_string),"No more than %d Huffman tables is supported (got %d)\n", HUFFMAN_TABLES, index&0xf);
#if TRACE
     fprintf(p_trace,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);
	 fflush(p_trace);
#endif
#endif

     if (index & 0xf0 )
       build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);
     else
       build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);

     length -= 1;
     length -= 16;
     length -= count;
     stream += count;
  }
#if TRACE
  fprintf(p_trace,"< DHT marker\n");
  fflush(p_trace);
#endif
  return 0;
}

8.解析DRI

static int parse_DRI(struct jdec_private *priv, const unsigned char *stream)
{
  unsigned int length;

#if TRACE
  fprintf(p_trace,"> DRI marker\n");
  fflush(p_trace);
#endif

  length = be16_to_cpu(stream);

#if SANITY_CHECK
  if (length != 4)
    snprintf(error_string, sizeof(error_string),"Length of DRI marker need to be 4\n");
#endif

  priv->restart_interval = be16_to_cpu(stream+2);

#if TRACE
  fprintf(p_trace,"Restart interval = %d\n", priv->restart_interval);
  fprintf(p_trace,"< DRI marker\n");
  fflush(p_trace);
#endif
  return 0;
}

9.parse_JFIF
完成标记解析

static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
{
  int chuck_len;
  int marker;
  int sos_marker_found = 0;
  int dht_marker_found = 0;
  const unsigned char *next_chunck;

  /* Parse marker */
  while (!sos_marker_found)
   {
     if (*stream++ != 0xff)
       goto bogus_jpeg_format;
     /* Skip any padding ff byte (this is normal) */
     while (*stream == 0xff)
       stream++;

     marker = *stream++;
     chuck_len = be16_to_cpu(stream);
     next_chunck = stream + chuck_len;
     switch (marker)
      {
       case SOF:
	 if (parse_SOF(priv, stream) < 0)
	   return -1;
	 break;
       case DQT:
	 if (parse_DQT(priv, stream) < 0)
	   return -1;
	 break;
       case SOS:
	 if (parse_SOS(priv, stream) < 0)
	   return -1;
	 sos_marker_found = 1;
	 break;
       case DHT:
	 if (parse_DHT(priv, stream) < 0)
	   return -1;
	 dht_marker_found = 1;
	 break;
       case DRI:
	 if (parse_DRI(priv, stream) < 0)
	   return -1;
	 break;
       default:
#if TRACE
	fprintf(p_trace,"> Unknown marker %2.2x\n", marker);
	fflush(p_trace);
#endif
	 break;
      }

     stream = next_chunck;
   }

  if (!dht_marker_found) {
#if TRACE
	  fprintf(p_trace,"No Huffman table loaded, using the default one\n");
	  fflush(p_trace);
#endif
    build_default_huffman_tables(priv);
  }

#ifdef SANITY_CHECK
  if (   (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)
      || (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor))
    snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n");
  if (   (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor)
      || (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))
    snprintf(error_string, sizeof(error_string),"Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr\n");
  if (   (priv->component_infos[cCb].Hfactor!=1) 
      || (priv->component_infos[cCr].Hfactor!=1)
      || (priv->component_infos[cCb].Vfactor!=1)
      || (priv->component_infos[cCr].Vfactor!=1))
    snprintf(error_string, sizeof(error_string),"Sampling other than 1x1 for Cr and Cb is not supported");
#endif

  return 0;
bogus_jpeg_format:
#if TRACE
  fprintf(p_trace,"Bogus jpeg format\n");
  fflush(p_trace);
#endif
  return -1;
}

10.解析文件头
初始化准备

int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
{
  int ret;

  /* Identify the file */
  if ((buf[0] != 0xFF) || (buf[1] != SOI))
    snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n");

  priv->stream_begin = buf+2;
  priv->stream_length = size-2;
  priv->stream_end = priv->stream_begin + priv->stream_length;

  ret = parse_JFIF(priv, priv->stream_begin);

  return ret;
}

11.指向宏块解码

typedef void (*decode_MCU_fct) (struct jdec_private *priv);
static const decode_MCU_fct decode_mcu_3comp_table[4] = {
   decode_MCU_1x1_3planes,
   decode_MCU_1x2_3planes,
   decode_MCU_2x1_3planes,
   decode_MCU_2x2_3planes,
};

static const decode_MCU_fct decode_mcu_1comp_table[4] = {
   decode_MCU_1x1_1plane,
   decode_MCU_1x2_1plane,
   decode_MCU_2x1_1plane,
   decode_MCU_2x2_1plane,
};

12.指向彩色空间转换

typedef void (*convert_colorspace_fct) (struct jdec_private *priv);
static const convert_colorspace_fct convert_colorspace_yuv420p[4] = {
   YCrCB_to_YUV420P_1x1,
   YCrCB_to_YUV420P_1x2,
   YCrCB_to_YUV420P_2x1,
   YCrCB_to_YUV420P_2x2,
};

static const convert_colorspace_fct convert_colorspace_rgb24[4] = {
   YCrCB_to_RGB24_1x1,
   YCrCB_to_RGB24_1x2,
   YCrCB_to_RGB24_2x1,
   YCrCB_to_RGB24_2x2,
};

static const convert_colorspace_fct convert_colorspace_bgr24[4] = {
   YCrCB_to_BGR24_1x1,
   YCrCB_to_BGR24_1x2,
   YCrCB_to_BGR24_2x1,
   YCrCB_to_BGR24_2x2,
};

static const convert_colorspace_fct convert_colorspace_grey[4] = {
   YCrCB_to_Grey_1x1,
   YCrCB_to_Grey_1x2,
   YCrCB_to_Grey_2x1,
   YCrCB_to_Grey_2x2,
};

理解三个结构体的设计目的

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

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

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

};

理解在视音频编解码调试中TRACE的目的和含义

trace:输出中间过程中的某些变量,或者错误信息。
将TRACE的值置为1,打开,进行上述说明的信息的输出
将TRACE的值置为0,关闭,跳过。

#if TRACE
	//添加希望看到的信息代码
#endif

3.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。

4. 输出DC图像并统计其概率分布。

5. 输出某一个AC值图像并统计其概率分布。