Handling the orientation EXIF tag in images using C#
Two weeks ago I received an interesting support ticket from a
user of our ImageBox control, stating that when being used
for .png
files, the control was absolutely file but when used
with .jpg
files that had been rotated using Windows Explorer
shell context menus, they were displayed in the original
orientation.
As soon as I read the ticket I had a hunch what was going to be the ultimate cause, and was able to quickly reproduce the problem and then confirm my hunch.
Introducing exchangeable image file format
JEPG images support extension attributes via the Exchangeable image file format (Exif). These attributes can be used to provide additional information about an image, such as how and where it was captured. It can also store information such as the software used to manipulate the image, artist and copyright information, or camera model details. In other words, it's similar to how you can add tags to an MP3 file with album information, although usually this doesn't cause your MP3 file to play backwards!
The Exif specification is available from the CIPA website, although at 191 pages long it is a hefty read.
Introducing the orientation attribute
I mentioned that MP3 tags don't cause your MP3 files to play backwards (at least I'm not aware of any that do!), but Exif supports an orientation attribute that means the source image has one orientation, but should be displayed in another in order to appear correctly.
The tag supports one of the following 8 values
EXIF Orientation Value | Row #0 is: | Column #0 is: |
---|---|---|
1 | Top | Left side |
2* | Top | Right side |
3 | Bottom | Right side |
4* | Bottom | Left side |
5* | Left side | Top |
6 | Right side | Top |
7* | Right side | Bottom |
8 | Left side | Bottom |
The starred values also flip the image in addition to rotating it.
Source: ImpulseAdventure - JPEG / Exif Orientation and Rotation
Enter Windows Explorer
Windows Explorer has knowledge of the Exif orientation tag, and automatically rotates thumbnail previews so they appear correct - very helpful!
However, if you context click an image and make use of the Rotate Left and Rotate Right commands, Explorer doesn't truly rotate the images. Instead, for JEPG images, an appropriate orientation attribute is added (plus a large block of XML, it wouldn't be Microsoft if they weren't adding extra unwanted data in files). This is quite clever in a way, as it means it doesn't touch the original image data at all, but does mean that any application that doesn't understand Exif is going to display the image wrong.
Also somewhat annoyingly, when you view the properties of the image, the Details tab includes lots of information gleaned from Exif - but neglects to mention that a custom orientation is in use.
Enter .NET
When it comes to loading images in .NET, I usually use
Image.FromFile
or Image.FromStream
if I'm just loading
common formats. Unfortunately, these return the images "as-is",
without doing any post processing. I said unfortunately, but
actually this is probably a good thing - most likely you don't
want .NET manipulating images for you.
Fixing the problem
The Image
class does provide access to Exif properties, albeit
in a very obsure way. Image.PropertyIdList
returns an int
array of defined extension values, and GetPropertyItem
will
return a PropertyItem
instance describing an existing
property. You can then use the Value
property to get the
attribute value, although this may require further conversion to
use (e.g. convert raw bytes into a string).
Note: Calling
GetPropertyItem
will throw anArgumentException
if you request a property that doesn't exist. You should usePropertyIdList
to check that the property exists first.
The following extension method (body code from this Stack
Overflow answer) will take a source Image
, check to see if
the orientation attribute is present and if it is rotate/flip
the image as appropriate and then remove the attribute.
public static void NormalizeOrientation(this Image image)
{
if (Array.IndexOf(image.PropertyIdList, ExifOrientationTagId) > -1)
{
int orientation;
orientation = image.GetPropertyItem(ExifOrientationTagId).Value[0];
if (orientation >= 1 && orientation <= 8)
{
switch (orientation)
{
case 2:
image.RotateFlip(RotateFlipType.RotateNoneFlipX);
break;
case 3:
image.RotateFlip(RotateFlipType.Rotate180FlipNone);
break;
case 4:
image.RotateFlip(RotateFlipType.Rotate180FlipX);
break;
case 5:
image.RotateFlip(RotateFlipType.Rotate90FlipX);
break;
case 6:
image.RotateFlip(RotateFlipType.Rotate90FlipNone);
break;
case 7:
image.RotateFlip(RotateFlipType.Rotate270FlipX);
break;
case 8:
image.RotateFlip(RotateFlipType.Rotate270FlipNone);
break;
}
image.RemovePropertyItem(ExifOrientationTagId);
}
}
}
If your application deals with user submitted images, having something like this in your arsenal may be useful; I know I have several applications that now need this "fix".
Demonstration program
The linked source code download includes a demonstration program that features each of 8 supported orientation values and shows how they are displayed both before and after processing the orientation attribute.
Fixing ImageBox
At this time I'm still mulling over if this is something I should be handling within ImageBox, or if it is the responsibility of the program using the control. Given ImageBox typical use cases, I'm leaning towards the idea of adding a new option to automatically handle this but am currently undecided.
Credits
- Tower of London photograph used for sample images - Vera Kratochvil
- Information on Exif orientation - ImpulseAdventure
- Code for processing Exif orientation tags in C# - ReenignE
Update History
- 2019-03-09 - First published
- 2020-11-22 - Updated formatting
Downloads
Filename | Description | Version | Release Date | |
---|---|---|---|---|
ExifOrientationDemo.zip
|
Sample project for the handling the orientation EXIF tag in images using C# blog post. |
09/03/2019 | Download |
Leave a Comment
While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?
Comments
Zeeshanef
#
Very nice point Greatly explained by Author! I received an image from client which was appearing fine in windows, but when loaded in my C# application so it was appearing rotated 180 degree. Now going to implement this.
cb
#
WTH is ExifOrientationTagId???
Richard Moss
#
I was going to make a brief and sarcastic comment to match the tone of yours, seems netiquette is a lost art these days. Then I realised that the constant was missing from the article and if you view it from the new blog, the download is a direct link unlike a source viewer in the previous blog. So I'll give the benefit of the doubt.
But maybe next time you could spend a little more time searching first.
https://www.cyotek.com/downloads/view/ExifOrientationDemo.zip/ImageExtensions.cs
public const int ExifOrientationTagId = 0x112;
Regards;
Richard Moss
Henrik
#
Thank you Richard, You just saved me hours, and this is a true nugget of gold. Sharing is Caring :o)