0

I'd like to replace the thumbnail in an image file's EXIF. I've tried several libraries: exif, piexif, Pillow/PIL, none were able to write the thumbnail to the EXIF.

Few examples from what I've tried so far:

import piexif
from PIL import Image
import exif

def replaceThumbnailMode1(sourceImageFilePath, targetImageFilePath):
    exifImage = exif.Image(sourceImageFilePath)
    thumbnailBytes = exifImage.get_thumbnail()

    piexif_dict = piexif.load(targetImageFilePath)
    piexif_dict["thumbnail"] = thumbnailBytes
    piexif_dict["1st"][513] = 1 # JPEGInterchangeFormat
    piexif_dict["1st"][514] = 1 # JPEGInterchangeFormatLength
    piexif_bytes = piexif.dump(piexif_dict)
    piexif.remove(targetImageFilePath)
    piexif.insert(piexif_bytes, targetImageFilePath)


def replaceThumbnailMode2(sourceImageFilePath, targetImageFilePath):
    exifImage = exif.Image(sourceImageFilePath)
    thumbnailBytes = exifImage.get_thumbnail()

    piexif_dict = piexif.load(targetImageFilePath)
    piexif_dict["thumbnail"] = thumbnailBytes
    piexif_dict["1st"][513] = 1 # JPEGInterchangeFormat
    piexif_dict["1st"][514] = 1 # JPEGInterchangeFormatLength
    piexif_bytes = piexif.dump(piexif_dict)
    
    im = Image.open(targetImageFilePath)
    im.save(targetImageFilePath + "2.jpg", exif = piexif_bytes)


sourceImageFilePath = "source_image.jpg"
targetImageFilePath = "target_image.jpg"

replaceThumbnailMode1(sourceImageFilePath, targetImageFilePath)
# replaceThumbnailMode2(sourceImageFilePath, targetImageFilePath)

What I've noted is that after loading the piexif_bytes with piexif.load(), in the returned exif_dict the JPEGInterchangeFormat, JPEGInterchangeFormatLength TAGs have seemingly correct / calculated values.

Please let me know what should I do differently. I don't know what I'm missing. Worths noting that I'm new to Python, sorry if I'm asking something basic.

UPDATE

I've tried the following and observed that the 2 dictionaries do not match: values for JPEGInterchangeFormat and JPEGInterchangeFormatLength in piexif_dict_from_bytes are way smaller compared to piexif_dict_from_image. For JPEGInterchangeFormat the value is around 1500, whereas it has a value of around 42 000 in piexif_dict_from_image:

def piexif_dict_test(sourceImageFilePath):
    piexif_dict_from_image = piexif.load(sourceImageFilePath)
    piexif_bytes = piexif.dump(piexif_dict_from_image)
    piexif_dict_from_bytes = piexif.load(piexif_bytes)

    print

pixeif.transplant() on the other hand, correctly copies the thumbnail from one image to the other, but I want to set a new one.

Currently I'm stuck. I'll try to find another library which does the job, if there will be no solution for piexif.

Robert
  • 131
  • 4
  • Did you check [`piexif.dump()`](https://piexif.readthedocs.io/en/latest/functions.html#piexif.dump) example? We don't know what `thumbnailBytes` is, post [mre] – buran Oct 14 '22 at 07:34
  • Yes, I've tried piexif.dump(), it didn't work unfortunately. As for thumbnailBytes, it contains the bytes of the thumbnail I want to insert into an image. Sorry, I thought that wouldn't be of much help. I've updated the example, so it can be executed. – Robert Oct 14 '22 at 08:22

1 Answers1

0

This code works fine. You probably missed out on some exif required data that is essential. Try this:

piexif_dict["Exif"][36864] = b'0221'  # Exif.Photo.ExifVersion

If not, look at the exif2 standard: https://exiv2.org/tags.html

  • Unfortunately it still does not work. Thank you for the idea. Please see the updated question for additional information. I think there is a bug in piexif.dump(). – Robert Oct 16 '22 at 12:30
  • How do you check the thumbnail in targetImageFilePath? Did you check it using this example? https://piexif.readthedocs.io/en/latest/functions.html#piexif.load – Wojciech Zając Oct 17 '22 at 11:48
  • Actually I'm using this class for reading the thumbnail: [Android ExifInterface](https://developer.android.com/reference/androidx/exifinterface/media/ExifInterface.html), as I need the generated data for testing the application. Also, the result file size is ~40 KB smaller than the original one. My expectation is that it should be the same, especially in case of the second test case in the question's _update_ section. – Robert Oct 17 '22 at 13:52