Reputation: 2974
I've been looking around for a decent way of reading metadata (specifically, the date taken) from JPEG files in C#, and am coming up a little short. Existing information, as far as I can see, shows code like the following;
BitmapMetadata bmd = (BitmapMetadata)frame.Metadata;
string a1 = (string)bmd.GetQuery("/app1/ifd/exif:{uint=36867}");
But in my ignorance I have no idea what bit of metadata GetQuery() will return, or what to pass it.
I want to attempt reading XMP first, falling back to EXIF if XMP does not exist. Is there a simple way of doing this?
Thanks.
Upvotes: 22
Views: 78492
Reputation: 1133
If you are trying to access these properties:
You can do the following:
Code Sample:
Shell32.Shell shell = new Shell32.Shell();
Shell32.Shell objShell = shell.Application;
Shell32.Folder folder = objShell.NameSpace(@"D:\TestFolder");
Shell32.FolderItem folderItem = folder.ParseName("TestMetadata.jpg");
for (int tagIndex = 0; tagIndex < 321; tagIndex++)
{
// Pass null in the first parameter to get the tagName
string tagName = folder.GetDetailsOf(null, tagIndex);
if (!string.IsNullOrEmpty(tagName))
{
// Pass an instance of Shell32.FolderItem to get the tag value.
string tagValue = folder.GetDetailsOf(folderItem, tagIndex);
Console.WriteLine($"[{tagIndex}] {tagName} = {tagValue}");
}
}
The console will display strings representing the values of interest. I'm not sure why, by dates containing single digit month and/or day values will display a ? for the 0 digit. These can easily be replaced and then the date can be parsed. Here is the output for some of the interesting ones displayed in the Properties\Details tab:
[3] Date modified = 9/3/2022 2:37 PM
[4] Date created = 9/3/2022 2:35 PM
[5] Date accessed = 9/3/2022 10:38 PM
[12] Date taken = ?1/?1/?2022 ??2:36 PM
[18] Tags = Metadata Tags
[21] Title = My Test Title
[22] Subject = Being and Nothingness
[24] Comments = Kilroy wuz here!
[25] Copyright = 2022
[136] Date acquired = ?1/?2/?2022 ??2:36 PM
As far as I am aware, there are as many as 320 different tag types.
Upvotes: 1
Reputation: 310907
I've ported my long-time open-source Java library to .NET recently, and it supports XMP, Exif, ICC, JFIF and many more types of metadata across a range of image formats. It will definitely achieve what you're after.
https://github.com/drewnoakes/metadata-extractor-dotnet
var directories = ImageMetadataReader.ReadMetadata(imagePath);
var subIfdDirectory = directories.OfType<ExifSubIfdDirectory>().FirstOrDefault();
var dateTime = subIfdDirectory?.GetDescription(ExifDirectoryBase.TagDateTime);
This library also supports XMP data, via a C# port of Adobe's XmpCore library for Java.
https://github.com/drewnoakes/xmp-core-dotnet
Upvotes: 11
Reputation: 4133
I think what you are doing is a good solution because the System.DateTaken handler automatically apply Photo metadata policies of falling back to other namespaces to find if a value exist.
Upvotes: 1
Reputation: 29002
If you're struggling with XMP jn jpeg, this works. It's not called brutal for nothing!
public class BrutalXmp
{
public XmlDocument ExtractXmp(byte[] jpegBytes)
{
var asString = Encoding.UTF8.GetString(jpegBytes);
var start = asString.IndexOf("<x:xmpmeta");
var end = asString.IndexOf("</x:xmpmeta>") + 12;
if (start == -1 || end == -1)
return null;
var justTheMeta = asString.Substring(start, end - start);
var returnVal = new XmlDocument();
returnVal.LoadXml(justTheMeta);
return returnVal;
}
}
Upvotes: 4
Reputation: 2974
The following seems to work nicely, but if there's something bad about it, I'd appreciate any comments.
public string GetDate(FileInfo f)
{
using(FileStream fs = new FileStream(f.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
BitmapSource img = BitmapFrame.Create(fs);
BitmapMetadata md = (BitmapMetadata)img.Metadata;
string date = md.DateTaken;
Console.WriteLine(date);
return date;
}
}
Upvotes: 29
Reputation: 49189
My company makes a .NET toolkit that includes XMP and EXIF parsers.
The typical process is something like this:
XmpParser parser = new XmpParser();
System.Xml.XmlDocument xml = (System.Xml.XmlDocument)parser.ParseFromImage(stream, frameIndex);
for EXIF you would do this:
ExitParser parser = new ExifParser();
ExifCollection exif = parser.ParseFromImage(stream, frameIndex);
obviously, frameIndex would be 0 for JPEG.
Upvotes: -5