Je veux créer une nouvelle palette de couleurs qui interpole entre le vert et le bleu (ou deux autres couleurs d'ailleurs). Mon objectif est d'obtenir quelque chose comme:
Tout d'abord, je ne sais vraiment pas si cela peut être fait en utilisant une interpolation linéaire du bleu et du vert. Si c'est le cas, je ne sais pas comment le faire, j'ai trouvé de la documentation sur l'utilisation d'une méthode matplotlib qui interpole les valeurs RVB spécifiées ici
Le vrai problème est de comprendre comment "cdict2" fonctionne ci-dessous. Pour l'exemple, la documentation dit:
"Exemple: supposons que vous vouliez que le rouge passe de 0 à 1 sur la moitié inférieure, le vert pour faire de même sur la moitié centrale et le bleu sur la moitié supérieure. Ensuite, vous utiliseriez:"
from matplotlib import pyplot as plt
import matplotlib
import numpy as np
plt.figure()
a=np.outer(np.arange(0,1,0.01),np.ones(10))
cdict2 = {'red': [(0.0, 0.0, 0.0),
(0.5, 1.0, 1.0),
(1.0, 1.0, 1.0)],
'green': [(0.0, 0.0, 0.0),
(0.25, 0.0, 0.0),
(0.75, 1.0, 1.0),
(1.0, 1.0, 1.0)],
'blue': [(0.0, 0.0, 0.0),
(0.5, 0.0, 0.0),
(1.0, 1.0, 1.0)]}
my_cmap2 = matplotlib.colors.LinearSegmentedColormap('my_colormap2',cdict2,256)
plt.imshow(a,aspect='auto', cmap =my_cmap2)
plt.show()
EDIT: Je comprends maintenant comment fonctionne l'interpolation, par exemple cela donnera une interpolation du rouge au blanc:
Blanc à rouge: En descendant les colonnes de la "matrice" pour chaque couleur, dans la première colonne, nous avons la coordonnée x de l'endroit où nous voulons que l'interpolation commence et se termine et les deux autres colonnes sont les valeurs réelles de la valeur de couleur à cette coordonnée .
cdict2 = {'red': [(0.0, 1.0, 1.0),
(1.0, 1.0, 1.0),
(1.0, 1.0, 1.0)],
'green': [(0.0, 1.0, 1.0),
(1.0, 0.0, 0.0),
(1.0, 0.0, 0.0)],
'blue': [(0.0, 1.0, 1.0),
(1.0, 0.0, 0.0),
(1.0, 0.0, 0.0)]}
Il est évident que le dégradé que je veux sera très difficile à créer en interpolant dans l'espace RVB ...
Une réponse simple que je n'ai pas encore vue est d'utiliser simplement le paquet de couleurs .
Installer via pip
pip install colour
Utilisez comme suit:
from colour import Color
red = Color("red")
colors = list(red.range_to(Color("green"),10))
# colors is now a list of length 10
# Containing:
# [<Color red>, <Color #f13600>, <Color #e36500>, <Color #d58e00>, <Color #c7b000>, <Color #a4b800>, <Color #72aa00>, <Color #459c00>, <Color #208e00>, <Color green>]
Remplacez les entrées par les couleurs de votre choix. Comme indiqué par @zelusp, cela ne se limitera pas à une combinaison fluide de seulement deux couleurs (par exemple, le rouge au bleu aura le jaune + le vert au milieu ), mais sur la base des votes positifs, il est clair qu'un certain nombre de personnes trouvent que c'est une approximation utile
Il est évident que votre exemple de gradient d'origine est pas linéaire. Jetez un œil à un graphique des valeurs rouges, vertes et bleues moyennes sur l'image:
Tenter de recréer cela avec une combinaison de dégradés linéaires va être difficile.
Pour moi, chaque couleur ressemble à l'ajout de deux courbes gaussiennes, j'ai donc fait les meilleurs ajustements et j'ai trouvé ceci:
L'utilisation de ces valeurs calculées me permet de créer un très joli dégradé qui correspond presque exactement au vôtre.
import math
from PIL import Image
im = Image.new('RGB', (604, 62))
ld = im.load()
def gaussian(x, a, b, c, d=0):
return a * math.exp(-(x - b)**2 / (2 * c**2)) + d
for x in range(im.size[0]):
r = int(gaussian(x, 158.8242, 201, 87.0739) + gaussian(x, 158.8242, 402, 87.0739))
g = int(gaussian(x, 129.9851, 157.7571, 108.0298) + gaussian(x, 200.6831, 399.4535, 143.6828))
b = int(gaussian(x, 231.3135, 206.4774, 201.5447) + gaussian(x, 17.1017, 395.8819, 39.3148))
for y in range(im.size[1]):
ld[x, y] = (r, g, b)
Malheureusement, je ne sais pas encore comment le généraliser à des couleurs arbitraires.
Si vous avez juste besoin d'interpoler entre 2 couleurs , j'ai écrit une fonction simple pour cela. colorFader
vous crée un code de couleur hexadécimal à partir de deux autres codes de couleur hexadécimaux.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
def colorFader(c1,c2,mix=0): #fade (linear interpolate) from color c1 (at mix=0) to c2 (mix=1)
c1=np.array(mpl.colors.to_rgb(c1))
c2=np.array(mpl.colors.to_rgb(c2))
return mpl.colors.to_hex((1-mix)*c1 + mix*c2)
c1='#1f77b4' #blue
c2='green' #green
n=500
fig, ax = plt.subplots(figsize=(8, 5))
for x in range(n+1):
ax.axvline(x, color=colorFader(c1,c2,x/n), linewidth=4)
plt.show()
résultat:
mise à jour en raison d'un intérêt élevé:
colorFader
fonctionne maintenant pour les couleurs RVB et les chaînes de couleurs comme 'rouge' ou même 'r'.
Le premier élément de chaque tuple (0, 0,25, 0,5, etc.) est l'endroit où la couleur doit avoir une certaine valeur. J'ai pris 5 échantillons pour voir les composants RVB (dans GIMP) et les ai placés dans les tableaux. Les composants RVB passent de 0 à 1, j'ai donc dû les diviser par 255,0 pour mettre à l'échelle les valeurs normales de 0 à 255.
Les 5 points sont une approximation assez grossière - si vous voulez une apparence "plus lisse", utilisez plus de valeurs.
from matplotlib import pyplot as plt
import matplotlib
import numpy as np
plt.figure()
a=np.outer(np.arange(0,1,0.01),np.ones(10))
fact = 1.0/255.0
cdict2 = {'red': [(0.0, 22*fact, 22*fact),
(0.25, 133*fact, 133*fact),
(0.5, 191*fact, 191*fact),
(0.75, 151*fact, 151*fact),
(1.0, 25*fact, 25*fact)],
'green': [(0.0, 65*fact, 65*fact),
(0.25, 182*fact, 182*fact),
(0.5, 217*fact, 217*fact),
(0.75, 203*fact, 203*fact),
(1.0, 88*fact, 88*fact)],
'blue': [(0.0, 153*fact, 153*fact),
(0.25, 222*fact, 222*fact),
(0.5, 214*fact, 214*fact),
(0.75, 143*fact, 143*fact),
(1.0, 40*fact, 40*fact)]}
my_cmap2 = matplotlib.colors.LinearSegmentedColormap('my_colormap2',cdict2,256)
plt.imshow(a,aspect='auto', cmap =my_cmap2)
plt.show()
A noter que le rouge est bien présent. C'est là parce que la zone centrale s'approche du gris - où les trois composants sont nécessaires.
Cela produit:
Cela crée une palette de couleurs contrôlée par un seul paramètre, y
:
from matplotlib.colors import LinearSegmentedColormap
def bluegreen(y):
red = [(0.0, 0.0, 0.0), (0.5, y, y), (1.0, 0.0, 0.0)]
green = [(0.0, 0.0, 0.0), (0.5, y, y), (1.0, y, y)]
blue = [(0.0, y, y), (0.5, y, y),(1.0,0.0,0.0)]
colordict = dict(red=red, green=green, blue=blue)
bluegreenmap = LinearSegmentedColormap('bluegreen', colordict, 256)
return bluegreenmap
red
monte de 0 à y
puis redescend à 0. green
monte de 0 à y
puis est constant. blue
étoiles à y
et est constant pour la première moitié, puis descend à 0.
Voici l'intrigue avec y = 0.7
:
Vous pouvez l'aplanir en ajoutant un ou deux segments supplémentaires.
J'en avais également besoin, mais je voulais saisir plusieurs points de couleur arbitraires. Considérez une carte thermique où vous avez besoin de noir, bleu, vert ... jusqu'aux couleurs "chaudes". J'ai emprunté le code de Mark Ransom ci-dessus et l'ai étendu pour répondre à mes besoins. J'en suis très content. Merci à tous, en particulier à Mark.
Ce code est neutre par rapport à la taille de l'image (pas de constantes dans la distribution gaussienne); vous pouvez le changer avec le paramètre width = en pixel (). Il permet également de régler la "propagation" (-> stddev) de la distribution; vous pouvez les embrouiller davantage ou introduire des bandes noires en changeant le paramètre spread = en pixel ().
#!/usr/bin/env python
import math
from PIL import Image
im = Image.new('RGB', (3000, 2000))
ld = im.load()
# A map of rgb points in your distribution
# [distance, (r, g, b)]
# distance is percentage from left Edge
heatmap = [
[0.0, (0, 0, 0)],
[0.20, (0, 0, .5)],
[0.40, (0, .5, 0)],
[0.60, (.5, 0, 0)],
[0.80, (.75, .75, 0)],
[0.90, (1.0, .75, 0)],
[1.00, (1.0, 1.0, 1.0)],
]
def gaussian(x, a, b, c, d=0):
return a * math.exp(-(x - b)**2 / (2 * c**2)) + d
def pixel(x, width=100, map=[], spread=1):
width = float(width)
r = sum([gaussian(x, p[1][0], p[0] * width, width/(spread*len(map))) for p in map])
g = sum([gaussian(x, p[1][1], p[0] * width, width/(spread*len(map))) for p in map])
b = sum([gaussian(x, p[1][2], p[0] * width, width/(spread*len(map))) for p in map])
return min(1.0, r), min(1.0, g), min(1.0, b)
for x in range(im.size[0]):
r, g, b = pixel(x, width=3000, map=heatmap)
r, g, b = [int(256*v) for v in (r, g, b)]
for y in range(im.size[1]):
ld[x, y] = r, g, b
im.save('grad.png')