Resize image in Python without losing EXIF data

Question:

I need to resize jpg images with Python without losing the original image’s EXIF data (metadata about date taken, camera model etc.). All google searches about python and images point to the PIL library which I’m currently using, but doesn’t seem to be able to retain the metadata. The code I have so far (using PIL) is this:

img = Image.open('foo.jpg')
width,height = 800,600
if img.size[0] < img.size[1]:
    width,height = height,width

resized_img = img.resize((width, height), Image.ANTIALIAS) # best down-sizing filter
resized_img.save('foo-resized.jpg')

Any ideas? Or other libraries that I could be using?

Asked By: Einar Egilsson

||

Answers:

import jpeg
jpeg.setExif(jpeg.getExif('foo.jpg'), 'foo-resized.jpg') 

http://www.emilas.com/jpeg/

Answered By: jfs

PIL handles EXIF data, doesn’t it? Look in PIL.ExifTags.

Answered By: PEZ

Why not using ImageMagick?
It is quite a standard tool (for instance, it is the standard tool used by Gallery 2); I have never used it, however it has a python interface as well (or, you can also simply spawn the command) and most of all, should maintain EXIF information between all transformation.

Answered By: rob

You can use pyexiv2 to modify the file after saving it.

Answered By: iny

You can use pyexiv2 to copy EXIF data from source image. In the following example image is resized using PIL library, EXIF data copied with pyexiv2 and image size EXIF fields are set with new size.

def resize_image(source_path, dest_path, size):
    # resize image
    image = Image.open(source_path)
    image.thumbnail(size, Image.ANTIALIAS)
    image.save(dest_path, "JPEG")

    # copy EXIF data
    source_image = pyexiv2.Image(source_path)
    source_image.readMetadata()
    dest_image = pyexiv2.Image(dest_path)
    dest_image.readMetadata()
    source_image.copyMetadataTo(dest_image)

    # set EXIF image size info to resized size
    dest_image["Exif.Photo.PixelXDimension"] = image.size[0]
    dest_image["Exif.Photo.PixelYDimension"] = image.size[1]
    dest_image.writeMetadata()

# resizing local file
resize_image("41965749.jpg", "resized.jpg", (600,400))
Answered By: Maksym Kozlenko

For pyexiv2 v0.3.2, the API documentation refers to the copy method to carry over EXIF data from one image to another. In this case it would be the EXIF data of the original image over to the resized image.

Going off @Maksym Kozlenko, the updated code for copying EXIF data is:

    source_image = pyexiv2.ImageMetadata(source_path)
    source_image.read()

    dest_image = pyexiv2.ImageMetadata(dest_path)
    dest_image.read()

    source_image.copy(dest_image,exif=True)
    dest_image.write()
Answered By: bgeo

There is actually a really simple way of copying EXIF data from a picture to another with only PIL. Though it doesn’t permit to modify the exif tags.

image = Image.open('test.jpg')
exif = image.info['exif']
# Your picture process here
image = image.rotate(90)
image.save('test_rotated.jpg', 'JPEG', exif=exif)

As you can see, the save function can take the exif argument which permits to copy the raw exif data in the new image when saving. You don’t actually need any other lib if that’s all you want to do. I can’t seem to find any documentation on the save options and I don’t even know if that’s specific to Pillow or working with PIL too. (If someone has some kind of link, I would enjoy if they posted it in the comments)

Answered By: Depado
from PIL import Image
img_path = "/tmp/img.jpg"
img = Image.open(img_path)
exif = img.info['exif']
img.save("output_"+img_path, exif=exif)

Tested in Pillow 2.5.3

Answered By: Cody

It seems @Depado’s solution does not work for me, in my scenario the image does not even contain an exif segment.

pyexiv2 is hard to install on my Mac, instead I use the module pexif https://github.com/bennoleslie/pexif/blob/master/pexif.py. To “add exif segment” to an image does not contain exif info, I added the exif info contained in an image which owns a exif segment

from pexif import JpegFile

#get exif segment from an image
jpeg = JpegFile.fromFile(path_with_exif)
jpeg_exif = jpeg.get_exif()

#import the exif segment above to the image file which does not contain exif segment
jpeg = JpegFile.fromFile(path_without_exif)
exif = jpeg.import_exif(jpeg_exif)
jpeg.writeFile(path_without_exif)
Answered By: Torrence

Here’s an updated answer as of 2018. piexif is a pure python library that for me installed easily via pip (pip install piexif) and worked beautifully (thank you, maintainers!). https://pypi.org/project/piexif/

The usage is very simple, a single line will replicate the accepted answer and copy all EXIF tags from the original image to the resized image:

import piexif
piexif.transplant("foo.jpg", "foo-resized.jpg")

I haven’t tried yet, but it looks like you could also perform modifcations easily by using the load, dump, and insert functions as described in the linked documentation.

Answered By: biomiker

Updated version of Maksym Kozlenko
Python3 and py3exiv2 v0.7

# Resize image and update Exif data
from PIL import Image
import pyexiv2

def resize_image(source_path, dest_path, size):
    # resize image
    image = Image.open(source_path)
    # Using thumbnail, then 'size' is MAX width or weight
    # so will retain aspect ratio
    image.thumbnail(size, Image.ANTIALIAS)
    image.save(dest_path, "JPEG")

    # copy EXIF data
    source_exif = pyexiv2.ImageMetadata(source_path)
    source_exif.read()
    dest_exif = pyexiv2.ImageMetadata(dest_path)
    dest_exif.read()
    source_exif.copy(dest_exif,exif=True)

    # set EXIF image size info to resized size
    dest_exif["Exif.Photo.PixelXDimension"] = image.size[0]
    dest_exif["Exif.Photo.PixelYDimension"] = image.size[1]
    dest_exif.write()
Answered By: Juanma Font