Making .png files with transparent pixels - python

I can make some .png files using PIL and tkinter:
import tkinter
from PIL import ImageGrab
canvas = tkinter.Canvas(width=100, height=100)
SomeInterestingPhotoImage = PhotoImage(file='Path/To/My/file.png')
canvas.create_image(0, 0, image=SomeInterestingPhotoImage, anchor='nw')
x0 = canvas.winfo_rootx()
y0 = canvas.winfo_rooty()
x1 = x0 + canvas.winfo_width()
y1 = y0 + canvas.winfo_height()
image = ImageGrab.grab((x0, y0, x1, y1))
image.save(name.png)
# I need to make some pixels transparent.
Here I can make an .png file, but all pixels have some colour. I need to make white pixels of canvas (not of image) transparent.

import glob
from PIL import Image
def transparent(myimage):
img = Image.open(myimage)
img = img.convert("RGBA")
pixdata = img.load()
width, height = img.size
for y in range(height):
for x in range(width):
if pixdata[x, y] == (0, 0, 0, 255):
pixdata[x, y] = (0, 0, 0, 0)
img.save(myimage, "PNG")
for image in glob.glob("*.png"):
transparent('mypic.png')
This code will basically change black (0, 0, 0, 255) to transparent (0, 0, 0, 0) pixels

Related

Tkinter - How to remove white background of pictures of buttons and in general

I'm pretty new to Tkinter and I have made for a program a screen of buttons that, when pressed, do a command.
Picture of the screen:
I'm using the pictures with the buttons. The background of the photos make the screen look aesthetically unpleasing, so I have there's a way to remove the white background of the pictures.
Any advice would be appreciated! thank you very much in advance.
Also, here is how I put those image on the screen with the button (example given is keyboard image):
keyb_img = resize_img('/ServerImages/KeyBPhoto.jpg', 100)
self.keyb_img = keyb_img
keyb_share_button = Button(self.top_level, image=self.keyb_img, command=lambda: self.keyb_press('Keyboard_Change'), borderwidth=0).grid(row=2, column=5, pady=100, padx =15)
As #Matiiss pointed out: .jpg images don't have transparent backgrounds.
So we have to remove the background from the image. To do that we can use PIL like this:
import tkinter as tk
from PIL import Image, ImageTk
width = 100
height = 100
image = Image.open("/ServerImages/KeyBPhoto.jpg").convert("RGBA")
image = image.resize((width, height), Image.LANCZOS)
# Load the pizels into memory
pixels = image.load()
# For each pixel in the image
for i in range(image.size[0]):
for j in range(image.size[1]):
# If the pixel is white
if pixels[i, j] == (255, 255, 255, 255):
# Make it transparent
pixels[i, j] = (255, 255, 255, 0)
# Save the now transparent image:
image.save("new_image.png", format="png")
# Show it on the screen
root = tk.Tk()
canvas = tk.Canvas(root, bg="red")
canvas.pack()
tk_image = ImageTk.PhotoImage(image)
canvas.create_image(0, 0, image=tk_image, anchor="nw")
root.mainloop()
Please note that this will only convert the pure while pixels into transparent. If a pixel has a value of #fffffe (pure white is #ffffff), the program will ignore it. Also it will convert all while pixels.
EDIT: by #furas
If you would use numpy array then it could be shorter
import numpy as np
# convert pillow.Image to numpy.array
array = np.array(image)
mask = np.all(array == [255, 255, 255, 255], axis=-1) # png without artefacts
array[ mask ] = [255, 255, 255, 0]
# convert numpy.array to pillow.Image
image = Image.fromarray(array)
But JPG may have artefacts like #fffffe instead of pure white #ffffff and then you can use mask with >=
mask = np.all(array >= [230, 230, 230, 255], axis=-1) # jpg with artefacts
Minimal working code:
I first remove color and later resize it to reduce artefacts.
import tkinter as tk
from PIL import Image, ImageTk
import numpy as np
def convert_pillow(image):
#print('[DEBUG] convert_pillow')
# Load the pizels into memory
pixels = image.load()
# For each pixel in the image
for i in range(image.size[0]):
for j in range(image.size[1]):
# If the pixel is white
if pixels[i, j] == (255, 255, 255, 255):
# Make it transparent
pixels[i, j] = (255, 255, 255, 0)
return image
def convert_numpy(image):
#print('[DEBUG] convert_numpy')
import numpy as np
# convert pillow.Image to numpy.array
array = np.array(image)
#mask = np.all(array == [255, 255, 255, 255], axis=-1) # png without artefacts
mask = np.all(array >= [230, 230, 230, 255], axis=-1) # jpg with artefacts
array[ mask ] = [255, 255, 255, 0]
# convert numpy.array to pillow.Image
image = Image.fromarray(array)
return image
# --- main ---
width = 100
height = 100
filename = "test/rgb.png"
filename = "test/rgb.jpg"
image = Image.open(filename).convert("RGBA")
#image = convert_pillow(image)
image = convert_numpy(image)
# resize after changing color - because resize creates new artefacts
image = image.resize((width, height), Image.LANCZOS)
# Save the now transparent image:
image.save("new_image.png", format="png")
# Show it on the screen
root = tk.Tk()
canvas = tk.Canvas(root, bg="gray")
canvas.pack()
tk_image = ImageTk.PhotoImage(image)
canvas.create_image(0, 0, image=tk_image, anchor="nw")
root.mainloop()
Images for tests:
rgb.png
rgb.jpg
Result:

How to move the circular image to the exact center position of the bigger image?

I want to move the circular image to the exact center position of the bigger image. How to do accomplish that task accurately?
from IPython.display import display
import numpy as np
from PIL import Image, ImageDraw, ImageFilter
def show_saved_image(str):
img = Image.open(str)
display(img)
im1 = Image.open('rocket.jpg')
im2 = Image.open('lena.jpg')
#height, width, channels = im1.shape
im1_width, im1_height = im1.size
im1_centreX, im1_centreY = int(im1_width/2), int(im1_height/2)
im2_width, im2_height = im2.size
im2_centreX, im2_centreY = int(im2_width/2), int(im2_height/2)
print(im1_width, im1_height)
print(im2_width, im2_height)
radius = int(min(im2_width/2, im2_height/2))
ulX, ulY = im2_centreX-radius, im2_centreY-radius
lrX, lrY = im2_centreX+radius, im2_centreY+radius
desired_pointX, desired_pointY = im1_centreX-radius, im1_centreY-radius
# ![rocket_pillow_paste_out](data/dst/rocket_pillow_paste_out.jpg)
mask_im = Image.new("L", im2.size, 0)
draw = ImageDraw.Draw(mask_im)
draw.ellipse((ulX, ulY, lrX, lrY), fill=255)
# mask_im_blur = mask_im.filter(ImageFilter.GaussianBlur(10))
# mask_im_blur.save('mask_circle_blur.jpg', quality=95)
back_im = im1.copy()
back_im.paste(im2, (desired_pointX, desired_pointY), mask_im)
#back_im.paste(im2, (desired_pointX, desired_pointY), mask_im_blur)
back_im.save('output.jpg', quality=95)
im = Image.open('output.jpg')
draw = ImageDraw.Draw(im)
draw.ellipse((im1_centreX-4, im1_centreY-4, im1_centreX+4, im1_centreY+4 ), fill=(0, 255, 0), outline=(0, 0, 0))
draw.ellipse((desired_pointX-4, desired_pointY-4, desired_pointX+4, desired_pointY+4 ), fill=(255, 0, 0), outline=(0, 0, 0))
im.save('imagedraw.jpg', quality=95)
show_saved_image("imagedraw.jpg")
Images:
rocket.jpg
lena.jpg
If there is another way, then please help me with that, too.
You just need to modify this line
desired_pointX, desired_pointY = im1_centreX - radius, im1_centreY - radius
to
desired_pointX, desired_pointY = im1_centreX - int(im2_width/2), im1_centreY - int(im2_height/2)
Your mask_im has shape im2.size, so you need to adapt to that, not just the radius of the circle. Since radius is int(im2_height/2), the vertical alignment is fine, but radius is smaller than int(im2_width/2), that's why the insufficient shift leftwards.

Python transparent watermark is not transparent

I have this issue, where whatever watermark I put, the background, which is transparent, appears as non-transparent.
Here is the code:
from PIL import Image
import glob
def watermark_with_transparency(input_image_path, output_image_path, watermark_image_path):
base_image = Image.open(input_image_path) # open base image
watermark = Image.open(watermark_image_path) # open water mark
width, height = base_image.size # getting size of image
width_of_watermark, height_of_watermark = watermark.size
position = [width / 2 - width_of_watermark / 2, height / 2 - height_of_watermark / 2]
position[0] = int(position[0])
position[1] = int(position[1])
water = watermark.copy()
water.convert('RGBA')
water.putalpha(70)
water.save('solid.png')
transparent = Image.new('RGBA', (width, height), (0, 0, 0, 0))
transparent.paste(base_image, (0, 0))
transparent.paste(water, position, mask=water)
transparent.show()
transparent.convert('RGB').save(output_image_path)
print('Image Done..!')
for inputImage in glob.glob('images/*.jpg'):
output = inputImage.replace('images\\', '')
outputImage = 'watermark images\\' + str(output)
watermark_with_transparency(inputImage, outputImage, 'watermark images/watermark.png')
Here you may see what the result is:

Find new coordinates of a point after image resize

I've resized my image to newX, newY. Prior to resizing I had a point (x,y). Now that I've resized my image I'd like to know where the point is on the new image. Sounds simple but I'm bad at math. Any ideas?
It is just a matter of ratios:
On the x-axis, you have resized by a ratio Rx = newX/oldX, and by a ratio Ry = newY/oldY on the y-axis.
Therefore, your new coordinates for point (x,y) are (Rx * x, Ry * y).
from heatmappy import Heatmapper
from PIL import Image
import cv2
import numpy as np
from PIL import ImageFont
from PIL import ImageDraw
def generate_heatmap_data_list(coord_list):
cumulative_data_list = []
for lsingledata in coord_list:
temp = []
for x in range(lsingledata[0][0],lsingledata[1][0]):
for y in range(lsingledata[0][1],lsingledata[1][1]):
temp.append([x,y])
data = [temp[i] for i in range(len(temp)) if (i%250)<lsingledata[2]]
cumulative_data_list += data
return cumulative_data_list
coord = [[[774,265],[909,409],1],[[985,809],[1139,992],5],[[514,842],[803,1024],10],[[127,629],[283, 869],20],[[258,442],[429, 584],30],
[[827,851],[980,1033],40],[[343,611],[514,753],1],[[500,358],[595,409],50],[[163,879],[334,999],15]]
data = generate_heatmap_data_list(coord)
example_img_path = r"C:\Workarea\heatmap\code_testing_pyheatmap\blue_print.jpg"
example_img = Image.open(example_img_path)
print("###", type(example_img))
width, height = example_img.size
original_dim = (width,height)
##resize_dim to plot the heatmap size
resize_dim = (1237,1036)
example_img = example_img.resize(resize_dim)
new_point_list = []
for lsingle_point in data:
x1 = int((lsingle_point[0] * (resize_dim[0] / original_dim[0])))
y1 = int((lsingle_point[1] * (resize_dim[1] / original_dim[1])))
new_point_list.append([x1,y1])
heatmapper = Heatmapper()
heatmap = heatmapper.heatmap_on_img(new_point_list, example_img)
print("PIL_type: ", type(heatmap))
heatmap.save('temp.png')
######if you want to plot percentage on image
img = cv2.imread('temp.png')
print("cv2_type:", type(img))
img = cv2.putText(img, '1%', (803,341), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##FRANGRANCE
img = cv2.putText(img, '5%', (1027,919), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##COSMETICS
img = cv2.putText(img, '10%', (661,977), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##HONEY
img = cv2.putText(img, '20%', (209,765), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##AJILE
img = cv2.putText(img, '30%', (337,539), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##ANNABELLE
img = cv2.putText(img, '40%', (909,953), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##SUNGLASSES
img = cv2.putText(img, '1%', (423,707), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##VANHEUSEN
img = cv2.putText(img, '50%', (539,405), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##JALLUS
img = cv2.putText(img, '15%', (231,961), cv2.FONT_HERSHEY_SIMPLEX,1, (0, 0, 0), 2, cv2.LINE_AA)##DENIM
cv2.imwrite("put_text_03_01_2022_heatmap.jpg", img)

Draw text image without crop need by PIL

I would like to draw a text by using PIL. But my problem is I need to crop the text image again after run the program. The thing i need is only text, no border. Any one can suggest?
Thank you.
This is my code:
import Image, ImageDraw, ImageFont
def draw (text, size, color) :
fontPath = '/home/FreeSansBold.ttf'
font = ImageFont.truetype(fontPath, size)
size2 = font.getsize(text)
im = Image.new('RGBA', size2, (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
draw.text((0, 0), text, font=font, fill=color)
im.save(text +'.png')
drawA = draw('A', 200, 'green')
drawC = draw('C', 200, 'blue')
drawG = draw('G', 200, 'yellow')
drawT = draw('T', 200, 'red')
Could you clarify what you mean by no border? Are you wanting text tight against edge of the image? If so this should work:
import Image, ImageDraw, ImageFont
def draw (text, size, color) :
fontPath = '/home/FreeSansBold.ttf'
font = ImageFont.truetype(fontPath, size)
size2 = font.getsize(text)
im = Image.new('RGBA', size2, (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
draw.text((0, 0), text, font=font, fill=color)
pixels = im.load()
width, height = im.size
max_x = max_y = 0
min_y = height
min_x = width
# find the corners that bound the letter by looking for
# non-transparent pixels
transparent = (0, 0, 0, 0)
for x in xrange(width):
for y in xrange(height):
p = pixels[x,y]
if p != transparent:
min_x = min(x, min_x)
min_y = min(y, min_y)
max_x = max(x, max_x)
max_y = max(y, max_y)
cropped = im.crop((min_x, min_y, max_x, max_y))
cropped.save(text +'.png')
drawA = draw('A', 200, 'green')
drawC = draw('C', 200, 'blue')
drawG = draw('G', 200, 'yellow')
drawT = draw('T', 200, 'red')
It produces an image like this (I filled in the transparent pixels with red to show the bounds of the image better: http://img43.imageshack.us/img43/3066/awithbg.png

Categories