I tried to connect several images with an alpha channel on 1 canvas, but the images are connected one after another (as I wrote in the code), but I am interested in how to make sure that all available transparent space is used, because I insert many different transparent images, and some have such a transparent space that in you can add a smaller image to them. Even before inserting the image, I crop them to the minimum transparency limits (i.e. up to 1 shaded pixel). In general, how to make sure that you use the transparent space of the image for insertion, because if you analyze every pixel (left, down, right, up) for transparency, then for a picture of 10k+ pixels it can take hours of work! (How to do this without using neural networks)[enter image description here] How does it work out
As it should turn out, only for a larger number of images
My code:
from PIL import Image
import pandas as pd
import time
import os
DESIGN_FOLDER = 'desings'
SM_SIZE = 100
def find_empty_transparent_regions(im, min_width, min_height):
alpha = im.split()[3]
width, height = im.size
transparent_regions = []
for y in range(0, height, int(SM_SIZE/2)):
for x in range(0, width, int(SM_SIZE/2)):
if transparent_regions:
for req in transparent_regions:
if x in range(req[0], req[2]) or y in range(req[1], req[3]):
continue
try:
if alpha.getpixel((x, y)) == 0:
x1 = x
while x < width and alpha.getpixel((x, y)) == 0:
x += 1
y1 = y
while y < height and alpha.getpixel((x1, y)) == 0:
y += 1
if x - x1 >= min_width and y - y1 >= min_height:
transparent_regions.append((x1, y1, x, y))
print(transparent_regions)
except IndexError:
continue
return transparent_regions
def crop_transparent(image_path=None, image=None):
if image_path:
image = Image.open(image_path)
image = image.convert("RGBA")
width, height = image.size
left, top, right, bottom = width, height, 0, 0
for x in range(width):
for y in range(height):
r, g, b, a = image.getpixel((x, y))
if a != 0:
left = min(left, x)
top = min(top, y)
right = max(right, x)
bottom = max(bottom, y)
image = image.crop((left, top, right + 1, bottom + 1))
return image
def rotate_image(img):
width, height = img.size
if height > width:
img = img.transpose(Image.ROTATE_90)
return img
def resize_image(image, target_size):
width, height = image.size
if width > height:
ratio = target_size / width
new_width = target_size
new_height = int(height * ratio)
else:
ratio = target_size / height
new_height = target_size
new_width = int(width * ratio)
resized_image = image.resize((new_width, new_height))
return resized_image
def merge_images(images_list):
total_height = sum([image['img'].size[1] for image in images_list])
max_width = 45*SM_SIZE
merged_image = Image.new('RGBA', (max_width, total_height))
y_offset = 0
width_line = 0
min_width = images_list[-1]['img'].width
count_set = int(len(images_list)//2)
heidth = 0
crop_cheack = 0
i = 0
while images_list:
i += 1
print(len(images_list))
image = images_list[0]['img']
min_width = image.width
min_height = image.height
if image.size[0] + width_line < max_width:
merged_image.paste(image, (width_line, y_offset))
width_line += image.size[0] + int(SM_SIZE*0.5)
if heidth < image.size[1]:
heidth = image.size[1]
del images_list[0]
continue
else:
d = 1
while True:
try:
img = images_list[d]['img']
if img.size[0] + width_line < max_width:
merged_image.paste(img, (width_line, y_offset))
width_line += img.size[0] + int(SM_SIZE*0.5)
if heidth < img.size[1]:
heidth = img.size[1]
del images_list[d]
continue
else:
d += 1
except IndexError:
if count_set <= i:
if crop_cheack:
merged = merged_image.crop((0, crop_cheack, max_width, total_height))
image_check = crop_transparent(image=merged)
else:
image_check = crop_transparent(image=merged_image)
empties = find_empty_transparent_regions(image_check, min_width+int(SM_SIZE*0.5), min_height+int(SM_SIZE*0.5))
# empties = extract_regions(image_check, min_height+int(SM_SIZE*0.5), min_width+int(SM_SIZE*0.5))
if empties:
k = 0
d = 1
while empties:
if d > len(images_list) - 1:
break
img = images_list[d]['img']
img_width, img_height = img.size
x, y, width, height = empties[k]
width = width - x
height = height - y
if img_width <= width and img_height <= height:
merged_image.paste(img, (x, y))
del images_list[d]
del empties[k]
d += 1
y_offset += heidth + int(SM_SIZE*0.5)
width_line = 0
heidth = 0
break
merged_image = crop_transparent(image=merged_image)
merged_image.save('merged_image.png')
def main():
start_time = time.time()
df = pd.read_excel('table.xlsx')
imgs = []
for i, design in enumerate(df['Name']):
size_sm = df['Size'][i]
size = size_sm * SM_SIZE
count = df['Count'][i]
path = f'{DESIGN_FOLDER}/{design}'
if os.path.exists(path + '.png'):
path += '.png'
elif os.path.exists(path + '.jpg'):
path += '.jpg'
else:
print(f'File {path} dont find!')
crop_img = crop_transparent(path)
crop_img = resize_image(crop_img, size)
crop_img = rotate_image(crop_img)
for c in range(count):
imgs.append({'size': sum(crop_img.size), 'img': crop_img})
imgs = sorted(imgs, key=lambda x: x['size'], reverse=True)
merge_images(imgs)
print("--- %s seconds ---" % (time.time() - start_time))
I tried to use opencv, but it takes longer to process the image than pillow. I will be glad if you can give me an idea of how best to organize this, you can do it without changes in the code, just an idea. You may use another language, i need an idea.