If you've ever looked at the properties of one of your digital photos, you've noticed a ton of information in the Details tab such as the date the picture was taken, what kind of camera was used, and many other camera details such as aperture, exposure, etc.
This information is supplied for you by the camera and stored in the picture when it is taken. Thankfully, all cameras use the same format when recording this metadata in their resulting JPEG or TIFF images. The name of this format is Exif.
Exif is an abbreviation for Exchangeable Image File Format. This standard basically spells out the properties that can be stored with an image. There are a lot of properties available. I've listed a few below and you can read more on it here and here.
Friendly Tag Name | Tag Value (Hexadecimal) | Sample Value | Data Type | Detail |
Manufacturer | 010f | Canon | ASCII String | |
Model | 0110 | Canon DIGITAL IXUS | ASCII String | |
Date and Time | 9003 | 2003:08:11 16:14:32 | ASCII String | YYYY:MM:DD HH:MM:SS |
Flash | 9209 | 0 | Unsigned Short | 0 indicates that the flash did not fire. |
Aperture Value | 9202 | 262144/65536 | Unsigned Rational | Indicates the amount of light the camera lets in. |
Firmware Version | 0007 | Firmware Version 1.0 | ASCII String |
Retrieving the Date/Time
What we're concerned about here is the date and time the picture was taken. In .NET we can easily tap into that data by using PropertyIdList and the PropertyItems properties of an instance of the System.Drawing.Image class. PropertyIdList gives us a listing of the Exif tags available in integer format. And PropertyItems gives us access to those values in a byte array. One collection for both of these would have been nice, but I can't really complain.So, the first step is to open our JPEG image and get an Image instance to work with:
Image image = Image.FromFile(fileName);
Next, we'll loop through all the tag ID's and find the date/time tag that we're looking for (0x0132):
int tagIndex = -1;The next step is to lookup the particular value and translate that to ASCII text that we can work with.
for (int i = 0; i < image.PropertyIdList.Length; i++)
{
if (image.PropertyIdList[i] == Int32.Parse("132", NumberStyles.HexNumber))
{
tagIndex = i;
break;
}
}
PropertyItem item = image.PropertyItems[tagIndex];Finally, let's go ahead and stuff that date back into a real DateTime structure for easy encapsulation.
dateString = ASCIIEncoding.ASCII.GetString(item.Value);
string[] dParts = dateString.Split(new string[] { ":", " " }, StringSplitOptions.RemoveEmptyEntries);Now that we have our pieces, we'll go ahead and create a nice, clean method that we can use on any given image object:
int year = Convert.ToInt32(dParts[0].Trim());
int month = Convert.ToInt32(dParts[1].Trim());
int day = Convert.ToInt32(dParts[2].Trim());
int hour = Convert.ToInt32(dParts[3].Trim());
int minute = Convert.ToInt32(dParts[4].Trim());
int second = Convert.ToInt32(dParts[5].Trim());
date = new DateTime(year, month, day, hour, minute, second);
private static Nullable<DateTime> GetImageDate(Image image)Now you can drop the above code in any application and have some fun.
{
int tagIndex = -1;
Nullable<DateTime> date = null;
// find the index of the id we're looking for
// Note: the Exif specification at http://exif.org specifies 0x9003 for
// the date/time the image was taken
for (int i = 0; i < image.PropertyIdList.Length; i++)
{
if (image.PropertyIdList[i] == Int32.Parse("9003", NumberStyles.HexNumber))
{
tagIndex = i;
break;
}
}
// return if the tag is not found
if (tagIndex < 0) return null;
// parse the date string which is in the format: yyyy:mm:dd hh:mm:ss
string dateString = null;
try
{
PropertyItem item = image.PropertyItems[tagIndex];
dateString = ASCIIEncoding.ASCII.GetString(item.Value);
string[] dParts = dateString.Split(new string[] { ":", " " }, StringSplitOptions.RemoveEmptyEntries);
int year = Convert.ToInt32(dParts[0].Trim());
int month = Convert.ToInt32(dParts[1].Trim());
int day = Convert.ToInt32(dParts[2].Trim());
int hour = Convert.ToInt32(dParts[3].Trim());
int minute = Convert.ToInt32(dParts[4].Trim());
int second = Convert.ToInt32(dParts[5].Trim());
date = new DateTime(year, month, day, hour, minute, second);
}
catch { }
// in case of an exception, we'll just ignore it and not return a date
return date;
}