51

I'm not familiar with PIL, but I know it's very easy to put a bunch of images into a grid in ImageMagick.

How do I, for example, put 16 images into a 4×4 grid where I can specify the gap between rows and columns?

Amir
  • 10,600
  • 9
  • 48
  • 75
pythonee
  • 914
  • 4
  • 12
  • 17

2 Answers2

96

This is easy to do in PIL too. Create an empty image and just paste in the images you want at whatever positions you need using paste. Here's a quick example:

import Image

#opens an image:
im = Image.open("1_tree.jpg")
#creates a new empty image, RGB mode, and size 400 by 400.
new_im = Image.new('RGB', (400,400))

#Here I resize my opened image, so it is no bigger than 100,100
im.thumbnail((100,100))
#Iterate through a 4 by 4 grid with 100 spacing, to place my image
for i in xrange(0,500,100):
    for j in xrange(0,500,100):
        #I change brightness of the images, just to emphasise they are unique copies.
        im=Image.eval(im,lambda x: x+(i+j)/30)
        #paste the image at location i,j:
        new_im.paste(im, (i,j))

new_im.show()

enter image description here

Haozhun
  • 6,331
  • 3
  • 29
  • 50
fraxel
  • 34,470
  • 11
  • 98
  • 102
17

Expanding on the great answer by fraxel, I wrote a program which takes in a folder of (.png) images, a number of pixels for the width of the collage, and the number of pictures per row, and does all the calculations for you.

#Evan Russenberger-Rosica
#Create a Grid/Matrix of Images
import PIL, os, glob
from PIL import Image
from math import ceil, floor

PATH = r"C:\Users\path\to\images"

frame_width = 1920
images_per_row = 5
padding = 2

os.chdir(PATH)

images = glob.glob("*.png")
images = images[:30]                #get the first 30 images

img_width, img_height = Image.open(images[0]).size
sf = (frame_width-(images_per_row-1)*padding)/(images_per_row*img_width)       #scaling factor
scaled_img_width = ceil(img_width*sf)                   #s
scaled_img_height = ceil(img_height*sf)

number_of_rows = ceil(len(images)/images_per_row)
frame_height = ceil(sf*img_height*number_of_rows) 

new_im = Image.new('RGB', (frame_width, frame_height))

i,j=0,0
for num, im in enumerate(images):
    if num%images_per_row==0:
        i=0
    im = Image.open(im)
    #Here I resize my opened image, so it is no bigger than 100,100
    im.thumbnail((scaled_img_width,scaled_img_height))
    #Iterate through a 4 by 4 grid with 100 spacing, to place my image
    y_cord = (j//images_per_row)*scaled_img_height
    new_im.paste(im, (i,y_cord))
    print(i, y_cord)
    i=(i+scaled_img_width)+padding
    j+=1

new_im.show()
new_im.save("out.jpg", "JPEG", quality=80, optimize=True, progressive=True)

Model Collapse Collage

Evan Rosica
  • 1,182
  • 1
  • 12
  • 22
  • Hi. Your answer looks great. Do you know how to avoid those black lines at the borders of the images? – Irbin B. Feb 14 '19 at 05:38
  • @IrbinB. When I wrote this, I wanted the division between images to be clear, so I included horizontal and vertical padding (the black lines). The vertical padding is given by the padding argument in the code, so just set that to 0 to remove them. The horizontal padding got accidentally hard-coded in, and I left it because I wanted that effect. I'd have to experiment to find out how to remove the horizontal padding; something is just off by 1 pixel every loop. – Evan Rosica Feb 14 '19 at 05:57
  • Thanks for your reply. Certainly, I realised about the padding argument but setting it to 0 didn't work for me. Curiously, only vertical paddings appear in my final image. Perhaps I should open a new question. – Irbin B. Feb 14 '19 at 06:42
  • @IrbinB. try changing: `scaled_img_height = ceil(img_height*sf)` to `scaled_img_height = floor(img_height*sf)`. That should remove the horizontal lines. For instance, I took 30 copies of the same picture (https://imgur.com/a/AtyrAR5) and fed them into the changed program and obtained: https://imgur.com/a/kbdga0G – Evan Rosica Feb 14 '19 at 07:30
  • It didn't work. But I have already solved my problem. I found out the issue was related to my image file. So, it wasn't a problem with your code. Thanks. – Irbin B. Feb 15 '19 at 00:34
  • This doesn't seem to add horizontal padding unless I manually add values to `scaled_img_height` – Jake Ireland Jan 28 '21 at 23:03
  • in fact, I had to add `padding` to the `scaled_img_height`: `scaled_img_height = ceil(img_height * sf) + padding` – Jake Ireland Jan 28 '21 at 23:37