5

I have a requirement to insert a unique ID into image files without modifying the image content – ie it’s just the metadata that I want to modify. I’m starting with the JPEG files because there is an appropriate EXIF property available: ImageUniqueID.

I’m using C# with .NET Core 3.1 for this exercise with ImageSharp. I can change the EXIF data with the ImageSharp using the following code (show simplified without existing record checks, etc):

using (var image = Image.Load(filename))
            {
            var imageUniqueIdentifier = Guid.NewGuid().ToString().ToLower().Replace("-", "");
            image.Metadata.ExifProfile.SetValue(ExifTag.ImageUniqueID, imageUniqueIdentifier);

            var jpegEncoder = new JpegEncoder() { Quality = 100 };
            image.Save(filename, jpegEncoder);
            }

I did play with the Quality setting in the JpegEncoder, but was still getting either unacceptable quality degradation or file size increases.

Is there a way of just reading the meta data, altering it and then writing it back without affecting the image at all?

I also looked at MetadataExtractor.NET but this doesn’t have a write facility and would happily look at other .NET Core methods or libraries.

CrispinH
  • 1,899
  • 4
  • 23
  • 38
  • Do you really need imagesharp or is it ok with WPF? – Simon Mourier Jan 14 '21 at 09:50
  • If you have 1000 dollars you could try this. It seems to do what you want: https://blog.groupdocs.com/2020/05/13/manage-exif-data-in-csharp-net-for-jpeg-png-tiff-webp-images/. Or one of the solutions [here](https://stackoverflow.com/questions/1005463/simple-way-to-remove-exif-data-from-a-jpeg-with-net) – VDWWD Jan 15 '21 at 12:57
  • @SimonMourier I used ImageSharp because I thought it might do the job. I was processing files that were going onto the web and wanted to give them a unique embedded ID so that if the file name got changed for any reason, I could still track it down. – CrispinH Jan 15 '21 at 15:00
  • I was asking because there's a .NET only solution, I have not tested in asp.net but it may work (not using any desktop UI). – Simon Mourier Jan 15 '21 at 15:54
  • I'm happy with a .NET solution, but is it confined to *Windows Presentation Foundation*? – CrispinH Jan 15 '21 at 18:07

2 Answers2

7

After some research I've found that there is ExifLibrary which allow you to modify only image metadata. Documentation (examples included)

Example how to add unique image id for jpg file:

var file = ImageFile.FromFile("path_to_jpg_file");
var imageUniqueIdentifier = Guid.NewGuid().ToString().ToLower().Replace("-", "");
file.Properties.Set(ExifLibrary.ExifTag.ImageUniqueID, imageUniqueIdentifier);

file.Save("path_to_jpg_file");

Nuget package: ExifLibNet.

user2250152
  • 14,658
  • 4
  • 33
  • 57
  • 2
    I took a look at this library and at first blush, it seems to do what I want. A test using _Beyond Compare_ between an original test image and the modified image showed that the _Image unique ID_ was correctly inserted. However, a few other properties were changed, though I'm still trying to work out if they are significant or matter. They were _GPS offset (8825), Interoperability offset (A005), Scene Type (A301) and JIF offset (0201)_. – CrispinH Jan 12 '21 at 21:10
2

Here is some code that just needs .NET with PresentationCore and WindowsBase. The underlying technology that WPF uses is WIC (Windows Imaging Component). WIC has full support for image metadata.

EXIF's ImageUniqueID is handled specifically as a Windows Property named System.Image.ImageID

Some other properties such as System.Photo.CameraModel can be seen directly in Windows Explorer detailed views if you add the corresponding column "Camera Model", but not System.Image.ImageID, AFAIK.

// needs PresentationCore & WindowsBase references
var frame = BitmapDecoder.Create(new Uri("test1.jpg", UriKind.RelativeOrAbsolute), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None).Frames[0];

// create encoder, add frame, we need to copy since we want to update metadata
var encoder = BitmapEncoder.Create(frame.Decoder.CodecInfo.ContainerFormat);
var copy = BitmapFrame.Create(frame);

// get frame metadata
var metaData = (BitmapMetadata)copy.Metadata;

// show existing System.Image.ImageID (if any)
Console.WriteLine("ImageUniqueID: " + metaData.GetQuery("System.Image.ImageID"));

// for some reason, we can't use "System.Image.ImageID" to set the meta data
// so use the "Metadata Query Language"
metaData.SetQuery("/app1/ifd/exif/{ushort=42016}", "My Super ID");

// write file back
encoder.Frames.Add(copy);
using (var stream = File.OpenWrite("test1copy.jpg"))
{
    encoder.Save(stream);
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • I'm struggling to find a means of referencing WIC in a .NET Core 5 console project (I'm just using a console project for experiment). I tried [this library](https://github.com/sbaeumlisberger/WIC-DotNet) as expedite things but it quickly got a bit too C++ for me with integer pointers, etc - I haven't done C++ for decades. My Visual Studio installation includes *.NET desktop development* but not C++ desktop development. I don't really want to install the latter if I can help it. – CrispinH Jan 18 '21 at 21:17
  • @CrispinH - You don't have to use WIC itself. If you're using a .NET Core Console app, it's the same code, just make sure you have this in your csproj: `net5.0-windowsDisableWinExeOutputInference>truetrue` – Simon Mourier Jan 18 '21 at 21:39
  • That's now worked but some parameters have changed compared to the original: *Exif offset (8769), GPS offset (8825), Interoperability offset (A005), JIF offset (0201)* and *Unknown (E1AD)* added. I'm guessing this last probably won't matter. As for the others, I will try to give some time to reading the original values for these and may be setting them back programmatically. – CrispinH Jan 20 '21 at 14:58