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

一、前导知识


1.变换编码:将输入信号变换,得到一些系数表示

在这里插入图片描述
去相关:将输入信号去相关,使得在量化时,对各系数单独量化(标量量化),而不会损伤过多效率(与矢量量化相比)
稀疏化:将原始信号的能量压缩到尽可能少的系数→对原始信号只用少数幅值较大的系数表示
可逆:可以重构输入信号
2.离散正交变换
预测编码中所有变换都是正交变换,离散正交变化具有如下特点:
1.正交矩阵的逆就是其转置,对于正交变换,反变换可以得到唯一复原信号。
2.正交变换总能量保持不变。
3.正交变化后个分量之间的相关性被全部去除,使得在量化时,对各系数单独量化不会损失过多效率。
3.DCT变换
变换核矩阵:

在这里插入图片描述
二维DCT变换:
在这里插入图片描述

变换核可分离,先进行垂直方向的81DCT变换,再进行水平方向的81DCT变换。

二、实验原理


1.JPEG文件
(1)Segment 的组织形式
JPEG 在文件中以 Segment 的形式组织,它具有以下特点:
均以 0xFF 开始,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示
Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);
采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;
Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;

(2)JPEG 的 Segment Marker
在这里插入图片描述在这里插入图片描述

2.基本JPEG编码器
在这里插入图片描述

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

(2)DCT变换
对每个单独的彩色图像分量,把整个分量图像分成8×8的图像块,如图所示,并作为两维离散余弦变换DCT的输入
在这里插入图片描述
(3)量化
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。如果原始图象中细节丰富,则去掉的数据较多
,量化后的系数与量化前差别;反之,细节少的原始图象在压缩时去掉的数据少些。
在这里插入图片描述
(4)DC系数的差分编码
由于直流系数 F(0,0)反映了该子图像中包含的直流成分,通常较大,又由于两个相邻的子图像的直流系数通常具有较大的相关性,所以对 DC 系数采用差值脉冲编码(DPCM),即对本像素块直流系数与前一像素块直流系数的差值进行无损编码。

(5)AC系数的Z字扫描、游程编码
首先,进行游程编码(RLC),并在最后加上块结束码(EOB);然后,系数序列分组,将非零系数和它前面的相邻的全部零系数分在一组内;每组用两个符号表示[(Run,Size),(Amplitude)]

Amplitude:表示非零系数的幅度值;Run:表示零的游程即零的个数;Size:表示非零系数的幅度值的编码位数;
在这里插入图片描述
(6)对DC和AC系数哈夫曼编码

3.解码重构(与编码相反)
(1)解码Huffman数据
(2)解码DC差值
(3)重构量化后的系数
(4)DCT逆变换
(5)丢弃填充的行/列
(6)反0偏置
(7)对丢失的CbCr分量差值(下采样的逆过程)
(8)YCbCr → RGB

三、程序流程及主要代码


1.读取文件

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

  // 如果命令行参数小于3,显示使用指南
  if (argc < 3)
    usage();

  //使用第一个命令行参数
  current_argument = 1;
  while (1)
   {
   
      //这里我将源代码更改了一下,认为这样更符合逻辑
     if (strcmp(argv[current_argument], "--benchmark")==0)
         benchmark_mode = 1;
     current_argument++; //使用第2个命令行参数 
     break;
   }

  //如果参数不符合要求,显示使用指南
  if (argc < current_argument+2)
    usage();
  //第2个参数为输入文静名
  input_filename = argv[current_argument];

  //根据第3个命令行参数,设置输出格式
  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");

  //第4个命令行参数为输出文件名
  output_filename = argv[current_argument+2];

  start_time = clock();

  //选择不同模式,命令行参数为--benchmark,为load_multiple_times()模式
  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;
}

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

2.解析 Segment Marker

//在Start of scan标签之前
  while (!sos_marker_found)
   {
   
     if (*stream++ != 0xff)
       goto bogus_jpeg_format;
     /* Skip any padding ff byte (this is normal) */
     //跳过0xff字节
     while (*stream == 0xff)
       stream++;

     //marker
     marker = *stream++;
     //chunk_len,包含自身,但不包含0xff+marker2字节
     chuck_len = be16_to_cpu(stream);
     //指针指向下一个chunk
     next_chunck = stream + chuck_len;
     //解析各种标签
     switch (marker)
      {
   
       case SOF:
           //SOF
	 if (parse_SOF(priv, stream) < 0)
	   return -1;
	 break;
       case DQT:
           //DQT
	 if (parse_DQT(priv, stream) < 0)
	   return -1;
	 break;
       case SOS:
           //SOS
	 if (parse_SOS(priv, stream) < 0)
	   return -1;
	 sos_marker_found = 1;
	 break;
       case DHT:
           //DHT
	 if (parse_DHT(priv, stream) < 0)
	   return -1;
	 dht_marker_found = 1;
	 break;
       case DRI:
           //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;
      }

     //解析下一个segment
     stream = next_chunck;
   }

(1)解析 SOI

//是否是JPEG文件
  if ((buf[0] != 0xFF) || (buf[1] != SOI)) //JPEG 文件必须以 0xFFD8(SOI)起始
    snprintf(error_string, sizeof(error_string),
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值