Du design génératif avec ImageMagick

TL;DR : à la fin on obtient ce genre de résultats (attention effets épileptiques)

Je cultive un certain intérêt pour toutes les notions d’aléatoires dans la création graphique et ce, au moins depuis qu’un intervenant nous a initié à Processing circa 2010 dans mes premières années d’études.

J'apprécie cette part d'imprévu, souvent source d'accidents graphiques d'autant plus complexes à concevoir ex nihilo.

C'est un aspect qui n'est pas spécialement évident à intégrer dans un processus de création autre qu'expérimental et c'est aussi une des raisons pour laquelle je m'y adonne que peu régulièrement.


Sauf quand Photoshop se met soudainement à créer des glitchs graphiques, merci Adobe 🙃


De temps à autre un nouvel usage vient réveiller mon intérêt pour le sujet, comme récemment les possibilités de certains plugins Figma ou encore des applications comme Cavalry.

D'autres fois encore, j'y retombe un peu par hasard, comme par le biais d'un outil pas vraiment conçu pour, et c'est typiquement ce qu'on va voir aujourd'hui avec ImageMagick.

ImageMagick ?

ImageMagick est un logiciel libre et open-source sans interface, principalement connu des dévellopeurs, qui permet de faire du traitement d'images en ligne de commande.


il a aussi pour mascotte une sorte de Merlin un peu chelou mais ça c'est une autre histoire...


Je m'en suis longtemps servi pour des opérations assez basiques comme de la simple compression ou de la combinaison d'images pour Pinterest par exemple, ce qui évite de devoir sortir un logiciel pour des taches qui peuvent être réalisées en un clic.

Puis un beau jour, je me suis retrouvé à devoir créer une série d'images avec une coloration particulière type gradient-map et je me suis dit que ça serait cool de pouvoir appliquer cette coloration d'un simple raccourci clavier sans même avoir à ouvrir Photoshop. Parce que oui, on est d'accord, une macro Photoshop aurait été tout aussi efficace, mais l'idée d'un script facilement partageable et utilisable à tout moment me paraissait intéressante à explorer.

Appliquer des LUTs

Je ne détaille pas ici les étapes d'installation d'ImageMagick, ça se passe sur cette page

Une des méthodes courante pour appliquer une teinte à une image est d'utiliser un LUT (a.k.a LookUp Tables) qui n'est autre qu'une pallette d'étallonage qui peut être stockée dans un simple jpeg (plus d'infos sur le fonctionnement ici)

Concrètement la logique est la suivante :



et pour appliquer un Lut avec ImageMagick on utilise la commande qui suit (à lancer dans le terminal) :

 convert input.png lut.png -hald-clut output.png

Pour la suite je vais utiliser de l'Apple Script ce qui permettra de nous simplifier certaines actions comme récupérer dynamiquement le fichier sélectionné dans le finder. Pour faire de même il vous suffit, si vous utilisez Mac OS, d'ouvrir l'éditeur de script qui se trouve dans Application/Utilitaires/Éditeur de script.app et de copier-coller le code qui suit)

// on récupère le chemin du fichier sélectionné dans le finder et on le stocke dans une variable filePath

tell application "Finder"
set theItems to selection
set filePath to (POSIX path of (the selection as alias))    

end tell

// on exécute la commande ImageMagick, les chemins du lut et de la sortie sont à modifier par les vôtres

do shell script "/opt/local/bin/convert " & filePath & "/Users/kevinronceray/lut.png -hald-clut /Users/kevinronceray/Desktop/output.png"

En temps normal, on récupère un LUT neutre auquel on applique la même coloration qu'à notre image, puis on exporte ce LUT qui pourra ainsi permettre de teinter tout autre image avec les mêmes paramètres, ce qui donne donc un résultat totalement convenu. Mais étant donné qu'aujourd'hui nous voulons plutôt obtenir des résultats étranges pourquoi ne pas tenter de modifier le LUT à l'aveugle ? 😱

les LUTs modifiés dans Photoshop et leurs résultats respectifs


Ok, c'est un début intéressant. La nature asynchrone du processus — en imaginant un résultat dans un premier temps puis en le visualisant dans un second — est propice à engendrer les accidents graphiques convoités. Mais, il faut le reconnaitre, après un certain nombre d'allers-retours à créer des LUTs et à découvrir leurs effets, on peut avoir l'impression de tourner en rond. Surtout, un LUT une fois créé générera toujours le même résultat (c'est ce qu'on lui demande d'ailleurs).

Et si on rajoutait un peu d'aléatoire afin d'obtenir des résultats différents à chaque nouveau lancement du script ? (la suite de l'article va parler de ça donc j'espère que la réponse est oui)

Colorations aléatoires et Gifs animés


On va combiner plusieurs commandes :

+level-colors pour appliquer un gradient-map.

    convert  input.png  +level-colors green,cyan  output.png  

Puis -modulate, cette commande prend 3 arguments pour faire varier la teinte, la saturation et la luminosité de 0 à 200 (100 étant la valeur neutre et ne modifie donc pas l'image). Pour choisir une valeur aléatoire nous utilisons la fonction Apple Script random number from 0 to 200 ce qui donne quelque chose comme ceci :

tell application "Finder"
    set theItems to selection
    set filePath to (POSIX path of (the selection as alias))

end tell

do shell script "/opt/local/bin/convert " & filePath & " +level-colors green,red -modulate " & (random number from 100 to 200) & ",100," & (random number from 10 to 200) & " /Users/kevinronceray/Desktop/output.png"



À partir de là on a un générateur de gradient map aléatoire. Mais, pour qui a l'habitude d'en voir, rien ici de bien surprenant. Pour ajouter une dimension supplémentaire et apporter de la variation on va générer plusieurs itérations et les combiner avec des modes de fusion grâce à la commande -compose en plus de ça on va déplacer légèrement chaque calque pour créer un effet de décalage de couches avec -geometry

tell application "Finder"
    set theItems to selection
    set filePath to (POSIX path of (the selection as alias))

end tell

do shell script "/opt/local/bin/convert " & filePath & " \\(  +level-colors cyan,white -modulate 100,100," & (random number from 0 to 200) & " \\) -compose linearlight \\( " & filePath & "   +level-colors magenta,white -geometry  +4+2 -modulate 100,100," & (random number from 0 to 200) & " \\) -compose difference -composite \\( " & filePath & "   +level-colors yellow,white -geometry  -3-1 -modulate 100,100," & (random number from 0 to 200) & " \\) -compose difference -contrast-stretch 5% -compose difference   -composite /Users/kevinronceray/Desktop/output.png"

À ce stade les résultats obtenus à chaque nouveau lancement du script commencent à présenter un spectre de variations assez large, des associations de couleurs improbables (parfois assez moche aussi, ne nous mentons pas) on tient quelque chose.

Étape ultime, pourquoi ne pas en faire des gifs ? (Citez une seule chose qui n'est pas mieux en gif). L'idée est de générer plusieurs itérations d'une même image et de la combiner en 1 gif qui fait défiler chaque itération.


Pour cela nous allons avoir besoin des commandes :

  • -write pour enregistrer une image au milieu d'une commande.
  • MPR: pour stocker temporairement une image dans une variable.
  • -delay pour définir l'intervalle de temps entre chaque image.


Concrètement ça donne ça :


// On génére chaque image et on l'enregistre temporairement dans une variable avec -write mpr:Nom
// On combine chaque image dans une séquence gif avec :

mpr:img1 mpr:img2 mpr:img3 -delay 2 /filepath/output.gif

Et appliqué à notre cas :
(ça commence à faire du monde, mais en réalité c'est à peine plus complexe que l'étape précédente)

tell application "Finder"
    set theItems to selection
    set filePath to (POSIX path of (the selection as alias))

end tell

do shell script "/opt/local/bin/convert " & filePath & " \\( -colorspace Gray +level-colors cyan,blue -modulate 100,100," & (random number from 50 to 150) & " \\) -compose linearlight \\( " & filePath & "  -colorspace Gray +level-colors magenta,green -geometry  +" & (random number from 0 to 5) & "+" & (random number from 0 to 4) & " -modulate 100,100," & (random number from 50 to 150) & " \\) -compose difference -composite \\( " & filePath & "  -colorspace Gray +level-colors yellow,purple -geometry  -" & (random number from 0 to 5) & "-" & (random number from 1 to 7) & " -modulate 100,100," & (random number from 80 to 120) & " \\) -compose difference -contrast-stretch 5% -compose difference   -composite -write mpr:img1 +delete " & filePath & " \\( -colorspace Gray +level-colors yellow,green -modulate 100,100," & (random number from 50 to 150) & " \\) -compose linearlight \\( " & filePath & "  -colorspace Gray +level-colors blue,red -geometry  +" & (random number from 2 to 5) & "+" & (random number from 2 to 7) & " -modulate 100,100," & (random number from 50 to 150) & " \\) -compose difference -composite \\( " & filePath & "  -colorspace Gray +level-colors magenta,orange -geometry  -" & (random number from 0 to 5) & "-" & (random number from 1 to 5) & " -modulate 100,100," & (random number from 80 to 120) & " \\) -compose difference -contrast-stretch 5% -compose difference   -composite -write mpr:img2 " & filePath & " \\( -colorspace Gray +level-colors yellow,green -modulate 100,100," & (random number from 50 to 150) & " \\) -compose linearlight \\( " & filePath & "  -colorspace Gray +level-colors blue,red -geometry  +" & (random number from 0 to 5) & "+" & (random number from 0 to 5) & " -modulate 100,100," & (random number from 50 to 150) & " \\) -compose difference -composite \\( " & filePath & "  -colorspace Gray +level-colors magenta,orange -geometry  -" & (random number from 0 to 7) & "-" & (random number from 1 to 6) & " -modulate 100,100," & (random number from 80 to 120) & " \\) -compose difference -contrast-stretch 5% -compose difference   -composite -write mpr:img3 +delete mpr:img1 mpr:img2 mpr:img3 -delay 2 /Users/kevinronceray/Desktop/output.gif"

Voilà, c'est tout pour aujourd'hui, on termine avec quelques images générées avec ce procédé ci-dessous, on pourrait évidemment continuer à itérer sur cette base en modifiant les paramètres ou en ajoutant des nouveaux.