Holgers Sammelsurium

Fotografie, Reisen, Nerdkrams

„Sofortbild“-Effekt mit Python generieren

Mit Python und der Bildbearbeitungs-Bibliothek Pillow kann man sich recht einfach ein Skript zusammenbauen, welches ein Foto in beliebigem Seitenformat als Eingabe empfängt und dieses zu einem „Instant-Bild“ aus einer Sofortbildkamera „umbaut“. Entsprechend der verschiedenen im Skript hinterlegten Zielformate wird hierfür zunächst ein Bereich aus dem Zentrum des Eingabebilds ausgeschnitten. Dann wird ein nicht ganz reinweißer Rand um das ausgeschnittene Bild ergänzt. Das so entstandene Instant-Bild wird schließlich auf einem reinweißen Hintergrund samt kleinem Schatten „gelegt“.

Das ganze sieht dann etwa so aus:

Damit das Skript funktioniert, muss noch Pillow per pip3 install Pillow nachinstalliert werden.

from PIL import Image, ImageOps, ImageFilter
from os.path import exists, isfile
from glob import glob

def polaroidize(image, type="wide", polaroidWAbs=1200):
    # relative dimensions of instant photo and the image in it
    if type == "mini":
        # Fujifilm Instax Mini Format
        imageH = 62
        imageW = 46
        polaroidH = 86
        polaroidW = 54
    elif type == "wide":
        # Fujifilm Instax Wide Format
        imageH = 62
        imageW = 99
        polaroidH = 86
        polaroidW = 108
    elif type == "square":
        # Fujifilm Instax Square
        imageH = 62
        imageW = 62
        polaroidH = 86
        polaroidW = 72
    elif type == "spectra":
        # Polaroid Image/Spectra Format
        imageH = 72
        imageW = 91
        polaroidH = 101
        polaroidW = 103
    elif type == "sx70":
        # Polaroid SX-70 / 600 Format
        imageW = 77
        imageH = 79
        polaroidW = 88
        polaroidH = 106
    else:
        exit()

    # compute the relative size of borders
    borderLR = (polaroidW - imageW) / 2
    borderTop = borderLR
    borderBottom = polaroidH - imageH - borderTop

    # compute scaling factor
    scale = polaroidW / polaroidWAbs

    # compute absolute sizes for the "paper", image and borders
    polaroidHAbs = round(polaroidH / scale)
    imageWAbs = round(imageW / scale)
    imageHAbs = round(imageH / scale)
    borderLRAbs = round(borderLR / scale)
    borderTopAbs = round(borderTop / scale)
    borderBottomAbs = round(borderBottom / scale)

    width, height = image.size
    target_ratio = imageW / imageH
    actual_ratio = width / height

    if actual_ratio > target_ratio:
        new_width = int(height * target_ratio)
        left = (width - new_width) // 2
        right = left + new_width
        top, bottom = 0, height
    else:
        new_height = int(width / target_ratio)
        top = (height - new_height) // 2
        bottom = top + new_height
        left, right = 0, width

    image = image.crop((left, top, right, bottom))
    image = image.resize((imageWAbs, imageHAbs))
    image = ImageOps.expand(image, border=(borderLRAbs, borderTopAbs, borderLRAbs, borderBottomAbs), fill=0xEEEEEE)
    return image

def dropShadow(image, offset=(15, 15), background=0xffffff, shadow=0x666666, border=100, iterations=5):
# code mostly taken from: https://code.activestate.com/recipes/474116-drop-shadows-with-pil/

    # create background
    totalWidth = image.size[0] + abs(offset[0]) + 2 * border
    totalHeight = image.size[1] + abs(offset[1]) + 2 * border
    back = Image.new(image.mode, (totalWidth, totalHeight), background)

    # create shadow
    shadowLeft = border + max(offset[0], 0)
    shadowTop = border + max(offset[1], 0)
    back.paste(shadow, [shadowLeft, shadowTop, shadowLeft + image.size[0], shadowTop + image.size[1]])

    # blur
    n = 0
    while n < iterations:
        back = back.filter(ImageFilter.BLUR)
        n += 1

    # combine
    imageLeft = border - min(offset[0], 0)
    imageTop = border - min(offset[1], 0)
    back.paste(image, (imageLeft, imageTop))

    return back

# the target instant photo's width
polaroidWAbs = 1200

choice = input(f"Pick a format: Instax Mini (1), Wide (2), Square (3), Polaroid Sx-70 (4) or Spectra (5): ")
try:
    choice = int(choice)
except:
    choice = 2

formats = ["mini", "wide", "square", "sx70", "spectra"]
format = formats[choice-1]

files = glob("*.jpg")
for file in files:
    tmp = file.split(("."))
    newFileName = f"{tmp[0]}_{format}.{tmp[1]}"

    if exists(file) and isfile(file):
        image = Image.open(file)
        print(f"Processing {file} as {format}.")
    else:
        print(f"{file} does not exist. Abort.")

    image = polaroidize(image, format, polaroidWAbs)
    image = dropShadow(image)
    image.save(newFileName)

Die automatische Auswahl aus dem Zentrum des Bilds ist nicht immer optimal bzw. exakt das, was man möchte. Wenn man den Fokus auf ein bestimmtes Bildelement richten will – vielleicht die Schaltkulisse rechts vom Lenkrad – muss man zunächst das Bild manuell auf das passende Instant-Bild-Format zuschneiden. Wenn man dann dieses Format im Skript auswählt, wird nichts mehr vom ohnehin beschnittenen Bild abgeschnitten – es ist ja schon im richtigen Format – und nur der Rahmen ergänzt. Das ganze ist natürlich mühselig, ich wollte die Möglichkeit aber kurz erwähnen. Vermutlich ist es besser einfach ein gut passendes Bildformat auszuwählen…


Beitrag veröffentlicht am

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert