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:
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.
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:
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.
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 = Spot4 = 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 |