Skalieren und Zuschneiden von Bildern mit Python

Bildquelle: by USGS on Unsplash

Skalieren und Zuschneiden von Bildern mit Python

Da ich sehr viele Bilder (und anfangs auch sehr große) Bilder hier verwendet habe, hat sich das enorm auf die Geschwindigkeit der Seite ausgewirkt. Seit Juli 2018 ist PageSpeed ein Ranking-Faktor für Suchmaschinen wie Google und Co und man möchte ja nicht irgendwo auf Seite 50 in den Suchergebnissen rumdümpeln sondern vorne mit dabei sein.

Die hier verwendeten Bilder sind hauptsächlich Satellitenbilder von ESA, die unter der Lizenz CC BY-SA 3.0 IGO IGO) veröffentlich wurden und somit auch für eigene Zwecke unter bestimmten Bedingungen genutzt werden dürfen.

Diese Bilder sind gerne auch mal ~30MB groß, was etwas zu groß für eine Website ist. + Da ich nicht alle Bilder manuell zuschneiden wollte, habe ich mich entschieden dieses Problem mit Python bzw. Pillow zu lösen.

Pillow Bibliothek

Pillow ist eine Python Bibliothek zur Bildverarbeitung, die man sich unter Windows mit

pip install Pillow

installieren und mit

from PIL import Image

in ein Python Script importieren kann.

Alle Bilder für Posts liegen in einem seperaten "images/" Ordner im Rootverzeichnis des Projekts. Als Erstes werden alle ".jpg" Dateien in einem bestimmten Verzeichnis mit Pillow geöffnet und alle Dateinamen in einen Array gespeichert. Außerdem wird eine Variable benötigt und später auf jeden Namen in dem Array zugreifen zu können.

count = 0
image_list = []

for file in glob.iglob('path/to/images/*.jpg'):
    im=Image.open(file)
    image_list.append(os.path.basename(file))

Größen definieren und Seitenverhältnis berechnen

Nun solltem man wissen, auf welche Größen die Bilder zugeschnitten und ob zum Beispiel Größenverhältnisse beibehalten werden sollen. Bei allen "PostCover" (Bilder in Beiträgen) wird das Seitenverhältnisse ignoriert und das Bild einfach auf eine bestimmte Größe zugeschnitten, die in einer globalen Variable deklariert wird.

size = (1903,453) #(width,height)

Bei allen "PostThumbnails" (Bilder Vorschau) soll das Seitenverhältnis beibehalten werden und sozusagen nur kleiner skaliert werden. Dafür wird eine globale Standardbreite definiert.

basewidth = 500

Anschließend wird die Originalbreite und -höhe der Bilder ermittelt, da wir diese brauchen um das Seitenverhältnis berechnen und beibehalten zu können. Hier wird nur die neue Höhe gebraucht, da die Standardbreite bereits vordefiniert wurde.

    width, height = im.size
    wpercent = (basewidth / float(im.size[0]))
    hsize = int((float(im.size[1]) * float(wpercent)))

"Cropping" und "Rescaling"

Nun kann man die Bilder mit Image.cropzuschneiden bzw. mit Image.resize skalieren. Bei der Skalierung werden jetzt die neue Breite "basewidth" und die berechnete Höhe "hsize" als Parameter verwendet.

    imThumbnail = im.resize((basewidth, hsize), Image.LANCZOS)
    imCover = im.crop(((width-size[0])//2, (height-size[1])//2, (width+size[0])//2, (height+size[1])//2))

Folgend habe ich die das Thumbnail noch umbenannt und beide neuen Dateien unter static/assets/ mit einer Qualität von "85" abspeichert. Mit dem zustätzlichen Parameter "optimize=True" können auch noch einmal ein paar KB gespart werden.

    newCover = 'static/assets/{}'.format(image_list[count])
    newThumbnail = 'static/assets/{}_thumbnail.jpg'.format(image_list[count].replace(".jpg", ""))
    imCover.save(newCover,optimize=True,quality=85)
    imThumbnail.save(newThumbnail,optimize=True,quality=90)
    count +=1 

Gesamtskript:

from PIL import Image
import glob, os

count = 0
image_list = []
basewidth = 500
size = (1903,453)
 

for file in glob.iglob('path/to/images/*.jpg'):
    im=Image.open(file)
    image_list.append(os.path.basename(file))
    width, height = im.size
    wpercent = (basewidth / float(im.size[0]))
    hsize = int((float(im.size[1]) * float(wpercent)))
    imThumbnail = im.resize((basewidth, hsize), Image.LANCZOS)
    imCover = im.crop(((width-size[0])//2, (height-size[1])//2, (width+size[0])//2, (height+size[1])//2))
    newCover = 'static/assets/{}'.format(image_list[count])
    newThumbnail = 'static/assets/{}_thumbnail.jpg'.format(image_list[count].replace(".jpg", ""))
    imCover.save(newCover,optimize=True,quality=85)
    imThumbnail.save(newThumbnail,optimize=True,quality=90)
    count +=1 

Skript automatisch ausführen

Um das Skript nicht jedes mal manuell ausführen zu müssen kann man in "package.json" noch folgendes ergänzen.

    "img-optimize": "py ./src/utils/resize_images.py"

Somit kann man mit npm run img-optimize alle Bilder automatisch optimieren.

Max Dietrich

Von Max Dietrich.

Geospatial specialist, passionate Web-Developer.