This article explains how to work with EXIF and IPTC metadata in Graphics Mill. The following themes will be discussed:
For general metadata information, see the Working with Metadata topic.
Graphics Mill supports the reading of EXIF and IPTC information from the JPEG, TIFF, and PSD files. Each file format has a corresponding image reader, a class from the Aurigma.GraphicsMill.Codecs namespace. The following image readers can be used to extract metadata: JpegReader, TiffReader, and PsdReader. These classes inherit their functionality from the ImageReader class, which exposes the Exif and Iptc properties. The properties return ExifDictionary and IptcDictionary collections providing access to the metadata.
The following code extracts and displays all EXIF and IPTC metadata from an image:
using (var jpegReader = new JpegReader(@"Images\in.jpg")) { //Read metadata var exif = jpegReader.Exif; var iptc = jpegReader.Iptc; //Show EXIF tags if (exif != null) { Console.WriteLine("EXIF"); Console.WriteLine("---------------"); foreach (long key in exif.Keys) { Console.WriteLine("{0}: {1}, {2}", exif.GetKeyDescription(key), exif[key], exif.GetItemString(key)); } } //Show IPTC tags if (iptc != null) { Console.WriteLine("IPTC"); Console.WriteLine("---------------"); foreach (long key in iptc.Keys) { Console.WriteLine("{0}: {1}, {2}", iptc.GetKeyDescription(key), iptc[key], iptc.GetItemString(key)); } } }
Graphics Mill supports writing EXIF and IPTC information to the JPEG and TIFF formats. There are two ways to write metadata to a file:
Image writers that support metadata expose two properties: Exif and Iptc. You can pass an ExifDictionary or IptcDictionary instance to the corresponding property, and enable the providing of your metadata.
The following code loads the in.jpg
file, sets the keyword and city IPTC fields and the software EXIF field, and writes the out.jpg
result file:
using (var jpegReader = new JpegReader(@"Images\in.jpg")) using (var jpegWriter = new JpegWriter(@"Images\out.jpg")) { var exif = new ExifDictionary(); exif[ExifDictionary.Software] = "Aurigma Graphics Mill"; jpegWriter.Exif = exif; var iptc = new IptcDictionary(); iptc[IptcDictionary.Keyword] = "mountain"; iptc[IptcDictionary.City] = "Olympia"; jpegWriter.Iptc = iptc; Pipeline.Run(jpegReader + jpegWriter); }
You can specify EXIF and IPTC metadata as writer settings (JpegSettings or TiffSettings) and pass the settings to Bitmap.Save. The following code sets the keyword and city IPTC fields and the software EXIF field and then writes these metadata to the out.jpg
file:
using (var bitmap = new Bitmap(@"Images\in.jpg")) { var settings = new JpegSettings(70); var exif = new ExifDictionary(); exif[ExifDictionary.Software] = "Aurigma Graphics Mill"; settings.Exif = exif; var iptc = new IptcDictionary(); iptc[IptcDictionary.Keyword] = "mountain"; iptc[IptcDictionary.City] = "Olympia"; settings.Iptc = iptc; bitmap.Save(@"Images\out.jpg", settings); }
There are EXIF tags that are represented by arrays, for example, GPS version and coordinates. It is not evident how to read or write such information, so let us show this on the example of writing and reading GPS version and latitude info. In the EXIF Specification it is written:
Tag Name | Field Name | Type | Count |
---|---|---|---|
GPS tag version | GPSVersionID | BYTE | 4 |
Latitude | GpsLatitude | RATIONAL | 3 |
The latitude info in EXIF is represented by 3 rational numbers, so we use the special class, UnsignedRational, that allows operating with unsigned rational numbers in Graphics Mill. The following code writes the 38 deg 48' 17"
latitude and GPS version 2.0.0.1
to the image's GpsLatitude and GPSVersionID EXIF fields:
using (var jpegReader = new JpegReader(@"Images\Chicago.jpg")) using (var jpegWriter = new JpegWriter(@"Images\ProcessComplexExifTypes.jpg", 70)) { var exif = new ExifDictionary(jpegReader.Exif); Object[] latitude = new Object[] { new UnsignedRational(38, 1), new UnsignedRational(48, 1), new UnsignedRational(17, 1) }; exif.SetItemArray(ExifDictionary.GpsLatitude, latitude); var gpsVer = new Object[] { (byte)2, (byte)0, (byte)0, (byte)1 }; exif.SetItemArray(ExifDictionary.GpsVersionId, gpsVer); jpegWriter.Exif = exif; Pipeline.Run(jpegReader + jpegWriter); }
After the GPS data is written to the image, let us read the same data:
using (var jpegReader = new JpegReader(@"Images\ProcessComplexExifTypes.jpg")) { var exif = new ExifDictionary(jpegReader.Exif); if (exif.Contains(ExifDictionary.GpsVersionId)) { Object[] ver = exif.GetItemArray(ExifDictionary.GpsVersionId); Console.WriteLine("GPS version " + ver[0] + "." + ver[1] + "." + ver[2]); } else { Console.WriteLine("There is no GPS version information in the EXIF metadata."); } if (exif.Contains(ExifDictionary.GpsLatitude)) { Object[] latitude = exif.GetItemArray(ExifDictionary.GpsLatitude); UnsignedRational latitudeDegrees = (UnsignedRational)latitude[0]; UnsignedRational latitudeMinutes = (UnsignedRational)latitude[1]; UnsignedRational latitudeSeconds = (UnsignedRational)latitude[2]; Console.WriteLine("GPS Latitude " + latitudeDegrees.Dividend / latitudeDegrees.Divider + " deg " + latitudeMinutes.Dividend / latitudeMinutes.Divider + "' " + latitudeSeconds.Dividend / latitudeSeconds.Divider + "\""); } else { Console.WriteLine("There is no GPS latitude information in the EXIF metadata."); } }
Creating thumbnails is a frequently performed task. A good example is a file browser application, where a user can preview image files without opening them. However, generating thumbnails for multiple files may take a lot of time. It is especially true if you are working with large images. For example, high-resolution photos can be 5 or more megapixels. Traditional methods of creating thumbnails require loading an image into memory and then resizing it. This method has very poor performance, especially when creating multiple thumbnails at the same time.
Graphics Mill offers another approach to creating thumbnails. It is applicable only for digital photos, but it works fast and provides good-quality thumbnails. The idea is to read thumbnails from EXIF metadata. All modern cameras write EXIF metadata when capturing an image. Besides a number of camera parameters and other details, most cameras save a thumbnail to the EXIF block. So, there is no need to read an entire bitmap into memory, as the predefined thumbnail is already available.
The following code snippet demonstrates how to extract a thumbnail from EXIF metadata:
using (var jpegReader = new JpegReader(@"Images\in.jpg")) using (var jpegWriter = new JpegWriter(@"Images\thumbnail.jpg")) { var thumbnail = (Bitmap)jpegReader.Exif[ExifDictionary.Thumbnail]; Pipeline.Run(thumbnail + jpegWriter); }
It is better to combine both approaches, because EXIF metadata may not contain a thumbnail, or the EXIF block may be absent in an image. Also, the EXIF-based approach cannot return thumbnail of any size, it can only provide a thumbnail in the stored default size (typically 160x120). To get a thumbnail of another size, you need to resize the one taken from EXIF metadata.
The following code reads a thumbnail from EXIF metadata (if there is one) and resizes it if needed. If there is no thumbnail in metadata or no metadata at all, the thumbnail is created by resizing the original image:
const int thumbnailSize = 120; const string filePath = @"Images\in.jpg"; Bitmap thumbnail = null; using (var jpegReader = new JpegReader(filePath)) { if ((jpegReader.Exif != null) && (jpegReader.Exif.Contains(ExifDictionary.Thumbnail))) thumbnail = (Bitmap)jpegReader.Exif[ExifDictionary.Thumbnail]; } if (thumbnail == null) thumbnail = new Bitmap(filePath); if (thumbnail.Width > thumbnail.Height) thumbnail.Transforms.Resize(thumbnailSize, 0); else thumbnail.Transforms.Resize(0, thumbnailSize); thumbnail.Save(@"Images\thumbnail.jpg");