PIL: Paste image with resized aspect ratio - python

I have 2 images (icon.png and background.png). In the background.png image, there is a blank area which will be the place for the icon.png to be pasted using PIL (Python). However, the icon.png is a little bit bigger compared to the blank frame in the background.png. How can I paste and make the icon.png smaller so it can fit with the frame?
My code so far:
icon = Image.open("./icon.png")
background = Image.open("./background.png")
mask = Image.new("L", icon.size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + icon.size, fill=255)
back_im = background.copy()
back_im.paste(icon, (200, 100), mask=mask)
back_im.save("./back_im.png")

Use resize after read the icon image to fit the desired size:
from PIL import Image, ImageDraw
iconSize=(200,100)
icon = Image.open("./icon.png")
icon=icon.resize(iconSize)
background = Image.open("./background.png")
mask = Image.new("L", icon.size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + icon.size, fill=255)
back_im = background.copy()
# back_im.paste(icon, iconSize, mask=mask)
back_im.paste(icon, icon.size, mask=mask)
back_im.save("./back_im.png")

Related

How to remove white background of gif when using pillow?

I'm trying to display a gif on top of an image. The gif originally has a transparent background. The code below does that, except it created a black background around the original gif that flickers white when the gif resets. If I don't resize the gif then there would still be a white background around the original gif after its merged with the image.
Is there a way to keep the original gif's background transparent when merging?
from PIL import Image
background = Image.open('GrassyField.png').convert("RGBA")
gif = Image.open("1.gif")
foreground_w, foreground_w = gif.size
background_w, background_h = background.size
frames = []
for num in range(gif.n_frames):
gif.seek(num)
layer = Image.new('RGBA', (foreground_w, foreground_w), (0, 0, 0, 0)).resize((background_w,
background_h))
layer.paste(background, (0, 0), mask=background)
layer.paste(gif.resize((100, 100)).convert('RGBA'), (220, 25))
frames.append(layer)
frames[0].save('output.gif',
save_all=True,
append_images=frames[1:],
duration=100,loop=0)
Let's say that 1.gif is:
and GrassyField.png is:
using this code:
import PIL.Image
import PIL.ImageSequence
with PIL.Image.open('1.gif') as foreground_animation, PIL.Image.open('GrassyField.png') as background_image:
image_roles = {
'foreground': foreground_animation,
'background': background_image.convert(mode='RGBA')
}
def create_frames(images_roles):
for current_frame in PIL.ImageSequence.Iterator(image_roles['foreground']):
current_background = image_roles['background'].copy()
current_foreground = current_frame.convert(mode='RGBA').resize((100, 100))
current_background.alpha_composite(current_foreground, dest=(220,25))
yield current_background
frames = tuple(create_frames(image_roles))
frames[0].save(
'output.gif',
save_all=True,
append_images=frames[1:],
duration=100,loop=0
)
You get output.gif:
Is that what you wanted?

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.

Is there a more efficient way to create images using python PIL?

I'm trying to generate an image that contains the text of a given string. For now I'm using the Image.draw.text() from the Pyhton PIL library to generate the result and the process is working fine, but it's exceedingly slow and it takes a handful of seconds to finish drawing, which is really hampering the scalability of the program.
This is the current function to generate images:
def draw_ascii(ascii_image, new_width) :
# Source text, and wrap it.
adjusted_ascii = ascii_image.replace("\n", " ")
text = textwrap.fill(adjusted_ascii, new_width)
# Font size, color and type.
fontcolor = (0, 0, 0)
fontsize = 14
font = ImageFont.truetype("FreeMono.ttf", fontsize)
# Determine text size using a scratch image.
img = Image.new("RGBA", (1,1))
draw = ImageDraw.Draw(img)
textsize = draw.textsize(text, font)
# Creating the final image and put the text on it
background = (255, 255, 255)
img = Image.new("RGB", textsize, background)
draw = ImageDraw.Draw(img)
draw.text((0, 0), text, fontcolor, font)
return(img)

Remove white background from image with opencv python

I have been trying to remove background from a logo and use it as a watermark on product images.I tried to remove background from logo with masking but it also removing black fonts from logo. i need help in removing background without changing the logo. images are attached below. output image is the image i get as output with this code.
logo image: https://drive.google.com/file/d/1yMG6cDuPt8q3EqOJ4Amzp_czWq5hrGS5/view?usp=sharing
product image: https://drive.google.com/file/d/13SmkTgBtWD3yIJq-qGI0aZJ-hjaLbyuB/view?usp=sharing
output image: https://drive.google.com/file/d/1k-fQ9tPUEJKQXPdmdB2ajtliAAG4irNs/view?usp=sharing
this is my code
import cv2
import numpy as np
img = cv2.imread('images/1L2Z3A443AAMC.jpg')
logo = cv2.imread('water2.png')
logo = cv2.resize(logo,(int(img.shape[0]/1.2),int(img.shape[1]/2)))
logo_gray = cv2.cvtColor(logo,cv2.COLOR_BGR2GRAY)
ret,mask= cv2.threshold(logo_gray,245,255,cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)
logo_final = cv2.bitwise_and(logo,logo, mask=mask_inv)
h_img,w_img,_ = img.shape
h_logo,w_logo,_ = logo.shape
center_y = int(h_img/2)
center_x = int(w_img/2)
top_y = center_y - (int(h_logo/2))
left_x = center_x - (int(w_logo/2))
bottom_y = top_y + h_logo
right_x = left_x + w_logo
roi = img[top_y:bottom_y,left_x:right_x]
result = cv2.addWeighted(roi,1,logo_final,1,0)
img[top_y:bottom_y,left_x:right_x] = result
cv2.imwrite('sample.jpg',img)
In order to remove the background from a logo. We need to used masking. A masking for the foreground and another masking for the background.
logo = cv2.imread("water2.png", -1)
logo_mask = logo[:,:,3]
logo_mask_inv = cv2.bitwise_not(logo_mask)
logo = logo[:,:,0:3]
logo_background = cv2.bitwise_and(roi, roi, mask=logo_mask_inv)
logo_foreground = cv2.bitwise_and(logo, logo, mask=logo_mask)
final_logo = cv2.add(logo_background, logo_foreground)

Categories