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

数据压缩实验五:JPEG解码

程序员文章站 2022-07-14 22:05:26
...

一、基本原理

JPEG编码的过程如图所示:

数据压缩实验五:JPEG解码

  1. 将图像分成(8*8)的块以便进行DCT变换,不够的要取边缘像素补齐
  2. 零偏置:对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值,使像素的绝对值出现3位10进制的概率大大减少。
  3. DCT:经过DCT变换后,图像中的低频分量会集中在左上角,低频能量高,故而左上角数值较大。
  4. 量化:根据人眼的视觉特性,对低频敏感所以细量化,对高频不敏感所以粗量化;对亮度信号敏感,对色度信号不敏感。所以亮度和色度分量分别采用不同的量化表。
  5. 之字形扫描:游程编码的扫描过程,由于DCT变换后,系数集中在左上角,于是采用之字形扫描。
  6. 熵编码:直流分量采用差分编码;交流分量采用游程编码

解码过程:

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

JPEG文件格式

  • SOI       0xFFD8     图像开始
  • APPn   0xFFEn     应用细节信息
  • DQT     0xFFDB    定义量化表
  • SOF0   0xFFC0    帧图像开始
  • DHT     0xFFC4    定义霍夫曼表
  • SOS     0xFFDA    扫描开始 12字节
  • EOI      0xFFD9    图像结束 2字节

二、实验流程

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

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

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

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

(4). 输出DC图像并经过huffman统计其概率分布(使用第三个实验中的Huffman编码器)。

(5). 输出某一个AC值图像并统计其概率分布(使用第三个实验中的Huffman编码器)。

三、关键代码及分析

1.将输出文件保存为YUV文件

tinyjpeg.h

enum tinyjpeg_fmt {
   TINYJPEG_FMT_GREY = 1,
   TINYJPEG_FMT_BGR24,
   TINYJPEG_FMT_RGB24,
   TINYJPEG_FMT_YUV420P, //add by cxy
   TINYJPEG_FMT_YUV,
};

tinyjpeg.c
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{..              /*add by cxy*/
		   case TINYJPEG_FMT_YUV:
			   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;
...}

loadjpeg.c

int load_multiple_times(const char *filename, const char *outfilename, int output_format)
{
	...
		switch (output_format)
	{
		...
			/* add by cxy */
		case TINYJPEG_FMT_YUV420:
			write_yuv_c(outfilename, width, height, components);
			break;
			...
	}
	...
}

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

int main(int argc, char *argv[])
{
	...
		if (strcmp(argv[current_argument + 1], "yuv420p") == 0)
			output_format = TINYJPEG_FMT_YUV420P;
	/* add by cxy */
		else if (strcmp(argv[current_argument + 1], "yuv420") == 0)
			output_format = TINYJPEG_FMT_YUV420;

	...
}

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

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];//一个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 */
	unsigned int reservoir, nbits_in_reservoir;//存储

	struct component component_infos[COMPONENTS];//一个mcu包含的块的结构体
	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随着文件解析,输出中间信息,输出txt文件在loadjpeg.c中打开和关闭,若要关闭TRACE,需在tinyjpeg.h中加上 #define TRACE 0。

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

tinyjpeg.h

/* add by cxy */
#define TABLES 1
FILE *p_tables;

tinyjpeg.c
static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
	...
		/* add by cxy*/
#if TABLES
	fprintf(p_tables, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
	fflush(p_tables);
#endif
	...
}
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
	...
		for (i = 0; i<8; i++)
		{
			for (j = 0; j<8; j++)
			{
				*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
				/* add by cxy */
#if TABLES
				fprintf(p_tables, "%2d\t", ref_table[*(zigzag + i * 8 + j)]);
				if (j == 7)
					fprintf(p_tables, "\n");
				fflush(p_tables);
#endif
			}
		}
	...
}

static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
	...
		while (stream < dqt_block_end)
		{
			qi = *stream++;
			/* add by cxy */
#if TABLES
			fprintf(p_tables, "Quantization_tables [%d] \n", qi);
			fflush(p_tables);
#endif

    ...
		}
	...
}

static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{
	...
		while (length>0)
		{
			...
				/* add by cxy */
#if TABLES
			fprintf(p_tables, "Huffman table %s[%d] length=%d\n", (index & 0xf0) ? "AC" : "DC", index & 0xf, count);
			fflush(p_tables);
#endif
			...
		}
	...
}

loadjpg.c
int main(int argc, char *argv[])
{
	...
		/* add by cxy */
#if TABLES
	snprintf(temp, 1024, "tables_of_%s.txt", output_filename);
	p_tables = fopen(temp, "w");
#endif
	...
		/* add by cxy */
#if TABLES
		fclose(p_tables);
#endif
	...
}

4. 输出DC图像并输出某一个AC值图像。

tinyjpeg.h

FILE *Q_table;
FILE *DC_table;
FILE *AC_table;

loadjpeg.c

int main(int argc, char *argv[])
{
	...
		/*add by cxy*/
	Q_table = fopen("Q_table.txt", "w");
	DC_table = fopen("DC_table.yuv", "w");
	AC_table = fopen("AC_table.yuv", "w");
	...
		/*add by cxy*/
	fclose(Q_table);
	fclose(DC_table);
	fclose(AC_table);
	return 0;
}


tinyjpeg.c
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
  ...
   while (stream < dqt_block_end)
   {
        .../*add by cxy*/
	for(i=0;i<64;i++)
	{
		 if((!(i%8)))
	 {
		 fprintf(Q_TABLE,"\n%f",table[i]);
	 }
	 else 
	 {
		 fprintf(Q_TABLE," %f",table[i]);
	 }
	}
	 fprintf(Q_TABLE,"\n");
	...
}





int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
	.../*add by cxy*/
		float DC[1];
	unsigned char DCimage[1], ACimage[1];
	...
		for (y = 0; y < priv->height / ystride_by_mcu; y++)
		{
			...
				for (x = 0; x < priv->width; x += xstride_by_mcu)
				{
					...
					/*add by cxy*/
					DC[0] = (priv->component_infos->DCT[0] + 512.0) / 4;
					DCimage[0] = (unsigned char)(DC[0] + 0.5); 
					fwrite(DCimage, 1, 1, DC_table);
					ACimage[0] = (unsigned char)(priv->component_infos->DCT[3] + 128);
					fwrite(ACimage, 1, 1, AC_table);
					...
				}
		}
	...
}


四、实验结果

原图                                                                                                                                                                  YUV文件

数据压缩实验五:JPEG解码                     数据压缩实验五:JPEG解码

输出的trace文件

数据压缩实验五:JPEG解码

输出的量化表

数据压缩实验五:JPEG解码

输出的huffman码表

数据压缩实验五:JPEG解码


数据压缩实验五:JPEG解码

输出的DC图像,只输出了Y分量的,测试图像是1024*1024,因此输出的DC图为128*1281024/8=128

数据压缩实验五:JPEG解码                          数据压缩实验五:JPEG解码


输出的AC图像,只输出了Y分量的,测试图像是1024*1024,因此输出的AC图为128*1281024/8=128

数据压缩实验五:JPEG解码                          数据压缩实验五:JPEG解码