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' }) | |