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

一、实验目的

掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。

二、实验内容

1、JPEG编解码原理

JPEG是最常用的图像文件格式。其主要是采用预测编码(DPCM)、离散余弦变换(DCT)以及熵编码的联合编码方式,以去除冗余的图像和彩色数据,属于有损压缩格式,它能够将图像压缩在很小的储存空间,一定程度上会造成图像数据的损伤。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量降低,如果追求高品质图像,则不宜采用过高的压缩比例。
在这里插入图片描述
在这里插入图片描述

  1. 零偏置 Level Offset:
    U、V分量是有正有负的,将Y分量减去128,也变为正负形式。
    对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数。
    对于n=8,即将0~ 255的值域,通过减去128,转换为值域在-128~ 127之间的值。
    目的:使像素的绝对值出现3位十进制的概率大大增加

  2. DCT变换:
    对每个单独的彩色图像分量,把整个分量图像分成8*8的图像块,并作为两维离散余弦变换DCT的输入
    在这里插入图片描述

  3. 量化:
    因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值
    根据人眼的视觉特性(对低频敏感,对高频不敏感),对低频分量采取较细的量化,对高频部分采取较粗的量化。 这样做会引入误差,如果原始图像中细节丰富,则去掉的数据较多。

  4. DC系数的差分编码:
    8*8图像块经过DCT变换之后得到的DC直流系数有两个特点,系数的数值较大,相邻的8 * 8图像块的DC系数值变化不大:冗余
    根据这个特点,JPEG算法采用DCMP(差分脉冲调制编码),对相邻图像块之间量化DC系数的差值DIFF进行编码。

  5. AC系数的Z字扫描:
    由于经DCT变换后,系数大多数集中在左上角,即 低频分量区,因此采用Z字形按频率高低顺序读出,可以出现连零,可以使用游程编码,如果都是0,可以直接给出EOB
    在这里插入图片描述

  6. Huffman编码:
    在这里插入图片描述

对DC系数DPCM的结果和AC系数RLE的结果进行Huffman编码,类别ID采用一元码编码,类内索引采用定长码编码。
以DC系数为例,例如差值DIFF = 3 ,对应的类别ID = 2,类内索引 = 3,则码字为100 11。
共有亮度DC、亮度AC、色差DC、色差AC四张Huffman编码表。
解码是编码的逆过程
在这里插入图片描述

2、JPEG文件格式的解析

JPEG以segment组成,每个segment以一个marker开始。maker均以0xFF开始,后跟 1字节的标记标识符 和 2字节的标记长度 以及 该标记所对应的payload

  • 标记长度部分高位在前,低位在后,不包含该标记的头两个字节
  • 熵编码部分的数据在0xFF后由编码器插入0x00,解码器解码时跳过此字节不予处理
  • SOI( Start Of Image)和EOI( End Of Image)标记没有payload
  • 一定以0xFFD8开始,即表示图像开始SOI
    一定以0xFFD9结束,表示图像结束EOI
    在这里插入图片描述
    使用下面这张图片作为测试图像
    在这里插入图片描述
  1. SOI和EOI
    在这里插入图片描述
    在这里插入图片描述

  2. APP0
    APP0块的长度:16字节(不含0xFFE0)
    在这里插入图片描述
    在这里插入图片描述

  3. 量化表DQT,数值0xDB
    一般为两张量化表,即亮度和色度各一张
    以0xFFDB开始,量化表长度一般为0043(或0084),此图片为0043。
    量化表信息(1字节)
    Bit 0~3 QT号(只能取值0 ~ 3,否则错误)
    Bit 4~7 QT精度(0为8bit,否则表示16 bit )
    在这里插入图片描述

  4. 帧图像开始SOF0,数值0xFFC0
    在这里插入图片描述
    在这里插入图片描述

  5. Huffman表,数值0xFFC4
    在这里插入图片描述
    一共有4张Huffman表,长度分别为29、62、30、47
    表ID和表类型:0x00 DC 0号表,0x10 AC 0号表,0x01 DC 1号表,0x11 AC 1号表
    不同位数的码字数量(16个字节):以DC 0号表为例,表示没有1位的Huffman码字,2位的Huffman码字有3个,3~9位的Huffman码字均有1个,没有10位及以上的Huffman码字。
    编码内容(10个字节):以DC 0号表为例,由上一部分知道此表该字段有10项。这段数据表示10个叶子结点按从小到大排列,其权值(Huffval)依次为04、 05、 06、 03、 02、 01、 00、09、 07、 08 (16进制)。

  6. SOS,数值为0xFFDA
    在这里插入图片描述
    以FFDA开始,表明字段的长度,说明了颜色分量数
    谱选择开始结束和固定值 003F00。后面是正式的图像数据了。

三、实验代码

1、结构体

存储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];
};

存储当前像块中关于解码的信息

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
{
   
  /* 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];

};

2.解码

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

JPEG文件头解析

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

  return ret;
}

解析marker标志

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 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值