Échelles de couleurs & dégradés

dimanche 20 juillet 2025



en cours de rédaction


?


1 – Coloriage naïf

?

# Un programme que nous étudierons plus tard
def Mandelbrot(f, Palette) :
    I = CréerDessin(LARGEUR, HAUTEUR)
    for px in range(LARGEUR) :
        for py in range(HAUTEUR) :
            
            # Chaque pixel se voit attribuer un certain entier n entre 0 et N_MAX
            (x, y) = PixelVersPoint(px, py)
            (n, u_n) = ItérationDeJulia(lambda z : f(z, x + 1j * y), 0.0)
            
            # La couleur du pixel est choisie en fonction de cet entier
            Couleur = Palette[int(n / N_MAX * (len(Palette) - 1))]
            I.putpixel( (px, py), Couleur )
    
    Afficher(I)


# On part du noir (i = 0) vers la couleur cible (i = TAILLE_PALETTE - 1)
def ConstruirePaletteDégradéSimple(TAILLE_PALETTE, CouleurCible) :
    P = [None] * TAILLE_PALETTE
    (r_Cible, g_Cible, b_Cible) = CouleurCible
    for i in range(TAILLE_PALETTE) :
        t = i / (TAILLE_PALETTE - 1)
        r = int(t * r_Cible)
        g = int(t * g_Cible)
        b = int(t * b_Cible)
        P[i] = (r, g, b)
    return P


>>> Centrer(0.0, 0.0, 2.0)
>>> MAGENTA = (255, 40, 245)
>>> P = ConstruirePaletteDégradéSimple(N_MAX + 1, MAGENTA)
>>> Mandelbrot(lambda z, c : z ** 2 + c, P)
l'ensemble de Mandelbrot

# Variante du précédent
def ConstruirePaletteDégradéContour(TAILLE_PALETTE, CouleurCible) :
    P = [None] * TAILLE_PALETTE
    (r_Cible, g_Cible, b_Cible) = CouleurCible
    # On fait comme si la palette comportait une case en moins...
    for i in range(TAILLE_PALETTE - 1) :
        t = i / (TAILLE_PALETTE - 2)
        r = int(t * r_Cible)
        g = int(t * g_Cible)
        b = int(t * b_Cible)
        P[i] = (r, g, b)
    # ...puis on attribue la couleur noire à la dernière case (l'indice maximal)
    P[-1] = (0, 0, 0)
    return P


>>> Centrer(0.0, 0.0, 2.0)
>>> MAGENTA_CLAIR = (255, 150, 255)
>>> P = ConstruirePaletteDégradéContour(N_MAX + 1, MAGENTA_CLAIR)
>>> Mandelbrot(lambda z, c : z ** 2 + c, P)
le contour de l'ensemble de Mandelbrot


2 – Coloriage lisse

?

>>> Centrer(-0.7, 0.7, 0.5)
>>> JAUNE = (255, 255, 40)
>>> P = ConstruirePaletteDégradéSimple(N_MAX + 1, JAUNE)
>>> Mandelbrot(lambda z, c : z ** 2 + c, P)
utilisation d'un dégradé sans lissage

# Extrapolation de la palette
from numpy import log as ln
def Lisser(n, u_n, d, Palette) :
    if n == N_MAX :
        return Palette[-1]
    else :
        ν = ln( ln(abs(u_n))/ln(R_MAX) ) / ln(d)
        ξ = (n + 1 - ν) / N_MAX * (len(Palette) - 2)
        i = int(ξ) ; ξ -= i
        (r1, g1, b1) = Palette[i]
        (r2, g2, b2) = Palette[i + 1]
        r = int(r1 + ξ * (r2 - r1))
        g = int(g1 + ξ * (g2 - g1))
        b = int(b1 + ξ * (b2 - b1))
        return (r, g, b)


# Utilisation de la palette avec lissage par extrapolation
def Mandelbrot_lisse(f, d, Palette) :
    I = CréerDessin(LARGEUR, HAUTEUR)
    for px in range(LARGEUR) :
        for py in range(HAUTEUR) :
            (x, y) = PixelVersPoint(px, py)
            (n, u_n) = ItérationDeJulia(lambda z : f(z, x + 1j * y), 0.0)
            Couleur = Lisser(n, u_n, d, Palette)
            I.putpixel( (px, py), Couleur )
    Afficher(I)


>>> Centrer(-0.7, 0.7, 0.5)
>>> JAUNE = (255, 255, 40)
>>> P = ConstruirePaletteDégradéSimple(N_MAX + 1, JAUNE)
>>> Mandelbrot_lisse(lambda z, c : z ** 2 + c, 2, P)
utilisation d'un dégradé avec lissage


3 – Palettes cycliques

?

>>> Centrer(0.44246363018149587, 0.20865628845064585, 0.0000001)
>>> N_MAX = 800
>>> VERT_FLUO = (90, 255, 140)
>>> P = ConstruirePaletteDégradéSimple(100, VERT_FLUO)
>>> Mandelbrot_lisse(lambda z, c : z ** 2 + c, d = 2, Palette = P)
utilisation d'un dégradé non cyclique

# Utilisation d'une palette cyclique
def Mandelbrot_cyclique(f, d, s, Palette) :
    I = CréerDessin(LARGEUR, HAUTEUR)
    for px in range(LARGEUR) :
        for py in range(HAUTEUR) :
            (x, y) = PixelVersPoint(px, py)
            (n, u_n) = ItérationDeJulia(lambda z : f(z, x + 1j * y), 0.0)
            Couleur = Cyclique(n, u_n, d, s, Palette)
            I.putpixel( (px, py), Couleur )
    Afficher(I)


# Extrapolation sur une palette cyclique
def Cyclique(n, u_n, d, s, Palette) :
    if n == N_MAX :
        return Palette[-1]
    else :
        ν = ln( ln(abs(u_n))/ln(R_MAX) ) / ln(d)
        i_moins_1 = int(((n - 1) / N_MAX * len(Palette)) ** s) % len(Palette)
        i         = int((n / N_MAX * len(Palette)) ** s) % len(Palette)
        # Attention i_moins_1 peut être très différent de i - 1
        (r1, g1, b1) = Palette[i_moins_1]
        (r2, g2, b2) = Palette[i]
        r = int(r1 + (1 - ν) * (r2 - r1))
        g = int(g1 + (1 - ν) * (g2 - g1))
        b = int(b1 + (1 - ν) * (b2 - b1))
        return (r, g, b)


>>> Centrer(0.44246363018149587, 0.20865628845064585, 0.0000001)
>>> N_MAX = 800
>>> VERT_FLUO = (90, 255, 140)
>>> P = ConstruirePaletteDégradéSimple(100, VERT_FLUO)
>>> Mandelbrot_cyclique(lambda z, c : z ** 2 + c, d = 2, s = 1.6, Palette = P)
utilisation d'un dégradé cyclique


4 – Dégradés multiples

?

# Extrapolation de la palette
from random import shuffle as Mélanger
def ConstruirePaletteDégradéMultiple(TAILLE_PALETTE, Couleurs) :
    P = [None] * TAILLE_PALETTE
    # Répartition des différents dégradés sur la palette
    (q, Reste) = divmod(TAILLE_PALETTE, len(Couleurs))
    Restes = [1] * Reste + [0] * (len(Couleurs) - Reste)
    Mélanger(Restes)
    Indices = [] ; i = 0
    for k in range(len(Couleurs)) :
        Indices.append(i)
        i += Restes[k]
        i += q
    # Maintenant i doit être égal à len(Palette)
    Indices.append(i)
    # Calcul de chaque dégradé
    for k in range(len(Couleurs)) :
        (r1, g1, b1) = Couleurs[k]
        (r2, g2, b2) = Couleurs[(k + 1) % len(Couleurs)]
        for i in range(Indices[k], Indices[k + 1]) :
            t = (i - Indices[k]) / (Indices[k + 1] - Indices[k])
            r = int(r1 + t * (r2 - r1))
            g = int(g1 + t * (g2 - g1))
            b = int(b1 + t * (b2 - b1))
            P[i] = (r, g, b)
    return P


>>> NOIR = (0, 0, 0)
>>> ROUGE = (255, 40, 70)
>>> ORANGE = (255, 160, 10)
>>> Centrer(0.0, 0.0, 2.0)
>>> N_MAX = 50
>>> P = ConstruirePaletteDégradéMultiple(100, [ROUGE, NOIR, ORANGE, NOIR])
>>> Mandelbrot_cyclique(lambda z, c : z ** 2 + c, d = 2, s = 1.6, Palette = P)
utilisation d'un dégradé sans lissage

>>> Centrer(-0.7797023086458332, 0.1503391603125, 0.0000005)
>>> N_MAX = 700
>>> P = ConstruirePaletteDégradéMultiple(100, [ROUGE, NOIR, ORANGE, NOIR])
>>> Mandelbrot_cyclique(lambda z, c : z ** 2 + c, d = 2, s = 1.6, Palette = P)
utilisation d'un dégradé lissage

Pour terminer cette page, amusons-nous un peu avec le cercle chromatique. Il est constitué des couleurs qu'on appelle saturées, c'est-à-dire ayant une de leurs trois composantes à \(0\) et une autre à \(255\) (la saturation d'une couleur étant l'écart entre la plus grande et la plus petite des trois composantes rouge/vert/bleu). On peut obtenir toutes les couleurs saturées en faisant des dégradés depuis les six couleurs ayant une (ou deux) composante(s) à \(0\) et deux (ou une) composante(s) à \(255\) : à savoir les trois couleurs primaires de la synthèse additive (rouge, vert et bleu) et les trois couleurs primaires de la synthèse soustractive (jaune, magenta, cyan). Attention, il faut les placer dans le « bon » ordre (on met toujours à la suite des couleurs ayant deux composantes en commun).

>>> Centrer(-0.7797023086458332, 0.1503391603125, 0.0000005)
>>> N_MAX = 700
>>> ROUGE = (255, 0, 0) ; VERT = (0, 255, 0) ; BLEU = (0, 0, 255)
>>> JAUNE = (255, 255, 0) ; MAGENTA = (255, 0, 255) ; CYAN = (0, 255, 255)
>>> P = ConstruirePaletteDégradéMultiple(1000, [ROUGE, JAUNE, VERT, CYAN, BLEU, MAGENTA])
>>> Mandelbrot_cyclique(lambda z, c : z ** 2 + c, d = 2, s = 1.6, Palette = P)
dégradé formé du cercle chromatique