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

数据压缩实验五

程序员文章站 2022-07-14 22:06:23
...

实验原理

jpeg编解码原理

数据压缩实验五

jpeg文件格式

数据压缩实验五

 

8*8块编码

数据压缩实验五


零偏置

对于灰度级是2^n的像素,通过减去2^n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值。

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

DCT变换

数据压缩实验五

量化

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

因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值。根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。如果原始图象中细节丰富,则去掉的数据较多,量化后的系数与量化前差别。反之,细节少的原始图象在压缩时去掉的数据少些。

ac游程编码

在JPEG和MPEG编码中规定为:(run, level)表示连续run个0,后面跟值为level的系数如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7  表示为(1, 2), (2, 3) ,...

编码:

Run: 最多15个,用4位表示RRRR

Level:类似DC分成16个类别,用4位表示SSSS表示类别号,类内索引

对(RRRR, SSSS)联合用Huffman编码  对类内索引用定长码编码

数据压缩实验五

ac系数的之字形扫描

数据压缩实验五

DC差分编码

8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:系数的数值比较大、相邻8×8图像块的DC系数值变化不大:冗余

根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值 DIFF进行编码:

DIFF=DCk- DCk-1

数据压缩实验五

数据压缩实验五

解码重构

与编码相反::

解码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图像并经过huffman统计其概率分布(使用第三个实验中的Huffman编码器)。

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

实验代码及分析

1、输出一个yuv文件

yuv输出的时候定义部分和yuv420,即分别输出y、u、v、文件的过程基本相同,照此进行修改

//LOADJPEG.C
//将三个yuv文件在一个文件中输出
static void write_yuv2(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);
}
/* 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;
	case TINYJPEG_FMT_YUV:
		write_yuv2(outfilename, width, height, components);
		break;
   }
static void usage(void)
{
    fprintf(stderr, "Usage: loadjpeg [options] <input_filename.jpeg> <format> <output_filename>\n");
    fprintf(stderr, "options:\n");
    fprintf(stderr, "  --benchmark - Convert 1000 times the same image\n");
    fprintf(stderr, "format:\n");
    fprintf(stderr, "  yuv420p - output 3 files .Y,.U,.V\n");
    fprintf(stderr, "  rgb24   - output a .tga image\n");
    fprintf(stderr, "  bgr24   - output a .tga image\n");
    fprintf(stderr, "  gray    - output a .pgm image\n");
	fprintf(stderr, "  yuv - output a .yuv image\n");
    exit(1);
}
主函数中
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 if (strcmp(argv[current_argument + 1], "yuv") == 0)
	  output_format = TINYJPEG_FMT_YUV;
  else
    exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");
  output_filename = argv[current_argument+2];
//TINYJPEG.H
enum tinyjpeg_fmt {
   TINYJPEG_FMT_GREY = 1,
   TINYJPEG_FMT_BGR24,
   TINYJPEG_FMT_RGB24,
   TINYJPEG_FMT_YUV420P,
   TINYJPEG_FMT_YUV,};
函数中的内容和YUV420中的相同
//TINYJPEG.C
  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;
2、结构体分析

component:存放MCU用来作为最小的数据块访问数据,其中Hfactor表示水平取样,Vfactor表示垂直

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

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


};

huffman_table:用来输出ac和dc系数的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];

};

3、trace

trace是用来输出文件中间信息的,想要关闭trace只需要在定义trace的时候也就是在tinyjpeg.h文件中另#define TRACE 0
static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
{
  int chuck_len;
  int marker;
  int sos_marker_found = 0;
  int dht_marker_found = 0;
  const unsigned char *next_chunck;


  /* Parse marker */
  while (!sos_marker_found)
   {
     if (*stream++ != 0xff)
       goto bogus_jpeg_format;
     /* Skip any padding ff byte (this is normal) */
     while (*stream == 0xff)
       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:
#if TRACE
fprintf(p_trace,"> Unknown marker %2.2x\n", marker);
fflush(p_trace);
#endif
 break;
      }


     stream = next_chunck;
   }


  if (!dht_marker_found) {
#if 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
  if (   (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)
      || (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor))
    snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n");
  if (   (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor)
      || (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))
    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[cCb].Vfactor!=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
  fprintf(p_trace,"Bogus jpeg format\n");
  fflush(p_trace);
#endif
  return -1;
}

4、输出huffman码表

首先在tinyjpeg.h文件中定义一个控制huffman码表的变量:tables

#define TABLES 1

函数dht下,输出acdc类别

if (index & 0xf0 )
	 {
	#if TABLES
         fprintf(AC_TABLE, "Huffman table AC[%d] length=%d\n", index & 0xf, count);
         fflush(AC_TABLE);
    #endif
		aqi=0;	//控制输出的是ac还是dc
       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;
       build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);
	 }

输出量化表

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

输出码表

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


4、输出ac、dc图像

在tinyjpeg.h文件中添加FILE *AC_table和*DC_table的定义
在主函数中开辟空间
	char temp[1024];	snprintf(temp, 1024, "%sDC.Y", output_filename);
	DC_FILE = fopen(temp, "wb");
	snprintf(temp, 1024, "%sAC.Y", output_filename);
	AC_FILE = fopen(temp, "wb");

//tinyjpeg.c文件 dc分量需要被压缩,写文件的时候是8*8的块所以输出文件大小变为128*128,ac的值比较小,所以加128之后再输出
	DC[0]=(priv->component_infos->DCT[0]+512.0)/4;
	DCimage[0]=(unsigned char)(DC[0]+0.5);
	fwrite(DCimage,1,1,DC_FILE);
	ACimage[0]=(unsigned char)(priv->component_infos->DCT[1]+128);
	fwrite(ACimage,1,1,AC_FILE);

实验结果


数据压缩实验五

数据压缩实验五


数据压缩实验五


数据压缩实验五数据压缩实验五


数据压缩实验五

数据压缩实验五数据压缩实验五