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/

Friday, December 24, 2010

METADATA using GDI+

1.0 - Introduction

Image files can contain extra information about the file, settings from a camera, geodata, and other info. This metadata is organized as a specific set of items, each with a name, an ID and a value. With the ID you can find the item and read the information or change it. There are more than 200 different items defined, but in practice only a limited number is used. Items can be added or removed.

From the .NET point of view there are two ways of addressing metadata in imges-files, with GDI+ and with the WIC (Windows Imaging Component), installed with Vista, XP SP3 or .NET Framework 3.0.
In this article we look at GDI+ and how metadata can be addressed from code, using Visual Studio 2008 and Framework 2.0 or higher.

2.0 - The metadata environment in Visual Studio

The prerequisites are Visual Studio 2008 and the .NET Framework 2.0 or higer.

There are only a few classes for getting access to metadata in images: the Image and Bitmap classes in System.Drawing and the PropertyItem Class in System.Drawing.Imaging.

Metadata in an image has a straightforward structure, it is a simple list with items. The Image (System.Drawing) has a PropertyItems Property, this is an array with all the pieces of metadata; the PropertyItem objects. Each PropertyItem has four properties, an ID (.Id), a value (.Value), the data-type of the Value (.Type) and the length of the data in Value (.Len).

The strategy for retrieving the metadata is to open the file, get the collection of PropertyItem objects with the PropertyItems property of the image and then search for a specific item with the Id. When the PropertyItem is found you can set or get the properties Id, Value, Type and Len.

The supported file formats are .jpg, .tif, .png and .exif.

These are the classes for working with metadata:


     Table 1 - Image metadata support (GDI+).
Class Description
Image Class
PropertyItems PropertyGets all the property-items stored in the Image
GetPropertyItem MethodGets the specified property-item from the Image
SetPropertyItem MethodStores a property-item in the Image
Bitmap Class
PropertyItems PropertyGets all the property-items stored in the Image
GetPropertyItem MethodGets the specified property-item from the Image
SetPropertyItem MethodStores a property-item in the Image
PropertyItem Class
Id Property Gets or sets the ID of the property
Len Property Gets or sets the length (in bytes) of the Value property
Type Property Defines the type of data contained in the Value property
Value Property Gets or sets the value of the property-item


The Bitmap PropertyItems Property, GetPropertyItem Method and SetPropertyItem Method are inherited from Image. In practice you can work with Image or Bitmap as desired.
The PropertyItems Property returns an array with PropertyItem objects.
There are more than 200 different property-items. Each item has a specific name, and the properties id, Type (the data-type in Value), Value and Len (the length of the data in Value).

Some examples of property-items:


     Table 2 - Image metadata property-items (GDI+).
PropertyType name
[Constant]
Id
[hex]
Type name
[Constant]
Description
PropertyTagImageWidth 0x0100 PropertyTagTypeShort
or PropertyTagTypeLong
Number of pixels per row
PropertyTagImageHeight 0x0101 PropertyTagTypeShort
or PropertyTagTypeLong
Number of pixel rows
PropertyTagDocumentName 0x010D PropertyTagTypeASCII Name of document from which image was scanned
PropertyTagImageDescription 0x010E PropertyTagTypeASCII The title of the image
PropertyTagDateTime 0x0132 PropertyTagTypeASCII Date and time the image was created
PropertyTagArtist 0x013B PropertyTagTypeASCII Name of the person who created the image
PropertyTagImageTitle 0x0320 PropertyTagTypeASCII The title of the image
PropertyTagIndexTransparent 0x5104 PropertyTagTypeByte Index of transparent color in GIF
PropertyTagCopyright 0x8298 PropertyTagTypeASCII Copyright information


Other property-items are for image properties, Exif, geodata (GPS), camera settings, thumbnails, printing, colors, and more. For a complete overview see [1].

For each property-item a data-type is specified (property PropertyItem.Type). These are all the possible types:


     Table 3 - Image Property Tag Type Constants (GDI+).
Type [Constant] Value
[dec]
Description
PropertyTagTypeByte 1An array of bytes
PropertyTagTypeASCII 2A null-terminated ASCII string
PropertyTagTypeShort 3An array of unsigned short (16-bit) integers
PropertyTagTypeLong 4An array of unsigned long (32-bit) integers
PropertyTagTypeRational 5An array of pairs of unsigned long integers
PropertyTagTypeUndefined6An array of bytes of any data type
PropertyTagTypeSLONG 7An array of signed long (32-bit) integers
PropertyTagTypeSRational10An array of pairs of signed long integers

See also [2] for a full description.

With these properties, methods and constants you can make code for reading and writing metadata in images.

3.0 - Things to Do

These are the functions we want to implement:

Open an image file and get access to the metadata.
Read all the metadata from the file to get an overview.
Get a specific piece of metadata (a property-item) and read or change its value.
Add property-items if they do not exist.
Remove property-items from the metadata.
Save an image file after changing the metadata.

We will make a class with some universal functions. The functions will have basic functionality for showing the priciples for working with metadata. You can change the functions or extend the class yourself.

Do not copy code from these pages for use in Visual Studio. There might be some small differences compared with the code in the download and spaces and returns are added. Please use the code from the download.

4.0 - Open an image file and get access to the metadata

Opening a file and retrieving the contents as Bitmap is as simple as this:


  C# - Open an image from file.
// Open bitmap from file.
 public Bitmap File_OpenImage(string sFileName)
 {
     Bitmap oBitmap = new Bitmap(sFileName);
     return oBitmap;
 }


However, the file is kept open until the bitmap is disposed. This prevents us from saving the bitmap again when the meta-data is modified.
This is a tricky situation and it can lead to a lot of gesswork when files refuse to save.
A standard workaround is to make a copy of the original Bitmap and dispose the original bitmap:

Bitmap oBitmapNew = new Bitmap(oBitmapOld);
oBitmapOld.Dispose();

However, in our case, the metadata is lost in this process. So, this is our new workaround when metadata is involved:

1) Open the imagefile as Bitmap.
2) Store all the metadata from the bitmap in a PropertyItems object.
3) Make a new Bitmap with the old bitmap as template. This new Bitmap has no metadata.
4) Copy the stored metadata from the PropertyItems object to the new Bitmap.
5) Dispose the original Bitmap, the file is closed and unlocked.


  C# - Open an image from file, and unlock.
// ---------------------------------------------------------------
 // Date      110708
 // Purpose   Open a bitmap from file.
 // Entry     sFileName - Filename of the image (+ path).
 // Return    The Bitmap from the file.
 // Comments  The file is unlocked.
 // ---------------------------------------------------------------
 public Bitmap File_OpenImage(string sFileName)
 {
     // Open bitmap from file.
     Bitmap oBitmap = new Bitmap(sFileName);
     // Get all metadata.
     PropertyItem[] propItems = oBitmap.PropertyItems;
     // Unlock by making a copy (metadata is lost here).
     Bitmap oBitmap2 = new Bitmap(oBitmap);
     // Copy original metadate to new bitmap.
     for (int i = 0; i < propItems.Length; i++)
     {
         oBitmap2.SetPropertyItem(propItems[i]);
     }
     // Dispose original bitmap.
     oBitmap.Dispose();
     // Return copy.
     return oBitmap2;
 }


This already shows how to get all the metadata from the file; with the Bitmap.PropertyItems property. This is an array with all the pieces of metadata (PropertyItem objects). We will use this in the next section to get an overview of all the items in the metadata.

5.0 - Read all the metadata from the file to get an overview

It is sometimes useful for having an overview of the metadata in an image-file, especially when you are developing a metadata application. For this purpose a function is made for showing the metadata as a single text.


  C# - Get a list of property-items from the metadata
// ---------------------------------------------------------------
 // Date      110708
 // Purpose   Get info from all the property-items in an image.
 // Entry     oBitmap - The bitmap from the image.
 // Return    A single string with the property-items info.
 // Comments  The PropertyItem Value bytes are converted to string. 
 // ---------------------------------------------------------------
 public string Image_GetPropertyItemsInfo(Bitmap oBitmap)
 {
     string s = "";
     string r = "\r\n";
     string sID;

     PropertyItem[] propItems = oBitmap.PropertyItems;

     s += "Number of items: " + propItems.Length.ToString() + r;
     s += r;
     for (int i = 0; i < propItems.Length; i++)
     {
         s += "Item: " + i.ToString() + r;        // Item number in PropertyItem[].
         sID = propItems[i].Id.ToString("x2");                // Hex.
         if (sID.Length == 1) sID = "0x000" + sID;
         if (sID.Length == 2) sID = "0x00" + sID;
         if (sID.Length == 3) sID = "0x0" + sID;
         if (sID.Length == 4) sID = "0x" + sID;
         s += "ID: " + sID + r;                               // ID, format 0xHHHH.
         s += "Type: " + propItems[i].Type.ToString() + r;    // Type (1..10).
         s += "Length: " + propItems[i].Len.ToString() + r;   // Length (bytes).
         s += r;
     }
     return s;
 }


The File_OpenImage() function returns a Bitmap from the file and the Bitmap.PropertyItems property is used to get all the metadata as an array of PropertyItem objects.
In a for-loop the properties Id, Type and Length of all the PropertyItem objects are retrieved and converted to string.

This is the result:

  Text - Property-items in the metadata.
Item: 0
 ID: 0x0301
 Type: 5
 Length: 8

 Item: 1
 ID: 0x010e
 Type: 2
 Length: 4

 Item: 2
 ID: 0x0131
 Type: 2
 Length: 16


The ID of a property-item identifies it (hence the name); there are only pre-defined items in the metadata, each with specific properties (see Table 2 for some examples).
The item with ID=0x0301 is PropertyTagGamma, the gamma value attached to the image.
The item with ID=0x010e is PropertyTagImageDescription, specifies the title of the image.
The item with ID=0x0131 is PropertyTagSoftwareUsed, specifies the name and version of the software or firmware of the device used to generate the image.

Item values are not shown with this function, this needs a specific action depending on the data-type (see Table 3).

If the image has no property-items or if the image format does not support property-items, PropertyItems returns an empty array (that is, an array of length zero).
Property-item ID's are usually expressed in hexadecimal form, e.g. "0x010E", with a prefix "0x" indicating that this number is hex and "010E" the actual value.
Hexadecimal numbers are 16-based, so "010E" converted to decimal is
0*(16^3) + 1*(16^2) + 0*(16^1) + 14*(16^0) = 270.
The meta-data (in PropertyItems) has no reference to a specific file or bitmap, it can be copied from one bitmap to another and it can exist as stand-alone object.

6.0 - Get a specific piece of metadata

When a specific property-item must be changed, or you want to know if a property-item exists in the metadata, you can search it with the ID (PropertyItem.Id).
This can be done in two ways, iterate the array with PropertyItem objects from the Bitmap.PropertyItems property or use the Bitmap.GetPropertyItem Method. In both cases you need the ID (PropertyItem.Id) of the item to be searched.

As an example here the Image_GetImageDescription() function from the download. This function searches for property-item PropertyTagImageDescription with ID=0x010E, the title of the image. In this function the Bitmap.PropertyItems array is iterated until the item with ID=0x010E is found.


  C# - Get the Value of the PropertyItem with ID=0x010E.
// ---------------------------------------------------------------
 // Date      110708
 // Purpose   Get value of the PropertyItem with 
 //           ID = PropertyTagImageDescription (0x010E) of an image.
 // Entry     oBitmap - The bitmap from the image.
 // Return    The vallue from the PropertyItem (as string).
 // Comments  PropertyTagImageDescription. 0x010E = 270.
 // ---------------------------------------------------------------
 public string Image_GetImageDescription(Bitmap oBitmap)
 {
     // int iH = 270;
     // MessageBox.Show(iH.ToString("x2"));

     int iID = 270;          // 0x010E = PropertyTagImageDescription.
     ASCIIEncoding textConverter = new ASCIIEncoding();

     string sValue = "";
     PropertyItem[] propItems = oBitmap.PropertyItems;
     for (int i = 0; i < propItems.Length; i++)
     {
         if (propItems[i].Id == iID)
         {
             sValue = textConverter.GetString(propItems[i].Value);
         }
     }
     return sValue;
 }


In the download is also function Image_SetImageDescription() which sets the value of the PropertyItem. This function uses the same method.


  C# - Set the Value of the PropertyItem with ID=0x010E.
// ---------------------------------------------------------------
 // Date      110708
 // Purpose   Set the value of the PropertyItem with 
 //           ID = PropertyTagImageDescription for an image.
 // Entry     oBitmap - The bitmap from the image.
 //           sImageDescription - The value for the PropertyItem.
 // Return    None (Bitmap is returned by ref)
 // Comments  When the PropertyItem with ID = PropertyTagImageDescription
 //           exists, the value of the item is replaced.
 //           When the PropertyItem does not exist, it is created and
 //           added to the image.
 // ---------------------------------------------------------------
 public void Image_SetImageDescription(ref Bitmap oBitmap, string sImageDescription)
 {

     int iID = 270;          // 0x010E = PropertyTagImageDescription.
     bool bItemExists = false;

     PropertyItem[] propItems = oBitmap.PropertyItems;

     // Set the value for an existing PropertyItem with 
     // ID = PropertyTagImageDescription.
     for (int i = 0; i < propItems.Length; i++)
     {
         if (propItems[i].Id == iID)
         {
             byte[] bytValue;
             ASCIIEncoding textConverter = new ASCIIEncoding();
             bytValue = textConverter.GetBytes(sImageDescription);
             propItems[i].Value = bytValue;
             oBitmap.SetPropertyItem(propItems[i]);
             bItemExists = true;
             break;
         }
     }

     // Create a new PropertyItem with ID = PropertyTagImageDescription (0x010E)
     // when such an item does not exist.
     if (bItemExists == false)
     {
         int iType = 2;              // = ASCII string.
         string sValue = sImageDescription;
         PropertyItem oItem = Image_CreateNewPropertyItem(iID, iType, sValue);
         // Add this new item to the image.
         oBitmap.SetPropertyItem(oItem);
     }
 }


When the item is found, the .Value property is set with the desired text (one of the parameters of the function). This value must be a byte-array so, the text is converted to byte-array first with the textConverter.

Setting the value of a property-item is not sufficient, you must use the Bitmap.SetPropertyItem() method for adding the changes to the metadata in the bitmap.

This function also creates the (searched) item if it does not exist in the meta-data. This is explained below.

7.0 - Add property-items if they do not exist

When new property-items must be added to the metadata, the first thing you want to do is to make a new PropertyItem object. This is, however, not possible directly because the PropertyItem class is not inheritable. See also [3]:

A PropertyItem object encapsulates a metadata property to be included in an image file. A PropertyItem object is not intended to be used (as) a stand-alone object. A PropertyItem object is intended to be used by classes that are derived from System.Drawing.Image. A PropertyItem object is used to retrieve and change the metadata of existing image files, not to create the metadata. Therefore, the PropertyItem class does not have a defined Public constructor, and you cannot create an instance of a PropertyItem object.

Microsoft suggests the following (amazing) workaround:

1) Make a dummy-image with metadata and put it somewhere on disk (almost any image created in a paint program has metadata).
2) Open (from code) the file, get the metadata, and retrieve the first PropertyItem.
3) Set the Id, Len, Type and Value properties.
4) Add this new PropertyItem to the metadata of the desired image.

Instead of getting an image from file I made a bitmap in memory and tried to add meta-data by setting different properties, but this did not succeed.

As a compromise I converted an existing image (with metadata) to Base64 code and assigned it to a constant (in code); this is a canned image.

1) Make a small image with metadata in a paint-program and save it.
2) Open this image from code and convert the bytes to Base64 code.
3) Assign the Base64 code to a constant.
4) When you need a PropertyItem object, convert the Base64 code to Bitmap again.

The advantage of this workaround is, that there are no external files. In the download you can find the functions Bitmap_To_Base64() and Base64_To_Bitmap() for doing this. The dummy-image Base_8_8.png for creating the Base64 code is also included.

The image (as Base64 code) looks like this:


  C# - Convert an image, as Base64 code, to Bitmap.
// ---------------------------------------------------------------
 // Date      140607
 // Purpose   Convert Base64 code to Bitmap.
 // Entry     sBase64 - The Base64 code (as single string).
 // Return    The bitmap.
 // Comments  
 // ---------------------------------------------------------------
 private Bitmap Base64_To_Bitmap(string sBase64)
 {
     Byte[] bytData = Convert.FromBase64String(sBase64);
     MemoryStream ms = new MemoryStream(bytData);
     Bitmap oB = new Bitmap(ms);
     ms.Close();
     ms.Dispose();
     return oB;
 }

 // Icon Base_8_8.png (8x8).
 private string Icon_Base_8_8 =
 "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAARnQU1BAACx" +
 "jwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAABZJREFU" +
 "KFNj/A8EDPgASAE+wDAsFAAAxQLjHkhOEUMAAAAASUVORK5CYIIAAAAAAAAAAAAAAAAAAAAAAAAA" +
 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";


The image (as Base64 code) and the conversion to Bitmap with Base64_To_Bitmap() are encapsulated in the Image_CreateNewPropertyItem() function which creates a new PropertyItem object from scratch:


  C# - Create a new PropertyItem from scratch.
// ---------------------------------------------------------------
 // Date      110708
 // Purpose   Create a new PropertyItem from scratch.
 // Entry     iId - The PropertyItem ID.
 //           iType - The PropertyItem type.
 //           sValue - The PropertyItem value (as string).
 // Return    A new PropertyItem object.
 // Comments  The first PropertyItem from a dummy-image is used
 //           for making a new item.
 // ---------------------------------------------------------------
 public PropertyItem Image_CreateNewPropertyItem(int iId, int iType, string sValue)
 {
     ASCIIEncoding textConverter = new ASCIIEncoding();
     Bitmap oBitmap = Base64_To_Bitmap(Icon_Base_8_8);
     PropertyItem[] propItems = oBitmap.PropertyItems;
     if (propItems.Length != 0)
     {
         propItems[0].Id = iId;                  // 0x010E = PropertyTagImageDescription.
         propItems[0].Type = (short)iType;       // Null-terminated ASCII string.
         propItems[0].Value = textConverter.GetBytes(sValue);
         propItems[0].Len = sValue.Length;       // Length (+1 ?).
         return propItems[0];
     }
     else
     {
         return null;
     }
 }


Notice that this function is not quite universal: there is always a specific relationship between the properties of the PropertyItem depending on the item ID. In this case a PropertyTagImageDescription is assumed.

In the prevous section you have seen how this function is used.

8.0 - Remove property-items from the metadata

This is not tested.
There are no methods for removing property-items from metadata directly, you can only retrieve and change them.

A possible solution could be:

1) Open the image file and get the image as Bitmap.
2) Get the metadata as Bitmap.PropertyItems (an array with PropertyItem objects).
3) Remove all the metadata from the Bitmap with
Bitmap oBitmapNew = new Bitmap(oBitmapOld).
4) Copy only the desired items from the Bitmap.PropertyItems array to the new bitmap using the Bitmap.SetPropertyItem method.
5) Save the new Bitmap to the original file.

See also function File_OpenImage() described earlier, most of the code is already present.

9.0 - Save an image file after changing the metadata

Saving the Bitmap when the metadata is changed seems to be the easy part, but when the file from which the metadata is retrieved, is not closed you get an error and the file will not save.
See the discussion in H 4.0.


  C# - Save a Bitmap to file.
// ---------------------------------------------------------------
 // Date      110708
 // Purpose   Save a bitmap to file.
 // Entry     oBitmap - The Bitmap to save.
 //           sFileName - Filename of the image (+ path).
 // Return    true if successful.
 // Comments  
 // ---------------------------------------------------------------
 public bool File_SaveImage(Bitmap oBitmap, string sFileName)
 {
     try
     {
         if (File.Exists(sFileName))
         {
             File.SetAttributes(sFileName, FileAttributes.Normal);
         }
         oBitmap.Save(sFileName);
         return true;
     }
     catch
     {
         return false;
     }
 }


10.0 - Hacking metadata

As has been said, this system of metadata is based on (a large number of) specific items, each with a predefined purpose. In the documentation [1] is a description for each item, what the purpose is, how large it is and the type of data which is allowed. As an example, the PropertyTagDateTime item is defined as 'Date and time the image was created', the type (property PropertyItem.Type) is PropertyTagTypeASCII (text), and the size (property PropertyItem.Len) is 20 bytes.
There are, however, less specific items e.g. PropertyTagImageTitle and PropertyTagImageDescription. These items also contain text, but the length of the text is not fixed.

When you look at the specification, you can handle the data in a strict way, but nothing holds you back when 'incorrect' data is stored in one or more items. Most items are for text-data so you can put any text in it. You could, more specifically, decide to use Base64 code (also text), but with Base64 you can store any data which can be expressed as byte-arry: XML, HTML, binary data, scripts, styleheets, other images, encrypted data, just what you like.
Before you add the metadata you can also compress it, so the impact on the size of the image can be reduced.
An alternative is storing data directly as byte-array in items which have Type=1 for the data (property PropertyItem.Type). Item PropertyTagThumbnailData is a suitable candidate for this.

When you look at metadata in this way, an image can be considered as a universal resourcefile or as a container for data-transport.

11.0 - Summary

Metadata in images is organized as a simple list of items, each item with an ID and a value.
There are more than 200 different items possible, for defining the properties of the image, camera settings, geo-information (GPS), Exif, thumbnails, printing, colors, and more. You can read all the information, set the information for each item and add- or remove items.
Approaching and changing metadata in images with GDI+ is straightforward and easy to implement with Visual Studio as is shown in this article.

Saving Images & Image Format Properties in GDI+

This article has been excerpted from book "Graphics Programming with GDI+".

Now we move to the Save File menu item. It allows you to save images in different file formats.

The Image class provides the Save method, which is used to save images to a specified format. The Save method takes a fine name (as string type) or a stream (a stream object), and a specified format of type ImageFormat class. Table 7.4 describes the properties of the ImageFormat class.

Note: The Emf and Wmf properties in the ImageFormat enumeration do not save a real metafile, but save the bitmap as one metafile record. It will still be a bitmap.

TABLE 7.4: ImageFormat properties
Property
Description
Bmp
Specifies BMP format.
Emf
Specifies EMF (Enhanced Metafile Format).
Exif
Specifies EXIF format.
Gif
Specifies GIF format.
Guid
Specifies a GUID structure that represents the ImageFormat object.
Icon
Specifies Windows icon format.
Jpeg
Specifies JPEG format.
MemoryBmp
Specifies memory bitmap format.
Png
Specifies PNG format.
Tiff
Specifies Tiff format.
Wmf
Specifies WMF (Windows Metafile Format).
Now we add code for the SaveFileMenu click event handler, as shown in Listing 7.3. We use SaveFileDialog, which lets us specify the file name and saves an image using the format specified in the dialog. We read the extension of the file name entered by the user, and on that basis we pass the ImageFormat property in the Save method.

Note: The ImageFormat enumeration is defined in the System.Drawing.Imaging namespace. Don't forget to add a reference to this namespace in your application.

LISTING 7.3: Using the Save method to save images

private void SaveFileMenu_Click (object sender System.EventArgs e)
{
            //If image is created
            if (curImage == null)
                return;

            //Call SaveFileDialog
            SaveFileDialog saveDlg = new SaveFileDialog();
            saveDlg.Title = "Save Image As";
             saveDlg.OverwritePrompt = true;
             saveDlg.CheckPathExists = true;
            saveDlg.Filter =
            "Bitmap File (*.bmp) | *.bmp |" +
            "Gif File (*.gif) | *.gif | " +
            "JPEG File (*.jpg) | *.jpg" +
            "PNG File (*.png) | *.png";
             saveDlg.ShowHelp = true;

            //If selected, save
            if (saveDlg.ShowDialog() == DialogResult.OK)
            {
                //Get the user-selected file name
                string fileName = saveDlg.FileName;

                //Get the extension
                string strFilExtn =
                 fileName.Remove(0, fileName.Length - 3);

                //Save file
                switch (strFilExtn)
                {
                    case "bmp":
                         curImage.Save(fileName, ImageFormat.Bmp);
                        break;
                    case "jpg":
                         curImage.Save(fileName, ImageFormat.Jpeg);
                        break;
                    case "gif":
                         curImage.Save(fileName, ImageFormat.Gif);
                        break;
                    case "tif":
                         curImage.Save(fileName, ImageFormat.Tiff);
                        break;
                    case "png":
                         curImage.Save(fileName, ImageFormat.Png);
                        break;
                    default:
                        break;
                }
            }
}

Now we write code for the ExitMenu click event handler. This menu simply closes the application. Hence we call the Form.Close method on this event handler, as shown in Listing 7.4.

LISTING 7.4: The ExitMenu click event handler

private void ExitMenu_Click (object sender, System.EventArgs e)
{
             this.Close();
}

Retrieving Image Properties

Table 7.2 listed the Image class properties. Now we will read and display the properties of an image. We add a Properties menu item to the main menu and write the code in Listing 7.5 as this menu click event handler. We read the size, format, resolution, and pixel format of an image.

LISTING 7.5: Getting image properties

    private void PropertiesMenu_Click (object sender, System.EventArgs e)
{
        if (curImage!=null)
        {
        //Viewing image properties
        string imageProperties = "Size:" + curImage.Size;
        imageProperties +=",\n RawFormat:"+ curImage.RawFormat.ToString();
        imageProperties +=",\n Vertical Resolution:"+ curImage.VerticalResolution.ToString();
        imageProperties +=",\n Horizontal Resolution:"+ curImage.HorizontalResolution.ToString();
        imageProperties +=",\n PixelFormat:"+ curImage.PixelFormat.ToString();
         MessageBox.Show(imageProperties);
        }
}

Figure 7.6 shows the properties of an image.

Figure 7.6.gif

Simplify access to an image metadata using GDI+ in C#

Introduction

Several image file formats enable you to store metadata along with an image. Metadata is information about an image, for example, title, width, camera model, and artist. Microsoft Windows GDI+ provides a uniform way of storing and retrieving metadata from image files in the Tagged Image File Format (TIFF), JPEG, Portable Network Graphics (PNG), and Exchangeable Image File (EXIF) formats. In GDI+, a piece of metadata is called a property item, and an individual property item is identified by a numerical constant called a property tag.

In .NET Framework, you can store and retrieve metadata by calling the SetPropertyItem and GetPropertyItem methods of the Image class, and you don't have to be concerned with the details of how a particular file format stores that metadata.
Another question is how can you use this data. You will receive just an array of bytes, and it is your problem how you interpret it. This library will simplify access to this image metadata by converting them to ordinary .NET data types and organizing them in the form of class properties and/or hashtable.

I'd like also to thank Jeffrey S. Gangel whose article "Photo Properties" at CodeProject brought me to the idea to write this class. His program makes basically the same, the only difference is that I wanted to have image information in the form that seems to me to be more comfortable and conventional.
Not all image property tags are implemented as class properties. Those are accessible only from PropertyItems hashtable. If anyone needs this library at all and even needs the specific property, I'll be very glad to help him. But I think it is not necessary, because the whole source code is available and modifiable.

Usage


// Accessing image proprties
public void ShowEquipModel() 
{ 
    Info inf=new Info("c:\\tmp\\Example.jpeg"); 
    MessageBox.Show(inf.EquipModel);
}

// Accessing image proprties as a hastable 
public void ShowAllProperties() 
{ 
    Info inf=new Info("c:\\tmp\\Example.jpeg");
    listView.Items.Clear(); 
    foreach (string propertyname in inf.PropertyItems.Keys) {
        ListViewItem item1 = new ListViewItem(propertyname,0);
        item1.SubItems.Add((inf.PropertyItems[propertyname]).ToString());
        listView.Items.Add(item1); 
    } 
}
The properties of the Info class are listed below:
  • Brightness Brightness value. The unit is the APEX value. Ordinarily, it is given in the range of -99.99 to 99.99.
  • Copyright Copyright information.
  • DateTime Date and time the image was created.
  • DTDigitized Date and time when the image was stored as digital data. If, for example, an image was captured by DSC and at the same time the file was recorded, then DateTimeOriginal and DateTimeDigitized will have the same contents.
  • DTOrig Date and time when the original image data was generated. For a DSC, the date and time when the picture was taken.
  • EquipMake The manufacturer of the equipment used to record the image.
  • EquipModel The model name or model number of the equipment used to record the image.
  • ExposureProg Class of the program used by the camera to set exposure when the picture is taken.
  • FNumber F number.
  • FocalLength Actual focal length, in millimeters, of the lens. Conversion is not made to the focal length of a 35 millimeter film camera.
  • Image Sets or returns the current Image object.
  • ISOSpeed ISO speed and ISO latitude of the camera or input device as specified in ISO 12232.
  • MeteringMode Metering mode.
  • Orientation Image orientation viewed in terms of rows and columns.
  • PixXDim Type is PropertyTagTypeShort or PropertyTagTypeLong. Information specific to compressed data. When a compressed file is recorded, the valid width of the meaningful image must be recorded in this tag, whether or not there is padding data or a restart marker. This tag should not exist in an uncompressed file.
  • PixYDim Type is PropertyTagTypeShort or PropertyTagTypeLong. Information specific to compressed data. When a compressed file is recorded, the valid height of the meaningful image must be recorded in this tag whether or not there is padding data or a restart marker. This tag should not exist in an uncompressed file. Because data padding is unnecessary in the vertical direction, the number of lines recorded in this valid image height tag will be the same as that recorded in the SOF.
  • PropertyItems Returns a HashTable of all available properties of a given Image. Keys of this HashTable are display names of the Property Tags, and values are transformed (typed) data.
  • ResolutionUnit Unit of measure for the horizontal resolution and the vertical resolution. 2 - inch 3 - centimeter.
  • XResolution Number of pixels per unit in the image width (x) direction. The unit is specified by PropertyTagResolutionUnit.
  • YResolution Number of pixels per unit in the image height (y) direction. The unit is specified by PropertyTagResolutionUnit.

    **The primary source of information to write this class was MSDN.
    MSDN Home > MSDN Library > Graphics and Multimedia > GDI+ > GDI+ Reference > Constants

Tuesday, December 14, 2010

CHRISTIAN & JOY meaning.

I learned this from a priest in Quiapo church:

The root word of CHRISTIAN is CHRIST. IAN is the word left, and does mean I Am Nothing. If we're gonna put the words together starting with the meaning of IAN and the root word of Christian, we will find a very meaningful, true sentence. I am nothing without CHRIST. This soothes my mind, heart and soul because it is indeed. It is the best saying, although short yet accurate and true, I've ever heard.

How about the word

Still from the priest, JOY stands for JESUS, our Savior, Others and Yourself. If you want to have JOY in your life.. just simply follow the very simple guide. Serve others, love yourself and praise Jesus. He blesses people who help his fellows. He blesses those who respect the holiness of their body. He blesses those who glorify Him. With this simple steps, you will surely have a very meaningful life. The essence of life is simply JOY.

stress no more

PDA is over... it's time to take a shot. With my colleagues, Virna & Noche. Burning out stress!

Saturday, December 11, 2010

it's gonna be 0K

Hey! What's up? Well here I am, making my first ever post using my gmail account. Actually, marami na akong blog sites even a website, I already created twice. One was for our school project, and the other one was for my personal use, making blogs, connecting to friends, etc. but unfortunately, dahil sa nawalan ako ng internet connection for how many months din due to some unexpected circumstances, I forgot the password and I couldn't access my own website anymore. Disgusting! However, I'll work for it as soon as I can. But first, I am writing this blog now right straight from my N70 cellular phone. Di naman ako mayaman, I am just thankful to, well, one software that I can manage to surf the internet. Ayoko banggitin yung software because it's not good to hack. Well, just leave it to me if you're thinking, bakit ako? If you're desperate to have one, go ask your friends or google. Pero peace tayo ha. L0L. Kuwentuhan muna kita.

Today, is today. L0L. I hate dramas but like it too. Gulo noh? Feeling ko daming nangyari ngayon even parang wala naman? Kasi, one of the major decisions happened today and it was unexpected. It wasn't our plan. I believe it was His plan. To start with, my mother now is sick. Her eyes isn't well, and because they reside in the province, they believed she gained it from the people who are not like us. You know what I mean if you are a true Filipino. It's been 2 months na rin that mama is having that sickness. They went to albularyo na rin seeking for cure. Every week I think, lagi siya napunta dun and thank you sa mga sumasama sa kanya anyway because I think Papa couldn't go accompany her kasi bantay siya for a while sa store ni mama. Albolaryo said to her na avoid daw eating salty food baka maano daw siya. I forgot the term, but lets say lumala kasi it is confirmed daw na she was been played by unnatural persons. I felt, well, uncomfortable knowing na wala akong magagawa because I'm too far from them. The next time I asked the condition of mama, papa said she's getting better unlike nung una pa lang and it was just last November 2010. I was happy then.

But now, it came back daw. And today is December 2010. Nagpadasal na sila for the unnatural person until this morning, my sister called me, since I've been updating her about what's happening here, she's really worried and cried while we were conversing. I was controlling my emotion. She said text ko sila mama na she's going to call them too, and so I did. Mahina kasi signal sa bgy namin so they need to go to pa near sa dagat. Baybay ang tawag sa amin. There, they talked and finally landed to a decision na pumunta nalang sila dito sa manila for a check-up with a specialist. Besides, christmas is coming and a perfect timing too for us to be reunited. Pero the first decision was I'm the one who will go home but since I was thinking para makasimba na rin si mama sa quiapo church which is through faith, Poong Nazareno will heal all our wounds miraculously. Trust me. And kung ano pang magandang gawin ni mama dito sa manila. Whatever the reason why I shared this, I am just happy na we will be reunited again and I am glad I can help mama and samahan siyang magpagamot. I am thankful to God, Lord Jesus Christ for this blessing. I can feel it deep down inside that this is the answer to my prayers. So, tomorrow alis na sila dun with mana estrella pala, thanks to her sa pagsama kay mama sa byahe. Sunod nalang daw si Papa.

Today, is today. L0L. I hate dramas but like it too. Umulit ba? L0L. I made a mistake na naman today. A mistake! I'm sorry. Sana di na maulit yun.

Data security 1st quiz? Waw, it was cool. Yung number 19 and 20 lang yung di ko sure. But i'm hoping tama ako. LoL.

General cleaning is the next big thing today, or tomorrow? LoL.

Of course, before I end this discussion, let me share you this;
- Life supposed to be difficult, if it doesn't, we would never learn. and
- in life, the goal is not only to be alive, but to live well.

:-)