数据压缩——JPEG原理分析及JPEG解码器的调试

JPEG编解码原理

在这里插入图片描述

level off(零偏置)

· 对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数;
· 对于n=8,即将0~ 255的值域,通过减去128,转换为值域在-128~ 127之间的值。
· 目的:使像素的绝对值出现3位10进制的概率大大减少。

8×8 DCT

· 把整个分量图像分成8×8的图像块,再以8×8的图像块为一个单位进行量化和编码处理。
· 目的:去相关,能量集中
图像使用二维DCT变换,其核矩阵为:
在这里插入图片描述

输入图像通过与2D变换核矩阵做矩阵相乘得到DCT系数,系数矩阵反应了输入图像的变化性质

量化

· 采用中平均匀量化器;
· 按照【系数所在位置和颜色分量】来确定量化步距;
· 由人眼对亮度信号比对色度信号更敏感,选择两种量化表:亮度量化表,色度量化表;
· 根据人眼视觉特性:对低频变化敏感于高频变化,采用低频细量化,高频粗量化;
· 真正的量化表=缩放因子×基本量化表
质量因子越大,缩放因子越小,量化表量化步长小,压缩程度小

DC系数的差分编码

· 8×8图像块经过DCT变换后得到的DC系数有两个特点:
系数数值比较大;
相邻 8×8 图像块的DC系数值变化不大——冗余
· 根据这个特点,JPEG算法使用了DPCM技术,对相邻图像块之间量化DC系数的差值DIFF进行Huffman编码:

在这里插入图片描述
在这里插入图片描述

AC系数的zizag(之字扫描)

由于经过DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用z字形按频率由低到高的顺序读出,可以出现很多连零的机会。最后,如果都是0,则给出EOB即可。
在这里插入图片描述

调试 JPEGParser程序

结果如图,得到的是对于码流结构的分析文档:
在这里插入图片描述
在这里插入图片描述

解码JPEG

三个结构体设计目的

  1. struct 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 */
  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
   计算256个值是否足够存储需要存储的所有值*/
  uint16_t slowtable[16-HUFFMAN_HASH_NBITS][256];
};
  1. struct component
    保存一个MCU的信息,处理完一个MCU,值更新一次。
struct component 
{
  unsigned int Hfactor;//水平采样
  unsigned int Vfactor;//垂直采样
  float *Q_table;		/* Pointer to the quantisation table to use指向使用的量化表 */ 
  struct huffman_table *AC_table; //Huffman AC系数表
  struct huffman_table *DC_table;//Huffman DC系数表
  short int previous_DC;	/* Previous DC coefficient 上一个DC系数 */
  short int DCT[64];		/* DCT coef *///该块的DCT系数,DCT[0]为该块直流,其他为交流
#if SANITY_CHECK
  unsigned int cid;
#endif
};
  1. struct jdec_private
    文件解码信息结构体
struct jdec_private
{
  /* Public variables */
  uint8_t *components[COMPONENTS];//分别指向YUV分量结构体的指针数组
  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];//反DCT后三个分量的数组

  jmp_buf jump_state;
  /* Internal Pointer use for colorspace conversion, do not modify it !!! */
  uint8_t *plane[COMPONENTS];

};

调试程序输出YUV格式

设置命令参数工作目录
在这里插入图片描述
结果如下:
在这里插入图片描述

TRACE的作用

开关作用:由以下if TRACE可知,TRACE为1时TRACEFILE中才会写入内容,其为0时不运行
在这里插入图片描述
在这里插入图片描述

打开trace_jpeg.txt
其包含了图像的宽高信息,文件大小,及Huffman码表等信息
在这里插入图片描述

输出量化矩阵

  1. 类比tracejpeg.txt的代码,在 tinyjpeg.h 中增加文件指针Q_tablefp
    在这里插入图片描述
  2. 在结尾加QFILE定义,输出的文件命名为“Q_tablefp.txt”
    在这里插入图片描述
  3. 在loadjpeg.c的主函数仿照p_trace,将量化表信息写入txt文件
#if TRACE
  p_trace=fopen(TRACEFILE,"w");
  //添加一个Q_tablefp量化表的写入
  Q_tablefp= fopen(QFILE, "w");
  if (p_trace==NULL)
  {
	  printf("trace file open error!");
  }
  if (Q_tablefp == NULL)
  {
      printf("Quantization file open error!");
  }
#endif
  1. 在tinyjpeg.c中添加代码:
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
  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;

  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++)
          {
              if (j == 7)
              {
                  fprintf(Q_tablefp, "%d\n", (int)ref_table[zigzag[i * 8 + j]]);
                  //到第八个数时换行,其余空格即可
              }
              else
              {
                  fprintf(Q_tablefp, "%d  ", (int)ref_table[zigzag[i * 8 + j]]);
              }
          }
      }
#endif
}
  1. 函数 parse_DQT 中添加:
#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
//添加部分:
#if TRACE
     fprintf(Q_tablefp, "Q_tablefp[%d]:\n", qi); //码表显示格式
#endif 

得到的量化表如下:
在这里插入图片描述

DC,AC图像及概率分布

图像

  1. 在tinyjpeg.h中定义文件指针,添加宏定义
    在这里插入图片描述
    在这里插入图片描述
  2. 在tinyjpeg.c中添加代码:
#include "tinyjpeg.h"
#include "tinyjpeg-internal.h"
#define  snprintf _snprintf
//添加部分:
unsigned char* DC_;
unsigned char* AC_;
unsigned char* UV_;
int i;
int count = 0;
//
	DC_ = (unsigned char*)malloc(1);
  AC_ = (unsigned char*)malloc(1);
  UV_ = (unsigned char*)malloc(1);
  *UV_ = 128;
  count++;
  *DC_ = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4 + 0.5);
  fwrite(DC_, 1, 1, DC);
  *AC_ = (unsigned char)(priv->component_infos->DCT[1] + 128);
  fwrite(AC_, 1, 1, AC);
#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 (i = 0; i < count / 4 * 2; i++)
  {
      fwrite(UV_, sizeof(unsigned char), 1, DC);
      fwrite(UV_, sizeof(unsigned char), 1, AC);
  }
  return 0;
  1. 在loadjpeg.c中写入文件:
 DC = fopen(DCFILE, "wb");
 AC = fopen(ACFILE, "wb");
  1. 关闭文件
#if TRACE
  fclose(p_trace);
  fclose(AC);
  fclose(DC);
#endif

得到DC和AC系数图如下:
在这里插入图片描述

统计DC图像,AC图像的概率分布

修改实验一中求熵的代码:

#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;
int main()
{
	FILE* test;
	test = fopen("F:\\大三下资料\\数据压缩\\实验\\实验5_JPEG解码\\jpeg_mindec\\test_images\\snow512yuv_out.yuv", "rb");
	unsigned char* yuv, * Y_buffer, * U_buffer, * V_buffer;
	yuv = (unsigned char*)malloc(32 * 32 * 3/2 * sizeof(unsigned char));
	Y_buffer = (unsigned char*)malloc(32 * 32 * sizeof(unsigned char));
	U_buffer = (unsigned char*)malloc(16 * 16 * sizeof(unsigned char));
	V_buffer = (unsigned char*)malloc(16 * 16 * sizeof(unsigned char));
	double Y_count[256] = { 0 };
	double U_count[256] = { 0 };
	double V_count[256] = { 0 };
	double Y_H[256] = { 0 };
	double U_H[256] = { 0 };
	double V_H[256] = { 0 };
	double YH = 0;
	double UH = 0;
	double VH = 0;

	fread(yuv, 1, 32*32*3/2, test);

	for (int i = 0; i < 32*32; i++)
	{
		*(Y_buffer + i) = *(yuv + i);

	}
	for (int i = 0; i < 32 * 32*3/2; i++)
	{
		*(U_buffer + i) = *(yuv + 32*32 +i);

	}
	for (int i = 0; i < 32 * 32 * 3 / 2; i++)
	{
		*(V_buffer + i) = *(yuv + 32 * 32*5/4 + i);

	}
	//将图像中的rgb值分别存放在 * Y_buffer, * U_buffer, * V_buffer中;

	for (int g = 0; g < 256; g++)
	{
		for (int j = 0; j < 32*32; j++)
		{
			if (*(Y_buffer + j) == g)
			{
				Y_count[g] ++;
			}
		}
		for (int u = 0; u < 16 * 16; u++)
		{ 
			if (*(U_buffer + u) == g)
			{
				U_count[g] ++;
			}
		}
		for (int v = 0; v < 16 * 16; v++)
		{ 
			if (*(V_buffer + v) == g)
			{
				V_count[g] ++;
			}
		}
		
	}//遍历3个指针,分别统计Y,U,V中0~255这些值出现的次数,其遍历的次数应该是:
	
	for (int cnt = 0; cnt < 256; cnt++)
	{
		cout << Y_count[cnt] << " ";
	}
	cout << endl;
	for (int cnt = 0; cnt < 256; cnt++)
	{
		cout << U_count[cnt] << " ";
	}
	cout << endl;
	for (int cnt = 0; cnt < 256; cnt++)
	{
		cout << V_count[cnt] << " ";
	}
	cout << endl;
	//输出值为cnt的像素的个数

	for (int t = 0; t < 256; t++)
	{
		Y_count[t] = Y_count[t] / (256 * 256);
		U_count[t] = U_count[t] / (256 * 256);
		V_count[t] = V_count[t] / (256 * 256);
	}//统计Y,U,V中0~255这些值的频率

	for (int n = 0; n < 256; n++)
	{
		if (*(Y_count + n) != 0)
		{
			Y_H[n] = -Y_count[n] * (log(Y_count[n]) / log(2.0));
		}
		if (*(U_count + n) != 0)
		{
			U_H[n] = -U_count[n] * (log(U_count[n]) / log(2.0));
		}
		if (*(V_count + n) != 0)
		{
			V_H[n] = -V_count[n] * (log(V_count[n]) / log(2.0));
		}
	}//计算单个值的熵

	for (int sum = 0; sum < 256; sum++)
	{
		YH += Y_H[sum];
		UH += U_H[sum];
		VH += V_H[sum];
	}//计算Y,U,V的总熵;

	cout << "Y的熵为" << YH << endl;
	cout << "U的熵为" << UH << endl;
	cout << "V的熵为" << VH << endl;

DC系数统计结果

在这里插入图片描述

YUV
在这里插入图片描述在这里插入图片描述在这里插入图片描述

AC系数统计结果

在这里插入图片描述

YUV
在这里插入图片描述在这里插入图片描述在这里插入图片描述

参考

  1. https://blog.csdn.net/weixin_48930495/article/details/117791055
  2. https://blog.csdn.net/yee_0217/article/details/72846107
  3. https://blog.csdn.net/weixin_53323848/article/details/117635923
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值