数据压缩第七次作业

一、JPEG简介

JPEG( Joint Photographic Experts Group)即联合图像专家组,是用于连续色调静态图像压缩的一种标准,文件后缀名为.jpg或.jpeg,是最常用的图像文件格式。

其主要是采用预测编码(DPCM)、离散余弦变换(DCT)以及熵编码的联合编码方式,以去除冗余的图像和彩色数据,属于有损压缩格式,它能够将图像压缩在很小的储存空间,一定程度上会造成图像数据的损伤。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量降低,如果追求高品质图像,则不宜采用过高的压缩比例。

JPEG的性能,用质量与比特率之比来衡量,是相当优越的。

二、实验原理

三、解码过程

(1)读取文件
(2)解析 Segment Marker
①解析 SOI

②解析 APP0
③解析 DQT
④解析 SOF0
⑤解析 DHT
⑥解析 SOS

得到解析每个颜色分量的 DC 、 AC 值所使用的 Huffman 表序号(与 DHT
中序号对应)
(3)依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8
宏块的个数
(4)对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解
码)

(5)解析到 EOI,解码结束
(6)将 Y、Cb、Cr 转化为需要的色彩空间并保存。

四、代码实现

(一)结构体

①huffman_table:

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];
};
②component:

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 */
  short int DCT[64];        /* DCT coef */
#if SANITY_CHECK
  unsigned int cid;
#endif
};
③jdec_private

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];
 
  jmp_buf jump_state;
  /* Internal Pointer use for colorspace conversion, do not modify it !!! */
  uint8_t *plane[COMPONENTS];
 
};
(二)主函数

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     //设定trace,边解码边写入文件
  p_trace=fopen(TRACEFILE,"w");
  if (p_trace==NULL)
  {
      printf("trace file open error!");
  }
#endif
  if (argc < 3)
    usage();
 
  current_argument = 1;
  while (1)
   {
     if (strcmp(argv[current_argument], "--benchmark")==0)   //strcmp字符串比较,用于比较两个字符串,如果二者相等返回0
       benchmark_mode = 1;   //如果相等,说明设置了benchmark,因此benchmark_mode置1
     else
       break;
     current_argument++;
   }
 
  if (argc < current_argument+2)
    usage();
 
  input_filename = argv[current_argument];    //输入文件名,是argv[1]
  if (strcmp(argv[current_argument+1],"yuv420p")==0)    //argv[2]判断输出格式,这里选择yuv420p
    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");
  output_filename = argv[current_argument+2];   //输出文件名,设为argv[3]
 
  start_time = clock();    //开始
 
 
  if (benchmark_mode)   //是否多次解码,若设置了benchmark_mode就调用load_multiple_times,否则就调用convert_one_image
    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;
}
 

(三)DC图像的输出

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
  unsigned int x, y, xstride_by_mcu, ystride_by_mcu;
  unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];
  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;
 
  if (setjmp(priv->jump_state))
    return -1;
 
  /* To keep gcc happy initialize some array */
  bytes_per_mcu[1] = 0;
  bytes_per_mcu[2] = 0;
  bytes_per_blocklines[1] = 0;
  bytes_per_blocklines[2] = 0;
 
  decode_mcu_table = decode_mcu_3comp_table;
  switch (pixfmt) {
     case TINYJPEG_FMT_YUV420P:
       colorspace_array_conv = convert_colorspace_yuv420p;
       if (priv->components[0] == NULL)
     priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
       if (priv->components[1] == NULL)
     priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4);
       if (priv->components[2] == NULL)
     priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4);
       bytes_per_blocklines[0] = priv->width;
       bytes_per_blocklines[1] = priv->width/4;
       bytes_per_blocklines[2] = priv->width/4;
       bytes_per_mcu[0] = 8;
       bytes_per_mcu[1] = 4;
       bytes_per_mcu[2] = 4;
       break;
 
     case TINYJPEG_FMT_RGB24:
       colorspace_array_conv = convert_colorspace_rgb24;
       if (priv->components[0] == NULL)
     priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
       bytes_per_blocklines[0] = priv->width * 3;
       bytes_per_mcu[0] = 3*8;
       break;
 
     case TINYJPEG_FMT_BGR24:
       colorspace_array_conv = convert_colorspace_bgr24;
       if (priv->components[0] == NULL)
     priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
       bytes_per_blocklines[0] = priv->width * 3;
       bytes_per_mcu[0] = 3*8;
       break;
 
     case TINYJPEG_FMT_GREY:
       decode_mcu_table = decode_mcu_1comp_table;
       colorspace_array_conv = convert_colorspace_grey;
       if (priv->components[0] == NULL)
     priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
       bytes_per_blocklines[0] = priv->width;
       bytes_per_mcu[0] = 8;
       break;
 
     default:
#if TRACE
         fprintf(p_trace,"Bad pixel format\n");
         fflush(p_trace);
#endif
       return -1;
  }
 
  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];
#if TRACE
     fprintf(p_trace,"Use decode 1x1 sampling\n");
     fflush(p_trace);
#endif
  } 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;
#if TRACE
     fprintf(p_trace,"Use decode 1x2 sampling (not supported)\n");
     fflush(p_trace);
#endif
  } 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;
#if TRACE 
     fprintf(p_trace,"Use decode 2x2 sampling\n");
     fflush(p_trace);
#endif
  } else {
     decode_MCU = decode_mcu_table[2];
     convert_to_pixfmt = colorspace_array_conv[2];
     xstride_by_mcu = 16;
#if TRACE
     fprintf(p_trace,"Use decode 2x1 sampling\n");
     fflush(p_trace);
#endif
  }
 
  resync(priv);
 
  /* Don't forget to that block can be either 8 or 16 lines */
  bytes_per_blocklines[0] *= ystride_by_mcu;
  bytes_per_blocklines[1] *= ystride_by_mcu;
  bytes_per_blocklines[2] *= ystride_by_mcu;
 
  bytes_per_mcu[0] *= xstride_by_mcu/8;
  bytes_per_mcu[1] *= xstride_by_mcu/8;
  bytes_per_mcu[2] *= xstride_by_mcu/8;
 
  //添加定义
  FILE* DCFile = fopen("DC.yuv", "wb");
  FILE* ACFile = fopen("AC.yuv", "wb");
  unsigned char* uv_buf = 128;
  unsigned char* dc_buf, * ac_buf;
  double dc_freq[256] = { 0 }, ac_freq[256] = { 0 };
  /* 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)
      {
    decode_MCU(priv);
 使用的yuv文件如下:

 


    //判断是否能打开yuv文件
    if (DCFile == NULL)
    {
        printf("Fail to open DC\n");
        return 0;
    }
    if (ACFile == NULL)
    {
        printf("Fail to open AC\n");
        return 0;
    }
 
   
    //获取DC与AC系数
    dc_buf = (unsigned char)((priv->component_infos->DCT[0] + 512) / 4.0);
    fwrite(&dc_buf, 1, 1, DCFile);
    ac_buf = (unsigned char)(priv->component_infos->DCT[1] + 128);
    fwrite(&ac_buf, 1, 1, ACFile);
    
    //统计每个值的出现次数
    for (int i = 0; i < 256; i++)
    {
        if (dc_buf == i)
            dc_freq[i]++;
        //printf("add dc succeed\n");
        if (ac_buf == i)
            ac_freq[i]++;
        //printf("add ac succeed\n");
    }
 
    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;
        }
     }
      }
   }
#if TRACE
  fprintf(p_trace,"Input file size: %d\n", priv->stream_length+2);
  fprintf(p_trace,"Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2);
  fflush(p_trace);
#endif
 
  for (int i = 0; i < priv->width * priv->height / 32; i++)
  {
      fwrite(&uv_buf, 1, 1, DCFile);
      fwrite(&uv_buf, 1, 1, ACFile);
  }
 
  fclose(DCFile);
  fclose(ACFile);
 
  //计算概率分布
  for (int i = 0; i < 256; i++)
  {
      dc_freq[i] = dc_freq[i] / (priv->width * priv->height / 64);
      ac_freq[i] = ac_freq[i] / (priv->width * priv->height / 64);
  }
 
  //输出概率分布
  FILE* ACfre = fopen("ac_freq.txt", "wb");
  if (ACfre == 0)
  {
      printf("Fail to open ac_freq.txt\n");
      return 0;
  }
 
 
  FILE* DCfre = fopen("dc_freq.txt", "wb");
  if (DCfre == 0)
  {
      printf("Fail to open dc_freq.txt\n");
      return 0;
  }
 
 
  return 0;
}

输出的dc图像:

输出的ac图像:

                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​

 

 (五)统计概率


int file_length = 0;
    FILE* DC_file = fopen("DC.yuv", "wb");
    FILE* AC_file = fopen("AC.yuv", "wb");
unsigned char dc;
         unsigned char ac;
         dc = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4);//DC系数的取值范围[-512,511],转换为[0,255]的范围
         ac = (unsigned char)(priv->component_infos->DCT[1] + 128);//AC系数的取值范围为[-128,127],将其转换为[0,255]的范围
         fwrite(&dc, 1, 1, DC_file);
         fwrite(&ac, 1, 1, AC_file);
         file_length++;
unsigned char uv = 128;
  for (int i = 0; i < file_length / 4 * 2; i++) {
      fwrite(&uv, 1, 1, DC_file);
      fwrite(&uv, 1, 1, AC_file);
  }
  fclose(DC_file);
  fclose(AC_file);
得到DC,AC图像概率:


 

 

 

 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值