【实验六】JPEG原理分析及JPEG解码器的调试

实验六、JPEG原理分析及JPEG解码器的调试

一、概述

    1.实验目的:在分析清楚JPEG文件结构的基础上,调试JPEG解码器,理解JPEG编解码的原理以及具体的编程实现。

    2.文章概述:本文将先从JPEG文件结构着手,再介绍JPEG编解码的流程,最后结合解码器程序来分析其具体实现并给出调试结果。

二、JPEG文件结构

    1.为什么先谈JPEG编码?

    想对JPEG文件进行解码,最最重要的便是了解其文件的结构。然而,不先了解编码的流程是很难以领会JPEG为什么要采用这样的文件结构。所以我觉得在文章的开头简单介绍一下JPEG编码的流程会能帮助读者更快的理解JPEG的精髓。

    2.JPEG编码的简单介绍

    在这里我简单介绍一下JPEG的编码流程,以之作为接下来介绍JPEG文件结构的铺垫。当然,由于是简单概述,很多技术细节会有谬误与疏漏,还请大家指出。

    (1)彩色空间转换

    一张图像进入JPEG编码器后,编码器将会对该图像进行彩色空间转换,将之转换到Y,Cb,Cr的彩色空间中。转换到YCbCr彩色空间的好处是,人眼对亮度信息更为敏感。在YCbCr空间中,对冗余的色度进行压缩,能够保证图像质量不甚下降而获得更好的压缩比。

    (2)8*8分块与DCT变换

    之后,编码器将对图像进行8*8的分块,以便进行之后的DCT(离散余弦变换),经DCT变换后,8*8的图像块将变为8*8的DCT系数块,而之后的操作便是针对这些DCT系数做的了。值得注意的是,当图像的宽高并不是8的倍数时,可对图像进行补0补至8的倍数,并在之后的解码中将这些0去掉即可。

    (3)DCT变换后的量化

    DCT变换后,去除了8*8DCT系数块将不同系数的相关性,如此,利用人眼对高频细节和色度细节不甚敏感的特性,我们能够对高频的AC分量以及色度分量进行粗量化而对低频的DC分量以及亮度信息进行细量化,从而获得更高的压缩比。

    (4)编码

    之后我们对DC系数进行DPCM编码,并利用量化后AC系数0值较多的特点对AC系数进行RLE游程编码。

    DC系数与每个AC系数都会经过过霍夫曼编码获得各自的码表。

    (5)总结

    简单概述了一下编码的流程,我们可以看出编码会带来哪些对文件结构的需求。首先图像的宽高采样精度等基本信息是肯定会有的,此外,对8*8分块不同的量化需要的量化系数需要保存在JPEG文件中,而不同DCT系数所对应的霍夫曼码表也是需要保存在JPEG文件中的。

    当然,由编解码带来的文件格式要求远远不止这么简单。但掌握这些概念已经能让我们快速产生一个对JPEG文件结构认知并能在此基础上调试JPEG解码器了。

    3.JPEG文件结构的分析——段及段的类型

    JPEG文件其基本的数据组织方式是段。而段的划分则是由段标记码来划分的,这样的标记码有数十种。

    且一个以JFIF(JPEG File Interchange Format,JPEG文件交换格式)格式存储的JPEG文件,其段标记码往往以如下的方式出现。

    首先是SOI(Start Of Image,0xFFD8),标识一个图像的开始,再便是APP0(application0,0xFFE0),之后便给出量化表DQT(Define Quantization Table,0xFFDB);

    然后进入下一层的内容SOF(Start Of Frame,0xFFC0),给出其对应的霍夫曼码表DHT(Define Huffman Table,0xFFC4);

    最后,进入扫描层SOS(Start Of Scan,0xFFDA),值得注意的是扫描层中为了防止DPCM的差错无限制的传递,还采用了DRI(Define Restart Interval,0xFFDD),来重置差分编码。

    而除此之外,JPEG的数据可视为三层,分别为图像层(image),帧层(frame)与扫描(scan)层。

    接下来我们将用二进制浏览器打开一个典型的JPEG文件,亲自看一看JPEG文件结构的组织方式。

    通过查找FF标识符,我们可以定位段标识符的位置(值得注意的是0xFF00并不是一个段标识符,而是JPEG标准中规定的一种比较特别的情况。)。

    例如我们需要对这个文档的APP0(0xFFE0)段进行解读,首先,我们需要找到JPEG相关的技术文档《JPEG压缩编码标准》或者相关的其他文章作为参考。

    我们给出的示例JPEG文件的APP0段的数据是这样的:

    FFE0 0010 4A46 4946

    0001 0100 0001 0001

    0000

    标记代码(2字节):0xFFE0 即APP0段,该段保存着文件应用相关的信息(APPn段可能保存拍摄该图片的相机的参数,甚至PS的版本信息等)

    数据长度(2字节): 0x0010 即16字节

    标示符(5字节): 0x4A46494600 即JFIF0

    版本号(2字节): 0x0101 即1.1版本

    X,Y方向密度单位(1字节): 0x00 即无单位

    X,Y方向像素密度(2字节 + 2字节): 0x0001 0x0001 即X,Y方向像素密度都为1

    缩略图RGB位图信息(2字节):0x0000 没有缩略图故为0

    对其他段信息的解读也是类似的工作,只要有耐心,对照着参考资料,怎样的JPEG文件信息我们都是能够解读的。而当我们可以通过二进制获取JPEG文件的相关信息时,解码已经是水到渠成的工作了。接下来我们只需要实现相应的编码器即可。

三、JPEG解码器程序的分析

    1.JPEG编码器所需的功能

    在上面的一章节内容中,我们了解了JPEG的编码过程以及JPEG文件结构的组织。有了这部分知识的铺垫,我们很快就能够想到JPEG编码器要有的功能。首先,能读JPEG文件,提取JPEG文件中的自包含的信息以及已压缩的数据流;其次,便是能够根据这些自包含的信息解压缩的部分。

     在实验二中我们已经实现了提取一个文件中自包含信息的功能;而之后的实验,我们对Huffman编码以及DPCM都有所涉猎,因此我打算重点分析一下DCT变换这一部分内容。

    2.根据具体的程序分析解码过程

    (1)mian()

    主函数首先通过命令行,选择对应的输出图像格式,并将之作为参数传入响应的函数

 convert_one_image(input_filename, output_filename, output_format);//转换一张图像
    我们仅解码一张图像,故会选择,output_format变量为TINYJPEG_FMT_YUV420P
/**
 * main
 *
 */
int main(int argc, char *argv[])
{
  int output_format = TINYJPEG_FMT_YUV420P;
  char *output_filename, *input_filename;
  clock_t start_time, finish_time;
  unsigned int duration;
  int current_argument;
  int benchmark_mode = 0;
#if TRACE
  p_trace=fopen(TRACEFILE,"w");
  if (p_trace==NULL)
  {
	  printf("trace file open error!");
  }
#endif
  if (argc < 3)
    usage();

  current_argument = 1;
  while (1)
   {
     if (strcmp(argv[current_argument], "--benchmark")==0)
       benchmark_mode = 1;
     else
       break;
     current_argument++;
   }

  if (argc < current_argument+2)
    usage();

  //根据命令行选择输出图像格式
  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_BGR24;
  else if (strcmp(argv[current_argument+1],"grey")==0)
    output_format = TINYJPEG_FMT_GREY;
  else
    exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");
  output_filename = argv[current_argument+2];

  start_time = clock();

  if (benchmark_mode)
    load_multiple_times(input_filename, output_filename, output_format);
  else
    convert_one_image(input_filename, output_filename, output_format);//转换一张图像

  finish_time = clock();
  duration = finish_time - start_time;
  snprintf(error_string, sizeof(error_string),"Decoding finished in %u ticks\n", duration);
#if TRACE
  fclose(p_trace);
#endif
  return 0;
}
    (2)convert_one_image()

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值