List an image’s EXIF properties in C#


[EXIF properties]

This post shows how you can read an image’s EXIF properties. Note that pictures taken with any modern camera, phone, tablet, etc. contain a lot of EXIF data, some of which can be very useful.

To read an image’s EXIF properties, you need to look through the image’s PropertyIdList property to get a list of its property IDs. Then you can use the image’s array to get information about the corresponding properties.

The items in the PropertyItems array have Type, Len, and Value properties that give you whatever data goes with the property. You need to use the Type value to figure out what type of data is stored in the Value buffer.

The program defines the following structure to hold property data.

public struct ExifPropertyData
{
    public int Id;
    public ExifPropertyTypes PropertyType;
    public ExifPropertyDataTypes DataType;
    public byte[] DataBuffer;
    public int DataLength;
    public string DataString;
}

The PropertyType enumeration was created by the previous example. The DataBuffer contains the property’s original data stored in a byte array. The DataLength property gives the length of the data.

DataString will be a textual representation of the property.

The following enumeration lists the property data types given by the EXIF standard.

public enum ExifPropertyDataTypes : short
{
    ByteArray = 1,
    String = 2,
    UShortArray = 3,
    ULongArray = 4,
    ULongFractionArray = 5,
    UByteArray = 6,
    LongArray = 7,
    LongFractionArray = 10,
}

To summarize, you look in an image’s PropertyIdList to find the ID you want. Then you look at the corresponding PropertyItems entry to get information about the property. The Type gives you a value in the ExifPropertyDataTypes enumeration that tells you what kind of data is stored in the Value property. Finally you can read that data appropriately.

The following GetExifPropertyData method gets a property’s data and uses it to build the DataString textual value.

// Get the data for an EXIF property.
private static ExifPropertyData GetExifPropertyData(
    Image img, int index)
{
    ExifPropertyData data = new ExifPropertyData();
    data.Id = img.PropertyIdList[index];
    data.PropertyType = (ExifPropertyTypes)data.Id;

    PropertyItem item = img.PropertyItems[index];
    data.DataBuffer = item.Value;
    data.DataType = (ExifPropertyDataTypes)item.Type;
    data.DataLength = item.Len;

    string result = "";
    int num_items, item_size;
    switch (data.DataType)
    {
        case ExifPropertyDataTypes.ByteArray:
        case ExifPropertyDataTypes.UByteArray:
            data.DataString =
                BitConverter.ToString(data.DataBuffer);
            break;

        case ExifPropertyDataTypes.String:
            data.DataString = Encoding.UTF8.GetString(
                data.DataBuffer, 0, data.DataLength - 1);
            break;

        case ExifPropertyDataTypes.UShortArray:
            result = "";
            item_size = 2;
            num_items = data.DataLength / item_size;
            for (int i = 0; i < num_items; i++)
            {
                ushort value = BitConverter.ToUInt16(
                    data.DataBuffer, i * item_size); 
                result += ", " + value.ToString();
            }
            if (result.Length > 0) result = result.Substring(2);
            data.DataString = "[" + result + "]";
            break;

        case ExifPropertyDataTypes.ULongArray:
            result = "";
            item_size = 4;
            num_items = data.DataLength / item_size;
            for (int i = 0; i < num_items; i++)
            {
                uint value = BitConverter.ToUInt32(
                    data.DataBuffer, i * item_size);
                result += ", " + value.ToString();
            }
            if (result.Length > 0) result = result.Substring(2);
            data.DataString = "[" + result + "]";
            break;

        case ExifPropertyDataTypes.ULongFractionArray:
            result = "";
            item_size = 8;
            num_items = data.DataLength / item_size;
            for (int i = 0; i < num_items; i++)
            {
                uint numerator = BitConverter.ToUInt32(
                    data.DataBuffer, i * item_size);
                uint denominator = BitConverter.ToUInt32(
                    data.DataBuffer,
                    i * item_size + item_size / 2);
                result += ", " + numerator.ToString() +
                    "/" + denominator.ToString();
            }
            if (result.Length > 0) result = result.Substring(2);
            data.DataString = "[" + result + "]";
            break;
                        
        case ExifPropertyDataTypes.LongArray:
            result = "";
            item_size = 4;
            num_items = data.DataLength / item_size;
            for (int i = 0; i < num_items; i++)
            {
                int value = BitConverter.ToInt32(
                    data.DataBuffer, i * item_size);
                result += ", " + value.ToString();
            }
            if (result.Length > 0) result = result.Substring(2);
            data.DataString = "[" + result + "]";
            break;
        
        case ExifPropertyDataTypes.LongFractionArray:
            result = "";
            item_size = 8;
            num_items = data.DataLength / item_size;
            for (int i = 0; i < num_items; i++)
            {
                int numerator = BitConverter.ToInt32(
                    data.DataBuffer, i * item_size);
                int denominator = BitConverter.ToInt32(
                    data.DataBuffer,
                    i * item_size + item_size / 2);
                result += ", " + numerator.ToString() +
                    "/" + denominator.ToString();
            }
            if (result.Length > 0) result = result.Substring(2);
            data.DataString = "[" + result + "]";
            break;
    }
    return data;
}

The method creates a new ExifPropertyData object and initializes its Id and PropertyType values.

It then gets the PropertyItems object for the property and sets the DataBuffer, DataType, and DataLength values.

The tricky part is reading the property’s actual value. The switch statement handles the different data types. For example, if the property contains a string, the code uses Encoding.UTF8.GetString to convert the data buffer into a string.

Read the code to see how the program handles the other data types.

The following method makes a list containing ExifPropertyData structures for all of an image’s properties.

// Make a list of EXIF properties for an image.
public static List<ExifPropertyData> GetExifProperties(Image img)
{
    List<ExifPropertyData> result = new List<ExifPropertyData>();

    for (int index = 0; index < img.PropertyIdList.Length; index++)
    {
        result.Add(GetExifPropertyData(img, index));
    }

    return result;
}

The method loops through the image’s PropertyIdList and calls the GetExifPropertyData method for each property.

The main program uses the following code to display the properties for an image file.

// Open the file.
using (Bitmap bm = new Bitmap(ofdFile.FileName))
{
    // Get EXIF property data.
    List<ExifStuff.ExifPropertyData> property_data
        = ExifStuff.GetExifProperties(bm);

    // Display the property information.
    List<string> results = new List<string>();
    foreach (ExifStuff.ExifPropertyData data in property_data)
    {
        ListViewItem item = lvwProperties.Items.Add(
            data.PropertyType.ToString());
        item.SubItems.Add(data.Id.ToString());
        item.SubItems.Add(data.DataType.ToString());
        item.SubItems.Add(data.DataLength.ToString());
        item.SubItems.Add(data.DataString);
    }
}

This code uses the image file to create a Bitmap. It calls GetExifProperties to get a list of the image’s property data. It then loops through that list and displays each property’s ID, data type, length, and textual value in a ListView control.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in files, graphics, image processing and tagged , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *