python_newbie python_newbie - 6 months ago 18
Python Question

Making a collage in PIL

I. Am. Stuck.

I have been working on this for over a week now, and I cannot seem to get my code to run correctly. I am fairly new to PIL and Python as a whole. I am trying to make a 2x3 collage of some pictures. I have my code listed below. I am trying to get my photos to fit without any access black space in the newly created collage, however when I run my code I can only get 2 pictures to be placed into the collage, instead of the 6 I want. Any suggestions would be helpful.

*CODE EDITED

from PIL import Image
im= Image.open('Tulips.jpg')




out=im.convert("RGB", (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
0.019334, 0.119193, 0.950227, 0 ))
out.save("Image2" + ".jpg")

out2=im.convert("RGB", (
0.9756324, 0.154789, 0.180423, 0,
0.212671, 0.715160, 0.254783, 0,
0.123456, 0.119193, 0.950227, 0 ))
out2.save("Image3" + ".jpg")



out3= im.convert("1")
out3.save("Image4"+".jpg")


out4=im.convert("RGB", (
0.986542, 0.154789, 0.756231, 0,
0.212671, 0.715160, 0.254783, 0,
0.123456, 0.119193, 0.112348, 0 ))
out4.save("Image5" + ".jpg")


out5=Image.blend(im, out4, 0.5)
out5.save("Image6" + ".jpg")

listofimages=['Tulips.jpg', 'Image2.jpg', 'Image3.jpg', 'Image4.jpg', 'Image5.jpg', 'Image6.jpg']

def create_collage(width, height, listofimages):
Picturewidth=width//3
Pictureheight=height//2
size=Picturewidth, Pictureheight
new_im=Image.new('RGB', (450, 300))
for p in listofimages:
Image.open(p)
for col in range(0,width):
for row in range(0, height):
image=Image.eval(p, lambda x: x+(col+row)/30)
new_im.paste(p, (col,row))
new_im.save("Collage"+".jpg")

create_collage(450,300,listofimages)

Answer

Here's some working code.

  1. When you call Image.open(p), that returns an Image object, so you need to store than in a variable: im = Image.open(p).

  2. I'm not sure what image=Image.eval(p, lambda x: x+(col+row)/30) is meant to do so I removed it.

  3. size is the size of the thumbnails, but you're not using that variable. After opening the image, it should be resized to size.

  4. I renamed Picturewidth and Pictureheight to thumbnail_width and thumbnail_height to make it clear what they are and follow Python naming conventions.

  5. I also moved the number of cols and rows to variables so they can be reused without magic numbers.

  6. The first loop opens each image into an im, thumbnails it and puts it in a list of ims.

  7. Before the next loops we initialise i,x, andy` variables to keep track of which image we're looking at, and the x and y coordinates to paste the thumbnails into the larger canvas. They'll be updated in the next loops.

  8. The first loop is for columns (cols), not pixels (width). (Also range(0, thing) does the same as range(thing).)

  9. Similarly the second loop is for rows instead of pixels. Inside this loop we paste the current image at ims[i] into the big new_im at x, y. These are pixel positions, not row/cols positions.

  10. At the end of the inner loop, increment the i counter, and add thumbnail_height to y.

  11. Similarly, at the end of the outer loop, and add thumnnail_width to x and reset y to zero.

  12. You only need to save new_im once, after these loops have finished.

  13. There's no need for concatenating "Image2" + ".jpg" etc., just do "Image2.jpg".

This results in something like this:

Collage.jpg

This code could be improved. For example, if you don't need them for anything else, there's no need to save the intermediate ImageX.jpg files, and rather than putting those filenames in listofimages, put the images directly there: listofimages = [im, out1, out2, etc...], and then replace for p in listofimages: with for im in listofimages: and remove im = Image.open(p).

You could also calculate some padding for the images so the blackspace is even.

from PIL import Image
im= Image.open('Tulips.jpg')

out=im.convert("RGB", (
    0.412453, 0.357580, 0.180423, 0,
    0.212671, 0.715160, 0.072169, 0,
    0.019334, 0.119193, 0.950227, 0 ))
out.save("Image2.jpg")

out2=im.convert("RGB", (
    0.9756324, 0.154789, 0.180423, 0,
    0.212671, 0.715160, 0.254783, 0,
    0.123456, 0.119193, 0.950227, 0 ))
out2.save("Image3.jpg")

out3= im.convert("1")
out3.save("Image4.jpg")

out4=im.convert("RGB", (
    0.986542, 0.154789, 0.756231, 0,
    0.212671, 0.715160, 0.254783, 0,
    0.123456, 0.119193, 0.112348, 0 ))
out4.save("Image5.jpg")

out5=Image.blend(im, out4, 0.5)
out5.save("Image6.jpg")

listofimages=['Tulips.jpg', 'Image2.jpg', 'Image3.jpg', 'Image4.jpg', 'Image5.jpg', 'Image6.jpg']

def create_collage(width, height, listofimages):
    cols = 3
    rows = 2
    thumbnail_width = width//cols
    thumbnail_height = height//rows
    size = thumbnail_width, thumbnail_height
    new_im = Image.new('RGB', (width, height))
    ims = []
    for p in listofimages:
        im = Image.open(p)
        im.thumbnail(size)
        ims.append(im)
    i = 0
    x = 0
    y = 0
    for col in range(cols):
        for row in range(rows):
            print(i, x, y)
            new_im.paste(ims[i], (x, y))
            i += 1
            y += thumbnail_height
        x += thumbnail_width
        y = 0

    new_im.save("Collage.jpg")

create_collage(450, 300, listofimages)
Comments