BytesIO replaces transparency in PNG files with black background - python-3.x

I want to keep transparent background at my 'image' variable.
If I write into a file, image looks fine. I mean image has a transparent background.
with urllib.request.urlopen(request) as response:
imgdata = response.read()
with open("temp_png_file.png", "wb") as output:
output.write(imgdata)
However, if I keep the image data in BytesIO, transparent background becomes a black background.
with urllib.request.urlopen(request) as response:
imgdata = response.read()
ioFile = io.BytesIO(imgdata)
img = Image.open(ioFile)
img.show()
(Above code piece, img.show line shows an image with black background.)
How can I keep a transparent image object in img variable ?

Two things...
Firstly, if you want and expect an RGBA image when opening a file with Pillow, it is best to convert whatever you get into that - else you may end up trying to display palette indices instead of RGB values:
So change this:
img = Image.open(ioFile)
to this:
img = Image.open(ioFile).convert('RGBA')
Secondly, OpenCV's imshow() can't handle transparency, so I tend to use Pillow's show() method instead. Like this:
from PIL import Image
# Do OpenCV stuff
...
...
# Now make OpenCV array into Pillow Image and display
Image.fromarray(numpyImage).show()

Related

Embedding Images and matplotlib Plots together on on tk canvas

A few years ago I wrote a script in python2 that would pull images and generate plots and show them together in one gui, following this old documentation. However, I'm trying to update this to python3, and I've run into an issue with the way that plots are put on tk windows in the latest version of matplotlib. There are updated guides for how to embed matplotlib graphs in tk windows, of course, but these don't seem to be exactly what I want, since I want to plot various images and graphs together.
Currently:
# Start by creating the GUI as root
root=Tk()
root.wm_title("JADESView")
# Create the canvas
canvas=Canvas(root, height=canvasheight, width=canvaswidth)
# Plot the EAZY SED
#image = Image.open(EAZY_files+str(current_id)+"_EAZY_SED.png")
image = getEAZYimage(current_id)
# Crop out the thumbnails
image = cropEAZY(image)
photo = resizeimage(image)
item4 = canvas.create_image(eazy_positionx, eazy_positiony, image=photo)
Label(root, text="EAZY FIT", font=('helvetica', int(textsizevalue*1.5))).place(x=eazytext_positionx, y = eazytext_positiony)
# Plot the BEAGLE SED
#new_image = Image.open(BEAGLE_files+str(current_id)+"_BEAGLE_SED.png")
new_image = getBEAGLEimage(current_id)
new_photo = resizeimage(new_image)
item5 = canvas.create_image(beagle_positionx, beagle_positiony, image=new_photo)
Label(root, text="BEAGLE FIT", font=('helvetica', int(textsizevalue*1.5))).place(x=beagletext_positionx, y = beagletext_positiony)
canvas.pack(side = TOP, expand=True, fill=BOTH)
# Plot the thumbnails
fig_photo_objects = np.empty(0, dtype = 'object')
fig_photo_objects = create_thumbnails(canvas, fig_photo_objects, current_id, current_index, defaultstretch)
I create a tk.canvas object, and then use canvas.create_image to place two PIL photo objects, with labels (the "EAZY SED" and BEAGLE SED"), and this still works with python3. However, the create_thumbnails function I've written creates individual figure objects and then calls a function "draw_figure" to embed them, which I'll post below, but it's from the tk example I linked above:
def draw_figure(canvas, figure, loc=(0, 0)):
""" Draw a matplotlib figure onto a Tk canvas
loc: location of top-left corner of figure on canvas in pixels.
Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
"""
figure_canvas_agg = FigureCanvasAgg(figure)
figure_canvas_agg.draw()
figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = PhotoImage(master=canvas, width=figure_w, height=figure_h)
# Position: convert from top-left anchor to center anchor
canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)
# Unfortunately, there's no accessor for the pointer to the native renderer
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
# Return a handle which contains a reference to the photo object
# which must be kept live or else the picture disappears
return photo
And this breaks in all sorts of ways.
I'm open to overhauling how everything is done, but I don't see examples that show how this sort of thing might work. The examples for the latest version of matplotlib all explain that you should use FigureCanvasTkAgg, but I don't exactly know how to use this if I already have an existing canvas with photos embedded.
Any help would be appreciated, and I can also explain more if necessary!

Why saving Image object is not the same for thumbnail() and resize()?

from PIL import Image, ImageEnhance, ImageFilter
image = "Ash and Pikachu.png"
image = Image.open(image)
images = image.thumbnail((400, 320)) # thumbnail() works by changing the var name
# image = image.thumbnail((400, 320)) # gives error by keeping same var name
image.save("NewImage.png")
The above code will convert the image to thumbnail as expected. But by replacing thumbnail() with resize(), it just copies and saves the source image with new name.
from PIL import Image, ImageEnhance, ImageFilter
image = "Ash and Pikachu.png"
image = Image.open(image)
# images = image.thumbnail((400, 320)) # doesn't throw errors but doesn't resize the image
image = image.resize((400, 320)) # resize() works by keeping same var name
image.save("NewImage.png")
I'm not using both at the same time, but just wanted to point out where I'm facing issues. Anyhow, can I use the same code to save images in both thumbnail() and resize()?
Let's see the documentation on Image.thumbnail:
Note that this function modifies the Image object in place.
And, the documentation on Image.resize states:
Returns a resized copy of this image.
So, the correct usage of both methods including saving looks like this:
from PIL import Image
image = Image.open('path/to/your/image.png')
image.thumbnail((200, 200))
image.save('thumbnail.png')
image = Image.open('path/to/your/image.png')
image = image.resize((200, 200))
image.save('resize.png')
Both save a (200, 200) version of the input image. Notice: There's no reassignment of image in the first case, but in the second. That's in line with your codes. Inspect images in your first case, it's no proper Image object (it's None actually), but saving image still works, because Image.thumbnail worked in place. In your second case, you explicitly reassign image, which is correct here.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.8.5
Pillow: 8.0.1
----------------------------------------

Python Pillow ImageChops.difference always None

I'm trying to compare screenshots of 2 interactive maps. The screenshots are taken with Selenium and using Pillow to compare.
...
from selenium.webdriver.common.by import By
from selenium import webdriver
from io import BytesIO
from PIL import ImageChops, Image
...
png_bytes1 = driver.find_element(By.CSS_SELECTOR, "body").screenshot_as_png
png_bytes2 = driver2.find_element(By.CSS_SELECTOR, "body").screenshot_as_png
img1 = Image.open(BytesIO(png_bytes1))
img2 = Image.open(BytesIO(png_bytes2))
diff = ImageChops.difference(img1, img2)
print(diff.getbbox())
But diff is always blank. I manually used img1.show() and img2.show() to obtain the images below. diff.show() is always blank and diff.getbbox() prints None. What am I doing wrong and is there a better way of doing it?
Update: It works if I first save these images as jpg. Anyone have ideas why?
It seems ImageChops.difference() will only work if the image parameters are Image objects. PNG files are PngImageFile objects, with an RGBA mode for an extra alpha layer and need to be converted using converted_img1 = img1.convert('RGB').

PyMuPDF insert image at bottom

I'm trying to read a PDF and insert an image at bottom(Footer) of each page in PDF. I've tried with PyMuPDF library.
Problem: Whatever the Rect (height, width) I give, it doesn't appear in the bottom, image keeps appearing only on the top half of the page (PDF page).
My code:
from fitz import fitz, Rect
doc = fitz.open("test_pdf2.pdf")
def add_footer(pdf):
img = open("footer4.jpg", "rb").read()
rect = Rect(0, 0, 570, 1230)
for i in range(0, pdf.pageCount):
page = pdf[i]
if not page._isWrapped:
page._wrapContents()
page.insertImage(rect, stream=img)
add_footer(doc)
doc.save('test_pdf5.pdf')
Processed_image with footer image in the middle of the page:
https://i.stack.imgur.com/HK9mm.png
Footer image:
https://i.stack.imgur.com/FRQYE.jpg
Please help!
Please let me know if this can be achieved by using any other library.
Some pages internally change the standard PDF geometry (which uses the bottom-left page point as (0, 0)) to something else, and at the same time do not encapsulate this doing properly as they should.
If you suspect this, use PyMuPDF methods to encapsulate the current page /Contents before you insert your items (text, images, etc.).
The easiest way is Page._wrapContents() which is also the friendly way to keep incremental PDF saves small. The MuPDF provided Page.cleanContents() does this and a lot more cleaning stuff, but bloats the incremental save delta, if this is a concern.
After doing little trial and error I was able to figure out the problem. I was missing out on proper dimensions of Rect, I had to give 0.85*h in second parameter.
below is the code:
from fitz import fitz, Rect
doc = fitz.open("test_pdf2.pdf")
w = 595
h = 842
def add_footer(pdf):
img = open("footer4.jpg", "rb").read()
rect = fitz.Rect(0, 0.85*h, w, h)
for i in range(0, pdf.pageCount):
page = pdf[i]
if not page._isWrapped:
page._wrapContents()
page.insertImage(rect, stream=img)
add_footer(doc)
doc.save('test_pdf5.pdf')
use:
wrapContents – Page.wrap_contents()
https://pymupdf.readthedocs.io/en/latest/znames.html?highlight=wrapContents
from fitz import fitz, Rect
doc = fitz.open("example.pdf")
w = 250
h = 400
def add_footer(pdf):
img = open("firmaJG.png", "rb").read()
rect = fitz.Rect(0, 0.85*h, w, h)
for i in range(0, pdf.pageCount):
page = pdf[i]
if not page._isWrapped:
page.wrap_contents()
page.insertImage(rect, stream=img)
add_footer(doc)
doc.save('test_pdf5.pdf')

Tkinter Background Image

I am not able to display an image as the background of my GUI window with the below code, any ideas? I have changed files extension types many times.
from tkinter import *
from PIL import ImageTk, Image
window = Tk()
window.title("My Application")
lbl = Label(window, text="Hello", font=("arial italic", 25))
lbl.grid(column=0, row=0)
backgroundImage = PhotoImage(file = "C:\\Users\\User
Person\\Desktop\\months.gif")
label = Label(master=window,
image = backgroundImage,
text='This is a test for stackflow',
height = 2
)
label.place(x=0, y=0, relwidth=1, relheight=1)
window.mainloop()
I am able to display an image with your code but I used a raw string to define the path for the gif image. You should use it like this:
backgroundImage = PhotoImage(file = r"C:\Users\User Person\Desktop\months.gif")
Notice the r" " which makes the image path a raw string, so you don't need to escape the \.
I also defined a window geometry:
window.title("My Application")
window.geometry("320x240")
Update:
As per comment, you can also use the PIL library to open the image first to fix the error Tkinter error: Couldn't recognize data in image file:
from PIL import ImageTk, Image
backgroundImage = ImageTk.PhotoImage(Image.open(r"C:\Users\User Person\Desktop\months.gif"))
Note:
You must also verify that this is a valid gif file. Try opening the file in Windows Picture viewer or similar software. Also, you mentioned that you have changed the file extension, this could make the file unstable and render it invalid. Therefore, make sure you use the original file extension. If you'd like to convert the file to a different format, use specialized software for that or write a simple Python script(although I've never tried this option) that could perform this conversion without breaking the file.

Resources