Previous Table of Contents Next

What About Color?

The sample programs in this chapter and most of the text have talked about how to compress images that have only one color component, usually a grey scale. This leaves the question of what to do with color images.

Color images are generally composed of three components, such as the red, green, and blue of RGB, or the luminance and chrominance of YUV. In these cases, JPEG treats the image as if it were actually three separate images. An RGB image would first have its red component compressed, then its green, then its blue. This is essentially just more of the same.

The Sample Program

The sample program used to demonstrate DCT compression in this chapter is in the C source file DCT.C. It scan be compiled and linked with the standard support source files, BITIO.C, ERRHAND.C, and either MAIN-C.C for compression or MAIN-E.C for expansion.

The DCT compression program takes an additional parameter on the command line, the quality factor. A factor of zero through twenty-five can be selected, zero being the best quality and twenty-five being the lowest. As was discussed earlier in this chapter, the quality factor is used to initialize the quantum table with the step sizes for each DCT element.

The command syntax for the compression program is:

DCT-C input-file output-file [quality]

If no quality value is selected, it defaults to a value of three, which is an arbitrarily chosen constant. The quality factor is encoded in the compressed file, so the expansion program doesn’t need that parameter on the command line. The syntax for expansion is:

DCT-E input-file output-file

The DCT sample program in this chapter is not an implentation of JPEG compression. It does closely duplicate the first and second stages of the algorithm, however, which are the DCT transformation of the input, followed by the quantization and zig-zag coding steps. The only significant difference from the JPEG algorithm at this point is that the DC coefficient at 0,0 is not encoded as a difference from the last coefficient.

The test program used here departs from being a JPEG implementation in the encoding phase after quantization is complete. DCT.C does not implement Huffman coding on the output, but it does implement a slightly different form of RLE and uses variable-length integer codes for output.

Input Format

Graphics files come in a plethora of formats. Decoding and understanding every format can become a bewildering problem, and the purpose of this book is not to be a treatise on file formats. Thus, the graphics examples used in this chapter are stored in the closest thing possible to a “non-format.”

All of the graphics files used in this section are stored in a row-major order, so that all the pixels in each row are stored adjacent to one another. The top of the screen is stored first, with subsequent rows working their way down the screen. Each file is a 320 column by 200 row grey-scale image, with pixels having eight bits, ranging from zero to 255. The grey-scale files have a file suffix of “GS,” which identifies them as “non-formatted” grey-scale files.

This format is particularly easy to display on IBM VGA displays, but should be easy to adapt to any system that can display 256 colors. A short program, GS.C, is included to display the files on IBM VGA displays. Since VGA displays can only handle sixty-four grey-scale colors, some of the resolution of the image is lost on display, but the effect is relatively insignificant to the human eye.

In addition to GS.C, which displays GS files on an IBM compatible VGA adaptor, there is a second display program called GSDIFF.C. This tests the differences between an original file and its reproduction after a compression/decompression cycle. First it gives a visual display of the differences between the two files. Then the root mean squared (rms) error is written to the screen. While the rms value is not the best way to assign a quality factor to a compression cycle, it does provide a good way to see how well compression is working.

The Code

A summarized version of the main compression module follows (a complete listing is at the end of the chapter). The main program first calls the initialization module, which sets up the quantization table and the cosine transform matrices. The quality parameter must be passed to this module to have it set up the quantization matrix properly.

The next step is to write out the quality factor to the output file. By outputting this information, we eliminate the possibility of inadvertently trying to decompress using the wrong quantization matrix. This would cause the output to be catastrophically in error if it happened.

   void CompressFile( FILE *input, BIT_FILE *output,
             int argc, char *argv[] )
   int row;
   int col;
   int i;
   unsigned char *input_array[ N ];
   int output_array[ N ][ N ];
   int quality;

   quality = atoi( argv[ 0 ] );
   printf( "Using quality factor of %d\n", quality );
   Initialize( quality );
   OutputBits( output, quality, 8 );
   for ( row = 0 ; row <ROWS ; row += N ) {
    ReadPixelStrip( input, PixelStrip );
    for ( col = 0 ; col < COLS ; col += N ) {
     for ( i = 0 ; i < N ; i++ )
      input_array[ i ] = PixelStrip[ i ] + col;
     ForwardDCT( input_array, output_array );
     WriteDCTData( output, output_array );
  OutputCode( output, 1 );

Finally, the main compression loop is entered. Since the data is stored a single row at a time, we need to read in a block of eight rows together before we can begin building 8-by-8 blocks to compress. This is accomplished in the routine called ReadPixelStrip. It reads an entire strip of pixels 8 rows deep and 320 columns wide.

The next part of the loop sets up the input_array. This actually gets passed to the DCT routine. It consists of a block of eight pointers into the pixel strip. When it is passed to the DCT routine, the input_array can be treated in the code as an 8-by-8 input matrix.

The DCT routine is then called. It is passed an 8-by-8 unsigned character matrix and returns an 8-by-8 integer matrix. The integer matrix is then passed to the WriteDCTData() routine for compression and to be written to the file.

The final step in the program is to call the OutputCode() routine one last time with a dummy non-zero value. The OutputCode() routine tracks consecutive zeros for the run-length encoding portion of the program. If the file ends with several consecutive zeros, they many need to be flushed before the program exits.

Previous Table of Contents Next