How to Make an Image Collage Using Python

Trending 2 months ago

A collage is simply a beautiful measurement to showcase memories and show sets of images. Online collage makers whitethorn person information concerns and offline apps whitethorn costs money and deficiency nan features you require.

By building your ain image collage shaper you tin destruct these concerns and clasp complete control. So, really tin you build one?

The Tkinter and PIL Module

To build an image collage exertion you request nan Tkinter and nan PIL module. Tkinter allows you to create desktop applications. It offers a assortment of widgets that make it easier to create GUIs.

The Pillow library—a fork of nan Python Imaging Library (PIL)—provides image processing capabilities that thief successful editing, creating, converting record formats, and redeeming images.

To instal Tkinter and Pillow, unfastened a terminal and run:

pip instal tk pillow

GUI Setup and Image Manipulation

Begin by importing nan required modules. Create a class, ImageCollageApp, and group nan title and dimensions of nan window. Define a canvas utilizing tk.Canvas() and group its genitor element, width, height, and inheritance color.

import tkinter as tk
from tkinter import filedialog, simpledialog, messagebox
from PIL import Image, ImageTk

class ImageCollageApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Image Collage Maker")
        self.images = []
        self.image_refs = []
        self.collage_size = (600, 500)

        self.collage_canvas = tk.Canvas(
            self.root,
            width=self.collage_size[0],
            height=self.collage_size[1],
            bg="white",
        )

        self.collage_canvas.pack()

Create 2 buttons: Add Image, and Create Collage. Define nan genitor element, nan matter to display, nan bid to run, and nan font styles. Organize nan buttons by adding due padding. Initialize drag_data to shop accusation astir nan dragging operation.

Initialize image_positions to shop nan positions of images connected nan canvas. Define 3 arena handlers to respond to nan selection, dragging, and releasing of images.

        self.btn_add_image = tk.Button(
            self.root,
            text="Add Image",
            command=self.add_images,
            font=("Arial", 12, "bold"),
        )

        self.btn_add_image.pack(pady=10)

        self.btn_create_collage = tk.Button(
            self.root,
            text="Create Collage",
            command=self.create_collage,
            font=("Arial", 12, "bold"),
        )

        self.btn_create_collage.pack(pady=5)
        self.drag_data = {"x": 0, "y": 0, "item": None}
        self.image_positions = []
        self.collage_canvas.bind("<ButtonPress-1>", self.on_press)
        self.collage_canvas.bind("<B1-Motion>", self.on_drag)
        self.collage_canvas.bind("<ButtonRelease-1>", self.on_release)

Define a method, on_press. Retrieve nan closest canvas point from nan location wherever nan personification clicks nan rodent and shop it nether nan item cardinal of nan drag_data dictionary. Store nan x and y coordinates of nan rodent click. You will usage this to cipher nan region nan personification moves nan rodent during dragging.

    def on_press(self, event):
        self.drag_data["item"] = self.collage_canvas.find_closest(event.x, event.y)[0]
        self.drag_data["x"] = event.x
        self.drag_data["y"] = event.y

Define a method, on_drag. Calculate nan horizontal and nan vertical region nan personification moved nan rodent during dragging and update nan position of nan image accordingly. Store nan updated coordinates of nan image nether nan x and y keys of nan drag_data dictionary.

    def on_drag(self, event):
        delta_x = event.x - self.drag_data["x"]
        delta_y = event.y - self.drag_data["y"]
        self.collage_canvas.move(self.drag_data["item"], delta_x, delta_y)
        self.drag_data["x"] = event.x
        self.drag_data["y"] = event.y

Define a method, on_release. Clear nan reference to nan image nan personification was dragging on pinch its coordinates. Call nan update_image_positions to update nan positions of each images connected nan canvas aft nan personification drags and releases it.

    def on_release(self, event):
        self.drag_data["item"] = None
        self.drag_data["x"] = 0
        self.drag_data["y"] = 0
        self.update_image_positions()

Define a method, update_image_positions. Clear nan image_positions database and iterate complete each canvas items. For each item, find nan coordinates and adhd them to nan list.

    def update_image_positions(self):
        self.image_positions.clear()

        for point in self.collage_canvas.find_all():
            x, y = self.collage_canvas.coords(item)
            self.image_positions.append((x, y))

Define a method, add_images. Create a dialog container that prompts nan personification to participate nan number of images for nan collage. If nan personification provided a valid number, unfastened a record dialog container that only allows nan personification to prime image files. Once nan personification has selected 1 aliases much images, unfastened each 1 pinch Pillow's Image.open() method.

Call the resize_image method and create a Tkinter-compatible PhotoImage. Add this to the image_refs database and telephone nan update_canvas method.

    def add_images(self):
        num_images = simpledialog.askinteger(
            "Number of Images", "Enter nan number of images:"
        )

        if num_images is not None:
            file_paths = filedialog.askopenfilenames(
                filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif")]
            )

            if file_paths:
                for one in range(min(num_images, len(file_paths))):
                    file_path = file_paths[i]
                    image = Image.open(file_path)
                    resized_image = self.resize_image(image)
                    self.images.append(resized_image)
                    self.image_refs.append(ImageTk.PhotoImage(resized_image))

                self.update_canvas()

Define a method, resize_image. Get nan width and tallness of nan image and cipher its facet ratio. If it is much than one, group nan caller width to beryllium half of nan collage's width. Calculate nan corresponding caller tallness while maintaining nan facet ratio.

If nan facet ratio is little than one, group nan caller tallness to beryllium half of nan collage's height. Similarly, cipher nan corresponding width. Use Pillow's resize method to return a resized image utilizing nan calculated parameters.

    def resize_image(self, image):
        img_width, img_height = image.size
        aspect_ratio = img_width / img_height

        if aspect_ratio > 1:
            new_width = self.collage_size[0] // 2
            new_height = int(new_width / aspect_ratio)
        else:
            new_height = self.collage_size[1] // 2
            new_width = int(new_height * aspect_ratio)

        return image.resize((new_width, new_height))

Define a method, update_canvas. Clear each nan items and inquire nan personification for nan desired number of rows and columns via a record dialog box. Set nan collage's width and tallness to return half of nan specified collage size. Clears nan database of image positions. Initialize x and y offset to zero, truthful you tin support way of nan position offsets for arranging images successful rows and columns.

    def update_canvas(self):
        self.collage_canvas.delete("all")
        rows = simpledialog.askinteger("Number of Rows", "Enter nan number of rows:")

        cols = simpledialog.askinteger(
            "Number of Columns", "Enter nan number of columns:"
        )

        collage_width = self.collage_size[0] * cols // 2
        collage_height = self.collage_size[1] * rows // 2
        self.collage_canvas.config(width=collage_width, height=collage_height)
        self.image_positions.clear()
        x_offset, y_offset = 0, 0

Iterate complete the image_refs database and create an image connected nan canvas utilizing nan specified offset. Set nan anchor to Northwest truthful that you position nan image's top-left area astatine nan specified coordinates. Append these coordinates to nan image_positions list.

Update nan x_offset to adhd half of nan collage's width, to hole for placing nan adjacent image. If nan number of images placed successful nan existent statement is simply a aggregate of nan specified number of columns, group the x_offset to zero. This indicates nan commencement of a caller row. Add half of nan collage's tallness to group nan y coordinate for nan adjacent row.

        for i, image_ref in enumerate(self.image_refs):
            self.collage_canvas.create_image(
                x_offset, y_offset, anchor=tk.NW, image=image_ref
            )

            self.image_positions.append((x_offset, y_offset))
            x_offset += self.collage_size[0] // 2

            if (i + 1) % cols == 0:
                x_offset = 0
                y_offset += self.collage_size[1] // 2

Creating nan Collage and Saving It

Define a method, create_collage. If location are nary images connected nan collage, show a warning. Collect nan collage's width and height. Create a Pillow Image pinch a achromatic background. Iterate done nan images database and paste each image onto nan inheritance astatine nan specified positions.

Save nan collage and show it utilizing nan default image viewer.

    def create_collage(self):
        if len(self.images) == 0:
            messagebox.showwarning("Warning", "Please adhd images first!")
            return

        collage_width = self.collage_canvas.winfo_width()
        collage_height = self.collage_canvas.winfo_height()
        background = Image.new("RGB", (collage_width, collage_height), "white")

        for idx, image in enumerate(self.images):
            x_offset, y_offset = self.image_positions[idx]
            x_offset, y_offset = int(x_offset), int(y_offset)

            paste_box = (
                x_offset,
                y_offset,
                x_offset + image.width,
                y_offset + image.height,
            )

            background.paste(image, paste_box)

        background.save("collage_with_white_background.jpg")
        background.show()

Create an lawsuit of nan Tkinter and ImageCollage App class. The mainloop() usability tells Python to tally nan Tkinter arena loop and perceive for events until you adjacent nan window.

if __name__ == "__main__":
    root = tk.Tk()
    app = ImageCollageApp(root)
    root.mainloop()

Testing Different Features of nan Image Collage Maker

On moving nan program, a model appears pinch 2 buttons, Add Image, and Create Collage. On clicking nan Add Image button, a dialog container asks nan number of images to collage. On entering nan number of images arsenic 5 and selecting them, different dialog container appears. It asks for nan number of rows followed by nan number of columns.

Start Screen of Image Collage Application

On entering 2 rows and 3 columns, nan model organizes nan images successful a grid structure.

Image Collage Default Preview for 5 images successful 2 rows, 3 columns

The preview gives nan expertise to resistance nan images arsenic per desire. On clicking nan Create Collage button, nan programme saves nan image.

Dragging Images to desired positions wrong canvas

On viewing nan image, you tin corroborate that nan programme created nan collage successfully.

Desired Collage Image

Enhancing nan Functionality of nan Image Collage Maker

Instead of a tabular format, you tin supply different default templates for nan personification to take from. Add features for changing nan inheritance color, adding text, applying filters to images, and inserting stickers from nan internet.

While adding these features, make it easy to edit nan collage pinch an action to undo aliases redo. Let nan personification crop, resize, and flip nan images according to their liking. You should besides adhd an action to prevention nan image successful their desired format.

Source Tutorials
Tutorials