• Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 1:解码文件头


    注:分析Tiny Jpeg Decoder源代码的文章:

    Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 1:解码文件头
    Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 2:解码数据

    ===================


    Tiny Jpeg Decoder是一个可以用于嵌入式系统的JPEG解码器。也可以在Windows上编译通过。在此分析一下它部分的源代码,辅助学习JPEG解码知识。

    通过TinyJpeg可以将JPEG(*.jpg)文件解码为YUV(*.yuv)或者RGB(*.tga)文件。

    真正的解码开始于convert_one_image()函数:

    /**
     * Load one jpeg image, and decompress it, and save the result.
     */
    int convert_one_image(LPVOID lparam,const char *infilename, const char *outfilename, int output_format)
    {
      FILE *fp;
      unsigned int length_of_file;
      unsigned int width, height;
      unsigned char *buf;
      struct jdec_private *jdec;
      unsigned char *components[3];
    
      /* Load the Jpeg into memory */
      fp = fopen(infilename, "rb");
      if (fp == NULL)
        exitmessage("Cannot open filename
    ");
      length_of_file = filesize(fp);
      buf = (unsigned char *)malloc(length_of_file + 4);
      if (buf == NULL)
        exitmessage("Not enough memory for loading file
    ");
      fread(buf, length_of_file, 1, fp);
      fclose(fp);
    
      /* Decompress it */
      //分配内存
      jdec = tinyjpeg_init();
      //传入句柄--------------
      jdec->dlg=(CSpecialVIJPGDlg *)lparam;
    
      if (jdec == NULL)
        exitmessage("Not enough memory to alloc the structure need for decompressing
    ");
      //解头部
      if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)
        exitmessage(tinyjpeg_get_errorstring(jdec));
      /* Get the size of the image */
      //获得图像长宽
      tinyjpeg_get_size(jdec, &width, &height);
    
      snprintf(error_string, sizeof(error_string),"Decoding JPEG image...
    ");
      //解码实际数据
      if (tinyjpeg_decode(jdec, output_format) < 0)
        exitmessage(tinyjpeg_get_errorstring(jdec));
      /* 
       * Get address for each plane (not only max 3 planes is supported), and
       * depending of the output mode, only some components will be filled 
       * RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane
       */
      tinyjpeg_get_components(jdec, components);
    
      /* Save it */
      switch (output_format)
       {
        case TINYJPEG_FMT_RGB24:
        case TINYJPEG_FMT_BGR24:
          write_tga(outfilename, output_format, width, height, components);
          break;
        case TINYJPEG_FMT_YUV420P:
    	  //开始写入成YUV420P文件
          write_yuv(outfilename, width, height, components);
          break;
        case TINYJPEG_FMT_GREY:
    	  //开始写入成灰度文件
          write_pgm(outfilename, width, height, components);
          break;
       }
    
      /* Only called this if the buffers were allocated by tinyjpeg_decode() */
    //modify by lei!  tinyjpeg_free(jdec);
      /* else called just free(jdec); */
    
      free(buf);
      return 0;
    }

    tinyjpeg_init()用于初始化:

    /**
     * Allocate a new tinyjpeg decoder object.
     *
     * Before calling any other functions, an object need to be called.
     */
    struct jdec_private *tinyjpeg_init(void)
    {
      struct jdec_private *priv;
     
      priv = (struct jdec_private *)calloc(1, sizeof(struct jdec_private));
      if (priv == NULL)
        return NULL;
      priv->DQT_table_num=0;
      return priv;
    }

    tinyjpeg_parse_header()用于解码JPEG文件头,可见函数前几句主要验证文件是否为JPEG文件:

    /**
     * Initialize the tinyjpeg object and prepare the decoding of the stream.
     *
     * Check if the jpeg can be decoded with this jpeg decoder.
     * Fill some table used for preprocessing.
     */
    int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
    {
      int ret;
      
      /* Identify the file */
      //0x FF D8
      //是否是JPEG格式文件?
      if ((buf[0] != 0xFF) || (buf[1] != SOI))
        snprintf(error_string, sizeof(error_string),"Not a JPG file ?
    ");
      //是
      char temp_str[MAX_URL_LENGTH];
      sprintf(temp_str,"0x %X %X",buf[0],buf[1]);
      //JPEG格式文件固定的文件头
      //begin指针前移2字节
      priv->stream_begin = buf+2;
      priv->stream_length = size-2;
      priv->stream_end = priv->stream_begin + priv->stream_length;
      //开始解析JFIF
      ret = parse_JFIF(priv, priv->stream_begin);
      return ret;
    }

    parse_JFIF()用于解析各种标签(SOF,SOS,DHT...):

    //解各种不同的标签
    static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
    {
      int chuck_len;
      int marker;
      int sos_marker_found = 0;
      int dht_marker_found = 0;
      const unsigned char *next_chunck;
    
      /* Parse 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是跳过0xff字节后1个字节的内容
         marker = *stream++;
    	 //chunk_len是marker后面2个字节的内容(大端模式需要转换)
    	 //包含自身,但不包含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;
    	 //Define quantization table
           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;
    	 //Define Huffman table
           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_PARAM
    	fprintf(param_trace,"> Unknown marker %2.2x
    ", marker);
    	fflush(param_trace);
    #endif
    	 break;
          }
    	 //解下一个segment
         stream = next_chunck;
       }
    
      if (!dht_marker_found) {
    #if TRACE_PARAM
    	  fprintf(param_trace,"No Huffman table loaded, using the default one
    ");
    	  fflush(param_trace);
    #endif
        build_default_huffman_tables(priv);
      }
    
    #ifdef SANITY_CHECK
      if (   (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)
          || (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor))
        snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr
    ");
      if (   (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor)
          || (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))
        snprintf(error_string, sizeof(error_string),"Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr
    ");
      if (   (priv->component_infos[cCb].Hfactor!=1) 
          || (priv->component_infos[cCr].Hfactor!=1)
          || (priv->component_infos[cCb].Vfactor!=1)
          || (priv->component_infos[cCr].Vfactor!=1))
        snprintf(error_string, sizeof(error_string),"Sampling other than 1x1 for Cr and Cb is not supported");
    #endif
    
      return 0;
    bogus_jpeg_format:
    #if TRACE_PARAM
      fprintf(param_trace,"Bogus jpeg format
    ");
      fflush(param_trace);
    #endif
      return -1;
    }

    parse_SOF()用于解析SOF标签:

    注意:其中包含了部分自己写的代码,形如:

    itoa(width,temp_str1,10);
      priv->dlg->AppendBInfo("SOF0","宽",temp_str1,"图像的宽度");

    这些代码主要用于在解码过程中提取一些信息,比如图像宽,高,颜色分量数等等

    static int parse_SOF(struct jdec_private *priv, const unsigned char *stream)
    {
      int i, width, height, nr_components, cid, sampling_factor;
      int Q_table;
      struct component *c;
    #if TRACE_PARAM
      fprintf(param_trace,"> SOF marker
    ");
      fflush(param_trace);
    #endif
      print_SOF(stream);
    
      height = be16_to_cpu(stream+3);
      width  = be16_to_cpu(stream+5);
      nr_components = stream[7];
    #if SANITY_CHECK
      if (stream[2] != 8)
        snprintf(error_string, sizeof(error_string),"Precision other than 8 is not supported
    ");
      if (width>JPEG_MAX_WIDTH || height>JPEG_MAX_HEIGHT)
        snprintf(error_string, sizeof(error_string),"Width and Height (%dx%d) seems suspicious
    ", width, height);
      if (nr_components != 3)
        snprintf(error_string, sizeof(error_string),"We only support YUV images
    ");
      if (height%16)
        snprintf(error_string, sizeof(error_string),"Height need to be a multiple of 16 (current height is %d)
    ", height);
      if (width%16)
        snprintf(error_string, sizeof(error_string),"Width need to be a multiple of 16 (current Width is %d)
    ", width);
    #endif
      char temp_str1[MAX_URL_LENGTH]={0};
      itoa(width,temp_str1,10);
      priv->dlg->AppendBInfo("SOF0","宽",temp_str1,"图像的宽度");
      itoa(height,temp_str1,10);
      priv->dlg->AppendBInfo("SOF0","高",temp_str1,"图像的高度");
      itoa(nr_components,temp_str1,10);
      priv->dlg->AppendBInfo("SOF0","颜色分量数",temp_str1,"图像的颜色分量数。一个字节,例如03,代表有三个分量,YCrCb");
      itoa(stream[2],temp_str1,10);
      priv->dlg->AppendBInfo("SOF0","精度",temp_str1,"图像的精度。一个字节,例如08,即精度为一个字节。");
      stream += 8;
      for (i=0; i<nr_components; i++) {
         cid = *stream++;
         sampling_factor = *stream++;
         Q_table = *stream++;
         c = &priv->component_infos[i];
    #if SANITY_CHECK
         c->cid = cid;
         if (Q_table >= COMPONENTS)
           snprintf(error_string, sizeof(error_string),"Bad Quantization table index (got %d, max allowed %d)
    ", Q_table, COMPONENTS-1);
    #endif
         c->Vfactor = sampling_factor&0xf;
         c->Hfactor = sampling_factor>>4;
         c->Q_table = priv->Q_tables[Q_table];
    	 //------------
    	 char temp_str2[MAX_URL_LENGTH]={0};
    	 sprintf(temp_str2,"垂直采样因子【%d】",i);
    	 itoa(c->Hfactor,temp_str1,10);
    	 priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:每个分量有三个字节,第一个为分量的ID,01:Y 02:U 03:V;第二个字节高位为水平采样因子,低位为垂直采样因子。");
    	 sprintf(temp_str2,"水平采样因子【%d】",i);
    	 itoa(c->Hfactor,temp_str1,10);
    	 priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:每个分量有三个字节,第一个为分量的ID,01:Y 02:U 03:V;第二个字节高位为水平采样因子,低位为垂直采样因子。");
    	 sprintf(temp_str2,"对应量化表ID【%d】",i);
    	 itoa((int)Q_table,temp_str1,10);
    	 priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:第三个字节代表这个分量对应的量化表ID,例如,Y对应的量化表ID索引值为00,而UV对应的量化表ID都为01,即它们共用一张量化表。");
    	//-------------
    #if TRACE_PARAM
         fprintf(param_trace,"Component:%d  factor:%dx%d  Quantization table:%d
    ",
               cid, c->Hfactor, c->Hfactor, Q_table );
    	 fflush(param_trace);
    #endif
    
      }
    
    
      priv->width = width;
      priv->height = height;
    #if TRACE_PARAM
      fprintf(param_trace,"< SOF marker
    ");
      fflush(param_trace);
    #endif
    
      return 0;
    }

    parse_DHT()用于解析DHT标签:

    //解析DHT表
    static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
    {
      unsigned int count, i,j;
      unsigned char huff_bits[17];
      int length, index;
      //------------------------------------------
      char *temp;
      FILE *fp;
      //------------------------------------------
      length = be16_to_cpu(stream) - 2;
      //跳过length字段
      stream += 2;	/* Skip length */
    #if TRACE_PARAM
      fprintf(param_trace,"> DHT marker (length=%d)
    ", length);
      fflush(param_trace);
    #endif
    
      while (length>0) {
    	  //跳过第1字节:
    	  //Huffman 表ID号和类型,高 4 位为表的类型,0:DC 直流;1:AC交流
    	  //低四位为 Huffman 表 ID。 
         index = *stream++;
    
         /* We need to calculate the number of bytes 'vals' will takes */
         huff_bits[0] = 0;
         count = 0;
    
    	 //不同长度 Huffman 的码字数量:固定为 16 个字节,每个字节代表从长度为 1到长度为 16 的码字的个数
         for (i=1; i<17; i++) {
    	huff_bits[i] = *stream++;
    	//count记录码字的个数
    	count += huff_bits[i];
         }
    #if SANITY_CHECK
         if (count >= HUFFMAN_BITS_SIZE)
           snprintf(error_string, sizeof(error_string),"No more than %d bytes is allowed to describe a huffman table", HUFFMAN_BITS_SIZE);
         if ( (index &0xf) >= HUFFMAN_TABLES)
           snprintf(error_string, sizeof(error_string),"No more than %d Huffman tables is supported (got %d)
    ", HUFFMAN_TABLES, index&0xf);
    #if TRACE_PARAM
         fprintf(param_trace,"Huffman table %s[%d] length=%d
    ", (index&0xf0)?"AC":"DC", index&0xf, count);
    	 fflush(param_trace);
    #endif
    #endif
    
         if (index & 0xf0 ){
    		 //---------------------
    		 char temp_str1[MAX_URL_LENGTH]={0};
    		 char temp_str2[MAX_URL_LENGTH]={0};
    		 temp=(char *)stream;
    		 //fp = fopen("DHT.txt", "a+");
    		 //fwrite(temp, 16, 1, fp);
    		 for(j=0;j<16;j++){
    			 //fprintf(fp,"%d ",temp[j]);
    			 sprintf(temp_str2,"%d ",temp[j]);
    			 strcat(temp_str1,temp_str2);
    		 }
    		 //fprintf(fp,"
    -----------------------
    ");
    		 //fclose(fp);
    		 //-----------------------------------------------------
    		 priv->dlg->AppendBInfo("DHT","定义霍夫曼表【交流系数表】",temp_str1,"Huffman表ID号和类型:1字节,高4位为表的类型,0:DC直流;1:AC交流 可以看出这里是直流表;低四位为Huffman表ID");
    		 //-----------------------------------------------------
    	//交流霍夫曼表
           build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);
    	 }
         else{
    		 //---------------------
    		 char temp_str1[MAX_URL_LENGTH]={0};
    		 char temp_str2[MAX_URL_LENGTH]={0};
    		 temp=(char *)stream;
    		 //fp = fopen("DHT.txt", "a+");
    		 //fwrite(temp, 16, 1, fp);
    		 for(j=0;j<16;j++){
    			 //fprintf(fp,"%d ",temp[j]);
    			 sprintf(temp_str2,"%d ",temp[j]);
    			 strcat(temp_str1,temp_str2);
    		 }
    		 //fprintf(fp,"
    -----------------------
    ");
    		 //fclose(fp);
    		 //-----------------------------------------------------
    		 priv->dlg->AppendBInfo("DHT","定义霍夫曼表【直流系数表】",temp_str1,"Huffman表ID号和类型:1字节,高4位为表的类型,0:DC直流;1:AC交流 可以看出这里是直流表;低四位为Huffman表ID");
    		 //-----------------------------------------------------
    		 //直流霍夫曼表
           build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);
    	 }
    
         length -= 1;
         length -= 16;
         length -= count;
         stream += count;
      }
    #if TRACE_PARAM
      fprintf(param_trace,"< DHT marker
    ");
      fflush(param_trace);
    #endif
      return 0;
    }

    parse_DQT()用于解析DQT标签:

    //解析Define quantization table
    static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
    {
      int qi;
      float *table;
      const unsigned char *dqt_block_end;
    
    //------------------------------------------------
        int j,k;
    	char *temp;
        FILE *fp;
    //------------------------------------------------
    #if TRACE_PARAM
      fprintf(param_trace,"> DQT marker
    ");
      fflush(param_trace);
    #endif
      //该Segment末尾
      dqt_block_end = stream + be16_to_cpu(stream);
      //跳过标签length(2字节)
      stream += 2;	/* Skip length */
      //没到末尾
      while (stream < dqt_block_end)
       {
    	//跳过该Segment的第1个字节,QT信息
    	//precision: 00  (Higher 4 bit)
    	//index: 00  (Lower 4 bit) 
    	//qi取1,第1张量化表(例如,亮度表),取2,第2张量化表(例如,色度表)
         qi = *stream++;
    #if SANITY_CHECK
         if (qi>>4)
           snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported
    ");
         if (qi>4)
           snprintf(error_string, sizeof(error_string),"No more 4 quantization table is supported (got %d)
    ", qi);
    #endif
    	 //table指向jdec_private的Q_tables数组,为了在其中写入数据
         table = priv->Q_tables[qi];
    	 //注意:一次搞定整张!写入
    	 //需要对数值进行变换!cos(k*PI/16) * sqrt(2)
    	 //这样才能得到离散余弦变换的系数
         build_quantization_table(table, stream);
    //----------------------------------------------------------
    
    		temp=(char *)stream;
    		//fp = fopen("DQT.txt", "a+");
    		//fwrite(temp, 64, 1, fp);
    		char temp_str1[MAX_URL_LENGTH]={0};
    		char temp_str2[MAX_URL_LENGTH]={0};
    		for(j=0;j<64;j++){
    			sprintf(temp_str2,"%d ",temp[j]);
    			strcat(temp_str1,temp_str2);
    			//fprintf(fp,"%d ",temp[j]);
    		}
    		//计数
    		char temp_str3[MAX_URL_LENGTH]={0};
    		sprintf(temp_str3,"量化表【%d】",priv->DQT_table_num);
    		priv->dlg->AppendBInfo("DQT",temp_str3,temp_str1,"JPEG格式文件的量化表,一般来说第一张是亮度的,后面是色度的");
    		priv->DQT_table_num++;
    		//fprintf(fp,"
    -----------------------
    ");
    		//fclose(fp);
    #if TRACE_PARAM
    		for(j=0;j<8;j++){
    	 		for(k=0;k<8;k++){
    				fprintf(param_trace,"%d ",temp[j*8+k]);
    			}
    			fprintf(param_trace,"
    ");
    		}
    		fprintf(fp,"
    -----------------------
    ");
    	 fflush(param_trace);
    #endif
    //----------------------------------------------------------
    	 //完事了!
         stream += 64;
       }
    #if TRACE_PARAM
      fprintf(param_trace,"< DQT marker
    ");
      fflush(param_trace);
    #endif
      return 0;
    }

    其他标签的解析不一一列举。

    待续未完。。。


    主页:http://www.saillard.org/programs_and_patches/tinyjpegdecoder/

    源代码下载:http://download.csdn.net/detail/leixiaohua1020/6383115

  • 相关阅读:
    d3.js(v5.7)的比例尺以及坐标轴
    d3.js(v5.7)的node与数据匹配(自动匹配扩展函数)
    d3.js(v5.7)的attr()函数完善(添加obj支持)
    d3.js入门之DOM操作
    d3.js入门学习
    最近在写个人网站,忙碌中。。。
    构建vue项目(vue 2.x)时的一些配置问题(持续更新)
    Python之元组
    Python之列表
    Python之字符串
  • 原文地址:https://www.cnblogs.com/leixiaohua1020/p/3902115.html
Copyright © 2020-2023  润新知