Home > front end >  How do I alter this function to create a grid based on the number of images in the folder using PIL
How do I alter this function to create a grid based on the number of images in the folder using PIL

Time:09-06

The idea is to pass a list of all images found using glob into the collage_maker function and then it will save the image as a 3x3, 4x4, 5x5 etc. If there aren't enough images it goes to the next highest one and just has empty black squares.

from PIL import Image
import numpy as np
import os
import glob

direct_loc = "/home/nikolas/Downloads/"



images = glob.glob(direct_loc   '*.jpg')
        
images_a = images[::2]
images_b = images[1::2]



def collage_maker(image1, image2, name):
    i1 = np.array(image1)
    i2 = np.array(image2)
    collage = np.vstack([i1, i2])
    image = Image.fromarray(collage)
    image.save(name)



collage_maker(Image.open(images_a[0]), Image.open(images_b[0]), 'collage.jpg')

As of right now it has no problem making a 1x2 image, but everything else I have tried just makes it longer, but never into a grid. Thank you!

CodePudding user response:

First of all, if you want to work with a list of image of arbitrary length, you would better have your function accepting some kind of iterable of images rather than two images, or else you would have to call it multiple times in a very convoluted way to obtain the desired result. You should then determine the shape of your collage square based on the number of images in said iterable. Also, you will have to check the shapes of the images during processing, and probably find the maximum value in each dimension, unless all images have exactly the same size. Something like this should work:

import numpy as np
from PIL import Image
from typing import Iterable


def collage_maker(img_iter: Iterable[Image.Image], name: str):
    # get the np arrays from the images
    img_arrays = [np.array(img) for img in img_iter]

    # find the size of the side of you collage square
    square_side = np.ceil(np.sqrt(len(img_arrays))).astype(int)

    # now determine the max height, width and number of channels
    shapes = np.array([img.shape for img in img_arrays]).T
    dims = [np.max(d) for d in shapes]

    # create an array to gather the images, with all values 0 (black / transparent)
    # you can see this as a collection of "empty" images
    holder = np.zeros((square_side ** 2, *dims), np.uint8)

    # now, for each image ...
    for i in range(len(img_arrays)):
        # ... get its shape ...
        img_shape = img_arrays[i].shape
        # ... and copy image to an "empty" location in the holder array.
        # you need to specify a target slice as target may be larger than the source
        holder[i, :img_shape[0], :img_shape[1], :img_shape[2]] = img_arrays[i]

    # turn the collage array in a "square" of desired size...
    square = holder.reshape((square_side, square_side, dims[0], dims[1], dims[2]))

    # ... and concatenate along height and width
    collage = Image.fromarray(np.concatenate(np.concatenate(square, axis=1), axis=1))

    collage.save(name)

Usage:

import glob


direct_loc = "/your/image/directory/"
images = glob.glob(direct_loc   '*.jpg')
collage_maker([Image.open(path) for path in images], 'collage.jpg')

CodePudding user response:

Don't use images_a, images_b but only images and for-loop.

This will create 1xN (but I did't test it)

import os
import glob
import numpy as np
from PIL import Image

direct_loc = "/home/nikolas/Downloads/"

filenames = glob.glob(direct_loc   '*.jpg')
        
images = []

for name in filenames:
    img = Image.open(name)
    images.append(img)
    
def collage_maker(images, name):
    arrays = []
    
    for img in images:
        arr = np.array(img)
        arrays.append(arr)

    collage = np.vstack(arrays)
    
    image = Image.fromarray(collage)
    image.save(name)

collage_maker(images, 'collage.jpg')

To create NxM you may get images as (flat) numpy.array and reshape it to NxM and later use nested for-loops

Something like this (but I did't test it)

import os
import glob
import numpy as np
from PIL import Image

direct_loc = "/home/nikolas/Downloads/"

filenames = glob.glob(direct_loc   '*.jpg')
        
images = []

for name in filenames:
    img = Image.open(name)
    images.append(img)

# get 9 images and reshape to `3x3`
images = np.array(images[:9])
images = images.reshape(3, 3)

def collage_maker(images, name):
    all_rows = []
    
    for row in images:
        arrays = []

        for img in row:
            arr = np.array(img)
            arrays.append(arr)

        row_arr = np.vstack(arrays)
        all_rows.append(row_arr)
        
    collage = np.hstack(all_rows)
    
    image = Image.fromarray(collage)
    image.save(name)

collage_maker(images, 'collage.jpg')
  • Related