JPEG

一.实验原理

JPEG是常见的一种图像格式,由ISO与CCITT建立并开发,是一个国际数字图像压缩标准。 
  根据人眼视觉特性:眼睛对亮度的敏感程度要大于对色彩的敏感程度。在图像中,为了利用人类的种视角特性,从而降低数据量,通常将RGB空间表示的彩色图像变换到YCbCr颜色空间中。由于人眼对亮度Y的敏感度大于色差CrCb,因此可以在适当程度上对CrCb进行削弱以达到压缩的目的。由于原始图像是由很多独立的像素组成的,其实人眼对于每个细微像素的分辨能力很弱,只有众多像素集合一块,才能呈现出颜色连续变化的图像,因此图像中相邻两像素点,其彩色分量在很大程度上是接近的。在一幅图像内,包含了各种频率的分量,但大多数分量都属于低频信号,只在占图像区域比例很小的图像边缘的像素才含有高频信号。因此在对图像编码的时候,在图像质量不出现可察觉损失的情况下,对包含信息量大的低频谱区分配较多比特数,对包含信息量较低的高频谱区域分配较少的比特数,就能达到数据压缩目的。 
  将图像的色彩空间域转换到频谱域,这就用到了DCT。其作用是将图像数据去相关化,去除图像数据内部的相关性后,以便在其后将对这些图像数据分类处理——即对不同的频路部分进行不同的量化。 
  量化编码是JPEG编码中产生信息损失的根源,也是图像质量下降的最主要原因。简单的说,就是将频谱领域中的每个值,除以量化表中对应的常数,且四舍五入取最接近的整数,这样会把很多高频的成分四舍五入为0。量化后左上角的值较小,右下角的值较大,这样就保持低频分量、抑制高频分量的目的。这一步在实现的时候会对Y进行细量化,对Cr、Cb采用粗量化,依次来提高压缩比。因此存在两张不同的表。 
  经过DCT变换后,图像中的低频分量会集中在左上角,而右下角有较多的0值,因此采用Z字形编排。JPEG 算法 使用了差分脉冲编码(DPCM)技术,对相邻图像块之间量化DC洗漱的差值进行单独编码,从而再次利用相邻特性简化数据。

  为了进一步提高压缩比例,JPEG算法对DPCM编码后的直流系数与行程编码后的交流系数使用Huffman熵编码。使用huffman码表可以简单的查表进行编码。对于AC与DC所采用的码表是不同的,对于色差和亮度的霍夫曼码表也不同。


JPEG图像格式介绍:
缩写名称说明标记代码字节数
SOIStart of Image图像开始固定值0xFFD82(标记代码)
EOIEnd of Image图像结束固定值0xFFD92(标记代码)
APP0Application应用程序保留标记固定值0xFFE0variable
DQTDefine Quantization Table定义量化表固定值0xFFDBvariable
SOF0Start of Frame帧图像开始固定值0xFFC0variable
DHTDefine Huffman Table定义哈夫曼表固定值0xFFC4variable
SOSStart of Scan扫描开始固定值0xFFDAvari

  • 以TXT文件输出所有的量化矩阵和所有的HUFFMAN码表:
    static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
    ......
      while (stream < dqt_block_end)
   {
     qi = *stream++;
    #if C_TRACE     //cdj add
         fprintf(c_trace, ">DQT marker\n");//输出进入Parse_DQT的提示符
         fprintf(c_trace, "DQT TABLE[%d]\n", qi & 0x0F);
         fflush(c_trace);
    #endif
    table = priv->Q_tables[qi];
     build_quantization_table(table, stream);
     stream += 64;
   }
    ......
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
    static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
  ......
  for (i=0; i<8; i++) {
     for (j=0; j<8; j++) {
       *qtable = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];          
#if C_TRACE
       fprintf(c_trace, "%2d\t", ref_table[zigzag[i*8+j]]);
       //ref_table指针中存取着量化表值,但储存顺序为z字形扫描的次序,需要利用zigzag[64]进行正确的排序
       if (j == 7)//如果输出够一行8个量化数值,则换行
           fprintf(c_trace, "\n");
       fflush(c_trace);
#endif
       qtable++;
     }
   }
......
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
    static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
        ......
    for (i=0; huffsize[i]; i++)
   {
       ......
    #if C_TRACE
     fprintf(c_trace, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
     //输出Huffman表内容:huffman码长、huffman对应码字、对应符号。
     fflush(c_trace);
#endif
        ......
   }
        ......
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 输出DC图像、与AC分量
    #if C_TRACE
  char temp[1024];
  snprintf(temp, 1024, "%s_DC.yuv", output_filename);
  C_DCpicture = fopen(temp, "wb");
  if (C_DCpicture == NULL)
  {
      printf("C_DCpicture FILE OPEN ERROR");
  }
  snprintf(temp, 1024, "%s_AC.yuv", output_filename);
  C_ACpicture = fopen(temp, "wb");
  if (C_ACpicture == NULL)
  {
      printf("C_ACpicture FILE OPEN ERROR");
  }
    #endif


    static void decode_MCU_1x1_3planes(struct jdec_private *priv)
{
  // Y
  process_Huffman_data_unit(priv, cY);
#if C_TRACE
  unsigned char tmp_dc = (unsigned char)(&priv->component_infos[cY])->DCT[0]/8;//获取DC分量,并将其变到可观测到变换的取值范围。
  fwrite(&tmp_dc, 1, 1, C_DCpicture);
  //将DC分量写入dc.yuv
  unsigned char tmp_ac = (unsigned char)(&priv->component_infos[cY])->DCT[1];//获取AC分量,这里获取的是DCT矩阵中第一行、第二列的AC分量,即DCT[1]
  fwrite(&tmp_ac, 1, 1, C_ACpicture);
  //将AC分量写入ac.yuv
#endif
  IDCT(&priv->component_infos[cY], priv->Y, 8);

  // Cb
  process_Huffman_data_unit(priv, cCb);
  IDCT(&priv->component_infos[cCb], priv->Cb, 8);

  // Cr
  process_Huffman_data_unit(priv, cCr);
  IDCT(&priv->component_infos[cCr], priv->Cr, 8);
}实验结果:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值