Objective
I want to "correct" the orientation of an image. My plan is to extract the orientation from the Exif data stored with the image and use that to inform a re-orientation of the image.
Problem
It's probably me, but for this particular Exif property, the GetDescription (or GetString) simply returns null; all other properties that I have tried (x15) return a value. In the sample code below (a Console App), "Approach 1" uses the preferred and efficient GetDescription
approach to grab the image Orientation, while "Approach 2" uses an inefficient foreach
loop to iterate through directories and tags searching for the Orientation.
using MetadataExtractor;
using MetadataExtractor.Formats.Exif;
string Filename = @"D:\Users\Simon\OneDrive\My Stuff\My Source\TestFuelPriceTracker\Originals\IMG_8490.jpg";
IEnumerable<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(Filename);
// Approach 1
var subIfdDirectory = directories.OfType<ExifSubIfdDirectory>().FirstOrDefault();
var Orientation1 = subIfdDirectory?.GetDescription(ExifDirectoryBase.TagOrientation);
Console.WriteLine($"Approach 1: Orientation = \"{Orientation1}\"");
// Approach 2
foreach (var directory in directories)
{
foreach (var tag in directory.Tags)
{
switch (tag.Name)
{
case "Orientation":
Console.WriteLine($"Approach 2: Orientation = \"{tag.Description}\"");
break;
}
}
}
// Approach 3
var Orientation2 = directories
.OfType<ExifSubIfdDirectory>()
.FirstOrDefault(s => string.Equals(s?.GetDescription(ExifDirectoryBase.TagOrientation), "Orientation", StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"Approach 3: Orientation = \"{Orientation2}\"");
When run, I get the following results...
Approach 1: Orientation = ""
Approach 2: Orientation = "Top, left side (Horizontal / normal)"
Approach 3: Orientation = ""
Approach 2 shows that the Orientation information is actually present in the image. Please note that I have tried numerous images and I get the same problem. Not sure if this is relevant, but all images were taken on an iPhone 12.
Approach 3 added based on a suggestion by @aybe.
Environment
I am using Visual Studio 2022 on a Windows 11 Professional machine, all software patched to latest versions. The framework is .NET 6. MetadataExtractor version 2.7.2.
CodePudding user response:
Proposed final solution is...
var ifd0Directory = directories.OfType<ExifIfd0Directory>().FirstOrDefault();
var Orientation = ifd0Directory.TryGetInt32(ExifDirectoryBase.TagOrientation, out int value) ? value : -1;
If successful it returns an integer value between 1 and 8, or -1 if there is a problem. For anyone whose interested, the meaning of these numbers is discussed well in the following article...
JPEG Image Orientation and Exif
I would also point you at the following MIT paper which has captured the data types for each of the Exif tags; along with a lot of other good information too...
Description of Exif file format
CodePudding user response:
Your code would never work because it was comparing the integer value of the tag, that would of course never match "Orientation"
.
This works but with how this might fail as some images don't necessarily have the tags as I said, it might be worth looking at computing the orientation either from other tags or from image dimensions as a fallback solution.
private static void GetOrientation(Stream stream)
{
var directories = ImageMetadataReader.ReadMetadata(stream);
if (TryGetOrientationTag(directories, out var result))
{
}
else
{
throw new NotImplementedException("Compute orientation from image size instead");
}
}
private static bool TryGetOrientationTag(IEnumerable<Directory> directories, out string result)
{
result = null!;
foreach (var directory in directories)
{
switch (directory)
{
case ExifDirectoryBase e:
foreach (var tag in e.Tags)
{
if (tag.Type is not ExifDirectoryBase.TagOrientation || tag.Description is null)
continue;
result = tag.Description;
return true;
}
break;
case IptcDirectory i:
foreach (var tag in i.Tags)
{
if (tag.Type is not IptcDirectory.TagImageOrientation || tag.Description is null)
continue;
result = tag.Description;
return true;
}
break;
}
}
return false;
}