Optimiser des JPEG par lots avec jpegtran sans masquer les échecs
Utilisez jpegtran pour modifier l’encodage de JPEG existants sans nouvelle passe de compression
avec perte. Pour un traitement par lots local, écrivez dans un nouveau répertoire et vérifiez le code
de sortie de chaque conversion. Le script Bash ci-dessous conserve vos originaux et vos métadonnées,
crée des JPEG progressifs et renvoie un code d’échec si l’une des images sélectionnées échoue.
Choisir ce qu’il faut conserver
jpegtran travaille sur les coefficients DCT compressés au lieu de décoder les pixels puis de les
recompresser. Les données d’image du JPEG existant sont ainsi préservées ; en revanche, cela ne peut
pas restaurer les détails perdus lors du premier encodage du JPEG. Le
guide d’utilisation de libjpeg-turbo explique cette distinction.
Cette procédure utilise -optimize pour le codage de Huffman et -progressive pour produire plusieurs
passes (scans) de l’image. Une visionneuse compatible peut afficher un premier aperçu avant d’avoir
reçu l’intégralité du JPEG progressif. Aucune de ces options ne garantit un fichier plus petit :
comparez donc le nombre d’octets avant de remplacer vos ressources.
Pour les métadonnées, la règle est -copy all : conserver les marqueurs supplémentaires, y compris
les données EXIF et les profils ICC. C’est important, car l’orientation EXIF et les profils de
couleur influent sur la façon dont les visionneuses affichent une image. Cela conserve aussi des
informations potentiellement privées, comme les coordonnées GPS. Ces fichiers de sortie sont destinés
à une vérification locale, pas à une mise en ligne publique automatique.
Utiliser -copy none supprimerait ces marqueurs, ce qui pourrait modifier l’orientation ou les
couleurs affichées, même si les coefficients sont conservés. -copy icc conserve le profil de
couleur mais supprime quand même l’orientation EXIF. Si vous avez besoin de supprimer les
métadonnées, choisissez une procédure distincte ; ce traitement par lots ne fait pas pivoter les
pixels et ne normalise pas l’orientation. Consultez les
options de copie des marqueurs.
Vérifier les outils
Utilisez Bash et le jpegtran de libjpeg-turbo sous Linux, avec les utilitaires habituels
mkdir, mv, rm et wc. L’exemple a été testé avec Bash 5.3.15 et
libjpeg-turbo 3.2.0. Installez les outils en ligne de commande de libjpeg-turbo via votre
distribution ou les paquets publiés du projet, puis vérifiez de quelle
implémentation vous disposez :
bash --version
jpegtran -version
La deuxième commande devrait indiquer libjpeg-turbo. D’autres projets distribuent aussi un programme
nommé jpegtran ; leurs options et leur sortie peuvent différer. Les entrées prises en charge ici
sont des JPEG ordinaires 8 bits basés sur la DCT, y compris les fichiers séquentiels et progressifs.
Lancer un traitement par lots vers un nouveau répertoire
Enregistrez ce script sous le nom optimize_images.sh dans le répertoire qui contient vos JPEG. Exécutez-le
avec Bash, pas avec sh. Il sélectionne les fichiers ordinaires se terminant par .jpg ou
.jpeg, y compris les extensions en majuscules et les fichiers cachés, sans descendre dans les
sous-répertoires ni suivre les entrées de liens symboliques. Les noms de fichiers contenant des
espaces, commençant par un tiret ou contenant des caractères % littéraux sont pris en charge.
Le répertoire de sortie ne doit pas déjà exister. Chaque fichier de sortie réussi conserve son nom
d’origine dans optimized/. Chaque conversion est écrite dans un fichier temporaire jusqu’à ce que
jpegtran réussisse ; une conversion échouée ne devient donc pas un fichier de sortie terminé.
Exécutez ce script dans un répertoire que vous contrôlez, sans que d’autres processus modifient ses
fichiers pendant le traitement.
#!/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"
Depuis ce même répertoire, exécutez :
bash optimize_images.sh
Un code de sortie égal à zéro signifie que chaque fichier sélectionné a été converti et déplacé vers
sa destination. Les images invalides et les avertissements de conversion provoquent un échec à cause
de -strict. Les diagnostics restent visibles, le traitement se poursuit avec les images
restantes et son code de sortie final est non nul. Dans un lot mixte, les fichiers de sortie réussis
restent disponibles pour examen. Une sélection vide renvoie aussi un code non nul et ne crée aucun
répertoire de sortie.
Une nouvelle exécution refuse tout répertoire, fichier ou lien symbolique optimized existant et le
laisse intact. Examinez et déplacez vous-même les résultats précédents avant de réessayer. Dans une
tâche de build, utilisez l’appel ci-dessus comme une étape dont l’échec interrompt le déploiement ;
ajouter || echo ... masquerait à nouveau cet échec. Il s’agit d’une procédure locale pour vos
ressources, pas d’un bac à sable pour des fichiers envoyés non fiables.
Mesurer le résultat
Pour un fichier d’entrée nommé photo.jpg, comparez les deux fichiers après un traitement par lots
réussi :
wc -c -- ./photo.jpg ./optimized/photo.jpg
Les deux premières lignes indiquent le nombre d’octets de l’original et du fichier de sortie ; la dernière ligne en donne la somme. Il n’y a pas de pourcentage de gain fixe. Un petit JPEG ou un fichier déjà optimisé peut grossir lorsqu’il est encodé en mode progressif. Ce script conserve ce résultat pour examen au lieu de le remplacer silencieusement par l’original. Ouvrez des paires représentatives dans la visionneuse qu’utilise votre public, en vérifiant l’orientation et les couleurs autant que la taille. Des données d’image décodées identiques ne suffisent pas, à elles seules, à tester la gestion des métadonnées par une visionneuse.
Comprendre les échecs et les limites de -perfect
Pour un JPEG corrompu ou tronqué, examinez le diagnostic et récupérez l’image source avant de relancer. N’ignorez pas stderr et n’acceptez pas un fichier partiel au seul motif que quelque chose a été écrit. Si le répertoire de sortie ne peut pas être créé, vérifiez s’il existe déjà et si vous pouvez écrire dans son répertoire parent.
-perfect est un contrôle destiné aux transformations géométriques, pas un réglage de qualité
supplémentaire ni une vérification de l’intégrité des fichiers. Il n’est pas nécessaire pour
l’optimisation entropique et la conversion progressive effectuées par ce lot. Pour une rotation ou
un retournement, il rejette les transformations dont les blocs de bord ne peuvent pas être
transformés entièrement. Avec l’échantillonnage courant 4:2:0, l’iMCU mesure 16 × 16 pixels : une
rotation de 90 degrés dans le sens horaire exige une hauteur divisible par 16, une rotation de
270 degrés exige la même condition pour la largeur, et une rotation de 180 degrés exige les deux.
D’autres facteurs d’échantillonnage modifient ces limites. Ces conditions figurent explicitement
dans le code de transformation de libjpeg-turbo.
-trim supprime les pixels de bord non transformables : c’est donc un choix de recadrage. Ce
n’est pas un moyen de préserver l’image complète après que -perfect a refusé une rotation. Faire
pivoter les pixels exige aussi une règle délibérée pour l’orientation EXIF et les miniatures ;
copier les métadonnées existantes ne corrige pas automatiquement l’orientation.
Décider quand réencoder plutôt
Si le gain mesuré est trop faible, modifier les dimensions, réduire la qualité JPEG ou convertir en
WebP ou en AVIF relève d’une autre tâche. Ces choix nécessitent de décoder puis d’encoder avec un
outil d’image adapté, et d’évaluer le rendu obtenu. Utilisez ce traitement par lots jpegtran
lorsque la contrainte est de conserver les données d’image du JPEG existant ; utilisez une procédure
de réencodage distincte lorsque vous pouvez accepter qu’elles soient modifiées.
