EXIF Quick Reference

Over the past few years, I’ve used the EXIF data stored by modern digital cameras in a couple of programs I’ve worked on. Having access to the date the photo was taken – or which way the camera was rotated when it was – is useful information to have. Even though it’s relatively easy to do, finding the right identifier for the information you want is much harder than it should be. This page fills that niche – the most useful/popular EXIF properties in an easy-to-find format.

EXIF in .NET

The .NET framework provides two ways of accessing EXIF data; functions in the System.Drawing namespace, and functions in the System.Windows.Media.Imaging namespace. There are also a number of libraries available to read EXIF data in .NET – which are generally easier to use than the built-in classes – but I’ve found that costs associated with loading and deploying an extra library are not worth it for the simpler programming interface. Most of the time I’m only reading a couple properties, so doing it directly isn’t that difficult.

Given those two namespaces each provide a way of reading EXIF data, which one is better? In my view, neither. Each provides the same functionality, and though the System.Windows.Media.Imaging namespace does provide properties to allow reading some values directly – like the camera’s manufacturer or the date the photo was taken – they are limited enough in practice that you’ll end up using the tag values anyway.

So which one should we choose? It’s actually quite simple; if you are using Windows Forms, use the System.Drawing version, since you’ve already loaded the appropriate DLL. Likewise, if you are using WPF, use the System.Windows.Media.Imaging version. And if your application doesn’t have a UI (or is using an alternate framework), then the choice is up to you – though I’d suggest using the System.Drawing version. The System.Drawing namespace resides in System.Drawing.dll, which weighs in at 612 KB on my system, while System.Windows.Media.Imaging resides in PresentationCore.dll, which weighs in at 4112 KB (PresentationCore contains the majority of WPF). The increased size of the latter DLL results in a slightly slower first-image processing time, as the framework loads the DLL into memory.

Accessing EXIF data with C

Using the System.Drawing/Windows Forms version of accessing EXIF data is relatively straight forward: load the image, request a property by number, and then parse the property bytes into the desired format (string or integer). Let’s have a look:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;

class EXIF
{
    static void Main()
    {
        // Create an Image object
        using (Image photo = Image.FromFile(filename))
        {
            // Read out the date taken property (string)
            PropertyItem dateProperty = photo.GetPropertyItem(0x9003);
            string dateTaken = (new UTF8Encoding()).GetString(dateProperty.Value);
            // And the ISO (unsigned short integer)
            PropertyItem isoProperty = photo.GetPropertyItem(0x8827);
            ushort iso = BitConverter.ToUInt16(isoProperty.Value, 0);
            // other properties can be read using similar methods...
        }
    }
}

Like the previous version, accessing the EXIF data with System.Windows.Media.Imaging/WPF is relatively easy and follows a similar pattern. However, in this case the BitmapMetadata class exposes some of the EXIF values through properties on the BitmapMetadata object. The rest must be accessed using the GetQuery method, using the WIC Metadata Query Language. This query language unifies the types of embedded information that can be accessed (EXIF, XMP, IPTC, ID3, etc.) into a general-purpose syntax. We’re only concerned with the EXIF ones here, which have the form /app1/ifd/exif:{uint=999} or /app1/ifd/exif/subifd:{uint=99999}, depending on the tag you’re accessing. In general, tags less than 1000 (decimal) are accessed using the former syntax, those greater using the latter syntax.

using System;
using System.IO;
using System.Windows.Media.Imaging;

class EXIF
{
    static void Main()
    {
        // Load and decode the photo
        using (FileStream stream = new FileStream(filename, FileMode.Open))
        {
            JpegBitmapDecoder decoder = new JpegBitmapDecoder(stream,
                                        BitmapCreateOptions.PreservePixelFormat,
                                        BitmapCacheOption.None);
            // Extract the photo's metadata
            BitmapMetadata metadata = (BitmapMetadata)decoder.Frames[0].Metadata;
            // Read out the date taken property...
            // ...  via the built-in property (as a System.DateTime)
            DateTime dateTaken1 = DateTime.Parse(metadata.DateTaken);
            // ... or via a query (as a string)
            string dateTaken2 = (string)metadata.GetQuery("/app1/ifd/exif/subifd:{uint=36867}");
            // And the ISO (unsigned short integer)
            ushort iso = (ushort)metadata.GetQuery("/app1/ifd/exif/subifd:{uint=34855}");
            // other properties can be read using similar methods...
        }
    }
}

Accessing EXIF data with Powershell

Since PowerShell has easy access to the .NET framework, grabbing metadata from a photo using PowerShell is just as easy as doing it in C#, which makes it great for scripting photo tasks. First up is the System.Drawing/Windows Forms version:

# Load the System.Drawing DLL before doing any operations
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") > $null
# And System.Text if reading any of the string fields
[System.Reflection.Assembly]::LoadWithPartialName("System.Text") > $null
# Create an Image object
$photo = [System.Drawing.Image]::FromFile($filename)
# Read out the date taken property (string)
$dateProperty = $photo.GetPropertyItem(0x9003)
$dateTaken = (new-object System.Text.UTF8Encoding).GetString($dateProperty.Value)
# And the ISO (unsigned short integer)
$isoProperty = $photo.GetPropertyItem(0x8827)
$iso = [System.BitConverter]::ToUInt16($isoProperty.Value, 0)
# other properties can be read using similar methods...
# Dispose of the Image once we're done using it
$photo.Dispose()

We can also use the System.Windows.Media.Imaging/WPF version from PowerShell. Like the C# version, some values are exposed through properties on the BitmapMetadata object directly; the rest must be accessed using the GetQuery method. Queries take the form /app1/ifd/exif:{uint=999} or /app1/ifd/exif/subifd:{uint=99999}, depending on the tag you’re accessing. In general, tags less than 1000 (decimal) are accessed using the former syntax, those greater using the latter syntax.

# Load the System.Windows.Media.Imaging DLL before doing any operations
[System.Reflection.Assembly]::LoadWithPartialName("PresentationCore") > $null
# Load and decode the photo
$stream = new-object System.IO.FileStream($filename, [System.IO.FileMode]::Open)
$decoder = new-object System.Windows.Media.Imaging.JpegBitmapDecoder($stream,
           [System.Windows.Media.Imaging.BitmapCreateOptions]::PreservePixelFormat,
           [System.Windows.Media.Imaging.BitmapCacheOption]::None)
# Extract the photo's metadata
$metadata = $decoder.Frames[0].Metadata
# Read out the date taken property...
# ...  via the built-in property (as a System.DateTime)
$dateTaken1 = $metadata.DateTaken
# ... or via a query (as a string)
$dateTaken2 = $metadata.GetQuery("/app1/ifd/exif/subifd:{uint=36867}")
# And the ISO (unsigned short integer)
$iso = $metadata.GetQuery("/app1/ifd/exif/subifd:{uint=34855}")
# other properties can be read using similar methods...
# Dispose of the FileStream once we're done using it
$stream.Dispose()

EXIF Tag Reference

The numbers used in the examples above can be found in the table below. This table is not intended to be comprehensive – there is far too much complexity in the EXIF format for that. Most manufacturers add their own information in proprietary formats (you can usually decode this, but you need to support each manufacturer separately – Canon camera’s don’t write the Nikon fields), and not all cameras write all fields, and there are non-EXIF storage formats that are used as well (XMP, IPTC, ID3, and more). If you are looking for comprehensive, I’d recommend this page on Phil Harvey’s ExifTool site; otherwise, here’s a list of the most common, relatively standard ones. All integers are 32 bits in length, except those with the notation ‘short’; they are 16 bits in length. Integer rationals are stored XY, where the value is equal to X/Y; most are 64 bits in length (two 32 bit integers back-to-back).

Decimal ValueHex ValueData TypeTag NameNotes
2710x010fASCII stringEquipment Maker‘Canon’, ‘Nikon’, etc.
2720×0110ASCII stringEquipment Model‘Canon PowerShot S5 IS’, etc.
2740×0112integer
(unsigned short)
Orientation1 = Horizontal
3 = Rotate 180 degrees
6 = Rotate 90 degrees clockwise
8 = Rotate 270 degrees clockwise
2820x011ainteger
(unsigned short)
X ResolutionUnit in Resolution Unit field (for pixels, see Pixel X Dimension field)
2830x011binteger
(unsigned short)
Y ResolutionUnit in Resolution Unit field (for pixels, see Pixel Y Dimension field)
2960×0128integer
(unsigned short)
Resolution Unit1 = None
2 = Inches
3 = Centimetres
3060×0132ASCII stringModified Date TimeYYYY:MM:DD HH:MM:SS
334340x829ainteger rational
(unsigned)
Exposure TimeFirst integer divided by the second integer produces the exposure time in seconds; for example, a value of ’1′ followed by a value of ’50′ is an exposure time of 1/50th of a second.
334370x829dinteger rational
(unsigned)
F NumberFirst integer divided by the second integer produces the F number; for example, a value of ’35′ followed by a value of ’10′ is F/3.5.
348550×8827integer
(unsigned short)
ISO Speed100, 200, 400, etc.
368670×9003ASCII stringDate TakenYYYY:MM:DD HH:MM:SS
368680×9004ASCII stringDate CreatedYYYY:MM:DD HH:MM:SS
373770×9201integer rational (signed)Shutter Speed
373780×9202integer rational (unsigned)Aperture
373800×9204integer rational (signed)Exposure CompensationFirst integer divided by the second integer produces the exposure compensation; for example, a value of ’2′ followed by a value of ’3′ is +2/3
373810×9205integer rational (unsigned)Maximum Aperature
373830×9207integer
(unsigned short)
Metering Mode0 = Unknown
1 = Average
2 = Center-weighted average
3 = Spot4 = Multi-spot
5 = Multi-segment
6 = Partial
255 = Unknown
373850×9209integer
(unsigned short)
Flash0 = No Flash
LSB (8th bit) set = Flash Fired
bits 4&5, L-R:
10 = Flash off
01 = Flash on
11 = Flash auto
373860x920ainteger rational
(unsigned)
Focal Length
375000x927cN/AEquipment Maker NoteCamera Maker specific information
375100×9286integer
(signed)
User Comment
409610xa001integer
(unsigned short)
Color Space1 = sRGB
409620xa002integer
(unsigned short)
Pixel X DimensionIn pixels
409630xa003integer
(unsigned short)
Pixel Y DimensionIn pixels
414860xa20einteger
(unsigned short)
Focal Plane X Resolution
414870xa20finteger
(unsigned short)
Focal Plane Y Resolution
414880xa210integer
(unsigned short)
Focal Plane Resolution Unit1 = None
2 = Inches
3 = Centimetres
4 = Millimetres
5 = Micrometres
414950xa217integer
(unsigned short)
Sensing Method1 = Not defined
2 = One-chip colour area
3 = Two-chip colour area
4 = Three-chip colour area
5 = Colour sequential area
7 = Trilinear
8 = Colour sequential linear
417280xa300integer
(signed)
File Source1 = Film scanner
2 = Reflection print scanner
3 = Digital camera
419850xa401integer
(unsigned short)
Custom Rendered0 = Normal
1 = Custom
419860xa402integer
(unsigned short)
Exposure Mode0 = Auto
1 = Manual
2 = Auto Bracket
419870xa403integer
(unsigned short)
White Balance0 = Auto
1 = Manual
419880xa404integer rational
(unsigned)
Digital Zoom Ratio
419890xa405integer
(unsigned short)
Focal Length in 35 mm Format
419900xa406integer
(unsigned short)
Scene Capture Type0 = Standard
1 = Landscape
2 = Portrait
3 = Night
419910xa407integer
(unsigned short)
Gain Control0 = None
1 =Low gain up
2 = High gain up
3 = Low gain down
4 = High gain down
419920xa408integer
(unsigned short)
Contrast0 = Normal
1 = Low
2 = High
419930xa409integer
(unsigned short)
Saturation0 = Normal
1 = Low
2 = High
419940xa40ainteger
(unsigned short)
Sharpness0 = Normal
1 = Soft
2 = Hard
419960xa40cinteger
(unsigned short)
Subject Distance Range0 = Unknown
1 = Macro
2 = Close
3 = Distant