JPEGs mit jpegtran stapeloptimieren, ohne Fehler zu verbergen
Mit jpegtran ändern Sie das Encoding vorhandener JPEGs ohne erneute
verlustbehaftete Komprimierung. Schreiben Sie bei einem lokalen Stapel in ein neues Verzeichnis
und prüfen Sie den Exit-Status jeder Konvertierung. Das folgende Bash-Skript erhält Ihre Originale
und Metadaten, erstellt progressive JPEGs und gibt einen Fehlerstatus zurück, wenn die Verarbeitung
eines der ausgewählten Bilder fehlschlägt.
Wählen Sie, was erhalten bleiben soll
jpegtran arbeitet mit komprimierten DCT-Koeffizienten, statt Pixel zu
decodieren und erneut zu komprimieren. So bleiben die Bilddaten des vorhandenen JPEGs erhalten;
Details, die beim ersten Encoding verloren gingen, lassen sich damit nicht wiederherstellen.
Die Nutzungsanleitung zu libjpeg-turbo erläutert diesen Unterschied.
Dieser Workflow nutzt -optimize für Huffman-Encoding und
-progressive für mehrere Bilddurchläufe. Ein kompatibler Bildbetrachter kann eine
erste Vorschau anzeigen, bevor er das vollständige progressive JPEG empfangen hat. Keines der
Flags garantiert eine kleinere Datei. Vergleichen Sie daher die Byte-Anzahl, bevor Sie Dateien
ersetzen.
Die Metadatenrichtlinie lautet -copy all: Zusätzliche Marker einschließlich
EXIF- und ICC-Profilen bleiben erhalten. Das ist wichtig, da EXIF-Ausrichtung und Farbprofile
beeinflussen, wie Bildbetrachter ein Bild anzeigen. Auch potenziell private Informationen wie
GPS-Koordinaten bleiben erhalten. Diese Ausgaben sind zur lokalen Prüfung bestimmt, nicht zum
automatischen öffentlichen Hochladen.
Mit -copy none würden diese Marker entfernt. Dadurch könnten sich die angezeigte
Ausrichtung oder Farbe ändern, obwohl die Koeffizienten erhalten bleiben.
-copy icc erhält das Farbprofil, entfernt aber weiterhin die EXIF-Ausrichtung.
Wählen Sie bei Bedarf einen separaten Workflow zum Entfernen von Metadaten; diese Stapelverarbeitung
dreht keine Pixel und vereinheitlicht nicht die Ausrichtung. Siehe die
Optionen zum Kopieren von Markern.
Prüfen Sie die Werkzeuge
Verwenden Sie unter Linux Bash und jpegtran von libjpeg-turbo mit den üblichen
Dienstprogrammen mkdir, mv,
rm und wc. Das Beispiel wurde mit Bash 5.3.15
und libjpeg-turbo 3.2.0 getestet. Installieren Sie die Befehlszeilenwerkzeuge von libjpeg-turbo über
Ihre Distribution oder die Release-Pakete des Projekts. Prüfen Sie
anschließend, welche Implementierung Sie haben:
bash --version
jpegtran -version
Der zweite Befehl sollte libjpeg-turbo ausweisen. Andere Projekte bieten ebenfalls ein Programm
namens jpegtran an; dessen Optionen und Ausgabe können abweichen. Als Eingabe
dienen hier gewöhnliche DCT-basierte 8-Bit-JPEGs, einschließlich sequenzieller und progressiver
Dateien.
Führen Sie die Stapelverarbeitung in ein neues Verzeichnis aus
Speichern Sie dies als optimize_images.sh im Verzeichnis mit Ihren JPEGs. Führen Sie es
mit Bash aus, nicht mit sh. Es wählt reguläre Dateien mit den Endungen
.jpg oder .jpeg aus, auch bei großgeschriebenen
Erweiterungen und versteckten Dateien. Unterverzeichnisse werden nicht durchsucht und symbolische
Links nicht verfolgt. Dateinamen mit Leerzeichen, führenden Bindestrichen und wörtlichen Zeichen
% werden unterstützt.
Das Ausgabeverzeichnis darf noch nicht existieren. Jede erfolgreiche Ausgabe behält ihren
ursprünglichen Dateinamen unter optimized/. Jede Konvertierung wird in einer
temporären Datei gespeichert, bis jpegtran erfolgreich abgeschlossen ist.
So wird eine fehlgeschlagene Konvertierung nicht zur fertigen Ausgabe. Führen Sie dies in einem
Verzeichnis unter Ihrer Kontrolle aus, ohne dass andere Prozesse dessen Dateien während der
Stapelverarbeitung ändern.
#!/usr/bin/env bash
set -u
if (( $# != 0 )); then
printf 'Run this script without arguments in your JPEG directory.\n' >&2
exit 2
fi
if ! command -v jpegtran >/dev/null; then
printf 'jpegtran is not installed or is missing from PATH.\n' >&2
exit 2
fi
shopt -s nullglob nocaseglob dotglob
images=()
for img in ./*.jpg ./*.jpeg; do
if [[ -f "$img" && ! -L "$img" ]]; then
images+=("$img")
fi
done
if (( ${#images[@]} == 0 )); then
printf 'No JPEG files selected.\n' >&2
exit 1
fi
if ! mkdir -- ./optimized; then
printf 'Cannot create optimized; move any previous results aside before rerunning.\n' >&2
exit 1
fi
temporary=./optimized/.jpegtran.tmp
trap 'rm -f -- "$temporary"' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
status=0
for img in "${images[@]}"; do
output="./optimized/${img#./}"
if jpegtran -strict -copy all -optimize -progressive -outfile "$temporary" "$img" &&
mv -- "$temporary" "$output"; then
printf 'Wrote %q\n' "$output"
else
printf 'Failed: %q\n' "$img" >&2
status=1
fi
done
exit "$status"
Führen Sie im selben Verzeichnis Folgendes aus:
bash optimize_images.sh
Ein Exit-Status von null bedeutet, dass jede ausgewählte Datei konvertiert und an ihren Zielort
verschoben wurde. Ungültige Bilder und Konvertierungswarnungen führen wegen
-strict zu einem Fehler. Diagnosemeldungen bleiben sichtbar, die
Stapelverarbeitung fährt mit den übrigen Bildern fort und ihr abschließender Status ist ungleich
null. Erfolgreiche Ausgaben eines Stapels mit teils fehlgeschlagenen Konvertierungen bleiben zur
Prüfung verfügbar. Auch eine leere Auswahl liefert einen Status ungleich null und erstellt kein
Ausgabeverzeichnis.
Bei erneuter Ausführung wird ein vorhandenes Verzeichnis, eine Datei oder ein symbolischer Link
namens optimized abgelehnt und unverändert belassen. Prüfen Sie frühere
Ergebnisse und verschieben Sie diese selbst, bevor Sie es erneut versuchen. Verwenden Sie in einem
Build-Job den obigen Aufruf als Schritt, dessen Fehlschlagen die Bereitstellung stoppt. Das
Hinzufügen von || echo ... würde diesen Fehler wieder verbergen. Dies ist ein
Workflow für lokale Dateien, keine Sandbox für nicht vertrauenswürdige Uploads.
Messen Sie das Ergebnis
Vergleichen Sie bei einer Eingabe namens photo.jpg nach erfolgreicher
Stapelverarbeitung die beiden Dateien:
wc -c -- ./photo.jpg ./optimized/photo.jpg
Die ersten beiden Zeilen zeigen die Byte-Anzahl von Original und Ausgabe, die letzte Zeile deren Summe. Es gibt keine feste prozentuale Einsparung. Ein kleines JPEG oder eine bereits optimierte Datei kann durch progressives Encoding größer werden. Dieses Skript behält das Ergebnis zur Prüfung, statt es stillschweigend durch das Original zu ersetzen. Öffnen Sie repräsentative Dateipaare im Bildbetrachter Ihrer Zielgruppe und prüfen Sie neben der Größe auch Ausrichtung und Farbe. Übereinstimmende decodierte Bilddaten allein sagen nichts darüber aus, wie ein Bildbetrachter mit Metadaten umgeht.
Verstehen Sie Fehler und die Grenzen von -perfect
Prüfen Sie bei einem beschädigten oder abgeschnittenen JPEG die Diagnosemeldung und stellen Sie das Quellbild wieder her, bevor Sie die Verarbeitung erneut ausführen. Verwerfen Sie stderr nicht und akzeptieren Sie keine unvollständige Datei, nur weil etwas geschrieben wurde. Wenn das Ausgabeverzeichnis nicht erstellt werden kann, prüfen Sie, ob es bereits existiert und ob Sie in das übergeordnete Verzeichnis schreiben können.
-perfect prüft geometrische Transformationen und ist weder eine zusätzliche
Qualitätseinstellung noch eine Prüfung der Dateiintegrität. Für die Entropieoptimierung und
progressive Konvertierung dieser Stapelverarbeitung ist es nicht nötig. Beim Drehen oder Spiegeln
lehnt es Transformationen ab, deren Randblöcke nicht vollständig transformiert werden können. Beim
üblichen 4:2:0-Sampling beträgt die iMCU-Größe 16 × 16 Pixel: Für eine Drehung um 90 Grad im
Uhrzeigersinn muss die Höhe durch 16 teilbar sein, für eine Drehung um 270 Grad die Breite und für
eine Drehung um 180 Grad beides. Andere Sampling-Faktoren ändern diese Grenzen. Diese Bedingungen
sind im Transformationscode von libjpeg-turbo explizit angegeben.
-trim verwirft nicht transformierbare Randpixel und ist somit eine
Entscheidung für einen Bildzuschnitt. Es ist keine Möglichkeit, das vollständige Bild zu erhalten,
nachdem -perfect eine Drehung abgelehnt hat. Das Drehen von Pixeln erfordert
außerdem eine gezielte Richtlinie für EXIF-Ausrichtung und Thumbnails; das Kopieren vorhandener
Metadaten korrigiert die Ausrichtung nicht automatisch.
Entscheiden Sie, wann Sie stattdessen neu codieren
Wenn die gemessene Einsparung zu gering ist, sind Größenänderungen, eine geringere JPEG-Qualität
oder die Konvertierung zu WebP oder AVIF eine andere Aufgabe. Diese Optionen erfordern Decodieren
und Encoding mit einem geeigneten Bildwerkzeug sowie eine Beurteilung des resultierenden Aussehens.
Verwenden Sie diese Stapelverarbeitung mit jpegtran, wenn die vorhandenen
JPEG-Bilddaten erhalten bleiben müssen. Nutzen Sie einen separaten Workflow zum erneuten Encoding,
wenn Sie Änderungen daran akzeptieren können.
