JPEG解码实验

来源:互联网 发布:java iterator() 编辑:程序博客网 时间:2024/06/16 06:43

一、JPEG编解码原理


JPEG编码的过程如上图所示。解码是编码的逆过程。 

以下是编码具体说明:

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

2、8*8DCT
将输入图像分为8*8的小块, 每个块里有64个像素。若边缘未满8*8,则用边缘像素进行填充。


总体来说,图像的低频部分集中在每个8*8块的左上角,高频部分在右下角。DCT变化可以做到以下三点:1、能量守恒  2、能量集中  3、去相关

3、量化 
根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。 由于上面的人眼视觉特性,量化表左上角的值较小,右上角的值较大,这样就起到了保持低频分量,抑制高频分量的目的。 

4、DC系数的差分编码

8X8图像块经过DC丁变换之后得到的DC直流系数有两个特点:1.系数的数值比较大2.相邻8X8图像块的DC系数值变化不大。根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:DIFFk =DCk - DCk-1。


5、AC系数的Z字扫描

由于经DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出EOB (End of Block)即可。


6、AC系数的游程编码

在JPEG和MPEG编码中规定为:(run, level),表示连续run个0,后面跟值为level的系数。
如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7表示为(1, 2), (2, 3) ,…

编码:
Run: 最多15个,用4位表示RRRR。 
Level:类似DC:分成16个类别,用4位表示SSSS表示类别号;类内索引。对(RRRR, SSSS)联合用Huffman编码;对类内索引用定长码编码

AC系数的游程编码
在JPEG和MPEG编码中规定为:(run, level),表示连续run个0,后面跟值为level的系数。
如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7表示为(1, 2), (2, 3) ,…

编码:
Run: 最多15个,用4位表示RRRR。 
Level:类似DC:分成16个类别,用4位表示SSSS表示类别号;类内索引。对(RRRR, SSSS)联合用Huffman编码;对类内索引用定长码编码

二、JPEG文件格式

  • SOI,Start of Image,图像开始
  • APP0,Application,应用程序保留标记0
  • DQT,Define Quantization Table,定义量化表
  • SOF0,Start of Frame,帧图像开始
  • DHT,Define Huffman Table,定义哈夫曼表
  • SOS,Start of Scan,扫描开始12字节
  • EOI,End of Image,图像结束2字节 
SOI ,Start of Image,   图像开始

 标记代码  2字节  固定值0xFFD8


EOI,End of Image,   图像结束 2字节

 标记代码  2字节  固定值0xFFD9


APP0应用程序保留标记

标记代码  2字节  固定值0xFFE0
包含9个具体字段:
  ① 数据长度  2字节  ①~⑨9个字段的总长度
  ②标识符  5字节   固定值0x4A46494600,即字符串“JFIF0”
  版本号  2字节   一般是0x0102,表示JFIF的版本号1.2
  ④ XY的密度单位  1字节  只有三个值可选
  0:无单位;1:点数/英寸;2:点数/厘米
  X方向像素密度      2字节  取值范围未知
  Y方向像素密度         2字节  取值范围未知  
  ⑦ 缩略图水平像素数目  1字节  取值范围未知
  ⑧ 缩略图垂直像素数目  1字节  取值范围未知
  ⑨ 缩略图RGB位图        长度可能是3的倍数  缩略图RGB位图数据

DQT 定义量化表 

标记代码  2字节  固定值0xFFDB
包含9个具体字段:
  ① 数据长度  2字节  字段①和多个字段②的总长度
  ② 量化表    数据长度-2字节

    a)精度及量化表ID  1字节 

       高4位:精度,只有两个可选值     0:8位;1:16位
     低4位:量化表ID,取值范围为0~3

    b)表项          (64×(精度+1))字节

         例如8位精度的量化表,其表项长度为64×(0+1)=64字节

n本标记段中,字段②可以重复出现,表示多个量化表,但最多只能出现4次
SOF0  帧图像开始 
标记代码  2字节  固定值0xFFC0
包含9个具体字段:
  ① 数据长度  2字节  ①~⑥六个字段的总长度
  ②精度           1字节  每个数据样本的位数
   通常是8位,一般软件都不支持 12位和16位
  ③ 图像高度  2字节  图像高度(单位:像素)
 ④图像宽度  2字节  图像宽度(单位:像素)
 ⑤颜色分量数  1字节  只有3个数值可选
   1:灰度图;3:YCrCb或YIQ;4:CMYK
   而JFIF中使用YCrCb,故这里颜色分量数恒为3
  颜色分量信息  颜色分量数×3字节(通常为9字节)                                       
              a)颜色分量ID  1字节 

              b)水平/垂直采样因子  1字节 

                              高4位:水平采样因子
                            低4位:垂直采样因子
              c)量化表  1字节                                                            当前分量使用的量化表的ID

DHT,定义哈夫曼表
标记代码  2字节  固定值0xFFC4
包含2个具体字段

     ① 数据长度      2字节 

     ② huffman 数据长度-2字节

ID和表类型 1字节 

                     高4位:类型,只有两个值可选

                                  0:DC直流;1:AC交流
        低4位:哈夫曼表ID,

                                  注意,DC表和AC表分开编码

不同位数的码字数量 16字节
编码内容  16个不同位数的码字数量之和(字节)
本标记段中,字段②可以重复出现(一般4次),也可以只出现1次。

SOS     扫描开始

标记代码         2字节  固定值0xFFDA
包含2个具体字段:
 ①数据长度           2字节  ①~④两个字段的总长度
 ②颜色分量数         1字节  应该和SOF中的字段⑤的值相同,即:
               1:灰度图是;3: YCrCb或YIQ;4:CMYK。
 ③颜色分量信息
    a)颜色分量ID   1字节
    b) 直流/交流系数表号  1字节 

           高4位:直流分量使用的哈夫曼树编号
        低4位:交流分量使用的哈夫曼树编号

  ④压缩图像数据
       a)谱选择开始  1字节  固定值0x00
        b)谱选择结束      1字节  固定值0x3F
        c)谱选择  1字节  在基本JPEG中总为00
三、代码及结果

struct huffman_table,struct component ,struct jdec_private三个结构体设计的目的如下:struct jdec_private处于最上层,支配整个图像,其中包含有 struct huffman_table和struct component。 struct huffman_table用来存放DC系数和AC系数的Huffman表;struct component用来存放8x8数据块组成的MCU即最小数据单元的数值,相当于移动像块,每次访问下一像块就给该结构体更新数值。

{    /* 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;//<span style="color:black;"><span style="color:black;"><span style="color:black;">DC系数的Huffman表</span></span></span>    struct huffman_table *DC_table;//<span style="color:black;"></span></span><span style="font-size:12px;">AC<span style="color:black;"></span></span><span style="font-size:12px;">系数的Huffman表    short int previous_DC;    /* Previous DC coefficient */    short int DCT[64];        /* DCT coef */  #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  {    /* 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];     /*by liushasha*///添加两个指针用来存放将要输出的DC、AC的图像。    short int*DC_Image;    short int*AC_Image;    /*by liushasha*/   jmp_buf jump_state;    /* Internal Pointer use for colorspace conversion, do not modify it !!! */    uint8_t *plane[COMPONENTS];  }
enum tinyjpeg_fmt {     TINYJPEG_FMT_GREY = 1,     TINYJPEG_FMT_BGR24,     TINYJPEG_FMT_RGB24,     TINYJPEG_FMT_YUV420P,     /*by liushasha*///将YUV分量合成YUV文件输出     TINYJPEG_FMT_YUV,      /*by liushasha*/  }
{     ......   switch (pixfmt) {   /*by liushasha*///对TINYJPEG_FMT_YUV的处理和对TINYJPEG_FMT_YUV420P的处理完全一致    case TINYJPEG_FMT_YUV:      ......     break;    case TINYJPEG_FMT_YUV420P:  <span style="color:black;"></span></span><pre name="code" class="cpp"><span style="font-size:12px;">    ......     break;    case TINYJPEG_FMT_RGB24:  <span style="color:black;"></span></span><pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-size:12px;">   ......     break;    case TINYJPEG_FMT_BGR24:     ......  </span></pre>  <pre></pre>  <span style="font-size:12px"><span style="color:black"></span></span><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-size:12px;">  break;</span></pre>  <pre></pre>  <pre></pre>  <pre></pre>  <span style="font-size:12px"> case TINYJPEG_FMT_GREY:<span style="color:black"></span></span><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-size:12px;">  ......  </span></pre>  <pre></pre>  <pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-size:12px;">  break;</span></pre>  <pre></pre>  <pre></pre>  <pre></pre>  <pre></pre>  <pre></pre>  <span style="font-size:12px">default;......}...... return 0;</span>  <pre></pre>  <span style="font-size:12px">}<br>  </span>  <pre></pre>  <pre name="code" class="cpp"><span style="font-size:12px;">int convert_one_image(const char *infilename, const char *outfilename, int output_format)  {    ......     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;      /*by liushasha*/  <span style="color:black;"></span></span><pre name="code" class="cpp"><span style="font-size:12px;">      case TINYJPEG_FMT_YUV:</span></pre><span style="font-size:12px"><span style="color:black"></span></span><pre name="code" class="cpp"><span style="font-size:12px;">       write_add_yuv(outfilename, width, height, components);         break;      /*by liushasha*/  </span></pre><span style="font-size:12px">    case TINYJPEG_FMT_GREY:<br>        write_pgm(outfilename, width, height, components);<br>        break;<br>     }<br>  ......<br>  }</span><br>  <pre></pre>  <pre name="code" class="cpp"><span style="font-size:12px;">/*by liushasha*///YUV文件写出  static void write_add_yuv(const char *filename, int width, int height, unsigned char **components)  {    FILE *F;    char temp[1024];    snprintf(temp, 1024, "%s.YUV", filename);    F = fopen(temp, "wb");    fwrite(components[0], width, height, F);    fwrite(components[1], width*height/4, 1, F);    fwrite(components[2], width*height/4, 1, F);    fclose(F);  }  /*by liushasha*/<span style="color:black;"></span>  <span style="color:black;"></span></span></pre><pre name="code" class="cpp"><span style="font-size:12px;">static void usage(void)  {      fprintf(stderr, "Usage: loadjpeg [options] <input_filename.jpeg> <format> <output_filename>\n");      fprintf(stderr, "options:\n");      fprintf(stderr, "  --benchmark - Convert 1000 times the same image\n");      fprintf(stderr, "format:\n");      fprintf(stderr, "  yuv420p - output 3 files .Y,.U,.V\n");      /*by liushasha*/      fprintf(stderr, "  yuv - output a files.YUV\n");      /*by liushasha*/      fprintf(stderr, "  rgb24   - output a .tga image\n");      fprintf(stderr, "  bgr24   - output a .tga image\n");      fprintf(stderr, "  gray    - output a .pgm image\n");      exit(1);  }</span></pre><span style="font-size:12px"></span><pre name="code" class="cpp">int main(int argc, char *argv[])  {   ......  /*by liushasha*/  #if TABLES   char temp[1024];   snprintf(temp,1024,"%s_tables.txt",ouput_filename);  #endif     /*by liushasha*/     ......  if (strcmp(argv[current_argument+1],"yuv420p")==0)      output_format = TINYJPEG_FMT_YUV420P;    /*by liushasha*/    else if (strcmp(argv[current_argument+1],"yuv")==0)      output_format = TINYJPEG_FMT_YUV;    /*by liushasha*/    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    ......  /*by liushasha*/  #if TABLES    fclose(f_tables);  #endif  /*by liushasha*/  return 0;  }  </pre>在tinyjepg.h中定义<br>  <pre name="code" class="cpp">/*by liushasha*/  #define TABLES1  FILE *f_tables;  /*by liushasha*/</pre><pre name="code" class="cpp">static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)  {    ......   while (stream < dqt_block_end)//检查是否还有表     {......       /*by liushasha*/        #if TABLES       fprintf(f_tables,"Quantization_table[%d]:\n",qi);       fflush(f_tables);        #endif      /*by liushasha*/      ......  }    ......  }  </pre><pre name="code" class="cpp">static void build_quantization_table(float *qtable, const unsigned char *ref_table)  {    /* Taken from libjpeg. Copyright Independent JPEG Group's LLM idct.    * For float AA&N IDCT method, divisors are equal to quantization    * coefficients scaled by scalefactor[row]*scalefactor[col], where    *   scalefactor[0] = 1    *   scalefactor[k] = cos(k*PI/16) * sqrt(2)    for k=1..7    * We apply a further scale factor of 8.    * What's actually stored is 1/divisor so that the inner loop can    * use a multiplication rather than a division.    */    int i, j;    static const double aanscalefactor[8] = {       1.0, 1.387039845, 1.306562965, 1.175875602,       1.0, 0.785694958, 0.541196100, 0.275899379    };    const unsigned char *zz = zigzag;      for (i=0; i<8; i++) {       for (j=0; j<8; j++) {   /*by liushasha*/  #if TABLES  fprintf(f_tables,"%d\t",ref_table[*zz]);  fflush(f_tables);  if(j==7)  {  fprintf(f_tables,"%\n");  fflush(f_tables);  }  #endif  <span style="color:black;"></span><pre name="code" class="cpp"> /*by liushasha*/</pre> *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; } }}  <pre></pre>  <pre name="code" class="cpp">static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)  {  ......         if (index & 0xf0 )       {      /*by liushasha*/  #if TABLES           fprintf(f_tables,"Huffman table AC[%d]length=%d\n",index & 0xf,count);           fflush(f_tables);  #endif           /*by liushasha*/         build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);       }       else       {           /*by liushasha*/  #if TABLES           fprintf(f_tables,"Huffman table DC[%d]length=%d\n",index & 0xf,count);           fflush(f_tables);  #endif           /*by liushasha*/         build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);       }    ......  }  ......  }  </pre><pre name="code" class="cpp">static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)  {     ......   for (i=0; huffsize[i]; i++)     {    ......      /*by liushasha*/     #if TABLES       fprintf(f_tables,"val==%2.2x code=%8.8x codesize=%2.2d\n",val, code, code_size);       fflush(p_tables);      #endif       /*by liushasha*/  ......  }  }  </pre>tinyjpeg.h<br>  <pre name="code" class="cpp">/*liushasha*/  FILE *DC_FILE;  FILE *AC_FILE;  static void output_DC_Image(struct jdec_private *priv);  static void output_AC_Image(struct jdec_private *priv);  static void output_DC_AC_Image(struct jdec_private *priv);  /*liushasha*/</pre>tinyjepg-internal.h<br>  <pre name="code" class="cpp">struct jdec_private  {  ......  /*by liushasha*/    short int *DC_Imaage;    short int *AC_Imaage;     /*by liushasha*/  ......  };  </pre><pre name="code" class="cpp">int main(int argc, char *argv[])  {  ......  /*by liushasha*/    char temp[1024];    snprintf(temp,1024,"%s_dc.y",output_filename);    DC_FILE=fopen(temp,"wb");    snprintf(temp,1024,"%s_ac.y",output_filename);    AC_FILE=fopen(temp,"wb");      /*by liushasha*/  ......  /*by liushasha*/    fclose(AC_FILE);    fclose(DC_FILE);    /*by liushasha*/    return 0;  }  </pre><pre name="code" class="cpp">int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)  {  ......   /*by liushasha*/    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);     /*by liushasha*/  ......  return 0;  }  </pre>以<span style="color:black"></span>decode_MCU_1x1plane为例添加代码,其他的类似。<br>  <pre name="code" class="cpp">static void decode_MCU_1x1_1plane(struct jdec_private *priv)  {    // Y    process_Huffman_data_unit(priv, cY);    /*by liushasha*/    output_DC_Image(priv);    output_AC_Image(priv);     /*by liushasha*/    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);  }  </pre><pre name="code" class="cpp">/*by liushasha*/  static void output_DC_Image(struct jdec_private *priv)  {      static long int i=0;      if(i<priv->height*priv->width/64)          priv->DC_Image[i]=priv->component_infos[0].DCT[0];      i++;  }  static void output_AC_Image(struct jdec_private *priv)  {      static long int i=0;      if(i<priv->height*priv->width/64)          priv->AC_Image[i]=priv->component_infos[0].DCT[1];      i++;  }  /*by liushasha*/  </pre><pre name="code" class="cpp">void tinyjpeg_free(struct jdec_private *priv)  {    int i;    for (i=0; i<COMPONENTS; i++) {       if (priv->components[i])         free(priv->components[i]);       priv->components[i] = NULL;    }    /*by liushasha*/    if(priv->AC_Image) free(priv->AC_Image);    if(priv->DC_Image) free(priv->DC_Image);     /*by liushasha*/    free(priv);  }</pre><pre name="code" class="cpp">/*by liushasha*/  static void write_DC_AC_Image(struct jdec_private *priv)   {       int i;       short int DC_min, DC_max, AC_min, AC_max;       unsigned char *temp;      DC_min = priv->DC_Image[0];      DC_max = priv->DC_Image[0];       AC_min = priv->AC_Image[0];      AC_max = priv->AC_Image[0];       temp = (unsigned char*)malloc(priv->height*priv->width / 64);      for (i = 0; i < (priv->height*priv->width/64); i++)      {       if (priv->DC_Image[i] > DC_max)      DC_max = priv->DC_Image[i];       if (priv->DC_Image[i] < DC_min)          DC_min = priv->DC_Image[i];       if (priv->AC_Image[i] > AC_max)           AC_max = priv->AC_Image[i];      if (priv->AC_Image[i] < AC_min)          AC_min = priv->AC_Image[i];       }      for (i = 0; i < (priv->height*priv->width/64); i++)      {           temp[i] = (unsigned char)255 * (priv->DC_Image[i] - DC_min) / (DC_max - DC_min);       }       fwrite(temp, 1, priv->width*priv->height / 64, DC_FILE);       for (i = 0; i < (priv->height*priv->width / 64); i++)       {           temp[i] = (unsigned char)255 * (priv->AC_Image[i] - AC_min) / (AC_max - AC_min);      }       fwrite(temp, 1, priv->width*priv->height / 64, AC_FILE);       if (temp) free(temp);  }  

可观看的 yuv文件:


以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。

输出DC图像并经过huffman统计其概率分布:


输出某一个AC值图像并统计其概率分布:


原创粉丝点击