数据压缩实验无--jpeg解码

博客探讨了JPEG图像格式,介绍了JPEG算法如何通过DPCM编码和Huffman熵编码提高压缩比例。强调了不同组件(如色差和亮度)使用的不同Huffman码表,以及在量化矩阵和图像输出过程中的应用。
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

部分代码:

3个结构体

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 */  //前一个宏块DC值
  short int DCT[64];        /* DCT coef */          //dct变换后的64个参数
#if SANITY_CHECK
  unsigned int cid;
#endif
};


typedef void (*decode_MCU_fct) (struct jdec_private *priv);
typedef void (*convert_colorspace_fct) (struct jdec_private *priv);

struct jdec_private //最上层结构体,MCU块层次,包含了该MCU的信息和元素
{
  /* Public variables */
  uint8_t *components[COMPONENTS];//指向该MCU所包含的宏块指针
  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;//若不包含Huffman码表时所用的缺省码表
  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];//三个彩色分量最基本MCU块大小 8x8x4、8x8、8x8

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

};
输出yuv图像:

量化矩阵:

输出直流交流图像:

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
    /*add by dyq*/
    priv->DC_Image = (short int *)malloc(sizeof(short int)*priv->height*priv->width / 64);
    priv->AC_Image = (short int *)malloc(sizeof(short int)*priv->height*priv->width / 64);
    /* end */
  unsigned int x, y, xstride_by_mcu, ystride_by_mcu;
  unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];

  /*
  typedef void (*decode_MCU_fct) (struct jdec_private *priv);
  typedef void (*convert_colorspace_fct) (struct jdec_private *priv);
  这两个函数里面结构体作为函数参数实现面向过程程序设计中又分层次的思想。
  */
  decode_MCU_fct decode_MCU;
  const decode_MCU_fct *decode_mcu_table;
  const convert_colorspace_fct *colorspace_array_conv;
  convert_colorspace_fct convert_to_pixfmt;
  /*...省略部分代码*/
  decode_mcu_table = decode_mcu_3comp_table;    
  return -1;
  }
//选择MCU的长宽
  xstride_by_mcu = ystride_by_mcu = 8;
  if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) 
  {
     decode_MCU = decode_mcu_table[0];
     convert_to_pixfmt = colorspace_array_conv[0];
  } else if (priv->component_infos[cY].Hfactor == 1) {
     decode_MCU = decode_mcu_table[1];
     convert_to_pixfmt = colorspace_array_conv[1];
     ystride_by_mcu = 16;
  } else if (priv->component_infos[cY].Vfactor == 2) {
     decode_MCU = decode_mcu_table[3];
     convert_to_pixfmt = colorspace_array_conv[3];
     xstride_by_mcu = 16;
     ystride_by_mcu = 16;
  } else {
     decode_MCU = decode_mcu_table[2];
     convert_to_pixfmt = colorspace_array_conv[2];
     xstride_by_mcu = 16;
  }
  resync(priv);
/*...省略部分代码*/

  /* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */
  for (y=0; y < priv->height/ystride_by_mcu; y++)
   {
     //trace("Decoding row %d\n", y);
     priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]);
     priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]);
     priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]);
     for (x=0; x < priv->width; x+=xstride_by_mcu)
     {  
        //在此处取直流分量和交流分量进行输出
        getDAC(priv, xstride_by_mcu, ystride_by_mcu);//add by dyq
        decode_MCU(priv);//huffman 解码
        convert_to_pixfmt(priv);//变换
        priv->plane[0] += bytes_per_mcu[0];
        priv->plane[1] += bytes_per_mcu[1];
        priv->plane[2] += bytes_per_mcu[2];
        if (priv->restarts_to_go>0)
        {
            priv->restarts_to_go--;
            if (priv->restarts_to_go == 0)
            {   
                priv->stream -= (priv->nbits_in_reservoir/8);
                resync(priv);
                if (find_next_rst_marker(priv) < 0)
                    return -1;
            }
        }
      } 
   }
  /* add by dyq */
  //全部取出后作范围调整[0,255]
  adjustDAC(priv, xstride_by_mcu, ystride_by_mcu);
  /* end */
  return 0;
}
static void getDAC(struct jdec_private *priv, int xstride_by_mcu , int ystride_by_mcu)
{
    static long int i = 0;
    //因为宏块为8*8 因此DC图像应该为实际图像大小除以宏块大小
    if (i < priv->height*priv->width / (xstride_by_mcu * ystride_by_mcu) )
    {
        priv->DC_Image[i] = priv->component_infos[0].DCT[0];
        priv->AC_Image[i] = priv->component_infos[0].DCT[1];
    }
    i++;
}

static void adjustDAC(struct jdec_private *priv, unsigned int xstride_by_mcu, unsigned int ystride_by_mcu)
{
    int i;
    short int min_AC, max_AC, min_DC, max_DC, size;
    unsigned char *temp_AC, *temp_DC;
    size = priv->height*priv->width / (xstride_by_mcu * ystride_by_mcu);
    temp_AC = (unsigned char*)malloc(sizeof(unsigned char)*size);
    temp_DC = (unsigned char*)malloc(sizeof(unsigned char)*size);

    //将AC/DC分量范围调整到[0-255]
    min_AC = priv->AC_Image[0];
    max_AC = priv->AC_Image[0];
    min_DC = priv->DC_Image[0];
    max_DC = priv->DC_Image[0];

    //遍历寻找最大最小值,进行中心化操作
    for (i = 0; i < size; i++)
    {
        if (priv->AC_Image[i] < min_AC)
            min_AC = priv->AC_Image[i];
        if (priv->AC_Image[i] > max_AC)
            max_AC = priv->AC_Image[i];
        if (priv->DC_Image[i] < min_DC)
            min_DC = priv->DC_Image[i];
        if (priv->DC_Image[i] > max_DC)
            max_DC = priv->DC_Image[i];
    }
    //范围调整
    for (i = 0; i < size; i++)
    {
        temp_AC[i] = (unsigned char)(255 * (priv->AC_Image[i] - min_AC) / (max_AC - min_AC));
        temp_DC[i] = (unsigned char)(255 * (priv->DC_Image[i] - min_DC) / (max_DC - min_DC));
    }
    fwrite(temp_AC, 1, size, AC_FILE);
    fwrite(temp_DC, 1, size, DC_FILE);

    free(temp_AC);
    free(temp_DC);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

得到结果如下:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值