The pillow package has a method called Image.putalpha() which is used to add or change the alpha channel of an image.
I tried to play with this method and found that I can not change the background color of an image. The original image is
This is my code to add alpha to it
from PIL import Image
im_owl = Image.open("owl.jpg")
alpha = Image.new("L", im_owl.size, 50)
im_owl.putalpha(alpha)
im_owl.show()
The produced image is nothing different from the original image. I have tried with different value of alpha and see no difference.
What could have been wrong?
try to save the image and see it.
I am also not able to see the image directly from
im_owl.show()
but when I saved it
im_owl.save()
I am able to see the image changed.
Try using
im_owl.save("alphadOwl.png")
And then view the saved image. It would seem that the alpha channel isn't applied to bmp or jpg files. It is a bmp file that gets displayed with im.show()
(For the record, I'm on a mac, I don't know if im.show() uses different applications on other devices).
As #sanyam and #Pam have pointed out, we can save the converted image and it shows correctly. This is because on Windows, images are saved as temporary BMP file before they are shown using the system default image viewer, as per the PIL documentation:
Image.show(title=None, command=None)
Displays this image. This method is mainly intended for debugging purposes.
On Unix platforms, this method saves the image to a temporary PPM file, and calls
either the xv utility or the display utility, depending on which one can be found.
On macOS, this method saves the image to a temporary BMP file, and opens it with
the native Preview application.
On Windows, it saves the image to a temporary BMP file, and uses the standard BMP
display utility to show it (usually Paint).
To fix this issue, we can patch the Pillow code to use PNG format as default. First, we need to find the root of Pillow package:
import PIL
print(PIL.__path__)
On my system, the output is:
[’D:\Anaconda\lib\site-packages\PIL’]
Go to this directory and open the file ImageShow.py. I add the following code after the line register(WindowsViewer):
class WindowsPNGViewer(Viewer):
format = "PNG"
def get_command(self, file, **options):
return ('start "Pillow" /WAIT "%s" '
'&& ping -n 2 127.0.0.1 >NUL '
'&& del /f "%s"' % (file, file))
register(WindowsPNGViewer, -1)
After that, I can show the image with alpha channel correctly.
References
https://github.com/python-pillow/Pillow/issues/3695
Related
I need to save an Image in Python (created as a Numpy array) as a JPEG file, while including a "comment" in the file with some specific metadata. This metadata will be used by another (third-party) application and is a simple ASCII string. I have a sample image including such a "comment", which I can read out using Pillow (PIL), via the image.info['comment'] or the image.app['COM'] property. However, when I try a simple round-trip, i.e. loading my sample image and save it again using a different file name, the comment is no longer preserved. Equally, I found no way to include a comment in a newly created image.
I am aware that EXIF tags are the preferred way to save metadata in JPEG images, but as mentioned, the third-party application only accepts this data as a "comment", not as EXIF, which I cannot change. After reading this question, I looked into the binary structure of my sample file and found the comment at the start of the file, after a few bytes of some other (meta)data. I do however not know a lot about binary file manipulation, and also I was wondering if there is a more elegant way, other than messing with the binary...
EDIT: minimum example:
from PIL import Image
img = Image.open(path) # where path is the path to the sample image
# this prints the desired metadata if it is correctly saved in loaded image
print(img.info["comment"])
img.save(new_path) # save with different file name
img.close()
# now open to see if it has been saved correctly
new_img = Image.open(new_path)
print(new_img.info['comment']) # now results in KeyError
I also tried img.save(new_path, info=img.info), but this does not seem to have an effect. Since img.info['comment'] appears identical to img.app['COM'], I tried img.save(new_path, app=img.app), again does not work.
Just been having a play with this and I couldn't see anything directly in Pillow to support this. I've found that the save() method supports a parameter called extra that can be used to pass arbitrary bytes to the output file.
We then just need a simple method to turn a comment into a valid JPEG segment, for example:
import struct
from PIL import Image
def make_jpeg_variable_segment(marker: int, payload: bytes) -> bytes:
"make a JPEG segment from the given payload"
return struct.pack('>HH', marker, 2 + len(payload)) + payload
def make_jpeg_comment_segment(comment: bytes) -> bytes:
"make a JPEG comment/COM segment"
return make_jpeg_variable_segment(0xFFFE, comment)
# open source image
with Image.open("foo.jpeg") as im:
# save out with new JPEG comment
im.save('bar.jpeg', extra=make_jpeg_comment_segment("hello world".encode()))
# read file back in to ensure comment round-trips
with Image.open('bar.jpeg') as im:
print(im.app['COM'])
print(im.info['comment'])
Note that in my initial attempts I tried appending the comment segment at the end of the file, but Pillow wouldn't load this comment even after calling the .load() method to force it to load the entire JPEG file.
Update: The upcoming version Pillow version 9.4.0 will support this by passing a comment parameter while saving, e.g.:
with Image.open("foo.jpeg") as im:
im.save('bar.jpeg', comment="hello world")
hopefully that makes things easier!
I want to remove exif from an image before uploading to s3. I found a similar question (here), but it saves as a new file (I don't want it). Then I found an another way (here), then I tried to implemented it, everything was ok when I tested it. But after I deployed to prod, some users reported they occasionally got a problem while uploading images with a size of 1 MB and above, so they must try it several times.
So, I just want to make sure is my code correct?, or maybe there is something I can improve.
from PIL import Image
# I got body from http Request
img = Image.open(body)
img_format = img.format
# Save it in-memory to remove EXIF
temp = io.BytesIO()
img.save(temp, format=img_format)
body = io.BytesIO(temp.getvalue())
# Upload to s3
s3_client.upload_fileobj(body, BUCKET_NAME, file_key)
*I'm still finding out if this issue is caused by other things.
You should be able to copy the pixel data and palette (if any) from an existing image to a new stripped image like this:
from PIL import Image
# Load existing image
existing = Image.open(...)
# Create new empty image, same size and mode
stripped = Image.new(existing.mode, existing.size)
# Copy pixels, but not metadata, across
stripped.putdata(existing.getdata())
# Copy palette across, if any
if 'P' in existing.mode: stripped.putpalette(existing.getpalette())
Note that this will strip ALL metadata from your image... EXIF, comments, IPTC, 8BIM, ICC colour profiles, dpi, copyright, whether it is progressive, whether it is animated.
Note also that it will write JPEG images with PIL's default quality of 75 when you save it, which may or may not be the same as your original image had - i.e. the size may change.
If the above stripping is excessive, you could just strip the EXIF like this:
from PIL import Image
im = Image.open(...)
# Strip just EXIF data
if 'exif' in im.info: del im.info['exif']
When saving, you could test if JPEG, and propagate the existing quality forward with:
im.save(..., quality='keep')
Note: If you want to verify what metadata is in any given image, before and after stripping, you can use exiftool or ImageMagick on macOS, Linux and Windows, as follows:
exiftool SOMEIMAGE.JPG
magick identify -verbose SOMEIMAGE.JPG
I have a chart function that saves the end figure as a file. After I run the function, I also want it to display the figure at the end. So, I use this:
from PIL import Image
filepath = 'image.png'
img = Image.open(filepath)
img.show()
It works just fine, but when the file opens, it opens with a random file name, not the actual file name.
This can get troublesome as I have a lot of different chart functions that work in a similar fashion, so having logical names is a plus.
Is there a way I can open an image file with Python and have it display it's original file name?
EDIT
I'm using Windows, btw.
EDIT2
Updated the example with code that shows the same behaviour.
Instead of PIL you could use this:-
import os
filepath = "path"
os.startfile(filepath)
Using this method will open the file using system editor.
Or with PIL,
import Tkinter as tk
from PIL import Image, ImageTk # Place this at the end (to avoid any conflicts/errors)
window = tk.Tk()
#window.geometry("500x500") # (optional)
imagefile = {path_to_your_image_file}
img = ImageTk.PhotoImage(Image.open(imagefile))
lbl = tk.Label(window, image = img).pack()
window.mainloop()
The function img.show() opens a Windows utility to display the image. The image is first written to a temporary file before it is displayed. Here is the section from the PIL docs.
https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.show
Image.show(title=None, command=None)[source] Displays this image. This
method is mainly intended for debugging purposes.
This method calls PIL.ImageShow.show() internally. You can use
PIL.ImageShow.register() to override its default behaviour.
The image is first saved to a temporary file. By default, it will be
in PNG format.
On Unix, the image is then opened using the display, eog or xv
utility, depending on which one can be found.
On macOS, the image is opened with the native Preview application.
On Windows, the image is opened with the standard PNG display utility.
Parameters title – Optional title to use for the image window, where
possible.
"
The issue is that PIL uses a quick-and-dirty method for showing your image, and it's not intended for serious application use.
When i open an image in image viewer the displayed image name is wrong (not the same as loaded image). Orginal image = 'image.PNG', name in image viewer='tmpy4uvijg0.BMP' (the new name always changeds, see in image below)
from PIL import Image
imName='image.PNG'
try:
with Image.open(imName) as im:
print(imName)
im.show()
except IOError:
pass
image.png
new image
What do i wrong? Why is the name not the same?
It is because the show method save the image to a temporary file, as say in the documentation:
Displays this image. This method is mainly intended for
debugging purposes.
On Unix platforms, this method saves the image to a temporary
PPM file, and calls the xv utility.
On Windows, it saves the image to a temporary BMP file, and uses
the standard BMP display utility to show it (usually Paint).
:param title: Optional title to use for the image window,
where possible.
:param command: command used to show the image
You can try to change the title by passing a string in parameter to show.
I'm resizing images in python using Pillow
image = Image.open("image_file.jpg")
print(image.format) # Prints JPEG
resized_image = image.resize([100,200],PIL.Image.ANTIALIAS)
print(resized_image.format) # Prints None!!
Why does resized_image.format Hold a None Value?
And How can i retain the format when resizing using pillow?
Because Image.resize creates a new Image object (resized copy of the image) and for any images when creating by the library itself (via a factory function, or by running a method on an existing image), the "format" attribute is set to None.
If you need the format attribute you still can to do this:
image = Image.open("image_file.jpg") #old image object
resized_image = image.resize([100,200],PIL.Image.ANTIALIAS)
resized_image.format = image.format # original image extension
Read the docs
As stated in the documentation:
The file format of the source file. For images created by the library itself (via a factory function, or by running a method on an existing image), this attribute is set to None.
You can specify the format on save:
image.save(fp, 'JPEG')
You can save the resized images with save comments
resized_image.save("New_image.png")
It will save into your current directory.
If you want to see in the python console itself you have to run
resized_image.show()