Konvertieren und Optimieren von Bildern über die Befehlszeile

Avatar of Ravgeet Dhillon
Ravgeet Dhillon am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Bilder machen bis zu 50% der Gesamtgröße einer durchschnittlichen Webseite aus. Und wenn Bilder nicht optimiert sind, laden Benutzer zusätzliche Bytes herunter. Und wenn sie zusätzliche Bytes herunterladen, dauert die Ladezeit der Seite nicht nur länger, sondern Benutzer verbrauchen auch mehr Daten. Beides kann, zumindest teilweise, gelöst werden, indem die Bilder optimiert werden, bevor sie heruntergeladen werden.

Forscher auf der ganzen Welt entwickeln eifrig neue Bildformate, die trotz hoher visueller Qualität kleiner sind als andere Formate wie PNG oder JPG. Obwohl diese neuen Formate noch in der Entwicklung sind und im Allgemeinen nur begrenzte Browserunterstützung haben, gewinnt eines davon, WebP, viel Aufmerksamkeit. Und obwohl sie nicht wirklich in der gleichen Klasse wie Rasterbilder spielen, sind SVGs ein weiteres Format, das viele von uns in den letzten Jahren aufgrund ihres inhärent leichten Gewichts verwendet haben.

Es gibt unzählige Möglichkeiten, kleinere und optimierte Bilder zu erstellen. In diesem Tutorial schreiben wir Bash-Skripte, die Bilder in verschiedenen Bildformaten erstellen und optimieren, und zielen auf die gängigsten Formate ab, darunter JPG, PNG, WebP und SVG. Die Idee ist, die Bilder vor dem Ausliefern zu optimieren, damit die Benutzer das visuell großartigste Erlebnis ohne den gesamten Byte-Overhead erhalten.

Unser Zielverzeichnis für Bilder
Unser Verzeichnis für optimierte Bilder

Dieses GitHub-Repository enthält alle Bilder, die wir verwenden, und Sie können sie gerne herunterladen und mitmachen.

Einrichtung

Bevor wir beginnen, bringen wir alle unsere Abhängigkeiten in Ordnung. Wir schreiben wieder Bash-Skripte, also werden wir Zeit auf der Kommandozeile verbringen.

Hier sind die Befehle für alle Abhängigkeiten, die wir benötigen, um mit der Optimierung von Bildern zu beginnen

sudo apt-get update
sudo apt-get install imagemagick webp jpegoptim optipng
npm install -g svgexport svgo

Es ist ratsam zu wissen, womit wir arbeiten, bevor wir mit der Verwendung beginnen

OK, wir haben unsere Bilder im Verzeichnis original-images aus dem GitHub-Repository. Sie können dem Commit 3584f9b folgen.

Hinweis: Es wird dringend empfohlen, Ihre Bilder zu sichern, bevor Sie fortfahren. Wir werden Programme ausführen, die diese Bilder ändern, und obwohl wir die Originale unverändert lassen wollen, könnte ein falscher Befehl sie auf eine Weise ändern, die nicht rückgängig gemacht werden kann. Sichern Sie also alles, was Sie für ein echtes Projekt verwenden möchten, um sich späteres Fluchen zu ersparen.

Bilder organisieren

OK, wir sind technisch eingerichtet. Aber bevor wir uns dem Optimieren aller Dinge widmen, sollten wir unsere Dateien etwas organisieren. Lassen Sie uns sie nach ihrem MIME-Typ in verschiedene Unterverzeichnisse aufteilen. Tatsächlich können wir ein neues Bash-Skript erstellen, das dies für uns erledigt!

Der folgende Code erstellt ein Skript namens organize-images.sh

#!/bin/bash

input_dir="$1"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
fi

for img in $( find $input_dir -type f -iname "*" );
do
  # get the type of the image
  img_type=$(basename `file --mime-type -b $img`)

  # create a directory for the image type
  mkdir -p $img_type

  # move the image into its type directory
  rsync -a $img $img_type
done

Das mag verwirrend aussehen, wenn Sie neu im Skriptschreiben sind, aber was es tut, ist eigentlich ziemlich einfach. Wir geben dem Skript ein Eingabeverzeichnis, in dem es nach Bildern sucht. Das Skript durchsucht dann dieses Eingabeverzeichnis, sucht nach Bilddateien und identifiziert ihren MIME-Typ. Schließlich erstellt es Unterverzeichnisse im Eingabeordner für jeden MIME-Typ und legt eine Kopie jedes Bildes in das jeweilige Unterverzeichnis.

Lassen Sie es uns ausführen!

bash organize-images.sh original-images

Super. Das Verzeichnis sieht jetzt so aus. Jetzt, da unsere Bilder organisiert sind, können wir mit der Erstellung von Varianten jedes Bildes fortfahren. Wir werden uns jeweils einen Bildtyp vornehmen.

Konvertieren in PNG

Wir werden in diesem Tutorial drei Arten von Bildern in PNG konvertieren: WebP, JPEG und SVG. Beginnen wir mit dem Schreiben eines Skripts namens webp2png.sh, das ziemlich selbsterklärend ist: Konvertieren von WebP-Dateien in PNG-Dateien.

#!/bin/bash

# directory containing images
input_dir="$1"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
fi

# for each webp in the input directory
for img in $( find $input_dir -type f -iname "*.webp" );
do
  dwebp $img -o ${img%.*}.png
done

Hier ist, was passiert

  • input_dir="$1": Speichert die Eingabe der Befehlszeile im Skript.
  • if [[ -z "$input_dir" ]]; then: Führt die nachfolgende Bedingung aus, wenn das Eingabeverzeichnis nicht definiert ist.
  • for img in $( find $input_dir -type f -iname "*.webp" );: Schleift durch jede Datei im Verzeichnis, die die Erweiterung .webp hat.
  • dwebp $img -o ${img%.*}.png: Konvertiert das WebP-Bild in eine PNG-Variante.

Und los geht's

bash webp2png.sh webp

Wir haben jetzt unsere PNG-Bilder im Verzeichnis webp. Als nächstes konvertieren wir JPG/JPEG-Dateien mit einem weiteren Skript namens jpg2png.sh in PNG.

#!/bin/bash

# directory containing images
input_dir="$1"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
fi

# for each jpg or jpeg in the input directory
for img in $( find $input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" );
do
  convert $img ${img%.*}.png
done

Dies verwendet den convert-Befehl, der vom installierten ImageMagick-Paket bereitgestellt wird. Wie das letzte Skript geben wir ein Eingabeverzeichnis an, das JPEG/JPG-Bilder enthält. Das Skript durchsucht dieses Verzeichnis und erstellt für jedes passende Bild eine PNG-Variante. Wenn Sie genau hinschauen, haben wir -o -iname "*.jpeg" in find hinzugefügt. Dies bezieht sich auf die logische ODER-Verknüpfung, die das Skript alle Bilder findet, die entweder die Erweiterung .jpg oder .jpeg haben.

So führen wir es aus

bash jpg2png.sh jpeg

Jetzt, da wir unsere PNG-Varianten aus JPG haben, können wir dasselbe auch für SVG-Dateien tun.

#!/bin/bash

# directory containing images
input_dir="$1"

# png image width
width="$2"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
elif [[ -z "$width" ]]; then
  echo "Please specify image width."
  exit 1
fi

# for each svg in the input directory
for img in $( find $input_dir -type f -iname "*.svg" );
do
  svgexport $img ${img%.*}.png $width:
done

Dieses Skript hat eine neue Funktion. Da SVG ein skalierbares Format ist, können wir die width-Direktive angeben, um unsere SVGs zu vergrößern oder zu verkleinern. Wir verwenden das zuvor installierte Paket svgexport, um jede SVG-Datei in eine PNG-Datei zu konvertieren.

bash svg2png.sh svg+xml

Commit 76ff80a zeigt das Ergebnis im Repository.

Wir haben hier viel gute Arbeit geleistet, indem wir eine Reihe von PNG-Dateien basierend auf anderen Bildformaten erstellt haben. Wir müssen noch dasselbe für die restlichen Bildformate tun, bevor wir zur eigentlichen Aufgabe der Optimierung übergehen.

Konvertieren in JPG

In Anlehnung an die Erstellung von PNG-Bildern werden wir WebP, JPEG und SVG in JPG konvertieren. Beginnen wir mit dem Schreiben eines Skripts namens png2jpg.sh, das PNG in SVG konvertiert.

#!/bin/bash

# directory containing images
input_dir="$1"

# jpg image quality
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
elif [[ -z "$quality" ]]; then
  echo "Please specify image quality."
  exit 1
fi

# for each png in the input directory
for img in $( find $input_dir -type f -iname "*.png" );
do
  convert $img -quality $quality% ${img%.*}.jpg
done

Sie bemerken vielleicht schon ein Muster in diesen Skripten. Aber dieses führt eine neue Funktion ein, bei der wir eine -quality-Direktive festlegen können, um PNG-Bilder in JPG-Bilder zu konvertieren. Der Rest ist derselbe.

Und so führen wir es aus

bash png2jpg.sh png 90

Wow. Wir haben jetzt JPG-Bilder in unserem png-Verzeichnis. Machen wir dasselbe mit einem webp2jpg.sh-Skript.

#!/bin/bash

# directory containing images
input_dir="$1"

# jpg image quality
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
elif [[ -z "$quality" ]]; then
  echo "Please specify image quality."
  exit 1
fi

# for each webp in the input directory
for img in $( find $input_dir -type f -iname "*.webp" );
do
  # convert to png first
  dwebp $img -o ${img%.*}.png

  # then convert png to jpg
  convert ${img%.*}.png -quality $quality% ${img%.*}.jpg
done

Auch hier ist es dasselbe wie bei der Konvertierung von WebP nach PNG. Es gibt jedoch einen Haken. Wir können das WebP-Format nicht direkt in das JPG-Format konvertieren. Daher müssen wir hier etwas kreativ werden und WebP mit dwebp in PNG konvertieren und *dann* PNG mit convert in JPG konvertieren. Deshalb haben wir in der for-Schleife zwei verschiedene Schritte.

Lassen Sie es uns jetzt ausführen

bash webp2jpg.sh jpeg 90

Voilà! Wir haben JPG-Varianten für unsere WebP-Bilder erstellt. Jetzt widmen wir uns SVG zu JPG.

#!/bin/bash

# directory containing images
input_dir="$1"

# jpg image width
width="$2"

# jpg image quality
quality="$3"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
elif [[ -z "$width" ]]; then
  echo "Please specify image width."
  exit 1
elif [[ -z "$quality" ]]; then
  echo "Please specify image quality."
  exit 1
fi

# for each svg in the input directory
for img in $( find $input_dir -type f -iname "*.svg" );
do
    svgexport $img ${img%.*}.jpg $width: $quality%
done

Sie denken vielleicht, dass Sie dieses Skript schon einmal gesehen haben. Das haben Sie! Wir haben dasselbe Skript verwendet, um PNG-Bilder aus SVG zu erstellen. Die einzige Ergänzung zu diesem Skript ist, dass wir die quality-Direktive unserer JPG-Bilder angeben können.

bash svg2jpg.sh svg+xml 512 90

Alles, was wir gerade getan haben, ist im Commit 884c6cf im Repository enthalten.

Konvertieren in WebP

WebP ist ein Bildformat, das für moderne Browser entwickelt wurde. Zum Zeitpunkt der Abfassung dieses Textes hat es eine weltweite Browserunterstützung von etwa 90 %, einschließlich teilweiser Unterstützung in Safari. Der größte Vorteil von WebP ist seine deutlich kleinere Dateigröße im Vergleich zu anderen Bildformaten, ohne Kompromisse bei der visuellen Qualität einzugehen. Das macht es zu einem guten Format für Benutzer.

Aber genug geredet. Schreiben wir ein png2webp.sh, das – Sie haben es erraten – WebP-Bilder aus PNG-Dateien erstellt.

#!/bin/bash

# directory containing images
input_dir="$1"

# webp image quality
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
elif [[ -z "$quality" ]]; then
  echo "Please specify image quality."
  exit 1
fi

# for each png in the input directory
for img in $( find $input_dir -type f -iname "*.png" );
do
  cwebp $img -q $quality -o ${img%.*}.webp
done

Dies ist einfach die Umkehrung des Skripts, das wir zum Erstellen von PNG-Bildern aus WebP-Dateien verwendet haben. Anstatt dwebp zu verwenden, verwenden wir cwebp.

bash png2webp.sh png 90

Wir haben unsere WebP-Bilder. Jetzt konvertieren wir JPG-Bilder. Das Schwierige ist, dass es keine Möglichkeit gibt, eine JPG-Datei direkt in WebP zu konvertieren. Daher werden wir in unserem jpg2webp.sh-Skript zuerst JPG in PNG konvertieren und dann das Zwischen-PNG in WebP konvertieren.

#!/bin/bash

# directory containing images
input_dir="$1"

# webp image quality
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
elif [[ -z "$quality" ]]; then
  echo "Please specify image quality."
  exit 1
fi

# for each webp in the input directory
for img in $( find $input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" );
do
  # convert to png first
  convert $img ${img%.*}.png

  # then convert png to webp
  cwebp ${img%.*}.png -q $quality -o ${img%.*}.webp
done

Jetzt können wir es so verwenden, um unsere WebP-Varianten von JPG-Dateien zu erhalten.

bash jpg2webp.sh jpeg 90

Commit 6625f26 zeigt das Ergebnis.

Alles in einem Verzeichnis zusammenführen

Nachdem wir nun mit der Konvertierung fertig sind, sind wir einen Schritt näher an der Optimierung unserer Arbeit. Aber zuerst bringen wir alle unsere Bilder wieder in ein einziges Verzeichnis, damit es einfacher ist, sie mit weniger Befehlen zu optimieren.

Hier ist der Code, der ein neues Bash-Skript namens combine-images.sh erstellt.

#!/bin/bash

input_dirs="$1"
output_dir="$2"

if [[ -z "$input_dirs" ]]; then
  echo "Please specify an input directories."
  exit 1
elif [[ -z "$output_dir" ]]; then
  echo "Please specify an output directory."
  exit 1
fi

# create a directory to store the generated images
mkdir -p $output_dir

# split input directories comma separated string into an array
input_dirs=(${input_dirs//,/ })

# for each directory in input directory
for dir in "${input_dirs[@]}"
do
  # copy images from this directory to generated images directory
  rsync -a $dir/* $output_dir/
done

Das erste Argument ist eine durch Kommas getrennte Liste von Eingabeverzeichnissen, die Bilder in ein Zielverzeichnis übertragen. Das zweite Argument definiert dieses kombinierte Verzeichnis.

bash combine-images.sh jpeg,svg+xml,webp,png generated-images

Das Endergebnis ist im Repository zu sehen.

SVG optimieren

Lassen Sie uns damit beginnen, unsere SVG-Bilder zu optimieren. Fügen Sie den folgenden Code zu optimize-svg.sh hinzu.

#!/bin/bash

# directory containing images
input_dir="$1"

if [[ -z "$input_dir" ]]; then
echo "Please specify an input directory."
exit 1
fi

# for each svg in the input directory
for img in $( find $input_dir -type f -iname "*.svg" );
do
  svgo $img -o ${img%.*}-optimized.svg
done

Wir verwenden hier das SVGO-Paket. Es hat viele Optionen, die wir verwenden können, aber um die Dinge einfach zu halten, bleiben wir beim Standardverhalten der Optimierung von SVG-Dateien.

bash optimize-svg.sh generated-images
Das spart uns 4 KB pro Bild. Nehmen wir an, wir würden 100 SVG-Icons ausliefern – wir hätten gerade 400 KB gespart!

Das Ergebnis ist im Commit 75045c3 im Repository zu sehen.

PNG optimieren

Machen wir weiter und optimieren unsere PNG-Dateien, indem wir diesen Code verwenden, um einen optimize-png.sh-Befehl zu erstellen.

#!/bin/bash

# directory containing images
input_dir="$1"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
fi

# for each png in the input directory
for img in $( find $input_dir -type f -iname "*.png" );
do
  optipng $img -out ${img%.*}-optimized.png
done

Hier verwenden wir das OptiPNG-Paket zur Optimierung unserer PNG-Bilder. Das Skript sucht im Eingabeverzeichnis nach PNG-Bildern und erstellt für jedes eine optimierte Version, wobei -optimized an den Dateinamen angehängt wird. Es gibt ein interessantes Argument, -o, mit dem wir den Optimierungsgrad festlegen können. Der Standardwert ist 2, **und die Werte reichen von 0 bis 7. Um unsere PNGs zu optimieren, führen wir

bash optimize-png.sh generated-images
Die PNG-Optimierung hängt von den im Bild gespeicherten Informationen ab. Einige Bilder können stark optimiert werden, während andere wenig bis gar keine Optimierung zeigen.

Wie wir sehen können, leistet OptiPNG hervorragende Arbeit bei der Optimierung der Bilder. Wir können mit dem -o-Argument experimentieren, um einen geeigneten Wert zu finden, indem wir zwischen Bildqualität und Größe abwägen. Sehen Sie sich die Ergebnisse in Commit 4a97f29 an.

JPG optimieren

Wir sind beim letzten Teil angekommen! Wir schließen das Ganze ab, indem wir JPG-Bilder optimieren. Fügen Sie den folgenden Code zu optimize-jpg.sh hinzu.

#!/bin/bash

# directory containing images
input_dir="$1"

# target image quality
quality="$2"

if [[ -z "$input_dir" ]]; then
  echo "Please specify an input directory."
  exit 1
elif [[ -z "$quality" ]]; then
  echo "Please specify image quality."
  exit 1
fi

# for each jpg or jpeg in the input directory
for img in $( find $input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" );
do
  cp $img ${img%.*}-optimized.jpg
  jpegoptim -m $quality ${img%.*}-optimized.jpg
done

Dieses Skript verwendet JPEGoptim. Das Problem mit diesem Paket ist, dass es keine Option zur Angabe der Ausgabedatei hat. Wir können die Bilddatei nur vor Ort optimieren. Wir können dies umgehen, indem wir zuerst eine Kopie des Bildes erstellen, es beliebig benennen und dann die Kopie optimieren. Das Argument -m wird verwendet, um die Bildqualität anzugeben. Es ist gut, damit ein wenig zu experimentieren, um die richtige Balance zwischen Qualität und Dateigröße zu finden.

bash optimize-jpg.sh generated-images 95

Die Ergebnisse sind im Commit 35630da zu sehen.

Zusammenfassung

Sehen Sie das? Mit ein paar Skripten können wir umfangreiche Bildoptimierungen direkt über die Befehlszeile durchführen und sie in jedem Projekt verwenden, da sie global installiert sind. Wir können CI/CD-Pipelines einrichten, um verschiedene Varianten jedes Bildes zu erstellen und sie mithilfe von gültigem HTML, APIs oder sogar durch Einrichtung eigener Bildkonvertierungs-Websites auszuliefern.

Ich hoffe, Sie haben das Lesen und Lernen aus diesem Artikel genauso genossen wie ich das Schreiben für Sie. Viel Spaß beim Coden!