Reading data metadata from JPEG, XMP or EXIF in C#
Asked Answered
S

6

22

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.

Sb answered 17/2, 2010 at 13:26 Comment(1)
Are you not also interested in IPTC metadata? Jpeg files can contain three distinct types of metadata which can include a date taken field.Voidance
S
29

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;
        }
    }
Sb answered 17/2, 2010 at 15:1 Comment(5)
@Lijo, I don't know if BitmapMetadata provides GPS data, but you can easily use my library to do so if you like.Tangleberry
@tsvallender, you should dispose the FileStream object.Tangleberry
Hi how to read xmp pano tags like mentioned here #39066546Pinero
@Sb - a using statement calls dispose at the end of the blockGnatcatcher
WHile this is the accepted answer and works perfectly, it will not be supported if you plan to run your code anywhere but Windows. You will either need to use MAUI or 3rd party app for this to workExclamation
T
11

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

Tangleberry answered 12/8, 2015 at 11:46 Comment(5)
If you need the original capture date, the last line should be string dateTime = subIfdDirectory?.GetDescription(ExifDirectoryBase.TagDateTimeOriginal); Or you can use this to get it in DateTime? object DateTime? dateTime = subIfdDirectory?.GetDateTime(ExifDirectoryBase.TagDateTimeOriginal);Marcusmarcy
@drew-noakes can that thing handle Picassa Region/Face data drew? I can confirm that his library does indeed work nicely for the basics you are requesting here.Belen
@Belen it's been a while since I used Picasa, but I believe it stores its metadata in its own database or in sidecar files. MetadatExtractor doesn't yet have any support for sidecar files, though I would accept a pull request if the implementation was decent.Tangleberry
Hi drew. I used a variation of the brutalXmp below and just ripped it out wholesale. (one can store the data inside the jpgs optionally, it's in options, and write previously externally stored data into the files - also in options) I shoved up the results for your perusal (and the next poor soul who spends days working it out how to do this, with no library support. Yup, Unity3d) REFERENCE: gist.github.com/twobob/ea6cb3b7c7d83c1b62513bcd67c0d39cBelen
Actually , this question #23596060 is also of use should one wish to go down the meta extractor route I now realiseBelen
N
4

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;
    }
}
Noachian answered 10/1, 2017 at 14:53 Comment(3)
This is perfect for cases where support is very limiting. Many many thanks for this. with just the application of a GetElementsByTagName("rdf:Description") and some care one can extract Picassa3 face Region data with this. Top job.Belen
Sometimes I wonder why in heavens don't the usual frameworks provide simple stuff like this. Any hints on doing similar things without reading the full stream?Mide
In order to get all metadata (not only xmp), this option works: codeproject.com/Articles/66328/…Mide
S
1

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.

Schwann answered 3/5, 2010 at 22:48 Comment(0)
E
1

If you are trying to access these properties:

enter image description here

You can do the following:

  1. Add a reference to C:\Windows\System32\Shell32.dll. VS 2022 automatically creates an interop to interact with the ActiveX library.
  2. I added the following code to a button click event to demonstrate getting the data desired.

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.

Ebby answered 4/9, 2022 at 4:2 Comment(0)
C
-4

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.

Cutis answered 17/2, 2010 at 13:41 Comment(2)
Thank you, but this isn't a project I can afford to spend money on I'm afraid.Sb
There are free and open source alternatives.Tangleberry

© 2022 - 2024 — McMap. All rights reserved.