Calling image in tkinter from PIL without save it - python

I have made a function that change the black colour of an image in png (a black icon with transparent background) to the colour of the accent theme in windows.
I use this function to made all my icons match the colour interface of my window, but with this function, I need to manually call the function to the image, and then, pick the image and define as a PhotoImage to put it as a Label in tkinter.
The goal of this, is to make a method to define the main png (the black icon) as a dynamic colour image that can be used as a PhotoImage, but even with the Image.TkPhotoImage method of the PIL library, I haven't done it.
The code of my function is this:
def changeImageColorToAccentColor(imagename):
imagename = str(imagename)
accent = str(getAccentColor().lstrip('#'))
rcolor = int(str(accent[0:2]),16)
gcolor = int(str(accent[2:4]),16)
bcolor = int(str(accent[4:6]),16)
im = Image.open(str(imagename))
im = im.convert('RGBA')
data = np.array(im) # "data" is a height x width x 4 numpy array
red, green, blue, alpha = data.T # Temporarily unpack the bands for readability
# Replace white with red... (leaves alpha values alone...)
white_areas = (red == 0) & (blue == 0) & (green == 0) & (alpha == 255)
data[..., :-1][white_areas.T] = (rcolor, gcolor, bcolor) # Transpose back needed
im2 = Image.fromarray(data)
image1 = ImageTk.PhotoImage(im2)
return(image1)
And then, I define my Label in tkinter giving the image option the function that returns the PhotoImage object.
icon = Label(image=changeImageColorToAccentColor('file.png'))
But it doesn't work for me, so, if this proof doesn't work, I won't be able to make the object.

You need ta save a reference to the PhotoImage object. If it gets garbage collected the image won't show. Passing it ti the Label as image parameter does not save a reference automatically. If you do
im = changeImageColorToAccentColor('image2.png')
icon = Label(root, image=im)
the PhotoImage object is saved as im and the image will show.

Related

Overlay Text Image to A Dirty Background Image in Python

I have two images: an image with a text and an image as the dirty background.
Clean Image
Dirty Background Image
How will I overlay the clean image to the dirty background image using Python? Please assume that the clean image has the smaller size compared to the dirty background image.
There's a library called pillow (which is a fork of PIL) that can do this for you. You can play around with the placements a little, but I think it looks good.
# Open your two images
cleantxt = Image.open('cleantext.jpg')
dirtybackground = Image.open('dirtybackground.jpg')
# Convert the image to RGBA
cleantxt = cleantxt.convert('RGBA')
# Return a sequence object of every pixel in the text
data = cleantxt.getdata()
new_data = []
# Turn every pixel that looks lighter than gray into a transparent pixel
# This turns everything except the text transparent
for item in data:
if item[0] >= 123 and item[1] >= 123 and item[2] >= 123:
new_data.append((255, 255, 255, 0))
else:
new_data.append(item)
# Replace the old pixel data of the clean text with the transparent pixel data
cleantxt.putdata(new_data)
# Resize the clean text to fit on the dirty background (which is 850 x 555 pixels)
cleantxt.thumbnail((555,555), Image.ANTIALIAS)
# Save the clean text if we want to use it for later
cleantxt.save("cleartext.png", "PNG")
# Overlay the clean text on top of the dirty background
## (0, 0) is the pixel where you place the top left pixel of the clean text
## The second cleantxt is used as a mask
## If you pass in a transparency, the alpha channel is used as a mask
dirtybackground.paste(cleantxt, (0,0), cleantxt)
# Show it!
dirtybackground.show()
# Save it!
dirtybackground.save("dirtytext.png", "PNG")
Here's the output image:

how to add an alpha channel of particular value in an BGR image

I tried the below code, it doesn't show any error and runs properly, but changing the value of the alpha channel, doesn't show any change in image
img3 = cv2.cvtColor(img2, cv2.COLOR_BGR2BGRA)
img3[:,:,3] = 100
cv2.imshow('img1',img2)
cv2.imshow('img',img3)
cv2.waitKey(0)
works ok, but the output of both images are same and there is no seen-able change after applying alpha channel
i have already tried the below code
Your code is actually correct.
The simple answer is that OpenCV's imshow() ignores transparency, so if you want to see its effect, save your image as a PNG/TIFF (both of which support transparency) and view it with a different viewer - such as GIMP, Photoshop or feh.
As an alternative, I made a wrapper/decorator for OpenCV's imshow() that displays images with transparency overlaid on a chessboard like Photoshop does. So, starting with this RGBA Paddington image and this grey+alpha Paddington image:
#!/usr/bin/env python3
import cv2
import numpy as np
def imshow(title,im):
"""Decorator for OpenCV "imshow()" to handle images with transparency"""
# Check we got np.uint8, 2-channel (grey + alpha) or 4-channel RGBA image
if (im.dtype == np.uint8) and (len(im.shape)==3) and (im.shape[2] in set([2,4])):
# Pick up the alpha channel and delete from original
alpha = im[...,-1]/255.0
im = np.delete(im, -1, -1)
# Promote greyscale image to RGB to make coding simpler
if len(im.shape) == 2:
im = np.stack((im,im,im))
h, w, _ = im.shape
# Make a checkerboard background image same size, dark squares are grey(102), light squares are grey(152)
f = lambda i, j: 102 + 50*((i+j)%2)
bg = np.fromfunction(np.vectorize(f), (16,16)).astype(np.uint8)
# Resize to square same length as longer side (so squares stay square), then trim
if h>w:
longer = h
else:
longer = w
bg = cv2.resize(bg, (longer,longer), interpolation=cv2.INTER_NEAREST)
# Trim to correct size
bg = bg[:h,:w]
# Blend, using result = alpha*overlay + (1-alpha)*background
im = (alpha[...,None] * im + (1.0-alpha[...,None])*bg[...,None]).astype(np.uint8)
cv2.imshow(title,im)
if __name__ == "__main__":
# Open RGBA image
im = cv2.imread('paddington.png',cv2.IMREAD_UNCHANGED)
imshow("Paddington (RGBA)",im)
key = cv2.waitKey(0)
cv2.destroyAllWindows()
# Open Grey + alpha image
im = cv2.imread('paddington-ga.png',cv2.IMREAD_UNCHANGED)
imshow("Paddington (grey + alpha)",im)
key = cv2.waitKey(0)
cv2.destroyAllWindows()
And you will get this:
and this:
Keywords: Image, image processing, Python, alpha channel, transparency, overlay, checkerboard, chessboard, blend, blending. OpenCV, imshow, cv2.imshow.

PIL fromarray function is giving wierd colormap

I am writing a little gui for testing opencv functions - to easily change parameter values (for thresholding, blob detection etc.). I started writing the gui using tkinter and get wierd result with the Image.fromarray function - my image gets a blue tint; when I display with cv2.imshow there's no such tint so its gotta be an artifact of fromarry, I blv. I checked the mode and its RGB as expected. The image pairs are before and after blob detection (which draws little circles). The left pair is opencv and the right pair is in my tkinter gui.
tk_img=Image.fromarray(newImg)
tk_photo=ImageTk.PhotoImage(tk_img)
mod=tk_img.mode
print('mode:'+str(mod))
label1 = Tkinter.Label(self, image=tk_photo)
label1.image = tk_photo
label1.grid(row = Imrow, column = Im2col, columnspan = Im2col, sticky=Tkinter.NW)
self.update()
cv2.imshow('orig', currentImg)
cv2.waitKey(0)
cv2.imshow('current', newImg)
cv2.waitKey(0)
cv2.destroyAllWindows()
OpenCV works with images using BGR color order. You need to change color order.
newImg = newImg[...,[2,1,0]]
Look this tread for more info PIL rotate image colors (BGR -> RGB)

Transparency bug in PhotoImage

I have two pieces of code, both of them should create test.png containing a black square. The first one does it, but the second returns a transparent square. The difference between them is that the first one has a clear stripe at the left and the second does not.
The first example:
root = Tk()
image = PhotoImage(width = 50, height = 50)
for x in range(1, 50):
for y in range(50):
pixel(image, (x,y), (0,0,0))
image.write('test.png', format='png')
The second example:
root = Tk()
image = PhotoImage(width = 50, height = 50)
for x in range(50):
for y in range(50):
pixel(image, (x,y), (0,0,0))
image.write('test.png', format='png')
I also import tkinter and use function pixel(), which has this code:
def pixel(image, pos, color):
"""Place pixel at pos=(x,y) on image, with color=(r,g,b)."""
r,g,b = color
x,y = pos
image.put("#%02x%02x%02x" % (r,g,b), (x, y))
To make it short: Tkinter's PhotoImage class can't really save PNGs. It does only support GIF, PGM and PPM. You may have noticed that the preview image is correctly colored, but when you open the file, it's blank.
To save PNG images, you have to use the Python Imaging Library or, for Python 3, Pillow.
With this, the image creation is even easier:
from PIL import Image
image = Image.new("RGB", (50, 50), (0,0,0))
image.save('test.png', format='PNG')
If you need, you can convert it to PIL's ImageTk.PhotoImage object that can be used in Tkinter.

Transparency in Tkinter PhotoImage

In my simple game I'm creating I currently have placeholder rectangle objects as graphics. I'm trying to replace them with sprites, but as I understand it Tkinter doesn't have support for PNGs or alpha transparency. I am using Python 3.3, which doesn't work with PIL (and since it is a school project, I am solely trying to use Tkinter as the only external library). Is there a way to use the alpha channel with the supported file formats so that I can have multiple layers of tiles? I just want to filter out the white pixels.
I was able to use an image with transparency. I understand your wish to avoid use of PIL, but the following code works and demonstrates that Tkinter will support formats with transparency.
from Tkinter import Tk, Canvas
import PIL
root = Tk()
tkimg = PIL.ImageTk.PhotoImage('cat1-a.gif')
canvas = Canvas(root, height=600, width=600)
canvas.grid()
def stamp(event):
canvas.create_image(event.x, event.y, image=tkimg)
canvas.bind('<ButtonPress-1>', stamp)
root.mainloop()
To make the white pixels transparent (I am assuming that white means #ffffff) you could use this function below or something like it. This does not require PIL. It has worked for me for pngs, but also will work for gif.
First, make a new blank image the same size as your image.
Second, copy pixel by pixel to the new image (unless the pixel is white).
Set your original image to the new image.
Here is an example of the function being used:
from tkinter import *
def makeTransparent(img, colorToMakeTransparentInHexFormat):
newPhotoImage = PhotoImage(width=img.width(), height=img.height())
for x in range(img.width()):
for y in range(img.height()):
rgb = '#%02x%02x%02x' % img.get(x, y)
if rgb != colorToMakeTransparentInHexFormat:
newPhotoImage.put(rgb, (x, y))
return newPhotoImage
root = Tk()
mycanvas = Canvas(root, width=200, height=200,bg="orange")
mycanvas.pack()
myphotoImage = PhotoImage(file="whitecar.gif")
#set your image to the image returned by the function
myphotoImage = makeTransparent(myphotoImage, "#ffffff")
canvasImage = mycanvas.create_image(100, 100, image=myphotoImage, anchor=CENTER)
root.mainloop()
Here is an example of a white car with a white background:
Here is an example of that car on the canvas using the example program:
So I hope I have answered your question.
I did not use PIL. nothing but the tkinter module.
I only used gif, not png as you asked.
Wherever white is, will now be transparent.
Note:
For whatever reason, processing transparency multiple times with the above function can result in viewing errors in tkinter. Below is a way to remove multiple colors by using a color switching function:
Here is a car:
Here is another function to switch colors, which can be implemented before making a color transparent.
def switchColors(img, currentColor,futureColor):
newPhotoImage = PhotoImage(width=img.width(), height=img.height())
for x in range(img.width()):
for y in range(img.height()):
rgb = '#%02x%02x%02x' % img.get(x, y)
if rgb == currentColor:
newPhotoImage.put(futureColor, (x, y))
else:
newPhotoImage.put(rgb, (x, y))
return newPhotoImage
Here it is in use
root = Tk()
mycanvas = Canvas(root, width=200, height=200,bg="orange")
mycanvas.pack()
myphotoImage = PhotoImage(file="car.png")
myphotoImage = switchColors(myphotoImage,"#db0000","#ffffff") #switch red to white
myphotoImage = switchColors(myphotoImage,"#d9d9d9","#ffffff") #switch greybackground to white
myphotoImage = switchColors(myphotoImage,"#6d6d6d","#ffffff") #switch windshield grey to white
myphotoImage = makeTransparent(myphotoImage,"#ffffff") #make white transparent
canvasImage = mycanvas.create_image(100, 100, image=myphotoImage, anchor=CENTER)
root.mainloop()
And here is the result of that process:
Here is a reference to a similar problem:
How to rotate an image on a canvas without using PIL?
There is a way to use PIL with Python 3 using non-official versions of PIL
Go to http://www.lfd.uci.edu/~gohlke/pythonlibs/ to download it.

Categories

Resources