Utilitaire Python pour générer des variantes d'images à partir d'un dossier source, selon une liste de breakpoints, puis les convertir en WEBP.
La version avancée du script génère également un manifest.json afin de faciliter l'exploitation des images responsive dans une application web, par exemple avec Symfony / Twig.
- Parcours récursif d'un dossier d'images
- Génération de variantes responsive selon des breakpoints prédéfinis
- Conversion en WEBP
- Conservation du ratio
- Pas d'upscale
- Option pour générer aussi une version WEBP de l'original
- Génération automatique d'un
manifest.json - Métadonnées exploitables côté backend/frontend :
- largeur
- hauteur
- poids du fichier
- chemin de sortie
- format
Le projet utilise :
Pillow
pipx install git+https://github.com/DenZaiyy/image-compressor.gitLa commande image-to-webp est ensuite disponible dans tout terminal :
image-to-webp images/ --keep-original-webp --pretty-manifestCréer un environnement virtuel :
python3 -m venv .venv
source .venv/bin/activateInstaller les dépendances :
pip install -r requirements.txtpython3 image_variants_to_webp.py imagesCette version :
- génère les variantes WEBP
- ne produit pas de manifest
python3 image_variants_to_webp.py images --keep-original-webp --pretty-manifestCette version :
- génère les variantes WEBP
- peut générer aussi l'original en WEBP
- produit automatiquement un
manifest.json
python3 image_variants_to_webp.py imagespython3 image_variants_to_webp.py images --keep-original-webppython3 image_variants_to_webp.py images -b 400 800 1200 1600python3 image_variants_to_webp.py images -o generated_webppython3 image_variants_to_webp.py images --pretty-manifestpython3 image_variants_to_webp.py images --skip-existingpython3 image_variants_to_webp.py images --losslessLe script utilise par défaut des breakpoints classiques :
- 320
- 480
- 768
- 1024
- 1280
- 1536
- 1920
images/
├── hero/banner.jpg
└── products/gpu.png
generated_webp/
├── hero/
│ ├── banner.webp
│ ├── banner-320w.webp
│ ├── banner-480w.webp
│ ├── banner-768w.webp
│ └── ...
├── products/
│ ├── gpu.webp
│ ├── gpu-320w.webp
│ ├── gpu-480w.webp
│ ├── gpu-768w.webp
│ └── ...
└── manifest.json
{
"meta": {
"generator": "image_variants_to_webp.py",
"breakpoints": [320, 480, 768, 1024, 1280, 1536, 1920],
"quality": 85,
"method": 6,
"lossless": false,
"keep_original_webp": true,
"flatten_alpha": false,
"images_count": 2
},
"images": {
"products/gpu.png": {
"source": "products/gpu.png",
"source_filename": "gpu.png",
"source_extension": ".png",
"original_width": 1600,
"original_height": 1600,
"generated_original": {
"width": 1600,
"height": 1600,
"path": "generated_webp/products/gpu.webp",
"filename": "gpu.webp",
"size_bytes": 123456,
"format": "webp"
},
"variants": [
{
"width": 320,
"height": 320,
"path": "generated_webp/products/gpu-320w.webp",
"filename": "gpu-320w.webp",
"size_bytes": 8450,
"format": "webp"
}
]
}
}
}Le manifest.json sert de registre des images générées.
Il permet de :
- retrouver automatiquement toutes les variantes d'une image
- construire un
srcset - éviter de recalculer les chemins à la main
- préparer une intégration backend propre
- servir de source de vérité pour les assets responsive
Le manifest peut ensuite être exploité pour produire un attribut HTML srcset :
<img
src="/generated_webp/products/gpu.webp"
srcset="
/generated_webp/products/gpu-320w.webp 320w,
/generated_webp/products/gpu-768w.webp 768w,
/generated_webp/products/gpu-1280w.webp 1280w
"
sizes="(max-width: 768px) 100vw, 33vw"
alt="GPU"
/>Le navigateur choisira automatiquement la variante la plus pertinente selon :
- la taille d'écran
- la largeur réelle de rendu
- la densité de pixels
-o, --output-dir: dossier de sortie-b, --breakpoints: liste de largeurs cibles--quality: qualité WEBP--method: niveau de compression WEBP--lossless: export sans perte--skip-existing: ignore les fichiers déjà présents--flatten-alpha: aplatit la transparence sur fond blanc
--keep-original-webp: génère aussi l'original en WEBP--manifest-name: personnalise le nom du manifest--pretty-manifest: formate le manifest avec indentation
Les fichiers d'intégration complets se trouvent dans le dossier examples/.
Flow complet :
- Le script génère les images et le
manifest.jsondanspublic/images/generated_webp/ - Un service Symfony charge le manifest
- Une extension Twig expose une fonction
responsive_image() - Tes templates appellent cette fonction avec la clé source
Fichiers fournis dans examples/symfony/ :
| Fichier | Rôle |
|---|---|
src/Service/ImageManifestService.php |
Charge et expose le manifest |
src/Twig/Extension/ImageManifestExtension.php |
Fonction Twig responsive_image() |
templates/_components/responsive_image.html.twig |
Partial HTML de l'image |
config/services.yaml |
Configuration du service |
Usage dans un template Twig :
{{ responsive_image('products/gpu.png', 'GPU RTX 4090', '(max-width: 768px) 100vw, 50vw') }}Avec des attributs supplémentaires :
{{ responsive_image('hero/banner.jpg', 'Bannière', '100vw', { class: 'hero__img', fetchpriority: 'high' }) }}Fichiers fournis dans examples/react/ :
| Fichier | Rôle |
|---|---|
useResponsiveImage.ts |
Hook qui lit le manifest et retourne src, srcset, dimensions |
ResponsiveImage.tsx |
Composant <img> responsive prêt à l'emploi |
Usage :
import { ResponsiveImage } from './ResponsiveImage';
// Utilisation directe du composant
<ResponsiveImage
source="products/gpu.png"
alt="GPU RTX 4090"
sizes="(max-width: 768px) 100vw, 33vw"
className="product__img"
/>
// Ou via le hook pour un usage custom
import { useResponsiveImage } from './useResponsiveImage';
const image = useResponsiveImage('products/gpu.png');
if (image) {
// image.src, image.srcset, image.width, image.height
}Le
manifest.jsondoit être importable par le bundler (Vite, webpack…). Place-le danssrc/ou configure un alias.
Fichiers fournis dans examples/vue/ :
| Fichier | Rôle |
|---|---|
useResponsiveImage.ts |
Composable réactif (accepte string ou ComputedRef<string>) |
ResponsiveImage.vue |
Composant SFC prêt à l'emploi |
Usage :
<script setup lang="ts">
import ResponsiveImage from './ResponsiveImage.vue';
</script>
<template>
<ResponsiveImage
source="products/gpu.png"
alt="GPU RTX 4090"
sizes="(max-width: 768px) 100vw, 33vw"
/>
</template>Avec le composable seul :
import { useResponsiveImage } from './useResponsiveImage';
const { image } = useResponsiveImage('products/gpu.png');
// image.value?.src, image.value?.srcset, ...- Export AVIF en complément du WEBP
- Génération d'un blur placeholder
- Support de profils de presets par contexte (
thumbnail,hero,product-card, etc.) - Publication sur PyPI