Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a montage of pictures in python

Tags:

python

image

I have no experience with python, but the owner of this script is not responding.

When I drag my photos over this script, to create a montage, it ends up cutting off half of the last photo on the right side edge.

Being 4 pictures wide,

1   2   3   4

5   6   7   8

Pictures 4 and 8 usually get halved. The space is there for the pictures (its blank though)

I was wondering what would be causing this.

I have thought it is possible that it was cropping, but its almost like the half of the picture isn't imported or detected.

Well, you drag selected photos over the script , it outputs something like this http://i.imgur.com/PzNylbV.png

So you can take a bunch of photos or screenshots, and combine them into one single file, easily instead of adding each photo individually.

Size of each photo is roughly 500x250 at max.

enter image description here

EDIT: Here is the upload of the preview, as you can see the images have the slots, but they are "disappearing" if that makes sense.

EDIT2: This script has worked at one time, I haven't edited it or anything. It had worked on a ~70 screenshot montage. No errors or anything. Is there something that my computer could be doing to disrupt the importing of the images?

#!/usr/bin/env python
import os
import sys
from time import strftime
import Image
import ImageDraw
import ImageFont

# parameters
row_size = 4
margin = 3

def generate_montage(filenames):
    images = [Image.open(filename) for filename in filenames]

    width = 0
    height = 0
    i = 0
    sum_x = max_y = 0 
    width = max(image.size[1]+margin for image in images)*row_size
    height = sum(image.size[0]+margin for image in images)

    montage = Image.new(mode='RGBA', size=(width, height), color=(0,0,0,0))
    try:
        image_font = ImageFont.truetype('font/Helvetica.ttf', 18)
    except:
        try:
            image_font = ImageFont.load('font/Helvetica-18.pil')
        except:
            image_font = ImageFont.load_default()
    draw = ImageDraw.Draw(montage)
    offset_x = offset_y = 0

    i = 0
    max_y = 0
    max_x = 0
    offset_x = 0
    for image in images:
        montage.paste(image, (offset_x, offset_y))

        text_coords = offset_x + image.size[0] - 45, offset_y + 120
        draw.text(text_coords, '#{0}'.format(i+1), font=image_font)

        max_x = max(max_x, offset_x+image.size[0])
        if i % row_size == row_size-1: 
            offset_y += max_y+margin
            max_y = 0
            offset_x = 0
        else:
            offset_x += image.size[0]+margin
            max_y = max(max_y, image.size[1])

        i += 1

    if i % row_size:
        offset_y += max_y

    filename = strftime("Montage %Y-%m-%d at %H.%M.%S.png")
    montage = montage.crop((0, 0, max_x, offset_y))
    montage.save(filename)

if __name__ == '__main__':
    old_cwd = os.getcwd()

    os.chdir(os.path.dirname(sys.argv[0]))
    try:
        if len(sys.argv) > 1:
            generate_montage(sys.argv[1:])
    finally:
        os.chdir(old_cwd)
like image 398
user2565679 Avatar asked Jul 09 '13 18:07

user2565679


1 Answers

In the size calculation, you use image.size[1] for the width, but that's the height! Use image.size[0] for the width and image.size[1] for the height instead.

Also, a couple of minor stylistic notes:

  • Do you really need the script to always run from the program's directory? In any case, os.chdir(os.path.dirname(sys.argv[0])) prevents the program from being executed as ./montage.py, so you may want to use a abspath to allow the invocation from the current directory.
  • Instead of having to update the counter i, you can change the for loop to

    for i,image in enumerate(images):
    
  • The following lines have no effect, since the variables are overwritten / never used:

    width = 0
    height = 0
    i = 0
    sum_x = max_y = 0 
    

All in all, the code could look like this:

#!/usr/bin/env python
import os.path
import sys
from time import strftime
import Image

row_size = 4
margin = 3

def generate_montage(filenames, output_fn):
    images = [Image.open(filename) for filename in filenames]

    width = max(image.size[0] + margin for image in images)*row_size
    height = sum(image.size[1] + margin for image in images)
    montage = Image.new(mode='RGBA', size=(width, height), color=(0,0,0,0))

    max_x = 0
    max_y = 0
    offset_x = 0
    offset_y = 0
    for i,image in enumerate(images):
        montage.paste(image, (offset_x, offset_y))

        max_x = max(max_x, offset_x + image.size[0])
        max_y = max(max_y, offset_y + image.size[1])

        if i % row_size == row_size-1:
            offset_y = max_y + margin
            offset_x = 0
        else:
            offset_x += margin + image.size[0]

    montage = montage.crop((0, 0, max_x, max_y))
    montage.save(output_fn)

if __name__ == '__main__':
    basename = strftime("Montage %Y-%m-%d at %H.%M.%S.png")
    exedir = os.path.dirname(os.path.abspath(sys.argv[0]))
    filename = os.path.join(exedir, basename)
    generate_montage(sys.argv[1:], filename)
like image 117
phihag Avatar answered Sep 20 '22 04:09

phihag