Fabien Le Frapper

Maurice Estève, ChatGPT et creative coding

2024-11-30

Cherchant à 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 : Maurice Estève

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 :

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

Forme 1 Forme 2 Forme 3 Forme 4 Forme 5 Forme 6 Forme 7 Forme 8 Forme 9 Forme 10 Forme 11 Forme 12 Forme 13 Forme 14 Forme 15 Forme 16 Forme 17 Forme 18 Forme 19 Forme 20 Forme 21 Forme 22 Forme 23 Forme 24 Forme 25 Forme 26 Forme 27 Forme 28 Forme 29 Forme 30 Forme 31 Forme 32 Forme 33 Forme 34 Forme 35 Forme 36 Forme 37 Forme 38 Forme 39 Forme 40 Forme 41 Forme 42 Forme 43 Forme 44 Forme 45 Forme 46 Forme 47 Forme 48 Forme 49 Forme 50 Forme 51 Forme 52 Forme 53 Forme 54 Forme 55 Forme 56 Forme 57 Forme 58 Forme 59 Forme 60 Forme 61 Forme 62 Forme 63 Forme 64 Forme 65 Forme 66 Forme 67 Forme 68 Forme 69 Forme 70 Forme 71 Forme 72 Forme 73 Forme 74 Forme 75 Forme 76 Forme 77 Forme 78 Forme 79 Forme 80 Forme 81 Forme 82 Forme 83 Forme 84 Forme 85 Forme 86 Forme 87 Forme 88 Forme 89 Forme 90 Forme 91 Forme 92 Forme 93 Forme 94 Forme 95 Forme 96 Forme 97 Forme 98 Forme 99