28

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?

Einar Egilsson
  • 3,438
  • 9
  • 36
  • 47
  • My answer to the following question shows how exif data can be retained using only PIL: http://stackoverflow.com/questions/17042602/preserve-exif-data-of-image-with-pil-when-resizecreate-thumbnail – Gary Kerr Jun 11 '13 at 15:20

11 Answers11

17

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)

Depado
  • 4,811
  • 3
  • 41
  • 63
  • thanks, exactly what I was looking for. It was not really explicit in the Pillow docs. To modify the exif tags, I use GExiv2 on the saved file. – barsanuphe Aug 30 '14 at 11:06
  • 1
    This actually works, not sure why it is not discussed more for simple tasks, since other libraries can be tricky adding to different OS environments. – Shane Feb 29 '16 at 22:25
  • 1
    This is great. Thank you. It works as I hoped. – Joe Thor May 16 '21 at 19:36
12
import jpeg
jpeg.setExif(jpeg.getExif('foo.jpg'), 'foo-resized.jpg') 

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

jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 2
    However, this will save wrong information, especially regarding the X and Y resolutions. – rob Dec 30 '08 at 21:42
  • Nothing prevents you from updating corresponding field in the EXIF info before saving it. – jfs Dec 31 '08 at 06:43
  • And when the exif data are to long, you get this error : http://stackoverflow.com/questions/1606514/how-to-use-pil-to-resize-and-apply-exif-information-to-the-file – Natim Oct 22 '09 at 11:14
  • 2
    @Jake: you can try http://tilloy.net/dev/pyexiv2/ as suggested by @iny http://stackoverflow.com/questions/400788/resize-image-in-python-without-losing-exif-data/403293#403293 . Links for old content (jpeg module): http://web.archive.org/web/20080425064422/http://www.emilas.com/jpeg/ http://code.google.com/p/pipp/downloads/detail?name=jpeg-0.1.5.win32.zip – jfs Oct 27 '10 at 06:48
  • Thanks @J.F. Sepastian, pyexiv2 looks like the best took for the job but it has a lot of dependencies for a seemingly simple task. – Jake Oct 27 '10 at 09:16
  • 3
    the jpeg library (if you can find it) looks to be win32 only. – RyanN Feb 20 '13 at 19:46
10

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))
Maksym Kozlenko
  • 10,273
  • 2
  • 66
  • 55
  • which version of pyexiv2? earlier the version, the less likely "copyMetadataTo" will be there. I'm using Ubuntu 8.04 & don't have this method, V0.1.1. – bootload Sep 07 '11 at 04:39
  • For a solution that works under pyexiv2 version 0.3 (the last before the author declared the project discontinued), see http://stackoverflow.com/a/18318254/1017348 – Joachim W Aug 19 '13 at 16:08
4

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.

rob
  • 36,896
  • 2
  • 55
  • 65
  • 1
    It just happens that PIL is the standard tool in python and so ImageMagick python bindings are not that well available. – iny Dec 30 '08 at 19:38
  • In that case, I would spawn a separate process. ImageMagick (as an executable) is available on almost every configuration. – rob Dec 30 '08 at 21:39
  • The problem with ImageMagick bound to Python is not availability, but lack of documentation. – Joachim W Aug 18 '13 at 19:43
4

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.

biomiker
  • 3,108
  • 1
  • 29
  • 26
3

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()
bgeo
  • 446
  • 5
  • 6
1

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

iny
  • 7,339
  • 3
  • 31
  • 36
0
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

Cody
  • 4,353
  • 4
  • 39
  • 42
0

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)
Torrence
  • 448
  • 3
  • 20
0

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()
Juanma Font
  • 346
  • 5
  • 10
-1

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

PEZ
  • 16,821
  • 7
  • 45
  • 66
  • 4
    resize() deletes EXIF information, and there's no way to put it back in PIL, AFAICT. You can get the data with img._getexif(), but after the resize the method no longer exists – Vinko Vrsalovic Dec 30 '08 at 17:05