Wyvern Semiconductors Expert in Digital IP Solutions  
 
 

A JFIF/JPEG decoder (jfif)

by Simon Southwell
12th July 2010

 

Contents

Introduction

Presented here is a program to decode JPEG files (including JFIF format jpeg files) and dumping the decoded RGB data to a 24 bit bitmap file. In addition, the decoded image can be displayed in a pop-up window for visual verification.


Source code is included in the download available from github, and the program has been written with a structure that reflects the intention to implement the code as HDL for synthesis in an FPGA or ASIC environment. The core functionality is written in C++ (i.e. the code that is compiled to a library), with ancilliary wrapper functions written in C (such as the top level command line user interface and the graphical output).


The article on JPEG concepts and format is a good place to start if you haven't done so already, as this article will assume that you have, as some of the implementation concepts are going to be put in to practice. The software itself has been written with speed in mind, but the main purpose of the code is as a reference for the digital hardware implementation to follow, and as a learning aide. As such, the architecture choices and some of the implementation is constructed to be hardware efficient, and allow direct comparisons between the software internal state and the hardware internal state.


Usage

The usage message for jfif is as follows:


  Usage: jfif [-h] [-d] [-i ] [-o ]
      -h display help message
      -d display generated bitmap file's image in a window
      -i define input filename (default test.jpg)
      -o define output filename (default test.bmp)

JFIF is simple to use. Just typing "jfif" will result in a file test.jpg being decoded (if exists), and a bitmap output test.bmp be written. The -i and -o options are used to alter the default input and output filenames. The resultant bitmap can also be optionally displayed in a popup window, scaled to a maximum display area of 800x600, for validating the conversion by eye, using the -d option. This is generated from the actual bitmap file rather than internal memory to guarantee no additional artifacts in bitmap generation are missed in the display. This delays the display of the file a fraction, but in the interests model integrity.


Top Level Program Structure

This article is not meant to be a tutorial on the JPEG format (see here), but the source code is available for download as a starting point for those who wish a working example to study. Below is show some outline pseudo-code of the way jfif is constructed, with all the major functions listed to aid navigation around the source code. Later sections will look at these functions in more details, but this should start as a good starting point.


    main()
       READ infile into buffer
    
       jpeg_process_jfif()             -- Converts input baseline DCT JFIF buffer to 24 bit bitmap
           jpeg_extract_header()       -- Extracts jpeg header information (scan, frame, quantisation/huffman tables, DRI)
               jpeg_dht()              -- Constructs a Huffman decode structure from huffman table data
           jpeg_bitmap_init()          -- Creates space for appropriately sized bitmap and initialises header data
       
         LOOP for each MCU:            -- jpeg_process_jfif() process an MCU at a time until EOI (or error)
           jpeg_huff_decode            -- Gets an adjusted Huffman/RLE decoded amplitude value and ZRLs, or marker or end-of-block
               jpeg_dht_lookup()       -- Does huffman lookup on code and extracts amplitude data, or flags a marker
                   jpeg_get_bits()     -- Gets top bits from barrel shifter and (optionally) removes. Pulls in extra input if needed.
                   jpeg_amp_adjust()   -- Adjusts decoded huffman decoded amplitude to +/- amplitude value
               <dequantise>            -- De-quantisation done in jpeg_huff_decode directly from selected table
           jpeg_idct()                 -- Inverse discrete cosine transform
           jpeg_ycc_to_rgb()           -- Converts YCbCr to RGB on an MCU
           jpeg_bitmap_update()        -- Updates bitmap data buffer with 8x8 RGB values
         ENDLOOP
    
       WRITE bitmap buffer to outfile
    
       IF display requested
           jpeg_display_bmp_file()     -- Display bitmap file in a popup window
    

At the coarsest level, the program simply reads a JPEG image into a buffer, calls jpeg_process_jfif() to process that buffer, and then writes out the returned bitmap data. An optional display of the written bitmap file can be done afterwards. All the hard work, of course, is done in the jpeg_process_jfif() function.


The jpeg_process_jfif() breaks down into to distinct phases. Firstly it parses the JPEG header, extracting information required for decode, including quantisation tables, and Huffman table data, which is processes into a useful format for later with jpeg_dht. In this phase, the bitmap buffer is initialised. The second phase is a loop to process MCUs until an end-of-image marker (see article on JPEG concepts and format), or some exception encountered. In each iteration, jpeg_huff_decode() is called which will decode all the 8x8 arrays within the MCU and return the results. The returned result is usually a pointer to the decoded scan data, but can indicate a marker (e.g. EOI). THe decode function makes use for two other functions: jpeg_get_bits() to manage a barrel shifter for extracting the variable length codes, and jpeg_amp_adjust() to rescale the returned codes to amplitude values (see article on JPEG concepts and format). Within the decode function is also located the dequantisation step—for no better reason than all the required data is available at this point, and the operation is simple, not requiring another function call.


After decode, the individual 8x8 arrays within the MCU are inverse DCT transformed with one ore more calls to jpeg_idct(). The data is updated in place, as no extra space is required since expansion has already occured. Within the call to the iDCT function the normalisation and clipping also occurs, to place the output values in the 0 to 255 range. At this point we now have YCbCr (or just Y) arrays. For colour images jpeg_ycc_to_rgb() is called to convert to RGB, and in all cases a call to jpeg_bitmap_update() places the data in the bitmap buffer created earlier by jpeg_bitmap_init() in the appropriate location and format.


The majority of the code is located in jfif.cpp. The two exceptions to this are the inverse DCT function (jpeg_idct) found in jpeg_idct.cpp, and the GTK+ code (jpeg_display_bmp_file) for displaying the bitmap in a window, found in jfif_gtk.c. The header files are pretty self explanitory, but the majority of internal definitions are in jfif_local.h, whilst the external definitions are in jfif.h.


It should be noted that decoding a JPEG file, strictly speaking, only needs to decode to the luminance and (if present) chrominance information. Converting this to RGB in a bitmap format is a jfif specific implementation. Thus the jpeg_bitmap_init(), jpeg_ycc_to_rgb(), jpeg_bitmap_update() and jpeg_display_bmp_file() functions are not strictly part of the JPEG decode proper. However, it is in the jpeg_ycc_to_rgb() function that sub-sampling reconstruction occurs—with averaged chrominance data being used with multiple luminance data, in both the vertical and horizontal directions.


The above description is just an overview of the top level features. More details of the sub-function architectures are provided in the JPEG decoder article on implementation.


Unsupported Features

The full JPEG standard is broad in it's scope, with alternatives, and enhancing features optionally available. The jfif program is intended to be a starting point for a hardware implementation, and support of all the features of the standard is not practical and not really desirable. For instance, the compression technique, post-DCT, can be a Huffman table based algorithm, or an Arithmetic coding algorithm. Also there are extended and Progressive DCT formats, as well as a lossless compression mode. jfif has been limited to supporting a sub-set of these options—chosen to be the sub-set with the broadest overlap with real jpeg encoded data, whilst being most likely to be suitable in hardware decoding applications. The following list gives details of the known unsupported features. Anything not on the list has either been ommited by mistake, or is a bug in the application.


  • Extended and Progressive DCT
  • All differential DCT
  • All Arithmetic coding modes
  • Lossless Compression
  • Image components (Nf) greater than 4
  • Define number of Lines (DNL)/multiple scans in a frame
  • Horizontal sub-sampling factor (Hi) > 2
  • Vertical sub-sampling factor (Vi) > 2

Download

The jfif program and source code is available from github. It includes a precompiled windows executable, the source code and a makefile for building under Cygwin or Linux, as well as files for MSVC Express 2010.

Useful Links and further reading

The following are some links to the standards and useful online and offline material.




Copyright © 2010-2014 Simon Southwell
simon@anita-simulators.org.uk