当前位置:网站首页>Experiment 5 JPEG

Experiment 5 JPEG

2022-07-23 07:58:00 Myster_ KID

JPEG Coding and decoding principle and code implementation


JPEG(Joint Photographic Experts Group) Is the abbreviation of the joint imaging expert group . The organization started from 1986 In, it officially began to formulate the compression and coding standard of still digital images , This standard applies to 1992 Officially adopted in , be called JPEG standard .JPEG It is the first international standard for digital image compression , It is not only suitable for still image compression , Yes Intra frame compression of TV image sequences is also often used JPEG Algorithm , therefore JPEG It is a general standard with a wide range of applications .

One 、JPEG The principle of encoding and decoding

The basic block diagram of encoding and decoding is :
 Insert picture description here

Coding principle

JPEG The standard itself does not specify a specific color space , Just encode the components of each color separately . In order to reduce the correlation between components , Reduce data redundancy , Usually RGB Convert color space to YUV To encode each component .

[ Y U V ] = [ 0.257 0.504 0.098 − 0.148 − 0.291 0.439 0.439 − 0.368 − 0.071 ] × [ R G B ] + [ 16 128 128 ] \begin{bmatrix} Y \\ U \\ V \end{bmatrix}= \begin{bmatrix} 0.257 & 0.504 & 0.098 \\ -0.148 & -0.291 & 0.439 \\ 0.439 & -0.368 & -0.071 \end{bmatrix} \times \begin{bmatrix} R \\ G \\ B \end{bmatrix} + \begin{bmatrix} 16 \\ 128 \\ 128 \end{bmatrix} YUV=0.2570.1480.4390.5040.2910.3680.0980.4390.071×RGB+16128128

[ R G B ] = [ 1.164 − 0.002 1.596 1.164 − 0.391 − 0.813 1.164 2.018 − 0.001 ] × [ Y − 16 U − 128 V − 128 ] \begin{bmatrix} R \\ G \\ B \end{bmatrix}= \begin{bmatrix} 1.164 & -0.002 & 1.596 \\ 1.164 & -0.391 & -0.813 \\ 1.164 & 2.018 & -0.001 \end{bmatrix} \times \begin{bmatrix} Y-16 \\ U-128 \\ V-128 \end{bmatrix} RGB=1.1641.1641.1640.0020.3912.0181.5960.8130.001×Y16U128V128

1. Level offset The zero bias level moves down

The function of this step is , The average brightness of image content is high , take 0 The level moves to the middle , The average brightness decreases , Easy DCT After transformation and quantization, the DC coefficient is greatly reduced , This reduces the amount of data .

First pair 8×8 The zero bias level of the image block is shifted downward (Level Offset), That is, the gray level is 2 n 2^n 2n The pixel , By subtracting 2 n − 1 2^{n-1} 2n1, Change an unsigned integer to a signed number , Change its value range to [ − 2 n − 1 , 2 n − 1 − 1 ] [-2^{n-1},2^{n-1}-1] [2n1,2n11], The probability of occurrence of a number with a large absolute value is greatly reduced , Improve coding efficiency .

2. 8x8 DCT

This step is mainly used to remove the correlation between image data , It is convenient for the quantization process to remove the spatial redundancy of image data .DCT It is a lossless transformation , Also cannot compress the image , The purpose of this is to prepare for the next step of quantification .

Divide the image into 8×8 Image block of ; For width ( high ) No 8 An integer multiple of the image , Fill with image edge pixels , So as not to change the spectrum distribution . Then, for each sub block DCT(Discrete Cosine Transform, Discrete cosine transform ), To achieve energy concentration and decorrelation , Easy to remove spatial redundancy , Improve coding efficiency . What needs special emphasis is ,DCT It is a lossless transformation , Also cannot compress the image , The purpose of this is to prepare for the next step of quantification .
F ( u , v ) = C f ( x , y ) C T = C f ( x , y ) C − 1 F(u, v)=Cf(x,y)C^T=Cf(x,y)C^{-1} F(u,v)=Cf(x,y)CT=Cf(x,y)C1
among , C C C yes 8 × 8 8\times8 8×8 Of DCT Transform two-dimensional kernel matrix , f ( x , y ) f ( x , y ) f(x,y) It's raw data .
because DCT Transformation is an orthogonal transformation , There are C T = C − 1 C^T=C^{-1} CT=C1

3. Uniform scalar quantization Uniform nominal quantization

The quantizer is mainly a matrix quantization designed by using the characteristics of human vision DCT coefficient , Reduce visual redundancy . take DCT Temporary results after transformation , Divide by the respective quantization steps and round to the nearest , Get the quantization coefficient .JPEG The system specifies the quantization tables of luminance component and chrominance component respectively , The quantization step of chrominance component is larger than that of luminance component . In the quantification step ,JPEG Medium flat type (Midtread) Uniform quantizer .

actually JPEG Compression coding algorithm , There are not many parts that can be adjusted :DCT、 The two main steps of entropy coding are completely determined , In fact, only quantification can be adjusted , Quantification is also natural JPEG The core of compression coding algorithm . Besides , Quantization is the only step in the coding process that will introduce error and compression .
 Insert picture description here
JPEG The standard adopts medium flat uniform quantification , Because human eyes are much more sensitive to low-frequency components than high-frequency components , And the sensitivity to brightness is much higher than chromaticity , Therefore, the standard has designed 2 A quantitative table ( brightness 、 One color difference ), Quantize the low frequency , High frequency rough quantization , Brightness quantization , Color difference rough quantization , To reduce visual redundancy .

The quantization matrix is not fixed , It can be adjusted according to different quality requirements .

4. DC Coefficient differential coding

We noticed that ,8×8 Like a block passing DCT Later obtained DC The coefficient has two characteristics : First, the value of the coefficient is large ; Second, adjacent image blocks DC The coefficient difference is not big ( There is redundancy ). According to this feature ,JPEG The standard adopts DPCM( Differential pulse code modulation ), To quantify between adjacent image blocks DC Coefficient Difference value DIFF Encoding .
D I F F k = D C k − D C k − 1 DIFF_k=DC_k-DC_{k-1} DIFFk=DCkDCk1
Yes DPCM Calculated later DIFF The difference is used Huffman code , Divide them into categories , Similar to index Golomb code ( It's just Golomb It's a dollar code + Fixed length code ), That's the category ID Use standard Huffman coding , In class index uses fixed length code ( Nature code ).

5. AC coefficient Zig-Zag Scanning and run length coding

because DCT after , Most of the coefficients are concentrated in the upper left corner , That is, the low-frequency component area , So we use Zig-Zag( Zigzag ) scanning , Read out the coefficients in the order of high and low frequencies , In this way, there can be many opportunities to connect zero , To facilitate RLE(Run Length Encoding, Run length encoding ), Especially in the end , If it's all zero , give EOB (End of Block) that will do .
 Insert picture description here
 Insert picture description here

for example 0, -2, -1, -1, -1, 0, 0, -1, EOB Expressed as :(1, -2), (0, -1), (0, -1), (0, -1), (2, -1), EOB.

6. Huffman code

Yes DC coefficient DPCM The result and AC coefficient RLE The result of Huffman code , Category ID Adopt unary code , The intra class index is encoded by fixed length code . With DC Coefficient as an example :
 Insert picture description here

For example, the difference DIFF = 3 , Corresponding category ID = 2, Intra class index = 3, Then the codeword is 100 11.

Common brightness DC、 brightness AC、 chromatic aberration DC、 chromatic aberration AC Four Huffman Encoding table .


Decoding principle

Decoding is the reverse process of encoding , Decoding system block diagram :
 Insert picture description here


Two 、JPEG File format analysis

JPEG Document to segment Organization in the form of , Each of them segment With a marker Start , And each marker in 0xFF And a marker Start with the identifier of , This was followed by 2 Bytes of marker length ( It doesn't contain marker The first two bytes of ) And corresponding payload(SOI and EOI marker Only 2 Byte identifier ).

Be careful , Successive 0xFF Bytes are not marker The starting sign of , It's a special character for filling .

Besides , In the part ,0xFF If later 0x00, Then skip this byte and do not process .

common marker as follows :

Short nameBytesPayloadNameComments
SOI0xFF, 0xD8noneStart Of Image
SOF00xFF, 0xC0variable sizeStart Of Frame (baseline DCT)Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components, and component subsampling (e.g., 4:2:0).
SOF20xFF, 0xC2variable sizeStart Of Frame (progressive DCT)Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components, and component subsampling (e.g., 4:2:0).
DHT0xFF, 0xC4variable sizeDefine Huffman Table(s)Specifies one or more Huffman tables.
DQT0xFF, 0xDBvariable sizeDefine Quantization Table(s)Specifies one or more quantization tables.
DRI0xFF, 0xDD4 bytesDefine Restart IntervalSpecifies the interval between RSTn markers, in Minimum Coded Units (MCUs). This marker is followed by two bytes indicating the fixed size so it can be treated like any other variable size segment.
SOS0xFF, 0xDAvariable sizeStart Of ScanBegins a top-to-bottom scan of the image. In baseline DCT JPEG images, there is generally a single scan. Progressive DCT JPEG images usually contain multiple scans. This marker specifies which slice of data it will contain, and is immediately followed by entropy-coded data.
RSTn0xFF, 0xDnnoneRestartInserted every r rr macroblocks, where r rr is the restart interval set by a DRI marker. Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7.
APPn0xFF, 0xEnvariable sizeApplication-specificFor example, an Exif JPEG file uses an APP1 marker to store metadata, laid out in a structure based closely on TIFF.
COM0xFF, 0xFEvariable sizeCommentContains a text comment.
EOI0xFF, 0xD9noneEnd Of Image

Use Synalyze it! Pro App Perform binary analysis , And for some marker Make some brief descriptions of the fields .

 Insert picture description here
Experimental picture test.jpg(1024x1024)

  • SOI And EOI
     Insert picture description here

  • APP0
     Insert picture description here

  • DQT
     Insert picture description here

  • SOF0
     Insert picture description here

  • DHT
     Insert picture description here

  • SOS
     Insert picture description here

  • SOS
     Insert picture description here


3、 ... and 、 Code implementation

1. Hierarchical structure

JPEG A major feature of compression coding algorithm is the idea of hierarchical structure design , The following describes the design intent of the three main structures :
struct huffman_table: Storage Huffman clock .

/* tinyjpeg-internal.h */

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: Save current 8×8 Information about decoding in the image block .

/* tinyjpeg-internal.h */

struct component 
{
    
  unsigned int Hfactor; //  Horizontal sampling factor 
  unsigned int Vfactor; //  Vertical sampling factor 
  float* Q_table;   //  Point to 8×8 Quantization table used by the block 
  struct huffman_table *AC_table;   //  Point to the AC Huffman surface 
  struct huffman_table *DC_table;   //  Point to the DC Huffman surface 
  short int previous_DC;    //  DC of the previous block DCT coefficient 
  short int DCT[64];    // DCT Coefficient array 
    
#if SANITY_CHECK
  unsigned int cid;
#endif
};

struct jdec_private:JPEG Data flow structure , Used to store JPEG Image width and height 、 Data stream pointer 、Huffman Code table and other contents , And includes struct huffman_table and struct component.

/* tinyjpeg-internal.h */

struct jdec_private
{
    
  /* Public variables */
  uint8_t *components[COMPONENTS];  /*  Point to respectively YUV Three pointers of three components  */
  unsigned int width, height;	/*  Image width and height  */
  unsigned int flags;

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

  const unsigned char *stream;	/*  Pointer to the current data 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];
};


2. Decoding process

/*  Read JPEG file , decode , And store the results  */
int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{
    
  FILE *fp;
  unsigned int length_of_file;  //  file size 
  unsigned int width, height;   //  Image width 、 high 
  unsigned char *buf;   //  buffer 
  struct jdec_private *jdec;
  unsigned char *components[3];

  /*  take JPEG Read in buffer  */
  fp = fopen(infilename, "rb");
  if (fp == NULL)
    exitmessage("Cannot open filename\n");
  length_of_file = filesize(fp);
  buf = (unsigned char *)malloc(length_of_file + 4);
  if (buf == NULL)
    exitmessage("Not enough memory for loading file\n");
  fread(buf, length_of_file, 1, fp);
  fclose(fp);

  /* Decompress it */
  jdec = tinyjpeg_init();   //  initialization 
  if (jdec == NULL)
    exitmessage("Not enough memory to alloc the structure need for decompressing\n");

  /*  analysis JPEG The file header  */
  if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)
    exitmessage(tinyjpeg_get_errorstring(jdec));

  /*  Calculate the width and height of the image  */
  tinyjpeg_get_size(jdec, &width, &height);

  snprintf(error_string, sizeof(error_string),"Decoding JPEG image...\n");
  if (tinyjpeg_decode(jdec, output_format) < 0) //  Decode the actual data 
    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 the output file in the specified 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;
    case TINYJPEG_FMT_GREY:
      write_pgm(outfilename, width, height, components);
      break;
   }

  /* Only called this if the buffers were allocated by tinyjpeg_decode() */
  tinyjpeg_free(jdec);
  /* else called just free(jdec); */

  free(buf);
  return 0;
}

3. Core module

analysis JPEG The file header

int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
{
    
  int ret;

  /* Identify the file */
  if ((buf[0] != 0xFF) || (buf[1] != SOI))  // JPEG The document must be marked with SOI marker As the starting point , Otherwise it is not legal JPEG file 
    snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n");

  priv->stream_begin = buf+2;   //  Skip identifier 
  priv->stream_length = size-2;
  priv->stream_end = priv->stream_begin + priv->stream_length;

  ret = parse_JFIF(priv, priv->stream_begin);   //  Start parsing JPEG

  return ret;
}

analysis marker identifier

/*  Omit trace part  */

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 */
  while (sos_marker_found == 0)
   {
    
     if (*stream++ != 0xff)
       goto bogus_jpeg_format;
     /* Skip any padding ff byte (this is normal) */
     while (*stream == 0xff)
       stream++;

     marker = *stream++;    //  obtain 0xFF The last byte ( That is to say marker identifier )
     chuck_len = be16_to_cpu(stream);   // length Field 
     next_chunck = stream + chuck_len;
     switch (marker)    //  Judge marker type 
      {
    
       case SOF:
	 if (parse_SOF(priv, stream) < 0)
	   return -1;
	 break;
       case DQT:
	 if (parse_DQT(priv, stream) < 0)
	   return -1;
	 break;
       case SOS:
	 if (parse_SOS(priv, stream) < 0)
	   return -1;
	 sos_marker_found = 1;
	 break;
       case DHT:
	 if (parse_DHT(priv, stream) < 0)
	   return -1;
	 dht_marker_found = 1;
	 break;
       case DRI:
	 if (parse_DRI(priv, stream) < 0)
	   return -1;
	 break;
       default:
	 break;
      }

     stream = next_chunck;  //  Analyze the next marker
   }

  if (!dht_marker_found) {
    
    build_default_huffman_tables(priv);
  }
  return 0;
    
bogus_jpeg_format:
  return -1;
}

analysis DQT

static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
    
  int qi;   //  Quantification table ID
  float *table; //  Point to the quantification table 
  const unsigned char *dqt_block_end;   //  Point to the end of the quantification table 
  dqt_block_end = stream + be16_to_cpu(stream);
  stream += 2;	//  Skip the length field 

  while (stream < dqt_block_end)	//  Check whether there is a quantification table 
   {
    
     qi = *stream++;    //  Assign the coefficients in the quantification table to qi
     table = priv->Q_tables[qi];
     build_quantization_table(table, stream);
     stream += 64;
   }
  return 0;
}

Establish a quantitative table


static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
    
  int i, j;
  static const double aanscalefactor[8] = {
    
     1.0, 1.387039845, 1.306562965, 1.175875602,
     1.0, 0.785694958, 0.541196100, 0.275899379
  };    //  The scaling factor 
  const unsigned char *zz = zigzag;

  for (i=0; i<8; i++) {
    
     for (j=0; j<8; j++) {
    
       *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
     }
   }
}

analysis DHT

static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{
    
  unsigned int count, i;
  unsigned char huff_bits[17];  //  Code length 1~16
  int length, index;

  length = be16_to_cpu(stream) - 2;
  stream += 2;	//  Skip the length field 

  while (length>0) {
        //  Check if there are any tables 
     index = *stream++;

     /* We need to calculate the number of bytes 'vals' will takes */
     huff_bits[0] = 0;
     count = 0;
     for (i=1; i<17; i++) {
    
	    huff_bits[i] = *stream++;
	    count += huff_bits[i];
     }

     if (index & 0xf0 )
       build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);  //  Establish a communication table 
     else
       build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);  //  Establish DC meter 

     length -= 1;
     length -= 16;
     length -= count;
     stream += count;
  }
  return 0;
}

establish Huffman clock

static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)  // bits Is the number of digital words in each digit ,val by Huffval,table For the... To be established Huffman surface 
{
    
  unsigned int i, j, code, code_size, val, nbits;
  unsigned char huffsize[HUFFMAN_BITS_SIZE + 1];    //  The length of each codeword 
  unsigned char* hz;
  unsigned int huffcode[HUFFMAN_BITS_SIZE + 1]; //  Every codeword 
  unsigned char* hc;
  int next_free_entry;

  /*  initialization  */
  hz = huffsize;
  for (i=1; i<=16; i++)
   {
    
     for (j=1; j<=bits[i]; j++)
       *hz++ = i;
   }
  *hz = 0;

  memset(table->lookup, 0xff, sizeof(table->lookup));
  for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++)
    table->slowtable[i][0] = 0;

  code = 0;
  hc = huffcode;
  hz = huffsize;
  nbits = *hz;
  while (*hz)
   {
    
     while (*hz == nbits)
      {
    
	*hc++ = code++;
	hz++;
      }
     code <<= 1;
     nbits++;
   }

  /* * Build the lookup table, and the slowtable if needed. */
  next_free_entry = -1;
  for (i=0; huffsize[i] != 0; i++)
   {
    
     /*  obtain Huffval、 Every codeword 、 The length of each codeword */
     val = vals[i];
     code = huffcode[i];
     code_size = huffsize[i];
     table->code_size[val] = code_size; // Huffval( A weight )
     if (code_size <= HUFFMAN_HASH_NBITS)
      {
    
	/* * Good: val can be put in the lookup table, so fill all value of this * column with value val */
	int repeat = 1UL<<(HUFFMAN_HASH_NBITS - code_size);
	code <<= HUFFMAN_HASH_NBITS - code_size;
	while ( repeat-- )
	  table->lookup[code++] = val;  //  obtain Huffval Length lookup table 
      }
     else
      {
    
	/* Perhaps sorting the array will be an optimization */
	uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1];
	while(slowtable[0])
	  slowtable+=2;
	slowtable[0] = code;
	slowtable[1] = val;
	slowtable[2] = 0;
	/* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */
      }
   }
}

analysis SOS


static int parse_SOS(struct jdec_private *priv, const unsigned char *stream)
{
    
  unsigned int i, cid, table;
  unsigned int nr_components = stream[2];   //  Number of color components 

  stream += 3;
  for (i=0;i<nr_components;i++) {
    
     /*  Get used Huffmann Table number  */
     cid = *stream++;
     table = *stream++;
      
     priv->component_infos[i].AC_table = &priv->HTAC[table&0xf];
     priv->component_infos[i].DC_table = &priv->HTDC[table>>4];
  }
  priv->stream = stream+3;
  return 0;
}

analysis SOF

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;

  print_SOF(stream);

  height = be16_to_cpu(stream+3);   //  Height of the image 
  width  = be16_to_cpu(stream+5);   //  The width of the image 
  nr_components = stream[7];    //  Number of color components 

  stream += 8;
  for (i=0; i<nr_components; i++) {
    
     /*  Analyze each component separately  */
     cid = *stream++;   //  component ID
     sampling_factor = *stream++;   //  Sampling factor 
     Q_table = *stream++;
     c = &priv->component_infos[i];
     c->Vfactor = sampling_factor&0xf;  //  Vertical sampling factor 
     c->Hfactor = sampling_factor>>4;   //  Horizontal sampling factor 
     c->Q_table = priv->Q_tables[Q_table];  //  Quantification table used 
  }
  priv->width = width;
  priv->height = height;

  return 0;
}

analysis JPEG Actual data

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)  // pixfmt For output format 
{
    
  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) {
    
     /*  According to different output formats MCU */
     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:
       return -1;
  }

  xstride_by_mcu = ystride_by_mcu = 8;  //  initialization :MCU The width and height of are 8px(4:4:4)
  if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {
    
     /*  level 、 The vertical sampling factors are 1 */
     decode_MCU = decode_mcu_table[0];  // MCU contain 1 individual Y
     convert_to_pixfmt = colorspace_array_conv[0];
  } else if (priv->component_infos[cY].Hfactor == 1) {
    
     /*  The horizontal sampling factor is 1, The vertical sampling factor is 2 */
     decode_MCU = decode_mcu_table[1];  // MCU contain 2 individual Y
     convert_to_pixfmt = colorspace_array_conv[1];
     ystride_by_mcu = 16;   // MCU high 16px, wide 8px
  } else if (priv->component_infos[cY].Vfactor == 2) {
    
     /*  level 、 The vertical sampling factors are 2 */
     decode_MCU = decode_mcu_table[3];  // MCU contain 4 individual Y
     convert_to_pixfmt = colorspace_array_conv[3];
     xstride_by_mcu = 16;   // MCU wide 16px
     ystride_by_mcu = 16;   // MCU high 16px
  } else {
    
     /*  The horizontal sampling factor is 2, The vertical sampling factor is 1 */
     decode_MCU = decode_mcu_table[2];  // MCU contain 2 individual Y
     convert_to_pixfmt = colorspace_array_conv[2];
     xstride_by_mcu = 16;   // MCU wide 16px, high 8px
  }

  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;

  /*  Decode each image block (8x8 / 8x16 / 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);
	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;
	    }
	 }
      }
   }

  return 0;
}


analysis MCU

/* * Decode a 2x2 * .-------. * | 1 | 2 | * |---+---| * | 3 | 4 | * `-------' */
static void decode_MCU_2x2_3planes(struct jdec_private *priv)
{
    
  // Y
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+8, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2, 16);
  process_Huffman_data_unit(priv, cY);
  IDCT(&priv->component_infos[cY], priv->Y+64*2+8, 16);

  // 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);
}

Output the file as .yuv Format

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);
}

Output yuv The documents are as follows :
 Insert picture description here
Output quantization matrix and Huffman clock

/* tinyjpeg.h Add  */
/*  Declare global variables  by S.Z.Zheng */
    FILE* qtabFilePtr;    //  Quantization table file pointer 
/*  End of statement  */


/* tinyjpeg.c Add */
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
    
  ...
  for (i=0; i<8; i++) {
    
      for (j=0; j<8; j++) {
    
          /* Added by S.Z.Zheng */
          fprintf(qtabFilePtr, "%-6d", ref_table[*zz]);
          if (j == 7) {
    
              fprintf(qtabFilePtr, "\n");
          }
          /* Addition ended */
		 ...
      }
  }
  fprintf(qtabFilePtr, "\n\n"); // Added by S.Z.Zheng
}

static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
    
    ...
    while (stream < dqt_block_end)    //  Check whether there is a quantification table 
    {
    
        ...
        fprintf(qtabFilePtr, "Quantisation table [%d]:\n", qi);   //  Quantification table ID(added by S.Z.Zheng)
        build_quantization_table(table, stream);
        ...
    }
	...
}


/* loadjpeg.c Add  */
int main(int argc, char *argv[])
{
    
  ...
  /* Added by S.Z.Zheng */
  const char* qtabFileName = "q_table.txt"; //  Quantization table file name 
  fopen_s(&qtabFilePtr, qtabFileName, "wb");    //  Open file 
  /* Addition ended */
  ...
  fclose(qtabFilePtr);  // Added by S.Z.Zheng
  return 0;
}

 Insert picture description here
Output DC、AC Images

/* tinyjpeg.h Add  */
/*  Declare global variables  by S.Z.Zheng */
		...
    FILE* dcImgFilePtr; // DC Image file pointer 
    FILE* acImgFilePtr; // AC Image file pointer 
/*  End of statement  */

/* tinyjpeg.c Add  */
int tinyjpeg_decode(struct jdec_private* priv, int pixfmt)
{
    
		...
    /* Added by S.Z.Zheng */
    unsigned char* dcImgBuff;
    unsigned char* acImgBuff;
    unsigned char* uvBuff = 128;
    int count = 0;
    /* Addition ended*/

    /*  Decode each image block (8x8 / 8x16 / 16x16) */
    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] by DC coefficient ;DC Coefficient range -512~512; Change to 0~255
            acImgBuff = (unsigned char)(priv->component_infos->DCT[1] + 128);   //  selection DCT[1] As AC Of observation;+128 Convenient for observation 
            fwrite(&dcImgBuff, 1, 1, dcImgFilePtr);
            fwrite(&acImgBuff, 1, 1, acImgFilePtr);
            count++;
						...
                }
            }
        }
    }
		...
    /* Added by S.Z.Zheng */
    for (int i = 0; i < count / 4 * 2; i++) {
    
        fwrite(&uvBuff, sizeof(unsigned char), 1, dcImgFilePtr);
        fwrite(&uvBuff, sizeof(unsigned char), 1, acImgFilePtr);
    }
    /* Addition ended */
    return 0;
}

/* loadjpeg.c Add  */
int main(int argc, char *argv[]) {
    
	...
  /* Added by S.Z.Zheng */
  ...
  const char* dcImgFileName = "test_decoded_dc.yuv";    // DC Image file name 
  const char* acImgFileName = "test_decoded_ac.yuv";    // AC Image file name 
  ...
  fopen_s(&dcImgFilePtr, dcImgFileName, "wb");    //  open DC image file 
  fopen_s(&acImgFilePtr, acImgFileName, "wb");    //  open AC image file 
  /* Addition ended */
	...
  /* Added by S.Z.Zheng */
  ...
  fclose(dcImgFilePtr);
  fclose(acImgFilePtr);
  /* Addition Ended */

  return 0;
}

 Insert picture description here

原网站

版权声明
本文为[Myster_ KID]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/204/202207222136291231.html