I'm creating a simple python app to go through a folder display each JPG and allow someone to edit the photo's Title,subject,comments and add tags, then save and move on to the next. (essentially, I want to avoid, in windows, having to right click>properties>details> and edit each of the above fields, then "OK".)
Can someone please recommend the libraries and modules I need to import to display the photo and edit the properties?
I'm new to Python, so a snippet of code to show how do do it would be most appreciated.
I'm using python 3.6 in Windows 10
Thanks in advance.
To change a photos EXIF data in python you can do something like:
Get EXIF Data:
import piexif
from PIL import Image
img = Image.open(fname)
exif_dict = piexif.load(img.info['exif'])
altitude = exif_dict['GPS'][piexif.GPSIFD.GPSAltitude]
Set / Save EXIF data:
exif_dict['GPS'][piexif.GPSIFD.GPSAltitude] = (140, 1)
exif_bytes = piexif.dump(exif_dict)
img.save('_%s' % fname, "jpeg", exif=exif_bytes)
Taken from this Answer
Related
I have a set of many songs, some of which have png images in metadata, and I need to convert these to jpg.
I know how to convert png images to jpg in general, but I am currently accessing metadata using eyed3, which returns ImageFrame objects, and I don't know how to manipulate these. I can, for instance, access the image type with
print(img.mime_type)
which returns
image/png
but I don't know how to progress from here. Very naively I tried loading the image with OpenCV, but it is either not a compatible format or I didn't do it properly. And anyway I wouldn't know how to update the old image with the new one either!
Note: While I am currently working with eyed3, it is perfectly fine if I can solve this any other way.
I was finally able to solve this, although in a not very elegant way.
The first step is to load the image. For some reason I could not make this work with eyed3, but TinyTag does the job:
from PIL import Image
from tinytag import TinyTag
tag = TinyTag.get(mp3_path, image=True)
image_data = tag.get_image()
img_bites = io.BytesIO(image_data)
photo = Image.open(im)
Then I manipulate it. For example we may resize it and save it as jpg. Because we are using Pillow (PIL) for these operations, we actually need to save the image and finally load it back to get the binary data (this detail is probably what should be improved in the process).
photo = photo.resize((500, 500)) # suppose we want 500 x 500 pixels
rgb_photo = photo.convert("RGB")
rgb_photo.save(temp_file_path, format="JPEG")
The last step is thus to load the image and set it as metadata. You have more details about this step in this answer.:
audio_file = eyed3.load(mp3_path) # this has been loaded before
audio_file.tag.images.set(
3, open(temp_file_path, "rb").read(), "image/jpeg"
)
audio_file.tag.save()
I need to add arbitrary data to a JPEG image. Specifically, I need to store two integers. From reading about EXIF data, I'm under the impression that it is not possible to make your own custom fields, but rather the EXIF standard fields must be used.
This post Custom Exif Tags however mentions a UserComment field which I gather it is possible to write a string to. If this is the only option it's fine since I can store two integers in a comma-delimited string, ex '2,5' to store the integers 2 and 5, so if I only have one string of storage to work with it's still sufficient.
I downloaded a few random images from a Google image search and found they don't seem to have EXIF data, perhaps it's stripped off purposefully by Google? Also I took a few images with my cell phone and found that as expected they have a significant amount of EXIF data (image size, GPS location, etc.)
Upon some Googleing I found this example on how to read/dump EXIF data:
from PIL import Image
image = Image.open('image.jpg')
exifData = image._getexif()
print('exifData = ' + str(exifData))
This works great, if I run this on an image with no EXIF data I get:
exifData = None
and if I run this on an image with EXIF data I get a dictionary showing the EXIF fields as expected.
Now my question is, how can I add to the EXIF data? Using the UserComment 37510 field mentioned in the above linked post, and also here https://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/usercomment.html, and using piexif this is my best attempt so far:
from PIL import Image
import piexif
image = Image.open('image.jpg')
exifData = image._getexif()
if exifData is None:
exifData = {}
# end if
exifData[37510] = 'my message'
exifDataBytes = piexif.dump(exifData)
image.save('image_mod.jpg', format='jpeg', exif=exifDataBytes)
If I then run the 1st code above on image_mod.jpg I get:
exifData = {}
So clearly the 37510 message was not properly written. I get this same empty dictionary result whether I'm using an image that has EXIF data or an image without EXIF data to begin with.
Before somebody marks this as a duplicate, I also tried what this post How can I insert EXIF/other metadata into a JPEG stored in a memory buffer? mentions in the highest-rated answer and got the same result when attempting to read the EXIF data (empty dictionary).
What am I doing wrong? How can I properly add custom EXIF data to an image using 37510, or any other means?
You're missing a step in handling the data passed to piexif.dump:
exif_ifd = {piexif.ExifIFD.UserComment: 'my message'.encode()}
exif_dict = {"0th": {}, "Exif": exif_ifd, "1st": {},
"thumbnail": None, "GPS": {}}
exif_dat = piexif.dump(exif_dict)
img.save('image_mod.jpg', exif=exif_dat)
You should be able to read it back out after this. See also this answer for dealing with custom metadata.
Rasterio tags are the easiest way to add metadata of any kind to an image. Easy and practical. example:
import rasterio
old_file=rasterio.open('old_image.tif')
profile=old_file.profile
data=old_file.read()
with rasterio.open('new_image.tif','w',**profile) as dst:
dst.update_tags(a='1', b='2')
dst.write(data)
dst.close()
#now access the tags like below:
im=rasterio.open('new_image.tif')
print(im.tags())
I am looking to convert a xml file to an image (ideally a png file) using a python script. I have not found much from my online research. I am trying to use PIL. From this post on StackOverflow I was able to find this code:
from PIL import Image
import ImageFont, ImageDraw
image = Image.new("RGBA", (288,432), (255,255,255))
usr_font = ImageFont.truetype("resources/HelveticaNeueLight.ttf", 25)
d_usr = ImageDraw.Draw(image)
d_usr = d_usr.text((105,280), "MYTEXT",(0,0,0), font=usr_font)
But I do not quite understand what's happening. I tried to replace "MYTEXT" with the actual xml file content and it did not work.
I am basically looking for any solution (ideally using PIL, but it can be another module for python). I came close using imgkit:
import imgkit
imgkit.from_file('example_IN.xml','example_OUT.png')
which returns a png file. The resolution of the image is terrible though, and it lies within a very large white rectangle. I may be missing something. I know you can modify options for imgkit, but I have no idea what modifications to bring, even after checking the documentation. Any help would be deeply appreciated.
Thank you so much!
Best regards.
I had a go in pyvips:
#!/usr/bin/env python3
import sys
import pyvips
from xml.sax.saxutils import escape
# load first arg as a string
txt = open(sys.argv[1], "r").read()
# pyvips allows pango markup in strings -- you can write stuff like
# text("hello <i>sailor!</i>")
# so we need to escape < > & in the text file
txt = escape(txt)
img = pyvips.Image.text(txt)
# save to second arg
img.write_to_file(sys.argv[2])
You can run it like this:
./txt2img.py vari.ws x.png
To make this:
It's pretty quick -- that took 300ms to run on this modest laptop.
The text method has a lot of options if you want higher res, to change the alignment, wrap lines at some limit, change the font, etc. etc.
https://libvips.github.io/libvips/API/current/libvips-create.html#vips-text
The solution suggested above by jcuppit using pyvips definitely works and is quick. I found another solution to make my previous code above work using imgkit (it is slower, I am giving it here just for reference): the resolution of the output image was bad. If this happens, width and height can be changed in the options (this is an easy fix I had missed):
import imgkit
options = {
'width' : 600,
'height' : 600
}
imgkit.from_file('example_IN.xml','example_OUT.png', options=options)
And that will convert a xml file into a png file as well.
I wanted read a image using PIL.Image.open().But I've image in different path.
The following is the path I've the python script
"D:\YY_Aadhi\holy-edge-master\hed\test.py"
The following is the path I've the image file.
"D:\YY_Aadhi\HED-BSDS\test\2018.jpg"
from PIL import Image
'''some code here'''
image = Image.open(????)
How should I fill the question mark to access the image file.
you can simply do
from PIL import Image
image = Image.open("D:\\YY_Aadhi\\HED-BSDS\\test\\2018.jpg")
or
from PIL import Image
directory = "D:\\YY_Aadhi\\HED-BSDS\\test\\2018.jpg"
image = Image.open(directory)
like this.
you have to write escape sequence twice in windows, when you want to define as directory. and It will be great if you try some stupid code. It helps you a lot.
Does this image = Image.open("D:\YY_Aadhi\HED-BSDS\test\2018.jpg") not do the trick?
You can use this to read an online image
from urllib.request import urlopen
url = 'https://somewebsite/images/logo.png'
msg_image = urlopen(url).read()
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.