MetadataExtractor
| Language | C# (.NET Standard 2.0 and 2.1, so .NET Framework and all modern .NET) |
|---|---|
| License | Apache 2.0 |
| Repository | github.com/drewnoakes/metadata-extractor-dotnet |
| Documentation | README, format notes |
| Depends on | XmpCore (for XMP) |
| Read / write | metadata only / no |
MetadataExtractor reads the metadata segments of a JPEG without decoding pixels: Exif (IFD0, Exif SubIFD, GPS, thumbnail), camera maker notes (Canon, Nikon, Sony, Fujifilm, Olympus, Panasonic and others), XMP, IPTC, ICC profiles, JFIF, Photoshop resources and the frame header (size, components, subsampling). It returns each block as a Directory of tags with both raw values and human-readable descriptions.
It also reads PNG, GIF, HEIC, AVIF, WebP, TIFF, camera raw files and video containers. It doesn’t write metadata; use Magick.NET or ExifTool for that. It’s the .NET port of the Java metadata-extractor.
Installation#
dotnet add package MetadataExtractor
Basic usage: print all metadata#
using MetadataExtractor;
var directories = ImageMetadataReader.ReadMetadata("photo.jpg");
foreach (var directory in directories)
{
foreach (var tag in directory.Tags)
Console.WriteLine($"[{directory.Name}] {tag.Name} = {tag.Description}");
foreach (var error in directory.Errors)
Console.Error.WriteLine($"ERROR: {error}");
}
Output looks like [Exif IFD0] Model = Canon EOS R6, [Exif SubIFD] Exposure Time = 1/250 sec, [GPS] GPS Latitude = 52° 13' 47.9".
How to get the camera model and the date a photo was taken#
using MetadataExtractor;
using MetadataExtractor.Formats.Exif;
var directories = ImageMetadataReader.ReadMetadata("photo.jpg");
var ifd0 = directories.OfType<ExifIfd0Directory>().FirstOrDefault();
string? make = ifd0?.GetDescription(ExifDirectoryBase.TagMake);
string? model = ifd0?.GetDescription(ExifDirectoryBase.TagModel);
var subIfd = directories.OfType<ExifSubIfdDirectory>().FirstOrDefault();
if (subIfd != null && subIfd.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out DateTime taken))
Console.WriteLine($"{make} {model}, taken {taken:yyyy-MM-dd HH:mm}");
How to read the GPS location of a photo#
using MetadataExtractor;
using MetadataExtractor.Formats.Exif;
var gps = ImageMetadataReader.ReadMetadata("photo.jpg").OfType<GpsDirectory>().FirstOrDefault();
if (gps != null && gps.TryGetGeoLocation(out GeoLocation location))
Console.WriteLine($"{location.Latitude}, {location.Longitude}"); // decimal degrees
How to read the Exif orientation#
var ifd0 = ImageMetadataReader.ReadMetadata("photo.jpg").OfType<ExifIfd0Directory>().FirstOrDefault();
int orientation = 1;
if (ifd0 != null && ifd0.TryGetInt32(ExifDirectoryBase.TagOrientation, out int value))
orientation = value; // 1 normal, 3 = 180°, 6 = 90° clockwise, 8 = 90° counter-clockwise
How to get JPEG dimensions and encoding#
The JpegDirectory comes from the SOF marker:
using MetadataExtractor;
using MetadataExtractor.Formats.Jpeg;
var jpeg = ImageMetadataReader.ReadMetadata("photo.jpg").OfType<JpegDirectory>().FirstOrDefault();
if (jpeg != null)
{
Console.WriteLine($"{jpeg.GetImageWidth()} x {jpeg.GetImageHeight()}");
Console.WriteLine(jpeg.GetDescription(JpegDirectory.TagCompressionType)); // "Baseline" or "Progressive, Huffman"
}
Tips#
using MetadataExtractor;brings in aDirectoryclass that clashes withSystem.IO.Directory. Usevaror a namespace alias.JpegMetadataReader.ReadMetadata(path)reads only JPEG segments and is a bit faster when you know the file type.- Exif dates have no time zone. The
OffsetTimeOriginaltag (ExifDirectoryBase.TagTimeZoneOriginal), when present, holds the UTC offset.