Preserve time stamp when shrinking an image - python

My digital camera takes pictures with a very high resolution, and I have a PIL script to shrink them to 800x600 (or 600x800). However, it would be nice for the resultant file to retain the original timestamp. I noticed in the docs that I can use a File object instead of a name in PIL's image save method, but I don't know if that will help or not.
My code is basically
name, ext = os.path.splitext(filename)
# open an image file (.bmp,.jpg,.png,.gif) you have in the working folder
image = Image.open(filename)
width = 800
height = 600
w, h = image.size
if h > w:
width = 600
height = 800
name = name + ".jpg"
shunken = image.resize((width, height), Image.ANTIALIAS)
shunken.save(name)
Thank you for any help you can give!

Use shutil.copystat
It appears that PIL does not save EXIF metadata.
To copy the EXIF data using Python you could use
pyexiv2. This is how Phatch, a batch photo resizer program written in Python, deals with EXIF data, for example.
I'm not sure if you're using Ubuntu, but if so, installation is easy since pyexiv2 is provided by python-pyexiv2 package.
Edit: If you don't mind losing the EXIF metadata, and would simply like to use the EXIF datetime stamp as the resized image's modification date, then you can do it without the pyexiv2 package, thus saving you an extra dependency. Here's how:
import os
import time
import Image
import ExifTags # This is provided by PIL
img=Image.open(filename,'r')
PIL can read EXIF data, but cannot yet write EXIF data. We can access the data using the _getexif() method:
d = dict((ExifTags.TAGS[k], v) for k, v in img._getexif().items())
print(d['DateTimeOriginal'])
Parsing the timestamp may depend on what format the camera uses. This works for my camera; YMMV. The dateutils package allows you to parse a wide variety of timestamps without you having to pre-specify the format, but that's another story.
timestamp=time.strptime(d['DateTimeOriginal'],"%Y:%m:%d %H:%M:%S")
Here's an alternative way to swap the width and height:
w, h = img.size
width,height = 800,600
if h > w: width,height = height,width
Resizing the image, and using os.utime to fix the atime and mtime:
filename = filename + "-800x600.jpg"
shunken = img.resize((width, height), Image.ANTIALIAS)
shunken.save(filename)
st = os.stat(filename)
os.utime(filename,(st.st_atime,time.mktime(timestamp)))

Related

File size went up instead of down when exif removed with PIL?

Not a huge deal, but for the sake of understanding what's going on to avoid future problems...
I'd like to know why the file size of a jpg is going up after the exif data is removed. I thought it was supposed to go down?
from PIL import Image
image = Image.open(fname)
name, ext = get_fname_ext(fname)
out_name = name + '_cleaned' + ext
out_abs = output_dir + "\\" + out_name
image.save(out_abs)
Filesize before: 192.65 KB
Filesize after: 202.46 KB
Difference: +9.82 KB
What happens here is that PIL recompresses the image (the source is a JPG, but it doesn't have to, so it's treated as image data). It would be safer/easier to rely on an external tool like exiftool, imagemagick or jpegtran. The answers on this related SO question might be a good resource.
As an PIL-only alternative, you might try if the python snippet from this answer works for you:
from PIL import Image
image = Image.open('image_file.jpeg')
# next 3 lines strip exif
data = list(image.getdata())
image_without_exif = Image.new(image.mode, image.size)
image_without_exif.putdata(data)
image_without_exif.save('image_file_without_exif.jpeg')

Is it possible to get the size of an image using Bytes.IO?

I'm working on a project where two imported librairies are not working well with each other, I know it is possible to get the size of an image using :
from PIL import Image
img = Image.open(logo)
width, height = img.size
But I'd like to know if it is also possible to do that usin io ? I couldn't find anything on that
logo = request.FILES.get('logo')
img = io.BytesIO(logo.read())
... ?

Create, Modify, and Save an image in python 3.x

I'm currently making a program that renders information to a buffer, and I want to save the information as an image file of some sort to my working directory. I've seen some examples using PIL, but that library isn't supported for python 3.x. Are there better alternatives?
First uninstall PIL than install Pillow
its a PIL's clone which works on python 3.x.
from PIL import Image
img = Image.open("test1.jpg") #jpg, png, etc.
pix = img.load()
print img.size #Get the width and height of the image for iterating over
print pix[15,15] #Get the RGBA Value of the a pixel of an image
pix[15, 15] = value # Set the RGBA Value of the image (tuple)
img.save("out.jpg") # Saves the modified pixels to image

Get image width and height without downloading in PYTHON

Is there anyway to get image width and height without downloading from original location in PYTHON. I have an idea how to get image info when it is in our server. Buy no idea this can do with online resource image in PYTHON.
Finally done this as follow in python. Anyway have to download and get image info
import cStringIO
import urllib
import Image
file = urllib.urlopen('http://static.php.net/www.php.net/images/php.gif')
im = cStringIO.StringIO(file.read())
img = Image.open(im)
print img.format, img.size, img.mode
GIF (120, 67) P
width, height = img.size
print width, height
You can't. You must download a certain amount of the file before you get to the metadata that contains the dimensions of the image.

Resize image in Python without losing EXIF data

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?
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)
import jpeg
jpeg.setExif(jpeg.getExif('foo.jpg'), 'foo-resized.jpg')
http://www.emilas.com/jpeg/
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))
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.
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.
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()
You can use pyexiv2 to modify the file after saving it.
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
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)
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()
PIL handles EXIF data, doesn't it? Look in PIL.ExifTags.

Categories

Resources