Monday, December 27, 2010

Extracting Image Properties in C#

One of the little tasks I have is maintaining the Thistle Society web site and that includes adding photos to the gallery pages.  I hold information about each picture in an XML file including the original file name, caption and date taken.   Up until now I have been entering all this information manually in the XML but I wanted to try to automatically extract the dates from the photos so I wrote a little application to make it easy to load and edit the XML file. 
Having read up on image file formats in Wikipedia and other places I know that all the information I need is will be stored in the image files as image metadata including the Date Created.  However, I tried parsing the file details myself – a long and complex job as there are several different versions in different image file formats.  So, when I found that GDI+ on Windows handles most of the messy bits I was somewhat relieved.
Starting with a new addition to the Kajabity Tools which provides all the file open/save/close functionality (it will be in the next release, coming soon) it is quite easy to build the application and display all the image details in a ListView (in Details mode).  Handling the Item Activated event I open a dialog to view and edit the original image file and stored details.
I added a PictureBox control to load and display the image and a small ListView control (Details mode) to display all of the metadata (PropertyItems).  Small problem when I initially tried this – the image hadn’t loaded so I got an exception that the object didn’t exist.  This was easily resolved by handling the PictureBox LoadCompleted event and filling the property list then – the columns are the property Id, property type, property length and property value. 
void PictureBoxLoadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
    try
    {
        // The PictureBox control is pictureBox - get all the PropertyItems.
        PropertyItem[] propItems = pictureBox.Image.PropertyItems;

        foreach ( PropertyItem propItem in propItems )
        {
            ListViewItem item = new ListViewItem( "0x" + propItem.Id.ToString( "x" ) );
            item.SubItems.Add( new ListViewItem.ListViewSubItem( item, propItem.Type.ToString() ) );
            item.SubItems.Add( new ListViewItem.ListViewSubItem( item, propItem.Len.ToString() ) );

            // Code here to extract the value of the property item values...

            // The ListView control is listImageProperties.
            listImageProperties.Items.Add( item );
        }
    }
    catch( Exception ex )
    {
        MessageBox.Show( this, ex.Message, "Error loading image metadata" );
    }
}
The property value is a byte array whose contents depend on the property id and must be interpreted according to the type and length.  GDI+ uses a fixed set of standardised property codes – so once you know the one(s) you need, it’s easy to fetch them.  The property I need has Id 0×0132 (PropertyTagDateTime) which has a data type of 2 (PropertyTagTypeASCII – a text string) and a length of 20.
The sample code below shows how to fetch the creation date property and convert it to a DateTime.
try
{
    // GDI+ provides standardised definitions!
    // http://msdn.microsoft.com/en-us/library/ms534415(VS.85).aspx
    PropertyItem propItem = pictureBox.Image.GetPropertyItem( 0x132 );
    if( propItem != null )
    {
        System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
        string text = encoding.GetString( propItem.Value, 0, propItem.Len - 1 );

        CultureInfo provider = CultureInfo.InvariantCulture;
        DateTime dateCreated = DateTime.ParseExact( text, "yyyy:MM:d H:m:s", provider );

        Debug.WriteLine( "converted " + text + " to " + dateCreated.ToString() );
    }
    else
    {
        Debug.WriteLine( "No Property Found" );
    }
}
catch( Exception ex )
{
    Debug.WriteLine( "Error: " + ex.Message );
}
First, the code shows how to retrieve the value of the property with a special twist – the strings appear to end with a zero byte (like a C style string) so you need to exclude the last byte.
Next, I needed to convert the text to a DateTime.  The strings appear to all be formatted using colons – for example “2008:09:27 22:15:35”.  I used the DateTime ParseExact method to convert it to a DateTime.  Job done.
Of course there was one final problem I had to resolve – many of the pictures were taken on cameras where the date hadn’t been set – so I’m back to manually editing them.  Still, it’s a lot easier with a DateTimePicker control.
Technorati Tags: ,,
 
Source: http://www.kajabity.com/index.php/2010/01/extracting-image-properties-in-c-2/

No comments:

Post a Comment