数据压缩实验五——JPEG解码器

该博客主要介绍了数据压缩实验中的JPEG解码器,旨在掌握JPEG编解码系统的基本原理。实验内容包括理解JPEG编码过程,解码是编码的逆操作。实验步骤涉及代码实现,如添加代码将JPG转化为YUV,使用特定结构体存储解码信息,并提供了量化表和Huffman表的输出示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据压缩实验五——JPEG解码器
一、实验目的
掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。
二、实验内容
1.JPEG编解码原理
在这里插入图片描述
JPEG编码的过程如上图所示。解码是编码的逆过程。

  1. JPEG格式
    在这里插入图片描述
    打开一个JPEG文件:
    在这里插入图片描述
    在这里插入图片描述
    三、实验步骤
    代码实现:
    在write_yuv中添加的代码
static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
  FILE *F;
  char temp[1024];

  snprintf(temp, 1024, "%s.Y", filename);
  F = fopen(temp, "wb");
  fwrite(components[0], width, height, F);
  fclose(F);
  snprintf(temp, 1024, "%s.U", filename);
  F = fopen(temp, "wb");
  fwrite(components[1], width*height/4, 1, F);
  fclose(F);
  snprintf(temp, 1024, "%s.V", filename);
  F = fopen(temp, "wb");
  fwrite(components[2], width*height/4, 1, F);
  fclose(F);
  //添加的代码
  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);
}

将JPG转化为yuv打开
在这里插入图片描述

三个重要结构:
struct huffman_table用于存储haffman表

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 用于存储当前8×8像块中有关解码的信息

struct component 
{
  unsigned int Hfactor; // 水平采样因子
  unsigned int Vfactor; // 垂直采样因子
  float* Q_table;   // 指向该8×8块使用的量化表
  struct huffman_table *AC_table;   // 指向该块使用的AC Huffman表
  struct huffman_table *DC_table;   // 指向该块使用的DC Huffman表
  short int previous_DC;    // 前一个块的直流DCT系数
  short int DCT[64];    // DCT系数数组
    
#if SANITY_CHECK
  unsigned int cid;
#endif
};

struct jdec_private定义了指向三个components的指针和三个components结构体;定义了图像的宽高、码流长度、始末指针,还有三张量化表(实际只用到两张,亮度+色度)、DC Huffman表和AC Huffman表各四张。

struct jdec_private
{
  /* Public variables */
  uint8_t *components[COMPONENTS];  /* 分别指向YUV三个分量的三个指针 */
  unsigned int width, height;	/* 图像宽高 */
  unsigned int flags;

  /* Private variables */
  const unsigned char *stream_begin, *stream_end;
  unsigned int stream_length;

  const unsigned char *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];
};

TRACE:将TRACE设置为1的时候打开,设置为0的时候关闭;
在代码中任意地方进行编译TRACE文件输出调试信息,方便检查。

输出:

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;
  //修改
  int m,n;
  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,*zz1=zigzag;
 
  for (i=0; i<8; i++) {
     for (j=0; j<8; j++) {
       *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
     }
   }

#if TRACE 
  for(m=0;m<8;m++)
  {
	  for(n=0;n<8;n++)
	  {
		  fprintf(p_trace,"%d ",ref_table[*zz1++]);
	  }
	  fprintf(p_trace,"\n");
  }
#endif
}

量化表:
在这里插入图片描述

huffman表节选:
在这里插入图片描述

输出DC,AC图像

/* tinyjpeg.h中添加 */

    FILE* dcImgFilePtr; // DC图像文件指针
    FILE* acImgFilePtr; // AC图像文件指针

tinyjpeg.c中添加

int tinyjpeg_decode(struct jdec_private* priv, int pixfmt)
{ 

    unsigned char* dcImgBuff;
    unsigned char* acImgBuff;
    unsigned char* uvBuff = 128;
    int count = 0;
  for (y = 0; y < priv->height / ystride_by_mcu; y++) {
        ...
        for (x = 0; x < priv->width; x += xstride_by_mcu) {
            decode_MCU(priv);

            dcImgBuff = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4 + 0.5);  // DCT[0]为DC系数;DC系数范围-512~512;变换到0~255
            acImgBuff = (unsigned char)(priv->component_infos->DCT[1] + 128);   // 选取DCT[1]作为AC的observation;+128便于观察
            fwrite(&dcImgBuff, 1, 1, dcImgFilePtr);
            fwrite(&acImgBuff, 1, 1, acImgFilePtr);
            count++;
						...
                }
            }
        }
    }
for (int i = 0; i < count / 4 * 2; i++) {
        fwrite(&uvBuff, sizeof(unsigned char), 1, dcImgFilePtr);
        fwrite(&uvBuff, sizeof(unsigned char), 1, acImgFilePtr);
    }
    return 0;
}

loadjpeg.c中添加

int main(int argc, char *argv[]) {
///
  const char* dcImgFileName = "test_decoded_dc.yuv";    // DC图像文件名
  const char* acImgFileName = "test_decoded_ac.yuv";    // AC图像文件名

  fopen_s(&dcImgFilePtr, dcImgFileName, "wb");    // 打开DC图像文件
  fopen_s(&acImgFilePtr, acImgFileName, "wb");    // 打开AC图像文件
  /* Addition ended */

  fclose(dcImgFilePtr);
  fclose(acImgFilePtr);

  return 0;
}

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值