JPEG原理分析及JPEG解码器的调试

实验原理

JPEG简介

        JPEG的全称是JointPhotographicExpertsGroup(联合图像专家小组),它是一种常用的图像存储格式, jpg/jpeg是24位的图像文件格式,也是一种高效率的压缩格式,文件格式是JPEG(联合图像专家组)标准的产物,该标准由ISO与CCI TT(国际电报电话咨询委员会)共同制定,是面向连续色调静止图像的一种压缩标准。其最初目的是使用64Kbps的通信线路传输720×576 分辨率压缩后的图像。

        JPEG的压缩,分为四个步骤:

        (1)颜色转换:由于JPEG只支持YUV颜色模式,而不支持RGB颜色模式,所以在将彩色图像进行压缩之前,必须先对颜色模式进据转换。转换完成之后还需要进行数据采样。一般采用的采样比例是2:1:1或4:2:2。由于在执行了此项工作之后,每两行数据只保留一行,因此,采样后图像数据量将压缩为原来的一半。

        (2)DCT变换:DCT(DiscreteConsineTransform)是将图像信号在频率域上进行变换,分离出高频和低频信息的处理过程。然后再对图像的高频部分(即图像细节)进行压缩,以达到压缩图像数据的目的。首先将图像划分为多个8*8的矩阵。然后对每一个矩阵作DCT变换(变换公式此略)。变换后得到一个频率系数矩阵,其中的频率系数都是浮点数。

        (3)量化:由于在后面编码过程中使用的码本都是整数,因此需要对变换后的频率系数进行量化,将之转换为整数。由于进行数据量化后,矩阵中的数据都是近似值,和原始图像数据之间有了差异,这一差异是造成图像压缩后失真的主要原因。

        (4)编码:编码采用两种机制:一是0值的行程长度编码;二是熵编码(EntropyCoding)。在JPEG中,采用曲徊序列,即以矩阵对角线的法线方向作“之”字排列矩阵中的元素。这样做的优点是使得靠近矩阵左上角、值比较大的元素排列在行程的前面,而行程的后面所排列的矩阵元素基本上为0值。行程长度编码是非常简单和常用的编码方式,在此不再赘述。编码实际上是一种基于统计特性的编码方法。在JPEG中允许采用HUFFMAN编码或者算术编码。
 

JPEG文件格式

Segment 的组织形式
   JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
  • 均以 0xFF 开始,后跟 1 byte Marker 2 byte Segment length(包含表示 Length 本身所占用的 2 byte,不含“0xFF+ Marker” 所占用的 2 byte);
  • 采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
  • Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;
名称 含义 固定值 具体内容
SOI(Start of Image) 图像开始 0xFFD8
APP0(Application) 应用程序保留标记0 0xFFE0

包含9个具体字段:
  ① 数据长度 2字节 ①~⑨9个字段的总长度
 ② 标识符 5字节 固定值0x4A46494600,即字符串“JFIF0”
  ③ 版本号2字节 一般是0x0102,表示JFIF的版本号1.2
  ④ X和Y的密度单位1字节 只有三个值可选
0:无单位;1:点数/英寸;2:点数/厘米
  ⑤ X方向像素密度  2字节 取值范围未知
  ⑥ Y方向像素密度 2字节  取值范围未知  
  ⑦ 缩略图水平像素数目1字节 取值范围未知
  ⑧ 缩略图垂直像素数目1字节 取值范围未知
  ⑨ 缩略图RGB位图  长度可能是3的倍数 缩略图RGB位图数据

DQT(Define Quantization Table) 定义量化表 0xFFDB

包含9个具体字段:
  ① 数据长度 2字节 字段①和多个字段②的总长度
  ② 量化表 数据长度-2字节

    a) 精度及量化表ID  1字节

     高4位:精度,只有两个可选值    0:8位;1:16位
     低4位:量化表ID,取值范围为0~3

    b) 表项  (64×(精度+1))字节

 例如8位精度的量化表,其表项长度为64×(0+1)=64字节

本标记段中,字段②可以重复出现,表示多个量化表,但最多只能出现4次

SOF0(Start of Frame) 帧图像开始 0xFFC0

包含9个具体字段:
  ① 数据长度 2字节 ①~⑥六个字段的总长度
  ② 精度        1字节 每个数据样本的位数
  通常是8位,一般软件都不支持 12位和16位
  ③ 图像高度 2字节 图像高度(单位:像素)

  ④ 图像宽度 2字节 图像宽度(单位:像素)

  ⑤ 颜色分量数 1字节 只有3个数值可选
  1:灰度图;3:YCrCb或YIQ;4:CMYK
  而JFIF中使用YCrCb,故这里颜色分量数恒为3
  ⑥颜色分量信息 颜色分量数×3字节(通常为9字节)      

          a)颜色分量ID 1字节

          b)水平/垂直采样因子 1字节

             高4位:水平采样因子
             低4位:垂直采样因子
          c) 量化表 1字节    当前分量使用的量化表的ID

DHT(Define Huffman Table) 定义哈夫曼表 0xFFC4

包含2个具体字段:

     ① 数据长度       2字节

     ② huffman表 数据长度-2字节 表ID和表类型 1字节

         高4位:类型,只有两个值可选 0:DC直流;1:AC交流

         低4位:哈夫曼表ID

注意,DC表和AC表分开编码

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

编码内容 16个不同位数的码字数量之和(字节)
本标记段中,字段②可以重复出现(一般4次),也可以只出现1次。

SOS(Start of Scan) 扫描开始 0xFFDA

包含2个具体字段:
 ①数据长度        2字节 ①~④两个字段的总长度
 ②颜色分量数        1字节 应该和SOF中的字段⑤的值相同,即:1:灰度图是;3: YCrCb或YIQ;4:CMYK。

 ③颜色分量信息
    a) 颜色分量ID 1字节
    b) 直流/交流系数表号 1字节

        高4位:直流分量使用的哈夫曼树编号
        低4位:交流分量使用的哈夫曼树编号

  ④ 压缩图像数据
        a)谱选择开始 1字节 固定值0x00
        b)谱选择结束  1字节 固定值0x3F
        c)谱选择 1字节 在基本JPEG中总为00

EOI(End of Image) 图像结束 0xFFD9
JPEG 的解码流程

 1.读取文件

2.解析 Segment Marker

        2.1​​​​​ 解析 SOI

        2.2 解析 APP0 
                ·检查标识“JFIF”及版本
                ·得到一些参数
        2.3 解析 DQT
                ·得到量化表长度(可能包含多张量化表)
                ·得到量化表的精度
                ·得到及检查量化表的序号(只能是 0 —— 3)
                ·得到量化表内容(64 个数据)
        2.4 解析 SOF0
                ·得到每个 sample 的比特数、长宽、颜色分量数
                ·得到每个颜色分量的 ID、水平采样因子、垂直采样因子、使用的量化表序号(与 DQT 中序号对应)
        2.5 解析 DHT
                ·得到 Huffman 表的类型(AC DC)、序号
                ·依据数据重建 Huffman 表
        2.6 解析 SOS
                ·得到解析每个颜色分量的 DC、AC 值所使用的 Huffman 表序号(与 DHT中序号对应)
3.依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8宏块的个数
4.对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解码)
                ·4.1 对每个宏块进行 Huffman 解码,得到 DCT 系数
                ·4.2 对每个宏块的 DCT 系数进行 IDCT ,得到 Y Cb 、Cr
                ·4.3 遇到 Segment Marker RST 时,清空之前的 DC DCT 系数
5.解析到 EOI,解码结束
6.将 Y Cb Cr 转化为需要的色彩空间并保存 

JPEG编解码原理

编码原理

JPEG原理分析及JPEG解码器的调试_第1张图片

 1.零偏置(Level Offset)

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

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

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

2.DCT变换

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

3.量化

        量化步距是按照系数所在的位置,颜色分量来确定

        ◼因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值

        ◼根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化

                ➢如果原始图象中细节丰富,则去掉的数据较多,量化后的系数与量化前差别

                ➢反之,细节少的原始图象在压缩时去掉的数据少些

4.DC系数的差分编码

        8×8的图像块经过DCT变换之后得到的DC直流系数有两个特点:系数的数值比较大;相邻8×8图像块的DC系数值变化不大(存在冗余),因此可以采用DPCM方法,对相邻图像块之间量化DC系数的差值进行编码,编码方式采用熵编码(Huffman编码),亮度信号与色度信号的DC系数采用不同的Huffman编码表

5.AC系数的zig-zag扫描与游程编码

        由于经过DCT变换后,系数大多集中在左上角,也就是低频分量区,因此采用Z字形扫描,按频率的高低顺序读出,这样会出现很多的连零,可以使用RLE游程编码,尤其是在最后,如果都是零,直接给出EOB(End of Block)即可

        ·在JPEG编码中,游程编码的形式为:(run,level)
        ·表示连续run个0后有值为level的系数
        ·run最多15个,用4位表示RRRR
        ·level,类似DC,分成16个类别,用4位SSSS表示类别号,类内索引
        ·对(RRRR,SSSS)采用Huffman编码,对类内索引采用定长编码
        ·亮度信号和色度信号的AC系数也有不同的Huffman码表

解码原理

        ·解码Huffman数据

        ·解码DC差值

        ·重构量化后的系数

        ·DCT逆变换

        ·丢弃填充的行/列

        ·反0偏置

        ·对丢失的CbCr分量差值(下采样的逆过程)

        ·YCbCr →RGB

实验过程

实验要求

1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。

2.  程序调试过程中,应做到:

  • 理解程序设计的整体框架
  • 理解三个结构体的设计目的
    • struct  huffman_table
    • struct  component
    • struct  jdec_private
  • 理解在视音频编解码调试中TRACE的目的和含义
    • 会打开和关闭TRACE
    • 会根据自己的要求修改TRACE

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

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

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

 

test.jpg yuv420p outyuv

JPEG原理分析及JPEG解码器的调试_第2张图片

JPEG原理分析及JPEG解码器的调试_第3张图片JPEG原理分析及JPEG解码器的调试_第4张图片

 JPEG原理分析及JPEG解码器的调试_第5张图片

 

代码分析

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

};

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 TRACE
  p_trace=fopen(TRACEFILE,"w");
  if (p_trace==NULL)
  {
	  printf("trace file open error!");
  }
#endif
  if (argc < 3)
    usage();

  current_argument = 1;
  while (1)
   {
     if (strcmp(argv[current_argument], "--benchmark")==0)
       benchmark_mode = 1;
     else
       break;
     current_argument++;
   }

  if (argc < current_argument+2)
    usage();

  input_filename = argv[current_argument];//选择输出文件的格式
  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");
  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;
}

解码

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

tinyjpeg_init

struct jdec_private *tinyjpeg_init(void)
{
  struct jdec_private *priv;
 
  priv = (struct jdec_private *)calloc(1, sizeof(struct jdec_private));
  if (priv == NULL)
    return NULL;
  return priv;
}

tinyjpeg_parse_header

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))    //判断是否是jpeg文件
    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;
}

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

parse_DHT

解码Huffman码表,并同时将Huffman码表写入trace文件

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

tinyjpeg_decode

以mcu为单位进行解码

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
  unsigned int x, y, xstride_by_mcu, ystride_by_mcu;
  unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];
  decode_MCU_fct decode_MCU;
  const decode_MCU_fct *decode_mcu_table;
  const convert_colorspace_fct *colorspace_array_conv;
  convert_colorspace_fct convert_to_pixfmt;

  if (setjmp(priv->jump_state))
    return -1;

  /* To keep gcc happy initialize some array */
  bytes_per_mcu[1] = 0;
  bytes_per_mcu[2] = 0;
  bytes_per_blocklines[1] = 0;
  bytes_per_blocklines[2] = 0;

  decode_mcu_table = decode_mcu_3comp_table;
  switch (pixfmt) {
     case TINYJPEG_FMT_YUV420P:
       colorspace_array_conv = convert_colorspace_yuv420p;
       if (priv->components[0] == NULL)
	 priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
       if (priv->components[1] == NULL)
	 priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4);
       if (priv->components[2] == NULL)
	 priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4);
       bytes_per_blocklines[0] = priv->width;
       bytes_per_blocklines[1] = priv->width/4;
       bytes_per_blocklines[2] = priv->width/4;
       bytes_per_mcu[0] = 8;
       bytes_per_mcu[1] = 4;
       bytes_per_mcu[2] = 4;
       break;

     case TINYJPEG_FMT_RGB24:
       colorspace_array_conv = convert_colorspace_rgb24;
       if (priv->components[0] == NULL)
	 priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
       bytes_per_blocklines[0] = priv->width * 3;
       bytes_per_mcu[0] = 3*8;
       break;

     case TINYJPEG_FMT_BGR24:
       colorspace_array_conv = convert_colorspace_bgr24;
       if (priv->components[0] == NULL)
	 priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
       bytes_per_blocklines[0] = priv->width * 3;
       bytes_per_mcu[0] = 3*8;
       break;

     case TINYJPEG_FMT_GREY:
       decode_mcu_table = decode_mcu_1comp_table;
       colorspace_array_conv = convert_colorspace_grey;
       if (priv->components[0] == NULL)
	 priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
       bytes_per_blocklines[0] = priv->width;
       bytes_per_mcu[0] = 8;
       break;

     default:
#if TRACE
		 fprintf(p_trace,"Bad pixel format\n");
		 fflush(p_trace);
#endif
       return -1;
  }

  xstride_by_mcu = ystride_by_mcu = 8;
  if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {
     decode_MCU = decode_mcu_table[0];
     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) {
     decode_MCU = decode_mcu_table[1];
     convert_to_pixfmt = colorspace_array_conv[1];
     ystride_by_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) {
     decode_MCU = decode_mcu_table[3];
     convert_to_pixfmt = colorspace_array_conv[3];
     xstride_by_mcu = 16;
     ystride_by_mcu = 16;
#if TRACE 
	 fprintf(p_trace,"Use decode 2x2 sampling\n");
	 fflush(p_trace);
#endif
  } else {
     decode_MCU = decode_mcu_table[2];
     convert_to_pixfmt = colorspace_array_conv[2];
     xstride_by_mcu = 16;
#if TRACE
     fprintf(p_trace,"Use decode 2x1 sampling\n");
	 fflush(p_trace);
#endif
  }

  resync(priv);

  /* Don't forget to that block can be either 8 or 16 lines */
  bytes_per_blocklines[0] *= ystride_by_mcu;
  bytes_per_blocklines[1] *= ystride_by_mcu;
  bytes_per_blocklines[2] *= ystride_by_mcu;

  bytes_per_mcu[0] *= xstride_by_mcu/8;
  bytes_per_mcu[1] *= xstride_by_mcu/8;
  bytes_per_mcu[2] *= xstride_by_mcu/8;

  /* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */
  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);
	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;
}

TRACE

在音视频编解码中,TRACE是很关键的,它会随时将解码的相关信息写入文件,在后面可以通过查阅TRACE写的文件来理解解码到的信息分别都是什么或判断是否正确解码。

#define TRACE 1//add by nxn  
#define  TRACEFILE "trace_jpeg.txt"//add by nxn   
#if TRACE     //设定trace,边解码边写入文件

#endif

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

  printf("write yuv begin!\n");
  //yuv都写入一个文件
  snprintf(temp, 1024, "%s.yuv", filename);
  F = fopen(temp, "wb");
  fwrite(components[0], width, height, F);   //写Y
  fwrite(components[1], width * height / 4, 1, F);
  fwrite(components[2], width * height / 4, 1, F);    //写UV
  fclose(F);   //关闭文件
  printf("write yuv done!\n");

}

量化矩阵

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;
  const unsigned char* zz_2 = zigzag;

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

#if TRACE
  for (i = 0; i < 8; i++)
  {
      for (j = 0; j < 8; j++)
      {
          fprintf(p_trace, "%-6d", ref_table[*zz_2++]);
      }
      fprintf(p_trace, "\n");
  }
#endif

}

JPEG原理分析及JPEG解码器的调试_第6张图片

 霍夫曼码表

直流系数

JPEG原理分析及JPEG解码器的调试_第7张图片

 JPEG原理分析及JPEG解码器的调试_第8张图片

交流系数

JPEG原理分析及JPEG解码器的调试_第9张图片 

JPEG原理分析及JPEG解码器的调试_第10张图片 

 JPEG原理分析及JPEG解码器的调试_第11张图片

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

每个8*8块解码后的DCT[0]即DC系数

更改后的 tinyjpeg_decode。

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
  unsigned int x, y, xstride_by_mcu, ystride_by_mcu;
  unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];
  decode_MCU_fct decode_MCU;
  const decode_MCU_fct *decode_mcu_table;
  const convert_colorspace_fct *colorspace_array_conv;
  convert_colorspace_fct convert_to_pixfmt;
  unsigned char* DCbuf, * ACbuf;
  unsigned char* uvbuf = 128;
  int count = 0;

  if (setjmp(priv->jump_state))
    return -1;

  /* To keep gcc happy initialize some array */
  bytes_per_mcu[1] = 0;
  bytes_per_mcu[2] = 0;
  bytes_per_blocklines[1] = 0;
  bytes_per_blocklines[2] = 0;

  decode_mcu_table = decode_mcu_3comp_table;
  switch (pixfmt) {
     case TINYJPEG_FMT_YUV420P:
       colorspace_array_conv = convert_colorspace_yuv420p;
       if (priv->components[0] == NULL)
	 priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
       if (priv->components[1] == NULL)
	 priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4);
       if (priv->components[2] == NULL)
	 priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4);
       bytes_per_blocklines[0] = priv->width;
       bytes_per_blocklines[1] = priv->width/4;
       bytes_per_blocklines[2] = priv->width/4;
       bytes_per_mcu[0] = 8;
       bytes_per_mcu[1] = 4;
       bytes_per_mcu[2] = 4;
       break;

     case TINYJPEG_FMT_RGB24:
       colorspace_array_conv = convert_colorspace_rgb24;
       if (priv->components[0] == NULL)
	 priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
       bytes_per_blocklines[0] = priv->width * 3;
       bytes_per_mcu[0] = 3*8;
       break;

     case TINYJPEG_FMT_BGR24:
       colorspace_array_conv = convert_colorspace_bgr24;
       if (priv->components[0] == NULL)
	 priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
       bytes_per_blocklines[0] = priv->width * 3;
       bytes_per_mcu[0] = 3*8;
       break;

     case TINYJPEG_FMT_GREY:
       decode_mcu_table = decode_mcu_1comp_table;
       colorspace_array_conv = convert_colorspace_grey;
       if (priv->components[0] == NULL)
	 priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
       bytes_per_blocklines[0] = priv->width;
       bytes_per_mcu[0] = 8;
       break;

     default:
#if TRACE
		 fprintf(p_trace,"Bad pixel format\n");
		 fflush(p_trace);
#endif
       return -1;
  }

  xstride_by_mcu = ystride_by_mcu = 8;
  if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {
     decode_MCU = decode_mcu_table[0];
     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) {
     decode_MCU = decode_mcu_table[1];
     convert_to_pixfmt = colorspace_array_conv[1];
     ystride_by_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) {
     decode_MCU = decode_mcu_table[3];
     convert_to_pixfmt = colorspace_array_conv[3];
     xstride_by_mcu = 16;
     ystride_by_mcu = 16;
#if TRACE 
	 fprintf(p_trace,"Use decode 2x2 sampling\n");
	 fflush(p_trace);
#endif
  } else {
     decode_MCU = decode_mcu_table[2];
     convert_to_pixfmt = colorspace_array_conv[2];
     xstride_by_mcu = 16;
#if TRACE
     fprintf(p_trace,"Use decode 2x1 sampling\n");
	 fflush(p_trace);
#endif
  }

  resync(priv);

  /* Don't forget to that block can be either 8 or 16 lines */
  bytes_per_blocklines[0] *= ystride_by_mcu;
  bytes_per_blocklines[1] *= ystride_by_mcu;
  bytes_per_blocklines[2] *= ystride_by_mcu;

  bytes_per_mcu[0] *= xstride_by_mcu/8;
  bytes_per_mcu[1] *= xstride_by_mcu/8;
  bytes_per_mcu[2] *= xstride_by_mcu/8;

  /* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */
  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);
    DCbuf = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4 + 0.5);
    ACbuf = (unsigned char)(priv->component_infos->DCT[1] + 128);
    fwrite(&DCbuf, 1, 1, DCfile);
    fwrite(&ACbuf, 1, 1, ACfile);
    count++;
 
	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

  for (int j = 0; j < count * 0.25 * 2; j++)
  {
      fwrite(&uvbuf, sizeof(unsigned char), 1, DCfile);
      fwrite(&uvbuf, sizeof(unsigned char), 1, ACfile);
  }

  return 0;
}

JPEG原理分析及JPEG解码器的调试_第12张图片

计算概率分布

直流系数

JPEG原理分析及JPEG解码器的调试_第13张图片

 交流系数JPEG原理分析及JPEG解码器的调试_第14张图片

 

 

你可能感兴趣的:(数据压缩作业,服务器)