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 Value Hex Value Data Type Tag Name Notes
271 0x010f ASCII string Equipment Maker ‘Canon’, ‘Nikon’, etc.
272 0×0110 ASCII string Equipment Model ‘Canon PowerShot S5 IS’, etc.
274 0×0112 integer (unsigned short) Orientation 1 = Horizontal; 3 = Rotate 180 degrees; 6 = Rotate 90 degrees clockwise; 8 = Rotate 270 degrees clockwise
282 0x011a integer (unsigned short) X Resolution Unit in Resolution Unit field (for pixels, see Pixel X Dimension field)
283 0x011b integer (unsigned short) Y Resolution Unit in Resolution Unit field (for pixels, see Pixel Y Dimension field)
296 0×0128 integer (unsigned short) Resolution Unit 1 = None; 2 = Inches; 3 = Centimetres
306 0×0132 ASCII string Modified Date Time YYYY:MM:DD HH:MM:SS
33434 0x829a integer rational (unsigned) Exposure Time First 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.
33437 0x829d integer rational (unsigned) F Number First 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.
34855 0×8827 integer (unsigned short) ISO Speed 100, 200, 400, etc.
36867 0×9003 ASCII string Date Taken YYYY:MM:DD HH:MM:SS
36868 0×9004 ASCII string Date Created YYYY:MM:DD HH:MM:SS
37377 0×9201 integer rational (signed) Shutter Speed
37378 0×9202 integer rational (unsigned) Aperture
37380 0×9204 integer rational (signed) Exposure Compensation First 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
37381 0×9205 integer rational (unsigned) Maximum Aperature
37383 0×9207 integer (unsigned short) Metering Mode 0 = Unknown; 1 = Average; 2 = Center-weighted average; 3 = Spot; 4 = Multi-spot; 5 = Multi-segment; 6 = Partial; 255 = Unknown
37385 0×9209 integer (unsigned short) Flash 0 = No Flash; LSB (8th bit) set = Flash Fired; bits 4&5, L-R: 10 = Flash off, 01 = Flash on, 11 = Flash auto
37386 0x920a integer rational (unsigned) Focal Length
37500 0x927c N/A Equipment Maker Note Camera Maker specific information
37510 0×9286 integer (signed) User Comment
40961 0xa001 integer (unsigned short) Color Space 1 = sRGB
40962 0xa002 integer (unsigned short) Pixel X Dimension In pixels
40963 0xa003 integer (unsigned short) Pixel Y Dimension In pixels
41486 0xa20e integer (unsigned short) Focal Plane X Resolution
41487 0xa20f integer (unsigned short) Focal Plane Y Resolution
41488 0xa210 integer (unsigned short) Focal Plane Resolution Unit 1 = None; 2 = Inches; 3 = Centimetres; 4 = Millimetres; 5 = Micrometres
41495 0xa217 integer (unsigned short) Sensing Method 1 = 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
41728 0xa300 integer (signed) File Source 1 = Film scanner; 2 = Reflection print scanner; 3 = Digital camera
41985 0xa401 integer (unsigned short) Custom Rendered 0 = Normal; 1 = Custom
41986 0xa402 integer (unsigned short) Exposure Mode 0 = Auto; 1 = Manual; 2 = Auto Bracket
41987 0xa403 integer (unsigned short) White Balance 0 = Auto; 1 = Manual
41988 0xa404 integer rational (unsigned) Digital Zoom Ratio
41989 0xa405 integer (unsigned short) Focal Length in 35 mm Format
41990 0xa406 integer (unsigned short) Scene Capture Type 0 = Standard; 1 = Landscape; 2 = Portrait; 3 = Night
41991 0xa407 integer (unsigned short) Gain Control 0 = None; 1 = Low gain up; 2 = High gain up; 3 = Low gain down; 4 = High gain down
41992 0xa408 integer (unsigned short) Contrast 0 = Normal; 1 = Low; 2 = High
41993 0xa409 integer (unsigned short) Saturation 0 = Normal; 1 = Low; 2 = High
41994 0xa40a integer (unsigned short) Sharpness 0 = Normal; 1 = Soft; 2 = Hard
41996 0xa40c integer (unsigned short) Subject Distance Range 0 = Unknown; 1 = Macro; 2 = Close; 3 = Distant