Question

I have found various code and libraries for editing Exif.

But they are only lossless when the image width and height is multiple of 16.

I am looking for a library (or even a way to do it myself) to edit just the Exif portion in a JPEG file (or add Exif data if it doesn't exist yet), leaving the other data unmodified. Isn't that possible?

So far I could only locate the Exif portion (starts with 0xFFE1) but I don't understand how to read the data.

Was it helpful?

Solution

Here are the specifications for the Exif interchange format, if you plan to code your own library for editing tags.

http://www.exif.org/specifications.html

Here's a library written in Perl that meets your needs that you may be able to learn from:

http://www.sno.phy.queensu.ca/~phil/exiftool/

Here's a decent .NET library for Exif evaluation from The Code Project:

http://www.codeproject.com/KB/graphics/exiftagcol.aspx

OTHER TIPS

You can do this without any external lib:

// Create image.
Image image1 = Image.FromFile("c:\\Photo1.jpg");

// Get a PropertyItem from image1. Because PropertyItem does not
// have public constructor, you first need to get existing PropertyItem
PropertyItem propItem = image1.GetPropertyItem(20624);

// Change the ID of the PropertyItem.
propItem.Id = 20625;

// Set the new PropertyItem for image1.
image1.SetPropertyItem(propItem);

// Save the image.
image1.Save("c:\\Photo1.jpg", ImageFormat.Jpg);

List of all possible PropertyItem ids (including exif) you can found here.

Update: Agreed, this method will re-encode image on save. But I have remembered another method, in WinXP SP2 and later there is new imaging components added - WIC, and you can use them to lossless write metadate - How-to: Re-encode a JPEG Image with Metadata.

exiv2net library (a .NET wrapper on top of exiv2) may be what you're looking for.

I wrote a small test where I compress one file many times to see the quality degradation and you can see it in the third-fourth compression, which is very bad.

But luckily, if you always use same QualityLevel with JpegBitmapEncoder there is no degradation.

In this example I rewrite keywords 100x in metadata and the quality seems not to change.

private void LosslessJpegTest() {
  var original = "d:\\!test\\TestInTest\\20150205_123011.jpg";
  var copy = original;
  const BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;

  for (int i = 0; i < 100; i++) {
    using (Stream originalFileStream = File.Open(copy, FileMode.Open, FileAccess.Read)) {
      BitmapDecoder decoder = BitmapDecoder.Create(originalFileStream, createOptions, BitmapCacheOption.None);

      if (decoder.CodecInfo == null || !decoder.CodecInfo.FileExtensions.Contains("jpg") || decoder.Frames[0] == null)
        continue;

      BitmapMetadata metadata = decoder.Frames[0].Metadata == null
        ? new BitmapMetadata("jpg")
        : decoder.Frames[0].Metadata.Clone() as BitmapMetadata;

      if (metadata == null) continue;

      var keywords = metadata.Keywords == null ? new List<string>() : new List<string>(metadata.Keywords);
      keywords.Add($"Keyword {i:000}");
      metadata.Keywords = new ReadOnlyCollection<string>(keywords);

      JpegBitmapEncoder encoder = new JpegBitmapEncoder {QualityLevel = 80};
      encoder.Frames.Add(BitmapFrame.Create(decoder.Frames[0], decoder.Frames[0].Thumbnail, metadata,
        decoder.Frames[0].ColorContexts));

      copy = original.Replace(".", $"_{i:000}.");

      using (Stream newFileStream = File.Open(copy, FileMode.Create, FileAccess.ReadWrite)) {
        encoder.Save(newFileStream);
      }
    }
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top