数据压缩实验:JPEG文件分析

目录

    • 解码输出YUV文件
    • 理解程序设计的框架--以三个关键结构体为补充
      • 框架
      • 三个结构体分析
        • huffman_table
        • component
        • jdec_private
    • 理解在视音频编解码调试中TRACE的目的和含义
    • 以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
    • 输出DC图像,AC图像

解码输出YUV文件

在loadjpeg中找到usage()提示,
命令行参数设置为 (可选模式 )<文件名 ><输出文件格式>< 输出文件名>
之后修改write_yuv即可,原函数单独输出了y,u,v三个分量
添加代码后如下

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

  snprintf(temp, 1024, "%s.YUV", filename);
  F = fopen(temp, "wb");

  fwrite(components[0], width* height, 1,F);
  fwrite(components[1], width * height / 4, 1, F);
  fwrite(components[2], width * height / 4, 1, F);
  fclose(F);

}

数据压缩实验:JPEG文件分析_第1张图片
输出yuv图片如上。

理解程序设计的框架–以三个关键结构体为补充

框架

loadjpeg通过命令行参数读取文件信息。
进入convert_one_image.通过一系列判断是否成功打开文件,获得文件长度,是否成功读入缓存。
进入tinyjpeg_parse_header函数,判断文件开头是是否满足jpg条件后,获得SOI后数据地址,文件长度信息。
调用parse_JFIF函数,分析拆解每个文件块进行分析。
解析DQT块进入parse_DQT,通过 build_quantization_table建立量化表,只支持小于4张量化表,每个表64个数据。
解析DHT块进入parse_DHT,通过 build_huffman_table建立哈夫曼表。
解析 SOS调用parse_SOS,解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT中序号对应)。
数据块解析完成后,先调用tinyjpeg_get_size得到jpg图像的长宽。
最后调用 tinyjpeg_decode进行解码:

  • 对每个宏块进行 Huffman 解码,得到 DCT 系数

  • 对每个宏块的 DCT 系数进行 IDCT,得到 Y、Cb、Cr 遇到

  • Segment Marker RST 时,清空之前的 DC DCT 系数

三个结构体分析

huffman_table

设计目的是加速解码速度。

struct huffman_table
{
     
  /* Fast look up table, using HUFFMAN_HASH_NBITS bits we can have directly the symbol,
  通过HUFFMAN_HASH_NBITS 可以直接找到符号值
   * if the symbol is <0, then we need to look into the tree table
   * 如果找到的符号值小于0,则需要查哈夫曼树*/
  short int lookup[HUFFMAN_HASH_SIZE];
  /* code size: give the number of bits of a symbol is encoded
  code size是符号加密后比特数 */
  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];
  //慢速表
};

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//debug用的
  unsigned int cid;
#endif
};

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即可使用下列语句在程序某一进程中输出到指定的文件。可以输出参数debug,也可以在步骤中经常输出trace理解整个解码流程。

#if TRACE
	输出内容
#endif

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

直接在trace文件里输出,用上述的if 语句即可。
要求输出所有量化矩阵,则在解析量化矩阵时输出即可,调用prase_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
  for (int i = 0; i < 8; ++i) {
     
	  for (int j = 0; j < 8; ++j) {
     
		  fprintf(p_trace, "%f ", table[i * 8 + j]);

	  }
	  fprintf(p_trace, "\n");
  }
  fprintf(p_trace,"< DQT marker\n");
  fflush(p_trace);
#endif
  return 0;
}

输出结果如下
数据压缩实验:JPEG文件分析_第2张图片
数据压缩实验:JPEG文件分析_第3张图片

输出DC图像,AC图像

每一个宏块计算一次DC与AC值,修改代码如下

  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)
      {
     
		 int count = y * (priv->width / xstride_by_mcu) + (x / xstride_by_mcu);
		 if (count < priv->width * priv->height / 64) {
     
			 DCYUV[count] = priv->component_infos[0].DCT[0];
			 ACYUV[count] = priv->component_infos[0].DCT[1];
		 }
	 decode_MCU(priv);
	 ...
	 }
	 }
#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);
 
 
  // 
  int DC_max = DCYUV[0];
  int AC_max = ACYUV[0];
  int DC_min = DCYUV[0];
  int AC_min = ACYUV[0];

  for (int i = 0; i< priv->width * priv->height / 64; i++)
  {
     
	  DC_max = (DC_max < DCYUV[i]) ? DCYUV[i] : DC_max;
	  AC_max = (AC_max < ACYUV[i]) ? ACYUV[i] : AC_max;
	  DC_min = (DC_min < DCYUV[i]) ? DC_min : DCYUV[i];
	  AC_min = (AC_min < ACYUV[i]) ? AC_min : ACYUV[i];
	  printf("%d ", i);
  }//求最大最小值为归一化准备
	 
	 // 归一化处理
	 for (int i = 0; i < priv->width * priv->height / 64; i++)
	 {
     
		 DC_op[i] = (unsigned char)(255 * (DCYUV[i] - DC_min) / (DC_max - DC_min));
		 AC_op[i] = (unsigned char)(255 * (ACYUV[i] - AC_min) / (AC_max - AC_min));
	 }

	 fwrite(DC_op, 1, priv->width * priv->height / 64, DC_file);
	 fwrite(AC_op, 1, priv->width * priv->height / 64, AC_file);
	 unsigned char* uvbuf = (unsigned char*)malloc(sizeof(unsigned char)*priv->width * priv->height / 32);
	 for (int i = 0; i < priv->width * priv->height / 32; i++) {
     

		 uvbuf[i] = 128;

	 }
	 fwrite( uvbuf, 1, priv->width * priv->height / 32, DC_file);
	 fwrite(uvbuf, 1, priv->width * priv->height / 32, AC_file);
#endif

最终输出图像如下
数据压缩实验:JPEG文件分析_第4张图片

你可能感兴趣的:(数据压缩实验:JPEG文件分析)