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

一、JPEG概述

1.1 JPEG简述

JPEG是Joint Photographic Experts Group的缩写,即ISO和IEC联合图像专家组,负责静态图像压缩标准的规定。这个专家组开发的算法称为JPEG算法,并已成为当前的通用标准,即JPEG标准。遵照JPEG标准建立的图像文件使用的格式称为JFIF格式,文件名称的后缀为“.jpg”或者“.jpeg”。
JPEG主要是采用预测编码(DPCM)、离散余弦变换(DCT)以及熵编码的联合编码方式,以去除冗余的图像和彩色数据,属于有损压缩格式,它能够将图像压缩在很小的储存空间,一定程度上会造成图像数据的损伤。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量降低,如果追求高品质图像,则不宜采用过高的压缩比例。

1.2 JPEG原理

(1)彩色空间

JPEG标准本身没有规定具体的颜色空间,只是对各分量分别进行编码。实现中通常将高度相关到的RGB颜色空间转换到相关性较小的YUV颜色空间。图像的主要信息包括在Y通道,U、V更平滑,更易压缩。由于人眼对色度分量不敏感,可以对色度分量进行下采样。

(2)进行8x8的DCT变换 实现能量集中和去相关,降低空间冗余度

(3)零偏置

对于灰度级是2^n的像素,通过减去2^(n-1)将无符号数转变为有符号数。对于n=8,即值域为0~255,通过减去128,将值域转换为-128~127,目的是使像素的绝对值出现3位10进制的概率大大减少。

(4)进行均匀量化  使用根据人眼特性制作的量化表,特点是低频细量化,高频粗量化

(5)对直流系数进行差分和VLC编码  对交流系数使用zig-zag扫描和游程编码,再进行VLC编码

1.3 JPEG文件格式

JPEG在文件中以Segment的形式组织,具有以下特点:

1.均以0xFF开始,后跟1byte的Marker和2byte的Segment Length(包含length本身所占用的2byte,不含“0xFF”+"Marker"所占用的2byte)
2.采用Motorola序,即保存时高位在前,低位在后
3.Data部分中,0xFF后若为0x00,则跳过次字节不予处理

二、代码实现

2.1 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 中每个分量宏块解码)
(1) 对每个宏块进行 Huffman 解码,得到 DCT 系数
(2) 对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr
(3) 遇到 Segment Marker RST 时,清空之前的 DC DCT 系数
5. 解析到 EOI,解码结束
6. 将 Y、Cb、Cr 转化为需要的色彩空间并保存。

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

2.2.1 struct huffman_table:存储huffman码表

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

2.2.2 struct component:存储当前8*8像块中有关解码的信息

struct component 
{
  unsigned int Hfactor;  //水平采样因子
  unsigned int Vfactor;  //垂直采样因子
  float *Q_table;		//该8*8块使用的量化表
  struct huffman_table *AC_table;//该8*8块使用的AC huffman码表
  struct huffman_table *DC_table;//该8*8块使用的DC huffman码表
  short int previous_DC;	//前一个块的直流DCT系数
  short int DCT[64];		//DCT系数数组
#if SANITY_CHECK
  unsigned int cid;
#endif
};

2.2.3 struct jdec_private:JPEG数据流的结构体,用于存储JPEG图像的宽高、压缩比特流及huffman码表等信息。包括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 */
  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;				/* 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 调试过程

(1)读取文件,进行解码并读取结果

//输入JPEG文件,进行解码并存储结果
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;             
  struct 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();  //初始化jdec结构体
  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);//计算图像宽高
 
  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;
}

(2)解析JPEG文件头

int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
{//解析JPEG文件头
  int ret;
 
  /* Identify the file */
  if ((buf[0] != 0xFF) || (buf[1] != SOI))  //JPEG文件以SOI标记图像的开始,解析 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);   //开始解析JPEG
 
  return ret;
}

(3)解析Segment Marker

while (sos_marker_found == 0)
   {
     if (*stream++ != 0xff)
       goto bogus_jpeg_format;
     /* Skip any padding ff byte (this is normal) */
     while (*stream == 0xff)
       stream++;
 
     marker = *stream++;    // 获取0xFF后的一个字节(即为marker标识符)
     chuck_len = be16_to_cpu(stream);   // length字段
     next_chunck = stream + chuck_len;
     switch (marker)    // 判断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:
	 break;
      }
 
     stream = next_chunck;  // 解析下一个marker
   }
 
  if (!dht_marker_found) {
    build_default_huffman_tables(priv);
  }
  return 0;
    
bogus_jpeg_format:
  return -1;
}

(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;
}
 
 
 
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
  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];
     }
   }
}

(5)实现Z字形扫描

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

(6)解析DHT,得到霍夫曼码表信息

static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{
  unsigned int count, i;
  unsigned char huff_bits[17];  // 码长1~16
  int length, index;
 
  length = be16_to_cpu(stream) - 2;// 表长(可能包含多张表)
  stream += 2;	// 跳过长度字段
 
  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 )// (index&0xf), Huffman 表序号
       build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);  // 建立AC表
     else
       build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);  // 建立DC表
 
     length -= 1;
     length -= 16;
     length -= count;
     stream += count;
  }
  return 0;
}

(7)加入代码输出所有霍夫曼码表

static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)  // bits为各个位数码字的数量,val为Huffval,table为要建立的Huffman表
{
  unsigned int i, j, code, code_size, val, nbits;
  unsigned char huffsize[HUFFMAN_BITS_SIZE + 1];    // 每个码字的长度
  unsigned char* hz;
  unsigned int huffcode[HUFFMAN_BITS_SIZE + 1]; // 每个码字
  unsigned char* hc;
  int next_free_entry;
 
  /* 初始化 */
  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;
 
  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] != 0; i++)
   {
     /* 得到Huffval、每个码字、每个码字的长度*/
     val = vals[i];
     code = huffcode[i];
     code_size = huffsize[i];
     table->code_size[val] = code_size; // Huffval(权值)
     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;  // 得到Huffval长度的查找表
      }
     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_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)  // bits为各个位数码字的数量,val为Huffval,table为要建立的Huffman表
{
  unsigned int i, j, code, code_size, val, nbits;
  unsigned char huffsize[HUFFMAN_BITS_SIZE + 1];    // 每个码字的长度
  unsigned char* hz;
  unsigned int huffcode[HUFFMAN_BITS_SIZE + 1]; // 每个码字
  unsigned char* hc;
  int next_free_entry;
 
  /* 初始化 */
  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;
 
  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] != 0; i++)
   {
     /* 得到Huffval、每个码字、每个码字的长度*/
     val = vals[i];
     code = huffcode[i];
     code_size = huffsize[i];
     table->code_size[val] = code_size; // Huffval(权值)
     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;  // 得到Huffval长度的查找表
      }
     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 */
      }
   }
}

(8)解析SOF0(FFC0),得到图像基本数据

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;
 
  print_SOF(stream);
 
  height = be16_to_cpu(stream+3);   // 当前图像高度
  width  = be16_to_cpu(stream+5);   // 当前图像宽度
  nr_components = stream[7];    // 颜色分量数(Y、Cb、Cr,共计3)
 
  stream += 8;
  for (i=0; i<nr_components; i++) {
     //得到每个分量的 ID 等
     cid = *stream++;   // 分量ID
     sampling_factor = *stream++;   // 该分量的水平、垂直采样因子
     Q_table = *stream++;// 该分量使用的量化表序号
     c = &priv->component_infos[i];
     c->Vfactor = sampling_factor&0xf;  // 垂直采样因子(低四位)
     c->Hfactor = sampling_factor>>4;   // 水平采样因子(高四位)
     c->Q_table = priv->Q_tables[Q_table];  // 使用的量化表
  }
  priv->width = width;
  priv->height = height;
 
  return 0;
}
 

(9)解析SOS,以得到颜色分量的霍夫曼表序号(对应DHT中的序号数据)

static int parse_SOS(struct jdec_private *priv, const unsigned char *stream)
{
  unsigned int i, cid, table;
  unsigned int nr_components = stream[2];   // 颜色分量数
 
  stream += 3;
  for (i=0;i<nr_components;i++) {// 对所有分量
     cid = *stream++;// 得到 ID
     table = *stream++;// Huffman 表序号,高四位:DC 低四位:AC
      
     priv->component_infos[i].AC_table = &priv->HTAC[table&0xf];
     priv->component_infos[i].DC_table = &priv->HTDC[table>>4];
  }
  priv->stream = stream+3;
  return 0;

(10)依据每个分量的水平垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8宏块的个数

 xstride_by_mcu = ystride_by_mcu = 8;//初始化采样格式为4;4;4
  if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {//水平、垂直采样因子都为1
     decode_MCU = decode_mcu_table[0];//每个MCU中1*1个Y分量块
     convert_to_pixfmt = colorspace_array_conv[0];
#if TRACE
     fprintf(p_trace,"Use decode 1x1 sampling\n");
	 fflush(p_trace);
#endif
  } else if (priv->component_infos[cY].Hfactor == 1) {//水平采样因子1,垂直采样因子2
     decode_MCU = decode_mcu_table[1];//每个MCU1*2个Y分量块
     convert_to_pixfmt = colorspace_array_conv[1];
     ystride_by_mcu = 16;//每个MCU的高为16
#if TRACE
     fprintf(p_trace,"Use decode 1x2 sampling (not supported)\n");
	 fflush(p_trace);
#endif
  } else if (priv->component_infos[cY].Vfactor == 2) {//水平采样因子2,垂直采样因子2
     decode_MCU = decode_mcu_table[3];//每个MCU2*2个Y分量块
     convert_to_pixfmt = colorspace_array_conv[3];
     xstride_by_mcu = 16;//每个MCU的宽为16
     ystride_by_mcu = 16;//每个MCU的高为16
#if TRACE 
	 fprintf(p_trace,"Use decode 2x2 sampling\n");
	 fflush(p_trace);
#endif
  } else {
     decode_MCU = decode_mcu_table[2];//水平采样因子2,垂直采样因子1,每个MCU2*1个Y分量块
     convert_to_pixfmt = colorspace_array_conv[2];
     xstride_by_mcu = 16;//每个MCU的宽为16
#if TRACE
     fprintf(p_trace,"Use decode 2x1 sampling\n");
	 fflush(p_trace);
#endif
  }

(11)对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解码)

/*
 * Decode a 2x2
 *  .-------.
 *  | 1 | 2 |
 *  |---+---|
 *  | 3 | 4 |
 *  `-------'
 */
static void decode_MCU_2x2_3planes(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+8, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2+8, 16);
 
  // Cb
  process_Huffman_data_unit(priv, cCb);
  IDCT(&priv->component_infos[cCb], priv->Cb, 8);
 
  // Cr
  process_Huffman_data_unit(priv, cCr);
  IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}
 
static void process_Huffman_data_unit(struct jdec_private *priv, int component)
 {// 以 8*8 宏块为单位进行 Huffman 解码
  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]);// 查表的 DC DCT 系数(残值)
     DCT[0] += c->previous_DC;//DC 系数采用差分编码,差值与前一DC系数相加,得到当前块DC系数
	 c->previous_DC = DCT[0];//更新前一DC系数值
  } 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)//0不是一个有效的Amplitude值,这里做零游程标志
      { /* 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]);
	//查表得到AC DCT系数
	j++;
      }
   }
 
  for (j = 0; j < 64; j++)
    c->DCT[j] = DCT[zigzag[j]];//以zig-zag序保存
 
}
 

(13)解完所有 MCU,解码结束

 for (y=0; y < priv->height/ystride_by_mcu; y++)//行循环
   {
     //trace("Decoding row %d\n", y);
     priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]);
     priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]);
     priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]);
     for (x=0; x < priv->width; x+=xstride_by_mcu)//列循环
      {
	decode_MCU(priv);//对一个MCU解码(Huffman解码+IDCT)
 
	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);//清空preDC(所有颜色分量)
	      if (find_next_rst_marker(priv) < 0)//查找RST标记
		return -1;
	    }
	 }
      }
   }

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

TRACE为1即可使用 #if TRACE 所需输出内容

#endif 在程序某一进程中输出到指定的文件,并且可以输出参数debug。

#if TRACE
	/* 输出内容 */
	p_trace=fopen(TRACEFILE,"w");
	if (p_trace==NULL)
	{
		printf("trace file open error!");
	}
#endif

#define  snprintf _snprintf
#define TRACE 1//定义TRACE为1
#define  TRACEFILE "trace_jpeg.txt"//TRACEFILE文件名为“trace_jpeg.txt”

三、实验结果

3.1 解码输入的jpeg文件,将其写成yuv文件,代码如下:

 
static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
	FILE *F;
	char temp[1024];
 
	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阅读器打开的输出文件:

3.2 输出JPEG文件的量化矩阵和HUFFMAN码表

在trace_jpeg.txt文件中可以看到输出的所有的量化矩阵和所有码表

量化矩阵:

 

 霍夫曼码表:

 

 

3.3 输出DC、AC图像与其概率分布图像

在tinyjpeg_decode()函数中添加如下代码


  unsigned char* dcbuff;
  unsigned char* acbuff;
  unsigned char* uvbuff = 128;
  int count = 0;

  dcbuff = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4 + 0.5);  // DCT[0]为DC系数;DC系数范围-512~512;变换到0~255
    acbuff = (unsigned char)(priv->component_infos->DCT[1] + 128);   // 选取DCT[1]作为AC的observation;+128便于观察
    fwrite(&dcbuff, 1, 1, dcfile);
    fwrite(&acbuff, 1, 1, acfile);
    count++;

  for (int i = 0; i < count / 4 * 2; i++) {
      fwrite(&uvbuff, sizeof(unsigned char), 1, dcfile);
      fwrite(&uvbuff, sizeof(unsigned char), 1, acfile);

  }

在loadjpeg.c的main函数中添加如下代码


  const char* dcfile_name = "test_dc.yuv";    // DC图像文件名
  const char* acfile_name = "test_ac.yuv";    // AC图像文件名

  fopen_s(&dcfile, dcfile_name, "wb");    // 打开DC图像文件
  fopen_s(&acfile, acfile_name, "wb");    // 打开AC图像文件

  fclose(dcfile);
  fclose(acfile);

输出的AC和DC图像用yuv文件阅读器打开如下

 

AC、DC分布图如下

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值