Maurice Estève, ChatGPT et creative coding
2024-11-30Cherchant à illustrer un futur article. J'ai voulu voir ce que permettait ChatGPT en la matière.
Pas convaincu au départ, l'outil a finalement révélé un potentiel intéressant, notamment pour du creative coding.
Objectif : obtenir une méthode réutilisable permettant de générer des illustrations.
Préambule : inspiré par des médias comme Le Monde Diplomatique, qui proposent des oeuvres en guise d’illustrations, je souhaitais illuser mes futurs articles. Pour autant, je ne souhaite pas utiliser Midjourney ou DALL-E car l’objectif est de conserver des pages légères.
Je suis donc parti sur des illustrations à base de CSS et HTML.
Première version
Le travail du mouvement De Stijl étant très géométrique, il se prête parfaitement bien à des illustrations légères. Dans ce premier jet, j’ai demandé à ChatGPT de me générer une forme en HTML/CSS inspirée de ce mouvement.
can you generate a design using css and html, inspired by de Stijl, using only #5a5766 and #f25c80 colors ?
Le résultat - ci-dessous - était assez catastrophique…bien que pas inintéressant.
Après plusieurs tentatives similaires, le résultat était toujours assez décevant. Même sans restreindre les couleurs à utiliser.
J’ai ajusté le prompt, pour lui demander de générer un SVG directement :
can you generate an svg, inspired by this painting
La peinture en question :
J’utilise assez rarement ChatGPT, et ai découvert qu’à chaque étape de génération du SVG, le code Python généré est accessible.
Le code généré était en réalité composé de deux scripts Python successif :
- Un premier passablement inutile :
- il télécharge l’image et l’analyse avec Pillow
- puis la convertit en array Numpy
- puis…n’en fait pas grand chose. A priori cette partie lui sert essentiellement à en déterminer les dimensions (et semble un peu overkill)
- Un second se chargeant de la génération du SVG proprement dite. C’est à lui qu’on s’intéresse ci-après.
Le script
Ci-dessous le script généré :
import random
from PIL import Image
import numpy as np
# Open the uploaded image
image_path = "./image.jpg"
image = Image.open(image_path)
# Define a function to generate random colors similar to the ones in the image
def generate_random_color():
return f"rgb({random.randint(100, 255)}, {random.randint(50, 200)}, {random.randint(50, 255)})"
# Define a function to create a simple SVG structure with abstract shapes
def generate_svg(width, height, num_shapes=10):
svg_header = (
f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}">'
)
svg_footer = "</svg>"
# Generate random abstract shapes
shapes = ""
for _ in range(num_shapes):
shape_type = random.choice(["circle", "rect", "polygon"])
if shape_type == "circle":
cx = random.randint(0, width)
cy = random.randint(0, height)
r = random.randint(20, 100)
shapes += f'<circle cx="{cx}" cy="{cy}" r="{r}" fill="{generate_random_color()}" />'
elif shape_type == "rect":
x = random.randint(0, width)
y = random.randint(0, height)
w = random.randint(50, 200)
h = random.randint(50, 200)
shapes += f'<rect x="{x}" y="{y}" width="{w}" height="{h}" fill="{generate_random_color()}" />'
elif shape_type == "polygon":
points = [
f"{random.randint(0, width)},{random.randint(0, height)}"
for _ in range(random.randint(3, 6))
]
points_str = " ".join(points)
shapes += (
f'<polygon points="{points_str}" fill="{generate_random_color()}" />'
)
return svg_header + shapes + svg_footer
# Generate the SVG with the dimensions of the uploaded image
svg_content = generate_svg(image.width, image.height, num_shapes=15)
svg_path = f"./generated_artwork-{i}.svg"
with open(svg_path, "w") as file:
file.write(svg_content)
Il n’est pas forcément utilisable tel quel, je l’ai très légèrement remanié pour fonctionner sur ma machine en local. Notamment pour charger une image locale plutôt que distante.
Dans un second temps, je l’ai retouché pour n’afficher que certaines couleurs, car le première version proposait des couleurs un peu trop ternes.
def generate_random_color():
# Define basic colors and their corresponding RGB values
basic_colors_rgb = {
"red": (255, 0, 0),
"blue": (0, 0, 255),
"yellow": (255, 255, 0),
"black": (0, 0, 0),
}
# Select a random color
color_name = random.choice(list(basic_colors_rgb.keys()))
# Get the RGB value and format it as 'rgb(r, g, b)'
rgb_value = basic_colors_rgb[color_name]
return f"rgb{rgb_value}"
Enfin, j’ai ajouté une boucle pour générer une centaine de résultats en faisant varier le nombre de formes.
for i in range(1, 99):
# Generate the SVG with the dimensions of the uploaded image
svg_content = generate_svg(image.width, image.height, num_shapes=i * 2)
svg_path = f"./generated_artwork-{i}.svg"
with open(svg_path, "w") as file:
file.write(svg_content)
Conclusion
On voit ici que ChatGPT (et les LLM plus généralement), détourné de l’utilisation initiale - à savoir obtenir un SVG - permet de créer une méthodologie réutilisable. J’aurais certainement pu aboutir à un résultat similaire en me creusant un peu la tête…mais ça m’aurait certainement pris quelques heures de plus.
Autant les LLM montrent leurs limites (à mes yeux) pour de la génération d’images, car on retrouve un style assez uniforme et parfois fade. Autant pour du creative coding, on peut facilement imaginer des usages vraiment intéressants, où les hallucinations peuvent amener à des
Le résultat