HOME/Articles/

pil example ImageCompositionTool (snippet)

Article Outline

Python pil example 'ImageCompositionTool'

Modules used in program:

  • import PIL
  • import datetime
  • import os

python ImageCompositionTool

Python pil example: ImageCompositionTool

import os
import datetime
import PIL
from PIL import Image
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw


class ImageCompositionTool(object):
    """
    """

    OUT_COUNT_TRESHOLD = 3

    def __init__(self, options, captures):
        """
        Keyword arguments:
        options -- dict of options:
            padding - Padding used here and there.
            size - Width and height tuple of the generated images.
            background_color - 3- or 4-tuple color for the background, defaults to black.
            background_color_mode - Defaults to rgb.
            font - Path to font family.
            font_size - Font size.
        captures -- list of dicts that contain capture configurations. A capture configuration contains:
            name - Name of the capture.
            image_folder -  Folder path for the images.
            placeholder_path - Path for the placeholder image.
            size - Size of the source image.
            target_size - Scale the image to this size. Leave empty for no change.
            target_position - Paste the image here on the generated image.
            crop - Crop configuration:
                placeholder_path - Path for the placeholder image.
                size - Size of the crop.
                position - Topleft position of the crop.
                target_size - Scale the crop to this size.
                target_position - Paste the crop here on the generated image.
        """
        self.captures = captures
        self.options = options

        if "background_color" not in self.options:
            self.options["background_color"] = 0

        if "background_color_mode" not in self.options:
            self.options["background_color_mode"] = "RGB"

        self.font = ImageFont.truetype(self.options["font"], self.options["font_size"])
        self.image_files = {}
        for capture in captures:
            capture["out_count"] = 0
            capture["placeholder_image"] = Image.open(capture["placeholder_path"])
            capture["previous"] = capture["placeholder_path"]
            if "crop" in capture:
                capture["crop"]["placeholder_image"] = Image.open(capture["crop"]["placeholder_path"])
            self.add_files(capture["image_folder"], capture["name"])
        self.captures = captures

    def generate_image(self):
        # create a blank image
        image = Image.new(
            self.options["background_color_mode"],
            self.options["size"],
            self.options["background_color"]
        )

        index = 1
        # loop through dict in date order (date is dict key)
        for file_created in sorted(self.image_files):

            entry = self.image_files[file_created]

            for capture in self.captures:
                if capture["name"] not in entry:
                    capture["out_count"] += 1
                    if capture["out_count"] >= self.OUT_COUNT_TRESHOLD:
                        capture["previous"] = capture["placeholder_path"]
                    entry[capture["name"]] = capture["previous"]
                else:
                    capture["out_count"] = 0
                current_capture_path = entry[capture["name"]]
                capture["previous"] = current_capture_path

                capture_image = ""
                crop_image = ""
                if current_capture_path == capture["placeholder_path"]:
                    capture_image = capture["placeholder_image"]
                    if "crop" in capture:
                        crop_image = capture["crop"]["placeholder_image"]
                else:
                    capture_image = Image.open(current_capture_path)
                    if "crop" in capture:
                        crop = capture["crop"]
                        crop_image = capture_image.crop(
                            (
                                crop["position"][0],
                                crop["position"][1],
                                crop["position"][0] + crop["size"][0],
                                crop["position"][1] + crop["size"][1]
                            )
                        )
                        crop_image = self.resize_image(crop_image, crop)
                    capture_image = self.resize_image(capture_image, capture)

                image.paste(
                    capture_image,
                    (
                        capture["target_position"][0],
                        capture["target_position"][1]
                    )
                )

                if "crop" in capture:
                    image.paste(
                        crop_image,
                        (
                            capture["crop"]["target_position"][0],
                            capture["crop"]["target_position"][1]
                        )
                    )

                self.draw_date(image, file_created)

            image.save("result/test {}.png".format(index), optimize=True)
            index += 1

    def resize_image(self, image, capture):
        if "target_size" in capture:
            if capture["target_size"][0] < capture["size"][0]:
                image = image.resize(capture["target_size"], Image.LANCZOS)
            else:
                image = image.resize(capture["target_size"], Image.BICUBIC)
        return image

    def add_files(self, path, name):
        for root, dirs, files in os.walk(path):
            for file in files:
                filepath = os.path.join(root, file)
                # get file created and convert to int (precision: seconds)
                file_created = datetime.datetime.fromtimestamp(int(os.stat(filepath).st_mtime))
                # round to nearest 5
                rounded_seconds = int(5 * round(float(file_created.second)/5))
                # if 60, get next minute
                if rounded_seconds > 59:
                    file_created = file_created + datetime.timedelta(minutes=1)
                    rounded_seconds = 0
                file_created = file_created.replace(second=rounded_seconds)

                # add to dict or modify current dict
                if file_created not in self.image_files:
                    self.image_files[file_created] = {}
                self.image_files[file_created][name] = filepath


    def draw_date(self, image, date):
        draw = ImageDraw.Draw(image)
        # draw a rectangle to 
        draw.rectangle(
            (
                (
                    1280 + 350,
                    150
                ),
                (
                    1280 + 350 + 800,
                    150 + 100
                )
            ),
            fill=(0, 0, 0)
        )
        draw.text((1280 + 350, 150), date.strftime("%Y.%m.%d %H:%M:%S"), font=self.font)

compositer = ImageCompositionTool(
    {
        'padding': 150,
        'size': (2560, 1440),
        'font': 'Rubik-Bold.ttf',
        'font_size': 72,
    },
    [
        {
            'name': 'first',
            'image_folder': 'images_first',
            'placeholder_path': 'placeholder_first.png',
            'size': (2560, 1440),
            'target_size': (1280, 720),
            'target_position': (0, 0),
            'crop': {
                'placeholder_path': 'placeholder_crop.png',
                'size': (320, 240),
                # crop webcam that is on the bottom right, just above taskbar (30px)
                'position': (
                    2560 - 320, # width - crop width
                    1440 - 240 - 30, # height - crop height - taskbar height
                    2560, # width
                    1440 - 30 # height - taskbar height
                ),
                'target_size': (960, 720),
                'target_position': (
                    int(2560 - 960 - 150),
                    int(1440 / 2 - 720 / 2)
                )
            }
        },
        {
            'name': 'second',
            'image_folder': 'images_second',
            'placeholder_path': 'placeholder_second.png',
            'size': (1920, 1080),
            'target_size': (1280, 720),
            'target_position': (0, 720)
        }
    ]
)
compositer.generate_image()