- About Graphics Mill
- What's New?
- Breaking Changes in Graphics Mill 12
- Breaking Changes in Graphics Mill 11
- Features
- Supported File Formats
- System Requirements
- Getting Started
- Working with Image Formats
- Transforming Images (Resize, Rotate, Crop)
- Working with Metadata
- Color Management and Color Reduction
- Drawing Text and Graphics
- Color Keying (Green Screen Removal)
- Channels and Pixel Data
- Color and Tone Correction
- Working with Photoshop (PSD) Files
- Image Filters
- Processing Large Images
- Basics of Image Processing
- Troubleshooting
- Class Library Reference
- Licensing
- End User License Agreement
- Copyright Notices
EXIF and IPTC Metadata
This article explains how to work with EXIF and IPTC metadata in Graphics Mill. The following themes will be discussed:
- reading EXIF and IPTC metadata
- writing EXIF and IPTC metadata
- processing complex EXIF and IPTC metadata
- loading thumbnails from EXIF metadata
For general metadata information, see the Working with Metadata topic.
Reading EXIF and IPTC Metadata
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));
}
}
}
Writing EXIF and IPTC Metadata
Graphics Mill supports writing EXIF and IPTC information to the JPEG and TIFF formats. There are two ways to write metadata to a file:
- using image writers
- using writer settings
Using Image Writers
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);
}
Using Writer Settings
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);
}
Processing Complex EXIF and IPTC Metadata
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.");
}
}
Loading Thumbnails from 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");
See Also
Reference
- Aurigma.GraphicsMill.Codecs Namespace
- ImageReader Class
- ImageReader.Exif Property
- ImageReader.Iptc Property
- ExifDictionary Class
- IptcDictionary Class
- JpegWriter.Exif Property
- JpegWriter.Iptc Property
- TiffWriter.Exif Property
- TiffWriter.Iptc Property
- Bitmap Class
- Bitmap.Save Overload