Task: Determine if a certain file format supports metadata
Task: Read a certain tag or comment by ID from a TIFF or EXIF file
Task: Write a certain tag or comment to a TIFF or EXIF file
Task: Read all tags and comments found in a TIFF or EXIF file without loading the image
Task: Update comments or tags in a TIFF or EXIF file without loading the image
Task: Automatically load, update and save the image tags
Task: Delete a tag by ID from a file without loading it
Task: Read or write the file stamp
"Metadata" refers to extra information that is stored with an image, but is not necessary for decoding the image. It can contain information about the camera used to take the image, the author, date and time the image was taken, where the image was taken, whether the camera has GPS capabilities, etc. When an image is modified, you should preserve the original information, if possible. Metadata includes markers, tags and comments. This topic will deal primarily with markers. For more information on tags and comments, refer to the following:
Implementing TIFF Comments and Tags.
JPEG files contain metadata information in user-defined markers. The user-defined markers are in the range 0xE0 to 0xFE. These markers can have a maximum size of 0xFFFD, or 65533 bytes. The markers below 0xE0 are reserved for the encoding of the image and inserting a reserved marker can make a file invalid. Therefore, you should not use reserved markers unless you are well acquainted with markers and image encoding in JPEG files.
TIFF files do not contain markers, however, LEADTOOLS provides a "workaround" for transferring Exif comments from JPEG files to TIFF files, and vice versa. The Exif metadata information from a TIFF file will be loaded as an APP1 marker. Therefore, you can load Exif metadata from an uncompressed file and store it in a compressed Exif file. You can also take metadata information from a compressed Exif file and store it in an uncompressed Exif file. Please note however, that some information cannot be stored inside uncompressed Exif files. For example, audio data is stored in APP2 markers and there is no built-in support for saving APP2 markers in TIFF files. However, you can still save the audio data yourself, using a custom tag. For more information on saving custom tags, refer to Implementing TIFF Comments and Tags.
Image metadata are defined by the RasterMarkerMetadata, RasterTagMetadata and RasterCommentMetadata classes. These classes can be used individually or in a collection when querying, reading or writing image metadata. The RasterImage class also contains RasterImage.Markers, RasterImage.Tags, RasterImage.Comments and RasterImage.GeoKeys collections to store the metadata in an image class. You can query, add or remove this metadata or use the RasterCodecs object to automatically read or write directly from/to these collections.
Markers (as opposed to tags and comments) are generic data types that contain all the file tags, comments and geo keys of a file. Generally, when working on an image tag or comment, the user must have prior knowledge of the metadata type and expected values. Markers are useful when dealing with image metadata without requiring any previous knowledge.
LEADTOOLS provides several methods for loading, creating, setting, modifying, getting information from and freeing markers.
To load markers (or enumerate available markers) from an existing JPEG file, call RasterCodecs.ReadMarkers. The resulting collection RasterCollection<RasterMarkerMetadata> contains all the markers from the file. Although TIFF files do not support markers, LEADTOOLS provides limited support through the use of APP1 Exif markers. When you call RasterCodecs.ReadMarkers for a TIFF file, an APP1 marker is created and all the TIFF, GPS and Exif comments will be stored in it. In addition, any other tag with an ID greater than 0x8000 (32768) will be stored in the APP1 marker. The other information will be considered as being useful only for the image and will not be loaded into the APP1 marker. The only exception is the resolution information, which will be loaded in the APP1 marker.
The RasterCollection<RasterMarkerMetadata> class lets you create a collection of markers from scratch. You can then add the created individual RasterMarkerMetadata with the RasterCollection<RasterMarkerMetadata>.Add method After you call RasterCollection<RasterMarkerMetadata>.Add once, you can call the RasterCodecs.ReadMarkers method.
Markers, like tags and comments can be set directly in the image, every image has a RasterImage.Markers collection that allows you to add, remove, insert, clear, get and set. So that when a file is saved using the RasterCodecs.Save method, the markers, tags and/or comments that have been set will be saved too.
The RasterCodecs class contains extensive support to query/load/save the markers such as file tags, comments fields and Geo keys (additional tags found in GeoTIFF files).
The following section lists common tasks performed when working with image markers as well as the LEADTOOLS class, properties and methods to use.
LEADTOOLS uses the RasterImageFormat enumeration to denote an image file format. You can obtain the format of a file by querying the CodecsImageInfo.Format property returned from RasterCodecs.GetInformation method. When you use any of the RasterCodecs.Load methods, the image format of the original source image will be stored in the RasterImage.OriginalFormat property.
Once you have a RasterImageFormat, you can pass it to the following RasterCodecs static (Shared in Visual Basic) methods to determine if this format supports the type of metadata required:
MMethod | Description |
---|---|
RasterCodecs.TagsSupported |
Determine if the format supports tags. TIFF and EXIF are some of the common File formats that support tags. |
RasterCodecs.CommentsSupported |
Determine if the format supports comment fields. TIFF, EXIF, DICOM and ICA are some of the common file formats that support comment fields. |
RasterCodecs.GeoKeysSupported |
Determine if the format supports Geo key tags. GeoTIFF is the only file format that currently supports Geo key tags. |
Many of the LEADTOOLS metadata query, read and write methods will throw an exception if the file format does not support metadata. For example, trying to call RasterCodecs.ReadTag on a BMP file will almost certainly throws a RasterExceptionCode.FeatureNotSupportred. To eliminate the need to catch and process these exceptions and speed up your code, you can use any of the methods above and only call the corresponding query, read or write metadata method if the format supports it.
For example, the following code will read the Description comment field from a disk image file. If the file supports comments, the value will be read and parsed, otherwise, it will return a default message:
private static string ReadDescriptionComment(RasterCodecs rasterCodecsInstance, string fileName)
{
// Get the file format
using(CodecsImageInfo info = rasterCodecsInstance.GetInformation(fileName, false))
{
// Check if this file format supports comment fields
if(RasterCodecs.CommentsSupported(info.Format))
{
// Yes, read it
RasterCommentMetadata comment = rasterCodecsInstance.ReadComment(fileName, 1, RasterCommentMetadataType.Description);
return comment.ToAscii();
}
else
{
// No, return a default comment
return "Description comment not supported";
}
}
}
Similarly, you can use RasterCodecs.TagsSupported and RasterCodecs.GeoKeysSupported to determine if the format supports tags and Geo TIFF keys respectively.
According to the EXIF format specification, the tag with ID 0x8298 (h8298) is the EXIF tag denoting the copyright string. The following code will print the copyright tag value found in an EXIF file:
private static void ReadTagById(RasterCodecs rasterCodecsInstance, string fileName)
{
// RasterCodecs.ReadTag will throw an exception if fileName does not
// support tags. If required, use RasterCodecs.TagsSupported first
const int exifCopyrightTagId = 0x8298;
RasterTagMetadata tag = rasterCodecsInstance.ReadTag(fileName, 1, exifCopyrightTagId);
if(tag != null)
{
// Tag is found, show the value
Console.WriteLine(tag.ToAscii());
}
}
The following code will print the description comment value found in an EXIF file:
private static void ReadCommentById(RasterCodecs rasterCodecsInstance, string fileName)
{
// RasterCodecs.ReadComment will throw an exception if fileName does not
// support comments. If required, use RasterCodecs.CommentsSupported first
RasterCommentMetadata comment = rasterCodecsInstance.ReadComment(fileName, 1, RasterCommentMetadataType.Description);
if(comment != null)
{
// comment is found, show the value
Console.WriteLine(comment.ToAscii());
}
}
Similarly, you can use the RasterCodecs.ReadGeoKey to read an individual TIFF Geo Key by ID from a file.
According to the EXIF format specification, the tag with ID 0x8298 (h8298) is the EXIF tag denoting the copyright string. The following code will update the copyright tag text in a EXIF file to "Copyright (c) My Company":
private static void WriteTagById(RasterCodecs rasterCodecsInstance, string fileName)
{
// Create the tag
const int exifCopyrightTagId = 0x8298;
RasterTagMetadata tag = new RasterTagMetadata(exifCopyrightTagId, RasterTagMetadataDataType.Ascii, null);
tag.FromAscii("Copyright (c) My Company");
// Write it to the file
rasterCodecsInstance.WriteTag(fileName, 1, tag);
}
The following code will update the description comment field in an EXIF file with "My description":
private static void WriteCommentById(RasterCodecs rasterCodecsInstance, string fileName)
{
// Create the comment
RasterCommentMetadata comment = new RasterCommentMetadata(RasterCommentMetadataType.Description, null);
comment.FromAscii("My description");
// Write it to the file
rasterCodecsInstance.WriteComment(fileName, 1, comment);
}
Similarly, you can use the RasterCodecs.WriteGeoKey to write an individual TIFF Geo Key by ID to a file.
Use the following code to read all the tags found in a TIFF or EXIF file:
private static void ReadAllTagsWithoutLoadingImage(RasterCodecs rasterCodecsInstance, string fileName)
{
// RasterCodecs.ReadTags will throw an exception if fileName does not
// support tags. If required, use RasterCodecs.TagsSupported first
RasterCollection<RasterTagMetadata> tags = rasterCodecsInstance.ReadTags(fileName, 1);
foreach(RasterTagMetadata tag in tags)
{
// Process tag
System.Diagnostics.Trace.WriteLine("Tag found, id = " + tag.Id.ToString("X"));
// The tag data can be obtained with tag.GetData() or the various .ToXXX methods
}
}
The code above reads all the tags as well as their data. To enumerate all the tags without reading the data, you can use the RasterCodecs.EnumTags method. Here is an example:
private static void ReadAllTagsWithoutData(RasterCodecs rasterCodecsInstance, string fileName)
{
// RasterCodecs.EnumTags will throw an exception if fileName does not
// support tags. If required, use RasterCodecs.TagsSupported first
rasterCodecsInstance.TagFound += new EventHandler<CodecsEnumTagsEventArgs>(rasterCodecsInstance_TagFound);
rasterCodecsInstance.EnumTags(fileName, 1);
rasterCodecsInstance.TagFound -= new EventHandler<CodecsEnumTagsEventArgs>(rasterCodecsInstance_TagFound);
}
private static void rasterCodecsInstance_TagFound(object sender, CodecsEnumTagsEventArgs e)
{
// Show the tag ID
System.Diagnostics.Trace.WriteLine("Tag found, id = " + e.Id.ToString("X"));
}
Use the following code to read all the comment fields found in a TIFF or EXIF file:
private static void ReadAllCommentsWithoutLoadingImage(RasterCodecs rasterCodecsInstance, string fileName)
{
// RasterCodecs.ReadComments will throw an exception if fileName does not
// support comments. If required, use RasterCodecs.CommentsSupported first
RasterCollection<RasterCommentMetadata> comments = rasterCodecsInstance.ReadComments(fileName, 1);
foreach(RasterCommentMetadata comment in comments)
{
// Process comment
System.Diagnostics.Trace.WriteLine("Comment found, type = " + comment.Type.ToString());
}
}
Similarly, you can use the RasterCodecs.ReadGeoKeys and RasterCodecs.EnumGeoKeys to read all TIFF Geo Keys by ID from a file.
According to the EXIF format specification, the tag with ID 0x8298 (&h8298) is the EXIF tag denoting the copyright string and tag with ID 0x010E(&h010E) is the EXIF tag denoting the image title. The following code will update these tags in an EXIF file on disk with "Copyright (c) My Company" and "My Image" respectively.
private static void WriteTagsWithoutLoadingImage(RasterCodecs rasterCodecsInstance, string fileName)
{
// RasterCodecs.WriteTags will throw an exception if fileName does not
// support tags. If required, use RasterCodecs.TagsSupported first
// Create the tags
const int exifCopyrightTagId = 0x8298;
const int exifImageTitleTagId = 0x010E;
RasterCollection<RasterTagMetadata> tags = new RasterCollection<RasterTagMetadata>();
RasterTagMetadata tag = new RasterTagMetadata(exifCopyrightTagId, RasterTagMetadataDataType.Ascii, null);
tag.FromAscii("Copyright (c) My Company");
tags.Add(tag);
tag = new RasterTagMetadata(exifImageTitleTagId, RasterTagMetadataDataType.Ascii, null);
tag.FromAscii("My Image");
tags.Add(tag);
// Write them to the file
rasterCodecsInstance.WriteTags(fileName, 1, tags);
}
Use the following code to update the copyright and description comment fields in an EXIF file without loading the image:
private static void WriteCommentsWithoutLoadingImage(RasterCodecs rasterCodecsInstance, string fileName)
{
// RasterCodecs.WriteComments will throw an exception if fileName does not
// support comments. If required, use RasterCodecs.CommentsSupported first
// Create the comments
RasterCollection<RasterCommentMetadata> comments = new RasterCollection<RasterCommentMetadata>();
RasterCommentMetadata comment = new RasterCommentMetadata(RasterCommentMetadataType.Copyright, null);
comment.FromAscii("Copyright (c) My Company");
comments.Add(comment);
comment = new RasterCommentMetadata(RasterCommentMetadataType.Description, null);
comment.FromAscii("My description");
comments.Add(comment);
// Write them to the file
rasterCodecsInstance.WriteComments(fileName, 1, comments);
}
Similarly, you can use the RasterCodecs.WriteGeoKeys to update the TIFF Geo Keys by ID in a file.
The RasterImage class contains a RasterImage.Tags and RasterImage.Comments collections that can be used to store all the tags and comments associated with an image. To load all the tags and comments from the disk file into this collection, you can either use the code described above to load the tags and comments manually then add them to the image tags and comments collection or use the CodecsLoadOptions.Tags and CodecsLoadOptions.Comments properties to automatically load all the tags and/or comments when the image is loaded.
Using these properties guarantee that no exceptions will be thrown even if the format does not support these metadata since LEADTOOLS will internally call RasterCodecs.TagsSupported and/or RasterCodecs.CommentsSupported and only load the metadata if the format supports them.
The following example will load a raster image along with all the tags and comment fields found in a disk file:
private static RasterImage LoadImageWithTags(RasterCodecs rasterCodecsInstance, string fileName)
{
// Setup the markers to be read when the image is loaded
rasterCodecsInstance.Options.Load.Markers = false;
rasterCodecsInstance.Options.Load.Tags = true;
rasterCodecsInstance.Options.Load.Comments = false;
rasterCodecsInstance.Options.Load.GeoKeys = false;
// Load and return the image
RasterImage image = rasterCodecsInstance.Load(fileName, 0, CodecsLoadByteOrder.BgrOrGray, 1, 1);
// Trace information:
System.Diagnostics.Trace.WriteLine("Format is: " + image.OriginalFormat);
System.Diagnostics.Trace.WriteLine("Number of tags loaded" + image.Tags.Count);
return image;
}
Similarly, you can use CodecsLoadOptions.GeoKeys and RasterImage.GeoKeys to read all TIFF Geo Key by ID from a file.
In addition to CodecsLoadOptions.Tags and CodecsLoadOptions.Comments discussed in the previous section, you can also use the CodecsSaveOptions.Tags and CodecsSaveOptions.Comments properties to save these metadata back to the file when the image is saved.
By using the CodecsLoadOptions.Tags, CodecsLoadOptions.Comments, CodecsSaveOptions.Tags and CodecsSaveOptions.Comments, you can perform any combination of the following tasks:
The following example will load an EXIF image, add a watermark to the image surface, and add the word "LEADTOOLS" to the image EXIF title tag and description comment fields. All other image tags and comments will be saved as is to the new image:
private static void LoadUpdateSaveImageMetadata(RasterCodecs rasterCodecsInstance, string fileName)
{
// 1. Setup the markers to be read when the image is loaded
rasterCodecsInstance.Options.Load.Markers = false;
rasterCodecsInstance.Options.Load.Tags = true;
rasterCodecsInstance.Options.Load.Comments = true;
rasterCodecsInstance.Options.Load.GeoKeys = false;
// 2. Load the image
using(RasterImage image = rasterCodecsInstance.Load(fileName, 0, CodecsLoadByteOrder.BgrOrGray, 1, 1))
{
// 3. Add the string "LEADTOOLS" to the image
// Note: If you do not require adding the string to the image, then
// skip to 4. Update te metadata
IntPtr hdc = image.CreateLeadDC();
using(Graphics g = Graphics.FromHdc(hdc))
using(Font f = new Font(FontFamily.GenericSansSerif, 40, FontStyle.Bold))
using(Brush b = new SolidBrush(Color.FromArgb(128, Color.Black)))
{
g.DrawString("LEADTOOLS", f, b, 0, 0);
}
RasterImage.DeleteLeadDC(hdc);
// 4. Update the metadata
// Since we set the CodecsLoadOptions.Tags and CodecsLoadOptions.Comments
// to true, the RasterCodecs object will read all the tags and comments from
// the file and store them in the RasterImage.Tags and RasterImage.Comments
// collections respectively.
// Find the EXIF title tag
const int exifImageTitleTagId = 0x010E;
RasterTagMetadata imageTitleTag = null;
foreach(RasterTagMetadata tag in image.Tags)
{
if(tag.Id == exifImageTitleTagId)
{
imageTitleTag = tag;
break;
}
}
// If the tag was found, update it
if(imageTitleTag != null)
{
string title = imageTitleTag.ToAscii();
title = "LEADTOOLS - " + title;
imageTitleTag.FromAscii(title);
}
// Find the description comment field
RasterCommentMetadata descriptionComment = null;
foreach(RasterCommentMetadata comment in image.Comments)
{
if(comment.Type == RasterCommentMetadataType.Description)
{
descriptionComment = comment;
break;
}
}
// If the comment was found, update it
if(descriptionComment != null)
{
string title = descriptionComment.ToAscii();
title = "LEADTOOLS - " + title;
descriptionComment.FromAscii(title);
}
// As well as updating the existing metadata, you can
// add and remove as many tags and comments from
// the collections here
// 5. Setup the markers to be written when the image is saved
rasterCodecsInstance.Options.Save.Markers = false;
rasterCodecsInstance.Options.Save.Tags = true;
rasterCodecsInstance.Options.Save.Comments = true;
rasterCodecsInstance.Options.Save.GeoKeys = false;
// Save it back
// Since we set the CodecsSaveOptions.Tags and CodecsSaveOptions.Comments
// to true, the RasterCodecs object will write all the tags and comments from
// RasterImage.Tags and RasterImage.Comments collections respectively and
// save them to the files
rasterCodecsInstance.Save(image, fileName, image.OriginalFormat, 0);
}
}
Similarly, you can use the CodecsLoadOptions.GeoKeys and CodecsSaveOptions.GeoKeys to automatically read/write all TIFF Geo Key from/to a file.
The fastest way to delete a tag with a known ID from a file is by using the RasterCodecs.DeleteTag method. This method has the advantage of deleting the tag directly from the disk file without requiring you to load or parse the image data.
The stamp is a special metadata used by only EXIF, CMP, JFIF and FlashPix formats to store a thumbnail of the image. To read or write the stamp, use RasterCodecs.ReadStamp and RasterCodecs.SaveStamp.