Documentation technique
Je présente ici un script Python autonome qui applique des filtres graphiques à des photographies pour les transformer en rendus d’estampe — linogravure, croquis au crayon, taille-douce ou plaque typographique. Il repose sur OpenCV pour les opérations de traitement d’image et sur NumPy pour les calculs matriciels. Son interface en ligne de commande permet d’ajuster le style, le niveau de détail et le contraste sans modifier le code source.
- 1. Architecture générale
- 2. Pipeline de traitement
- 3. Détail des algorithmes de rendu
- 4. Paramètres de la ligne de commande
- 5. Recommandations d’usage
1. Architecture générale
Le script est organisé autour de deux fonctions principales : process_directory(), qui pilote le traitement en lot, et apply_sketch_filter(), qui applique la chaîne de traitement à une image individuelle. Ces deux fonctions sont appelées depuis main(), qui gère l’interface en ligne de commande via argparse.
Structure de répertoires attendue
project/├── sketch_filter.py├── input_images/ ← images source (jpg, png, bmp, tiff, webp)└── output_sketchs/ ← généré automatiquement au premier lancement
La fonction process_directory() parcourt le répertoire source, filtre les fichiers par extension, puis appelle apply_sketch_filter() pour chaque image. Les erreurs sur une image individuelle sont interceptées et consignées sans interrompre le traitement des images suivantes.
2. Pipeline de traitement
Chaque image passe par cinq étapes successives, quelle que soit la combinaison de paramètres choisie.
Étape 1 — Conversion en niveaux de gris
L’image est chargée par cv2.imread() au format BGR, puis convertie en niveaux de gris via cv2.cvtColor(img, cv2.COLOR_BGR2GRAY). Cette conversion est nécessaire car tous les algorithmes de rendu opèrent sur un canal unique d’intensité lumineuse.
Étape 2 — Réduction du bruit
Un débruitage non-local est appliqué avec cv2.fastNlMeansDenoising(). Les paramètres utilisés sont h=7 (force du filtre), templateWindowSize=7 (taille du patch de comparaison) et searchWindowSize=21 (fenêtre de recherche). Cette étape est critique : sans elle, les algorithmes de seuillage amplifient le bruit de capteur sous forme de granularité indésirable.
Étape 3 — Application du style graphique
Le style sélectionné détermine l’algorithme de rendu. Les quatre implémentations sont détaillées à la section 3.
Étape 4 — Ajustement du contraste
Un ajustement linéaire est appliqué via la fonction _adjust_contrast(). L’opération centre le contraste autour du point médian (128) :
| adjusted = (img_float – 128) * factor + 128 |
Un facteur supérieur à 1.0 étend la plage dynamique (noir plus noir, blanc plus blanc). Un facteur inférieur à 1.0 la compresse, produisant un résultat plus doux. L’image résultante est recadrée entre 0 et 255 par np.clip() avant reconversion en entiers 8 bits.
Étape 5 — Inversion optionnelle
Si le paramètre –invert est activé, cv2.bitwise_not() permute les valeurs noir et blanc en sortie. Cette opération est appliquée en dernier, après l’ajustement de contraste, pour garantir sa compatibilité avec tous les styles.
3. Détail des algorithmes de rendu
3.1 Style linocut
Ce style imite la linogravure : larges zones noires opaques séparées par des traits blancs nets. Il est obtenu par la combinaison de quatre opérations.
Filtrage bilatéral. Un filtre bilatéral (cv2.bilateralFilter) lisse les textures tout en préservant les contours nets. Le paramètre sigmaSpace est inversement proportionnel au niveau de détail, ce qui permet de contrôler la quantité de micro-texture conservée.
Masque de Laplace. Le Laplacien de l’image accentuée (cv2.Laplacian avec ksize=3) produit un masque de contours normalisé entre 0 et 1. Ce masque est multiplié par 180 et soustrait au résultat du seuillage, ce qui assombrit les zones de forte transition — renforçant le côté gravé.
Seuillage adaptatif. La fonction cv2.adaptiveThreshold avec ADAPTIVE_THRESH_GAUSSIAN_C calcule un seuil local pour chaque région de l’image selon une fenêtre de taille blockSize. Cette approche est préférable à un seuil global sur les images à fort contraste local (lumière latérale, contre-jour).
Érosion morphologique. Une érosion avec un noyau elliptique 2×2 (cv2.erode) épaissit légèrement les zones noires, renforçant l’aspect massif typique de la linogravure.
3.2 Style pencil
Le rendu crayon repose sur une technique de division d’image. L’image originale en niveaux de gris est divisée pixel par pixel par son négatif flouté :
inv = cv2.bitwise_not(gray)blurred_inv = cv2.GaussianBlur(inv, (sigma, sigma), 0)sketch = cv2.divide(gray, 255 - blurred_inv, scale=256.0)
Cette opération produit un résultat proche du blanc là où l’image est uniforme, et des traits sombres là où se trouvent les transitions. Un seuillage THRESH_TRUNC à 230 écrête les hautes lumières, puis une normalisation étire la plage de valeurs pour maximiser le contraste des traits fins.
3.3 Style engraving
Ce style simule la taille-douce par superposition de hachures diagonales régulières, dont la densité varie selon la luminosité locale.
Génération des hachures. Un tableau blanc (255) est rempli de lignes diagonales espacées de spacing pixels (inversement proportionnel au niveau de détail). L’inclinaison est obtenue en décalant les coordonnées de fin de chaque ligne de 10 pixels par rapport au début.
Masquage par luminosité. Un masque binaire identifie les zones sombres de l’image (valeur < 128). Ce masque supprime les hachures dans les zones sombres, simulant l’accumulation de tailles dans les demi-teintes. Les bords détectés par cv2.Canny() sont dilatés et soustraits par opération bitwise_and pour ajouter des contours nets.
3.4 Style printing
Ce style produit l’effet d’une plaque typographique ou offset : grandes plages noires denses avec trouées blanches nettes.
Égalisation d’histogramme. La fonction cv2.equalizeHist() redistribue les niveaux de l’image pour qu’ils occupent toute la plage 0–255. Cette étape normalise les images sous-exposées ou surexposées avant le seuillage.
Unsharp mask renforcé. Un masque flou gaussien est soustrait à l’image lissée avec un coefficient de 1.8 (contre 1.5 pour le style linocut), produisant une accentuation plus agressive des transitions.
Fermeture morphologique. Après inversion du seuillage, une fermeture (cv2.morphologyEx avec MORPH_CLOSE) comble les petits trous dans les plages noires, renforçant l’aspect massif des aplats.
Réinjection des contours. Les bords détectés par cv2.Canny() avec des seuils bas (30 et 100) sont dilatés et fusionnés par cv2.bitwise_or() avec le résultat de la fermeture, assurant des contours toujours nets même sur les zones d’aplat.
4. Paramètres de la ligne de commande
| Paramètre | Valeurs | Défaut | Effet |
|---|---|---|---|
| –style | linocut | pencil | engraving | printing | linocut | Sélectionne l’algorithme de rendu. |
| –detail | 0.5 → 2.0 | 1.0 | Contrôle la finesse des textures. Influence la taille des noyaux et des blocs adaptatifs dans chaque style. |
| –contrast | 0.5 → 2.0 | 1.2 | Ajustement linéaire noir/blanc en sortie, indépendant du style. |
| –invert | drapeau | désactivé | Inverse noir et blanc en dernière étape. Compatible avec tous les styles. |
| –input | chemin | input_images/ | Répertoire source. Formats acceptés : jpg, jpeg, png, bmp, tiff, tif, webp. |
| –output | chemin | output_sketchs/ | Répertoire de sortie. Créé automatiquement. Noms de fichiers conservés. |
Le paramètre –detail influe concrètement sur plusieurs valeurs internes selon le style : taille du bloc de seuillage adaptatif (blockSize = int(31 / detail)), espacement des hachures pour l’engraving (spacing = int(6 / detail)), et sigma du filtre bilatéral (sigma = int(15 / detail)). Une valeur élevée produit donc des détails plus fins mais peut amplifier le bruit sur les images de faible résolution.
5. Recommandations d’usage
Photographies d’archives
python sketch_filter.py –style printing –invert –detail 1.5 –contrast 1.2
Combinaison recommandée par la documentation pour les clichés à fort contraste local. L’inversion restitue un fond blanc sur les images numérisées à fond sombre.
Images à texture dense (végétation, fourrure, tissu)
python sketch_filter.py –style linocut –detail 2.0 –contrast 1.0
Le niveau de détail maximal est compensé par une réduction du contraste pour éviter l’amplification du bruit de texture.
Rendu proche d’un négatif photographique
python sketch_filter.py –style engraving –invert –detail 1.5 –contrast 1.3
L’inversion du style engraving produit des hachures claires sur fond sombre, évoquant un négatif argentique.
| Note : au-delà d’un niveau de détail de 1.8, il est conseillé d’abaisser le contraste à 1.0 ou moins pour compenser l’amplification du bruit sur les images de faible résolution. |
Et voici quelques rendus :


Laisser un commentaire