#!/usr/bin/env python3   import glob from PIL import Image from random import shuffle   MAXSIZE = (640,480)   images = []   imagefilelist = list(glob.glob("images/*.*"))   for imagefile in imagefilelist:     image = Image.open(imagefile)     image.thumbnail(MAXSIZE)     images.append(image)   images.sort(key=lambda image: image.size[0] * image.size[1], reverse=True) #shuffle(images)   class Rectangle:     def __init__(self, x1, y1, x2, y2, image=None):         self.x1 = x1         self.x2 = x2         self.y1 = y1         self.y2 = y2         self.width = x2 - x1         self.height = y2 - y1         self.area = self.width * self.height         self.ratio = self.width / self.height         self.image = image     def __eq__(self, other):         return (self.x1 == other.x1 and self.y1 == other.y1 and                 self.x2 == other.x2 and self.y2 == other.y2)     def __hash__(self):         return (self.x1, self.y1, self.x2, self.y2).__hash__()     def __repr__(self):         return "(" + str(self.x1) + " " + str(self.y1) + " " + str(self.x2) + " " + str(self.y2) + ")"     def intersects(self, other):         if type(other) == Rectangle:             return (self.x1 < other.x2 and self.x2 > other.x1 and                     self.y1 < other.y2 and self.y2 > other.y1)         for r in other:             if self.intersects(r):                 return True         return False     def grow(self, other):         if type(other) != Rectangle:             raise Exception()         return Rectangle(self.x1 if self.x1 < other.x1 else other.x1,                          self.y1 if self.y1 < other.y1 else other.y1,                          self.x2 if self.x2 > other.x2 else other.x2,                          self.y2 if self.y2 > other.y2 else other.y2)     OFFSET = 16 offset = OFFSET   rectangles = [] overall = None inflections = set([]) c = 0 for image in images:     if not len(rectangles):         rectangles.append(Rectangle(0,0, image.size[0], image.size[1], image))         overall = Rectangle(0, 0, image.size[0], image.size[1])         inflections.update([(overall.x1-OFFSET, overall.y1-OFFSET),                             (overall.x2+OFFSET, overall.y1-OFFSET),                             (overall.x1-OFFSET, overall.y2+OFFSET),                             (overall.x2+OFFSET, overall.y2+OFFSET)])         continue     options = set([])     for point in inflections:         options.update([(point[0],point[1]),                         (point[0]-image.size[0],point[1]),                         (point[0],point[1]-image.size[1]),                         (point[0]-image.size[0],point[1]-image.size[1])])     potentials = []     for option in options:         testrect = Rectangle(option[0], option[1], option[0]+image.size[0], option[1]+image.size[1])         if testrect.intersects(rectangles):             continue         potentials.append(testrect)     #print("Overall:", overall)     smallestgrow = None     bestrect = None     shuffle(potentials)     for pot in potentials:         newgrow = overall.grow(pot)         if (smallestgrow is None) or (newgrow.width + newgrow.height < smallestgrow.width + smallestgrow.height):             if newgrow.ratio > 1.33:                 smallestgrow = newgrow                 bestrect = pot     bestrect.image = image     rectangles.append(bestrect)     #print("Best Match:", bestrect)     overall = smallestgrow     #print("New Overall:", overall)     inflections.update([(bestrect.x1-OFFSET, bestrect.y1-OFFSET),                         (bestrect.x2+OFFSET, bestrect.y1-OFFSET),                         (bestrect.x1-OFFSET, bestrect.y2+OFFSET),                         (bestrect.x2+OFFSET, bestrect.y2+OFFSET)])     c += 1     #if c == 1000:     #    break   minx = rectangles[0].x1 miny = rectangles[0].y1 for rectangle in rectangles:     if minx > rectangle.x1:         minx = rectangle.x1     if miny > rectangle.y1:         miny = rectangle.y1 for rectangle in rectangles:     rectangle.x1 -= minx     rectangle.y1 -= miny     rectangle.x2 -= minx     rectangle.y2 -= miny   overall.x1 -= minx overall.y1 -= miny overall.x2 -= minx overall.y2 -= miny   final = Image.new("RGB", (overall.x2,overall.y2), (128,128,128))   for rectangle in rectangles:     final.paste(rectangle.image, (rectangle.x1, rectangle.y1))   final.save("/tmp/out.png")