Spaces:
Runtime error
Runtime error
#!/usr/bin/env python | |
""" | |
Create image grid | |
""" | |
import os | |
import argparse | |
import math | |
import logging | |
from pathlib import Path | |
import filetype | |
from PIL import Image, ImageDraw, ImageFont | |
from util import log | |
params = None | |
def wrap(text: str, font: ImageFont.ImageFont, length: int): | |
lines = [''] | |
for word in text.split(): | |
line = f'{lines[-1]} {word}'.strip() | |
if font.getlength(line) <= length: | |
lines[-1] = line | |
else: | |
lines.append(word) | |
return '\n'.join(lines) | |
def grid(images, labels = None, width = 0, height = 0, border = 0, square = False, horizontal = False, vertical = False): # pylint: disable=redefined-outer-name | |
if horizontal: | |
rows = 1 | |
elif vertical: | |
rows = len(images) | |
elif square: | |
rows = round(math.sqrt(len(images))) | |
else: | |
rows = math.floor(math.sqrt(len(images))) | |
cols = math.ceil(len(images) / rows) | |
size = [0, 0] | |
if width == 0: | |
w = max([i.size[0] for i in images]) | |
size[0] = cols * w + cols * border | |
else: | |
size[0] = width | |
w = round(width / cols) | |
if height == 0: | |
h = max([i.size[1] for i in images]) | |
size[1] = rows * h + rows * border | |
else: | |
size[1] = height | |
h = round(height / rows) | |
size = tuple(size) | |
image = Image.new('RGB', size = size, color = 'black') # pylint: disable=redefined-outer-name | |
font = ImageFont.truetype('DejaVuSansMono', round(w / 40)) | |
for i, img in enumerate(images): # pylint: disable=redefined-outer-name | |
x = (i % cols * w) + (i % cols * border) | |
y = (i // cols * h) + (i // cols * border) | |
img.thumbnail((w, h), Image.Resampling.HAMMING) | |
image.paste(img, box=(x + int(border / 2), y + int(border / 2))) | |
if labels is not None and len(images) == len(labels): | |
ctx = ImageDraw.Draw(image) | |
label = wrap(labels[i], font, w) | |
ctx.text((x + 1 + round(w / 200), y + 1 + round(w / 200)), label, font = font, fill = (0, 0, 0)) | |
ctx.text((x, y), label, font = font, fill = (255, 255, 255)) | |
log.info({ 'grid': { 'images': len(images), 'rows': rows, 'cols': cols, 'cell': [w, h] } }) | |
return image | |
if __name__ == '__main__': | |
log.info({ 'create grid' }) | |
parser = argparse.ArgumentParser(description='image grid utility') | |
parser.add_argument("--square", default = False, action='store_true', help = "create square grid") | |
parser.add_argument("--horizontal", default = False, action='store_true', help = "create horizontal grid") | |
parser.add_argument("--vertical", default = False, action='store_true', help = "create vertical grid") | |
parser.add_argument("--width", type = int, default = 0, required = False, help = "fixed grid width") | |
parser.add_argument("--height", type = int, default = 0, required = False, help = "fixed grid height") | |
parser.add_argument("--border", type = int, default = 0, required = False, help = "image border") | |
parser.add_argument('--nolabels', default = False, action='store_true', help = "do not print image labels") | |
parser.add_argument('--debug', default = False, action='store_true', help = "print extra debug information") | |
parser.add_argument('output', type = str) | |
parser.add_argument('input', type = str, nargs = '*') | |
params = parser.parse_args() | |
output = params.output if params.output.lower().endswith('.jpg') else params.output + '.jpg' | |
if params.debug: | |
log.setLevel(logging.DEBUG) | |
log.debug({ 'debug': True }) | |
log.debug({ 'args': params.__dict__ }) | |
images = [] | |
labels = [] | |
for f in params.input: | |
path = Path(f) | |
if path.is_dir(): | |
files = [os.path.join(f, file) for file in os.listdir(f) if os.path.isfile(os.path.join(f, file))] | |
elif path.is_file(): | |
files = [f] | |
else: | |
log.warning({ 'grid not a valid file/folder', f}) | |
continue | |
files.sort() | |
for file in files: | |
if not filetype.is_image(file): | |
continue | |
if file.lower().endswith('.heic'): | |
from pi_heif import register_heif_opener | |
register_heif_opener() | |
log.debug(file) | |
img = Image.open(file) | |
# img.verify() | |
images.append(img) | |
fp = Path(file) | |
if not params.nolabels: | |
labels.append(fp.stem) | |
# log.info({ 'folder': path.parent, 'labels': labels }) | |
if len(images) > 0: | |
image = grid( | |
images = images, | |
labels = labels, | |
width = params.width, | |
height = params.height, | |
border = params.border, | |
square = params.square, | |
horizontal = params.horizontal, | |
vertical = params.vertical) | |
image.save(output, 'JPEG', optimize = True, quality = 60) | |
log.info({ 'grid': { 'file': output, 'size': list(image.size) } }) | |
else: | |
log.info({ 'grid': 'nothing to do' }) | |