Previous Table of Contents Next


Initialization

DCT.C has single initialization routine that is called for both compression and expansion. It first sets up the quantization matrix, using the quality parameter passed to it. This uses the simple formula for defining step sizes discussed earlier.

Once the quantization matrix is set up, the next step is to set up the cosine transform matrix and the transposed cosine transform matrix These matrices are used by the forward DCT and the inverse DCT, so they can be set up in a common routine. Setting them up involves nothing more than a simple translation of the formula shown in Figure 11.8.

The final step in initialization is to initialize the run-length encoding counters used on input and output. These values are used when either outputting or inputting codes, and they track the number of consecutive zero codes that have output or will be input.

 void Initialize( int quality )
 {
  int i;
  int j;

  for ( i = 0 ; i < N ; i++ )
   for (j = 0 ; j < N ; j++ )
    Quantum[ i ][ j ] = 1 + ( ( 1 + i + j ) * quality );
  for ( j = 0 ; j < N ; j++ ) {
   C[ 0 ][ j ] = 1.0 / sqrt( N );
   Ct[ j ][ 0 ] = C[ 0 ][ j ];
  }
  for ( i = 1 ; i < N ; i++ ) {
   for ( j = 0 ; j < N ; j++ ) {
    C[ i ][ j ] = sqrt( 2.0 / N ) *
           cos( ( 2 * j + 1 ) * i * pi / ( 2.0 * N ) ) ;
    Ct[ j ][ i ] = C[ i ][ j ];
   }
  }
  OutputRunLength = 0;
  InputRunLength = 0;
 }

The Forward DCT Routine

Despite the seeming complexity of the DCT, it is accomplished in a very short routine. All it does is first perform a matrix multiplication of the input pixel data matrix by the transposed cosine transform matrix and store the result in a temporary N-by-N matrix. Then the temporary matrix is multiplied by the cosine transform matrix, and the result is stored in the output matrix, which is passed back to the caller.

Note here that all input pixel values are scaled before being multiplied by the transposed cosine transform matrix. After being scaled, they have a range of -128 to 127 instead of the zero to 255 range they had before. This is consistent with the way the JPEG algorithm handles input data.

  void ForwardDCT( input, output )
  unsigned char *input[ N ];
  int output[ N ][ N ];
  {
   double temp[ N ][ N ];
   double temp1;
   int i;
   int j;
   int k;

  /*MatrixMultiply( temp, input, Ct ); */

   for ( i = 0 ; i < N ; i++ ) {
    for ( j = 0 ; j < N ; j++ ) {
     temp[ i ][ j ] = 0.0;
     for ( k = 0 ; k < N ; k++ )
      temp[ i ][ j ] += ( input[ i ][ k ] - 128 ) *
               Ct[ k ] [ j ];
    }
   }
  /*MatrixMultiply( output, C, temp ); */

   for ( i = 0 ; i < N ; i++ ) {
    for ( j = 0 ; j < N ; j++ ) {
     temp1 = 0.0;
     for ( k = 0 ; k < N ; k++ )
      temp1 += C[ i ][ k ] * temp[ k ][ j ];
     output[ i ][ j ] = ROUND( temp1 );
   }
  }
 }

Another point to observe is that we are dealing with several different data types here and a certain amount of care needs to be exercised so as not to cause problems during conversions. The input data coming from the pixel strip is unsigned character converted during the matrix multiplication to integer, then multiplied by a double. The result is stored in a double temporary matrix. Finally, the last matrix multiplication produces double values, which are then rounded to integers for storage in the output matrix. If everything goes as planned, the integers should be in the range of -1,024 to 1,023, and they are ready for quantization.

WriteDCTData()

This routine is responsible for ordering the DCT result matrix into the zigzag pattern, then quantizing the data. Both of these just involve table lookups of values that have been stored either during initialization or at compile time. Then, the quantized value is rounded to the nearest integer and sent to the routine that outputs codes.

  void WriteDCTData( BIT_FILE *output_file, output_data[ N ][ N ] )
  {
   int i;
   int row;
   int col;
   double result;

   for ( i = 0 ; i < ( N * N ) ; i++ ) {
    row = ZigZag[ i ].row;
    col = ZigZag[ i ].col;
    result = output_data[ row ][ col ] / Quantum[ row ][ col ];
    OutputCode( output_file, ROUND( result ) );
   }
 }


Previous Table of Contents Next