Silverlight is a great environment for developing rich web applications. Using Silverlight, you can easily perform expensive tasks on the client's machine, reducing the cost of bandwidth and processing power required by more traditional web applications.
This article explains how to retrieve valuable image information from the Exif data found in JPEG images, and provides a Silverlight class library to perform this task entirely on the client's machine.
Note: Unfortunately for Silverlight developers, the sandboxed .NET Framework available to them does not expose the GDI+ wrappers implemented in the
System.Drawing.Imaging namespace - at least not at the time of writing this article. These wrappers include all the necessary tools to retrieve various information from image metadata, so if they're available, don't waste your time re-inventing the wheel.
A great article was written by Simon McKenzie on the same subject: ExifLib - A Fast Exif Data Extractor for .NET 2.0. The libraries even have the same name... small world. I haven't looked at his code, but the article states that he is not using the
System.Drawing.Imaging methods as well - so it should work just fine in Silverlight with no major modifications.
This article differs in that it explains the basics of the Jpeg and Exif formats, and was written specifically for Silverlight.
The Jpeg File Format
Understanding the general structure of a Jpeg file is important to extract the Exif data. A Jpeg image is delimited by two byte markers. The first marker byte is always 0xFF, while the second identifies what comes after it. For example, the mandatory "Start Of Image marker", or SOI, is defined by the number 0xD8. This is why every Jpeg image starts with the sequence 0xFF 0xD8. With the exception of the SOI marker, each marker is followed by two bytes specifying the length of the marker' section data in bytes. If our goal is to extract Exif data from a Jpeg image, then we're looking for the Exif marker: 0xE1.
The Exif Format
Following the two bytes indicating the Exif section length, the ASCII string "Exif" is found, followed by two zero bytes. The next two bytes indicate the endianess of the data stored in the Exif directories. If II is found, meaning that Intel ordering is used, then the byte order is little endian. If MM is found, meaning Motorola ordering is used, then the byte order is big endian. Any primitive read beyond this point needs to consider the Exif byte order.
The Exif data is stored in a directory structure: the Image File Directory, or IFD structure. The first four bytes store the length of the directory in bytes. Then, two bytes are used to indicate the number of entries, or tags, found in this directory. Each Exif entry is first defined by its tag identifier (two bytes), then its format (two bytes) and finally its component count (four bytes). Exif formats are defined as follows:
- Unsigned Byte (1 byte per component)
- ASCII String (1 bpc)
- Unsigned Short (2 bpc)
- Unsigned Long (4 bpc)
- Unsigned Rational (8 bpc, 4 for the numerator and 4 for the denominator)
- Signed Byte
- Undefined (1 bpc)
- Signed Short
- Signed Long
- Signed Rational
- Single (4 bpc)
- Double (8 bpc)
If the total byte count exceeds four bytes for an Exif entry, the next four bytes represent an offset into the Exif section from where to find the actual entry data. This way it is possible to iterate through a list of Exif entries simply by skipping 12 bytes.
IFDs can be nested under the root Exif directory. The tag number 0x8769 defines the start of a new Exif directory. Other IFD tags are also used to group tag numbers into categories. For example, 0x8825 is the tag number for the GPS IFD. At the end of an IFD, another IFD can be defined, creating a linked list. This linkage is defined by the next four bytes after the end of the directory's list of entries. If this integer is an offset that falls within the boundaries of the Exif section, it defines the starting point of the next IFD.
A camera creating a 10 megapixel image creates a 2-7 MB Jpeg file on its memory card, depending on the image. Storing a raw 10,000,000 pixel color image in main memory requires at least 28 MB. Keeping a few hundreds of those monster images in memory is not very practical, especially if your only goal is to display a small preview of the image. Even if memory is available, decoding a Jpeg image is a very expensive operation, and decoding a few hundreds of them would take minutes today. Again, not very practical.
So, how is your digital camera able to preview a dozen or more of those 10 MP pictures, in under a second? Using thumbnails, of course. A thumbnail is a smaller version of the full sized image, and is used to preview the image without having to decode it or store it in memory. The Exif format includes entries for thumbnail data, so digital cameras, when creating a Jpeg image, also typically create a 160x120 version of the same image and store it in the Exif section of the image. This image can be decoded very quickly, and takes under 60K of memory.
Exif entries also contain other very useful information, such as the date the image was taken, the camera model, user comments, the exposure time, whether or not the camera flash was used, and even GPS coordinates in recent models or cell phones.
ExifLib is a small class library that can be used to rapidly and efficiently parse Exif data found in a given Jpeg stream. The
static method takes a
System.IO.FileInfo instance for input, and returns a new
JpegInfo object. If the
JpegInfo.IsValid field is set to
True, then the object is now filled with all the available Exif data found in the Jpeg stream.
Note that not all known Exif tags are covered by this library. If more information needs to be extracted, the
ExifTag class can be modified along with the appropriate tag enumerations.
In order to test and demonstrate the
ExifLib capabilities, I wrote a little Silverlight application that lets you load and preview Jpeg images, as well as display some of the Exif information extracted. When no Exif thumbnail is found, it falls back on loading the entire image using the
BitmapImage.SetSource method. Just click on the "Add Images" button and select a few Jpeg images to get it started.
Understanding and using image file formats and metadata is a first step in any image processing application. I hope this little utility will save you some time.
- JPEG - Wikipedia
- Exif file format
- Exif Jpeg header manipulation tool