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

65,516

Solution 1

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;
        }
    }

Solution 2

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

Solution 3

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;
    }
}

Solution 4

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.

Share:
65,516
tsvallender
Author by

tsvallender

A full-stack developer with an emphasis on the backend and Ruby on Rails.

Updated on July 09, 2022

Comments

  • tsvallender
    tsvallender almost 2 years

    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.

  • tsvallender
    tsvallender over 14 years
    Thank you, but this isn't a project I can afford to spend money on I'm afraid.
  • springy76
    springy76 over 9 years
    Whoever at Microsoft implemented BitmapMetaData.DateTaken is an PERFECT IDIOT! 1. Why is it string at all? Last line in get is DateTime.ToString() and first line in set is Convert.ToDateTime(). and 2.: get returns culture specific string and set expects culture insensitive string. IS THERE ANY QUALITY MANAGEMENT AT MICROSOFT AT ALL???
  • Drew Noakes
    Drew Noakes almost 9 years
    @springy76, actually you're being a bit unfair. In Exif data, dates are represented as strings. Some cameras use different formats to others, so there's no guarantee that MS's could ever write code to successfully parse any date string it encounters. At least it passes the raw string along to you so you can debug what's going on.
  • Drew Noakes
    Drew Noakes almost 9 years
    @Lijo, I don't know if BitmapMetadata provides GPS data, but you can easily use my library to do so if you like.
  • Drew Noakes
    Drew Noakes almost 9 years
    @tsvallender, you should dispose the FileStream object.
  • springy76
    springy76 almost 9 years
    @DrewNoakes no I'm not: Use Reflector/ILSpy/JustDecompile/DotPeek and look at BitmapMetaData.DateTaken yourself: WIC already provides a binary datetime value for this (a System.Runtime.InteropServices.ComTypes.FILETIME struct counting the number of 100-nanosecond intervals since January 1, 1601), but this property is f*cking it all up (please just reread what I already wrote).
  • Andi AR
    Andi AR over 7 years
    Hi how to read xmp pano tags like mentioned here stackoverflow.com/questions/39066046/…
  • Drew Noakes
    Drew Noakes over 7 years
  • modeeb
    modeeb about 6 years
    If you need the original capture date, the last line should be string dateTime = subIfdDirectory?.GetDescription(ExifDirectoryBase.TagDateTim‌​eOriginal); Or you can use this to get it in DateTime? object DateTime? dateTime = subIfdDirectory?.GetDateTime(ExifDirectoryBase.TagDateTimeOr‌​iginal);
  • twobob
    twobob almost 6 years
    @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.
  • Drew Noakes
    Drew Noakes almost 6 years
    @twobob 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.
  • twobob
    twobob almost 6 years
    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/ea6cb3b7c7d83c1b62513bcd67c0d39c
  • twobob
    twobob almost 6 years
    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.
  • twobob
    twobob almost 6 years
    Actually , this question stackoverflow.com/questions/23595560/… is also of use should one wish to go down the meta extractor route I now realise
  • Daniel Möller
    Daniel Möller over 4 years
    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?
  • Daniel Möller
    Daniel Möller over 4 years
    In order to get all metadata (not only xmp), this option works: codeproject.com/Articles/66328/…