Home > front end >  Python PIL/Image generate grid of images of different width/height
Python PIL/Image generate grid of images of different width/height

Time:11-19

I came across the following example:

from PIL import Image

def image_grid(imgs, rows, cols):
    assert len(imgs) == rows*cols

    w, h = imgs[0].size
    grid = Image.new('RGB', size=(cols*w, rows*h))
    grid_w, grid_h = grid.size
    
    for i, img in enumerate(imgs):
        grid.paste(img, box=(i%cols*w, i//cols*h))
    return grid

grid = image_grid(imgs, rows=3, cols=3)

Works great, but what I need is a way to generate a grid of images of different width/height. I haven't been able to find any such example while searching.

What I've been able to do is to iterate all images, get the max image dimensions encountered and change:

grid.paste(img, box=(i%cols*w, i//cols*h))

to:

grid.paste(img, box=(i%cols*maxWidth, i//cols*maxHeight))

But that ends up wasting a lot of space.

Perhaps to avoid this the max width/height of each column/row would have to be calculated instead, but all the ways I've tried so far don't get the job done.

Your help is much appreciated.

CodePudding user response:

I was able to solve it in the following way:

First, we iterate all images and gather the max dimensions for each column and row.

size = 3
maxWidth = {}
maxHeight = {}

for i, img in enumerate(imgs):
    col = i%size
    row = i//size

    if col not in maxWidth:
        maxWidth[col] = 0

    if row not in maxHeight:
        maxHeight[row] = 0

    if img.size[0] > maxWidth[col]:
        maxWidth[col] = img.size[0]

    if img.size[1] > maxHeight[row]:
        maxHeight[row] = img.size[1]

We then calculate their sums in order to determine the image size.

x = sum(maxWidth for maxWidth in maxWidth.values())
y = sum(maxHeight for maxHeight in maxHeight.values())

grid = Image.new('RGB', size=(x, y))

Then we iterate all images again and position them based on the max dimensions that we gathered. We reset and increment x1 and y1 accordingly depending on where we are.

for i, img in enumerate(imgs):
    col = i%size
    row = i//size

    if col == 0:
        x1 = 0
    else:
        x1  = maxWidth[col-1]

    if row == 0:
        y1 = 0
    elif col == 0:
        y1  = maxHeight[row-1]
    
    grid.paste(img, box=(x1, y1))

This way, there is no need to allocate more space to an image than its column/row requires.

  • Related