JPEG解码

一、实验原理

JPEG编码原理图:

JPEG解码_第1张图片
  • 分块:将图像分成(8*8)的块以便进行DCT变换,不够的要取边缘像素补齐
  • 零偏置:零偏置,对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。
  • DCT:经过DCT变换后,图像中的低频分量会集中在左上角,由于图像低频能量高,故而左上角数值大,右下角有较多的0值。
  • 量化:人眼对低频敏感,对高频不敏感。所以低频细量化,高频粗量化;人眼对亮度信号敏感,对色度信号不敏感。所以亮度和色度分量分别采用不同的量化表。
  • 之字形扫描:游程编码的扫描过程,由于DCT变换后,越往右下角的分量越小甚至为0,于是采用之字形扫描。将二维降到一维后会在高频部分出现连0,由此可以提高编码效率。
  • 熵编码:直流分量采用差分编码;交流分量采用游程编码

JPEG解码原理:

  • 读入文件的相关信息 
  • 初步了解图像数据流的结构 
  • 颜色分量单元的内部解码 
  • 直流系数的差分编码 
  • 反量化 & 反Zig-zag编码 
  • 反离散余弦变换 

JPEG图像格式介绍:

缩写 名称 说明 标记代码 字节数
SOI Start of Image 图像开始 固定值0xFFD8 2(标记代码)
EOI End of Image 图像结束 固定值0xFFD9 2(标记代码)
APP0 Application 应用程序保留标记 固定值0xFFE0 variable
DQT Define Quantization Table 定义量化表 固定值0xFFDB variable
SOF0 Start of Frame 帧图像开始 固定值0xFFC0 variable
DHT Define Huffman Table 定义哈夫曼表 固定值0xFFC4 variable
SOS Start of Scan 扫描开始 固定值0xFFDA variable

二、解码流程+代码分析

JPEG解码过程:

1、读入文件

进入load_multiple_times函数进行解码,先后进入tinyjpeg_parse_header()解析头函数和tinyjpeg_decode()解码函数;

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))
    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);//解析JFIF

  return ret;
}
在解析函数parse_JFIF()中,分别进行对下述7个segment marker的解析。
static int parse_JFIF(struct jdec_private *priv, const unsigned char *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:
……
}

2、解析segment marker

1)解析SOI

2)解析APP0

检查表示JFIF以及版本

得到一些参数

3)解析DQT(获取与量化表相关信息)

量化表长度(可能有多长量化表)

量化表精度

得到并检查量化表序号(0-3)

量化表内容

static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
  int qi,i,j;
  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)//check是否还有表,若有则进入循环
   {
     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);//得到量化表
	 /////////////*added by xy*///////////增添了输出量化表的代码
	 for(i=0; i<8;i++)
	 {
		 for(j=0;j<8;j++)
		 {
			 fprintf(Q_TABLE, " %f", table[i*8+j]);
		 }
		 fprintf(Q_TABLE, "\n");
	 }
	 fprintf(Q_TABLE, "\n");
	 ///////////////////////////////////////
     stream += 64;
	 
   }
#if TRACE
  fprintf(p_trace,"< DQT marker\n");
  fflush(p_trace);
#endif
  return 0;
}

4)解析SOF0

得到每个采样的比特数/width/height/颜色分量数

得到每个颜色分量的ID/水平采样因子/垂直采样因子/所采用的量化表的序号(DQT中对应)

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];//颜色分量数
……
  stream += 8;
  for (i=0; icomponent_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;
#if TRACE
  fprintf(p_trace,"< SOF marker\n");
  fflush(p_trace);
#endif

  return 0;
}

5)解析DHT

得到Huffman表的类型及序号(AC/DC)

依据数据重建Huffman表

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 (index & 0xf0 )
	 {//AC表
	//added by xy
	#if TABLES
         fprintf(AC_TABLE, "Huffman table AC[%d] length=%d\n", index & 0xf, count);
         fflush(AC_TABLE);
    #endif
		aqi=0;
       build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);
	 }
     else
	 {//DC表
	#if TABLES
         fprintf(DC_TABLE, "Huffman table DC[%d] length=%d\n", index & 0xf, count);
         fflush(DC_TABLE);
    #endif
		 aqi=1;
       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;
}

6)解析SOS

得到解析每个颜色分量的DC/AC值所用的Huffman表序号(与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;icomponent_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;
}

3、根据每个分昂的垂直和水平采样因子计算CMU的大小,并计算CMU中8*8宏块的数量

在tinyjpeg_decode中实现:

 xstride_by_mcu = ystride_by_mcu = 8;//初始化每个MCU的宽、高为8,即采样格式4;4;4
  if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) //水平:垂直=1: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) //水平:垂直=1:2
  {
     decode_MCU = decode_mcu_table[1];//每个MCU1*2个Y分量块
     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) //水平:垂直=2: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 //水平:垂直=2:1
  {
     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


4、对每个CMU解码

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

2)对每个宏块的DCT系数进行IDCT,得到Y/Cb/Cr

3)遇到Segment Marker RST时,清空之前的DC DCT系数

表示为一系列decode_MCU_nxn_nplanes()函数,不一一列举。

static void decode_MCU_1x1_1plane(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 8);
  
  // 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);
}

5、解析到EOI,解码结束

6、将Y、Cb、Cr转换并输出。

三、实验任务+实验结果

TASK 1: 将单独输出的Y/U/V分量文件合并输出成.yuv文件

调试源代码,设置命令参数如下,可得到Y/U/V三个分别写出的文件:
JPEG解码_第2张图片

原始代码将JPEG解码后分解三个文件分别输出,入下图所示。
JPEG解码_第3张图片 

通过添加输出格式将其合并成一个yuv文件输出,首先定义输出类型,添加yuv类型:

enum tinyjpeg_fmt {
TINYJPEG_FMT_GREY = 1,
TINYJPEG_FMT_BGR24,
TINYJPEG_FMT_YUV420P,
TINYJPEG_FMT_RGB24, //*added by xy*//
};
TINYJPEG_FMT_YUV,


在load_multiple_times函数中添加当输出格式为yuv时的case:

/* 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;
break; case TINYJPEG_FMT_GREY: write_pgm(outfilename, width, height, components);
///////////////////////////////////////
///*added by xy*//// case TINYJPEG_FMT_YUV: write_YUV(outfilename, width, height, components); break;
}

此处我们需要自己添加write_YUV函数,参考write_yuv可以得到:

///////*added by xy*////////
static void write_YUV(const char *filename, int width, int height, unsigned char **components)
{ FILE *F; char temp[1024]; snprintf(temp, 1024, "%s.YUV", filename);
fwrite(components[1], width*height/4, 1, F);
F = fopen(temp, "wb"); fwrite(components[0], width, height, F);
//////////////////////////////////////////
fwrite(components[2], width*height/4, 1, F); fclose(F);
}

此处可知,当第二个命令行参数为“yuv”时,文件输出格式会置为TINYJPEG_FMT_YUV,文件会以yuv格式输出。

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_GREY;
output_format = TINYJPEG_FMT_BGR24; else if (strcmp(argv[current_argument+1],"grey")==0) ///*added by xy*///
exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");
else if (strcmp(argv[current_argument+1],"yuv")==0) output_format = TINYJPEG_FMT_YUV; ////////////////// else
output_filename = argv[current_argument+2];


在VS2010下运行时可能报错,“此源文件已更改,不再于以下版本的文件匹配"和"源文件与模块生成时的文件不同

解决方法:点击“生成”选项卡,选择里面的选项弹出下面的对话框,把对勾去掉即可。

JPEG解码_第4张图片

TASK 2: 理解程序

1)三个结构体定义在tinyjpeg-internal.h文件中,代码分别如下:

1,码表结构体:

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 */
/* code size: give the number of bits of a symbol is encoded */
short int lookup[HUFFMAN_HASH_SIZE];//快速查表,找到对应symbol unsigned char code_size[HUFFMAN_HASH_SIZE];//码长
* FIXME: Calculate if 256 value is enough to store all values
/* some place to store value that is not encoded in the lookup table */ uint16_t slowtable[16-HUFFMAN_HASH_NBITS][256];//存编码前符号
};

2,宏块结构体component:

struct component //相当于一个数据块,用于解码
{ unsigned int Hfactor;//垂直采样
float *Q_table; /*量化表 Pointer to the quantisation table to use */
unsigned int Vfactor;//水平采样
struct huffman_table *AC_table;//交流huuffman码表结构体,注意,此处是一个结构体当中包含了另一个结构体!
struct huffman_table *DC_table;//直流huffman码表结构体!
short int DCT[64]; /* DCT变换后的DC系数,8*8的块,DCT coef */
short int previous_DC; /* 变换前直留系数Previous DC coefficient */ #if SANITY_CHECK unsigned int cid; #endif
};


3,数据流结构体,jdec_private

struct jdec_private
{
/* Public variables */
uint8_t *components[COMPONENTS];//一个MCU指向它包含的块
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 */
struct component component_infos[COMPONENTS];//一个mcu包含的component的结构体
unsigned int reservoir, nbits_in_reservoir;//用于存储bit float Q_tables[COMPONENTS][64]; /* 量化表quantization tables */
int default_huffman_table_initialized;
struct huffman_table HTDC[HUFFMAN_TABLES]; /* DC huffman tables */ struct huffman_table HTAC[HUFFMAN_TABLES]; /* AC huffman tables */ int restart_interval;
/* Temp space used after the IDCT to store each components */
int restarts_to_go; /* MCUs left in this restart interval */ int last_rst_marker_seen; /* Rst marker is incremented each time */ 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];

以上三个结构体为层层包括,即,jdec_private表示一个MCU包含component 结构体,而在component的结构体定义中又包含一个huffman_table结构体。

2)TRACE的打开和关闭

TRACE随着文件的解析,输出中间信息,输出到txt文件。txt文件在主函数中实现打开和关闭,输出信息的过程在tinyjpeg.c中。
关闭TRACE,则需要在tinyjpeg.h中加一条定义:#define TRACE 0 即可实现关闭TRACE.

文件的解析:
 
    
static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
{ int chuck_len; int marker; int sos_marker_found = 0;
/* Parse marker */
int dht_marker_found = 0; const unsigned char *next_chunck;
goto bogus_jpeg_format;
while (!sos_marker_found) { if (*stream++ != 0xff)
stream++;
/* Skip any padding ff byte (this is normal) */ while (*stream == 0xff) marker = *stream++;
case SOF:
chuck_len = be16_to_cpu(stream); next_chunck = stream + chuck_len; switch (marker) {
return -1;
if (parse_SOF(priv, stream) < 0)//解析SOF,此过程TRACE会随之输出中间信息,下同。 return -1; break; case DQT: if (parse_DQT(priv, stream) < 0) break; case SOS:
dht_marker_found = 1;
if (parse_SOS(priv, stream) < 0) return -1; sos_marker_found = 1; break; case DHT: if (parse_DHT(priv, stream) < 0) return -1; break; case DRI:
stream = next_chunck;
if (parse_DRI(priv, stream) < 0) return -1; break; default: #if TRACE//TRACE fprintf(p_trace,"> Unknown marker %2.2x\n", marker); fflush(p_trace); #endif break; } }
if ( (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)
if (!dht_marker_found) { #if TRACE//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
snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n");
|| (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor)) if ( (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor) || (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))
|| (priv->component_infos[cCb].Vfactor!=1)
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[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//TRACE fprintf(p_trace,"Bogus jpeg format\n"); fflush(p_trace); #endif
return -1;

运行一次之后会自动产生txt文件,输出的中间信息如下图。
JPEG解码_第5张图片
JPEG解码_第6张图片

TASK 3: 输出hiffman码表,量化表

1)量化表的输出

1、定义量化表输出的文件指针,此处一并定义了直流分量和交流分量输出文件的指针。

FILE *p_trace;//add by nxn
FILE *Q_TABLE, *DC_TABLE, *AC_TABLE;

2、在主函数中打开文件(在文件末尾记得关闭!)

3、在解析DQT函数中加入for循环输出量化表,函数在tinyjpeg.c文件中。

 
     
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{ …… while (stream < dqt_block_end) { qi = *stream++;
snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported\n");
#if SANITY_CHECK if (qi>>4) 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); /////////////*added by xy*/////////// for(i=0; i<8;i++) {
stream += 64;
for(j=0;j<8;j++) { fprintf(Q_TABLE, "%c%f"," ", table[i*8+j]); } fprintf(Q_TABLE, "\n"); } /////////////////////////////////////// } ……
}
return 0;

量化表的输出如图所示:
JPEG解码_第7张图片

2)AC/DC码表的输出

1、定义文件指针,AC和DC分量分两个table分别输出,因此在tinyjpeg.h中定义了一个全局变量以控制程序分别向AC_TABLE和DC_TABLE中写入huffman码表。
tinyjpeg.h:
FILE *Q_TABLE, *DC_TABLE, *AC_TABLE;//added by xy
int aqi;//added by xy
#define snprintf _snprintf//add by nxn
#define TRACE 1//add by nxn
#define TABLES 1 //用于输出,同打开和关闭trace同理
//#define TRACE 0//added by xy用于关闭TRACE
#define TRACEFILE "trace_jpeg.txt"//add by nxn

2、在tinyjpeg.c中的parse_DHT函数里增加写出huffman表头信息的输出代码:
 
      
static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{ …… if (index & 0xf0 ) { //added by xy #if TABLES
fprintf(AC_TABLE, "Huffman table AC[%d] length=%d\n", index & 0xf, count);
fflush(AC_TABLE);
#endif
aqi=0; //全局变量,为1则输出AC到DC_TABLE
build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);
} else { #if TABLES
fprintf(DC_TABLE, "Huffman table DC[%d] length=%d\n", index & 0xf, count);
fflush(DC_TABLE);
#endif
aqi=1;//全局变量,为0则输出DC到DC_TABLE
build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);
} …… return 0;
}

3、调用build_huffman_table()函数输出码表。
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{ …… for (i=0; huffsize[i]; i++) { val = vals[i]; code = huffcode[i];
fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
code_size = huffsize[i]; #if TRACE fflush(p_trace); #endif /////////////////////added by xy
fprintf(AC_TABLE, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
#if TABLES if(aqi==0)//通过全局变量来控制输出的文件 { fflush(AC_TABLE); } else {
#endif
fprintf(DC_TABLE, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size); fflush(DC_TABLE); } ////////////////////////// …… }
}
做如上修改后,运行程序应生成码表和量化表如下:
JPEG解码_第8张图片

DC_TABLE如下:
JPEG解码_第9张图片 
AC_TABLE如下:
JPEG解码_第10张图片    JPEG解码_第11张图片

TASK 4 : 输出DC图像和某个AC值图像

输出DC/AC图像:

在成功解码后,加入输出的代码即可,首先定义三个数组,作为从DCT[0]/[1]/[2]/[3]输出的中转站。
unsigned char DCimage[1],ACimage[1];
float DC[1];

随后在tinyjpeg_decode当中解析每一个CMU后输出相应的值
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
	unsigned char DCimage[1],ACimage[1];
	float DC[1];
  ……
  /* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */
  for (y=0; y < priv->height/ystride_by_mcu; y++)
   {
     ……
     for (x=0; x < priv->width; x+=xstride_by_mcu)
	 {
		decode_MCU(priv);
		……
	  //////*added by xy*////////
		DC[0]=(priv->component_infos->DCT[0]+512.0)/4;//将DCT[0]的范围压缩到0-255
		DCimage[0]=(unsigned char)(DC[0]+0.5);
		fwrite(DCimage,1,1,DC_FILE);//输出到DC_FILE文件中,每解析一块MCU,就输出一个分量值。原图1024,每个MCU8*8,所以输出图像128*128
		ACimage[0]=(unsigned char)(priv->component_infos->DCT[1]+128);//AC分量有正负,幅度很小,集中在0附近。为了方便看,将分量抬高128
		fwrite(ACimage,1,1,AC_FILE);

	  //write_DCAC(priv);
	  ////////////////////////////
	 }
   }
……
  return 0;
}

输出图像:

直流DC:
JPEG解码_第12张图片   JPEG解码_第13张图片

交流AC(DCT[1])
JPEG解码_第14张图片   JPEG解码_第15张图片

四、补充实验

蚊子噪声:

用画图生成五角星的位图如下图所示:
JPEG解码_第16张图片
放大一定的倍数,原图如下图所示,虽然出现锯齿,但边缘清晰可见:
JPEG解码_第17张图片
而经JPEG压缩后,边缘出现蚊子噪声,这表示图像的高频分量被损失掉,使得边缘不再清晰:
JPEG解码_第18张图片

你可能感兴趣的:(JPEG解码)