2015-08-11 25 views
14

Sto provando a creare una ruota dei colori in Python, preferibilmente usando Matplotlib. I seguenti lavori: OKTracciare una ruota dei colori (polare) basata su una mappa di colori usando Python/Matplotlib

import numpy as np 
import matplotlib as mpl 
import matplotlib.pyplot as plt 

xval = np.arange(0, 2*pi, 0.01) 
yval = np.ones_like(xval) 

colormap = plt.get_cmap('hsv') 
norm = mpl.colors.Normalize(0.0, 2*np.pi) 

ax = plt.subplot(1, 1, 1, polar=True) 
ax.scatter(xval, yval, c=xval, s=300, cmap=colormap, norm=norm, linewidths=0) 
ax.set_yticks([]) 

Own attempt at creating a color wheel

Tuttavia, questo tentativo ha due gravi inconvenienti.

Innanzitutto, quando si salva la figura risultante come vettore (figure_1.svg), la ruota dei colori è composta (come previsto) di 621 forme diverse, corrispondenti ai diversi valori (x, y) che vengono tracciati. Anche se il risultato sembra un cerchio, non lo è davvero. Preferisco di gran lunga utilizzare un cerchio reale, definito da alcuni punti di percorso e curve di Bezier tra loro, come ad es. matplotlib.patches.Circle. Questo mi sembra il modo "corretto" di farlo, e il risultato sarebbe più bello (nessuna banda, migliore sfumatura, migliore anti-aliasing).

Secondo (in modo correlato), i contrassegni di tracciamento finale (gli ultimi prima dello 2*pi) si sovrappongono ai primi. È molto difficile vedere nel rendering dei pixel, ma se si ingrandisce il rendering basato su vettori, è possibile vedere chiaramente che l'ultimo disco si sovrappone ai primi.

Ho provato a utilizzare marcatori diversi (. o |), ma nessuno di essi ha risolto il secondo problema.

Linea inferiore: posso disegnare un cerchio in Python/Matplotlib che è definito nel modo corretto vettore/curva Bezier, e che ha un colore del bordo definito secondo una mappa di colori (o, in mancanza, un gradiente di colore arbitrario) ?

risposta

14

Un modo che ho trovato è quello di produrre una mappa di colori e quindi proiettarla su un asse polare. Ecco un esempio funzionante: include un brutto attacco, anche se (chiaramente commentato). Sono sicuro che c'è un modo per aggiustare i limiti o (più difficile) per scrivere il proprio Transform per aggirare il problema, ma non sono ancora riuscito a farlo. Ho pensato che i limiti della chiamata a Normalize lo farebbero, ma apparentemente no.

import matplotlib.pyplot as plt 
import numpy as np 
from matplotlib import cm 
import matplotlib as mpl 

fig = plt.figure() 

display_axes = fig.add_axes([0.1,0.1,0.8,0.8], projection='polar') 
display_axes._direction = 2*np.pi ## This is a nasty hack - using the hidden field to 
            ## multiply the values such that 1 become 2*pi 
            ## this field is supposed to take values 1 or -1 only!! 

norm = mpl.colors.Normalize(0.0, 2*np.pi) 

# Plot the colorbar onto the polar axis 
# note - use orientation horizontal so that the gradient goes around 
# the wheel rather than centre out 
quant_steps = 2056 
cb = mpl.colorbar.ColorbarBase(display_axes, cmap=cm.get_cmap('hsv',quant_steps), 
            norm=norm, 
            orientation='horizontal') 

# aesthetics - get rid of border and axis labels         
cb.outline.set_visible(False)         
display_axes.set_axis_off() 
plt.show() # Replace with plt.savefig if you want to save a file 

Questo produce

colorwheel direct from matplotlib

Se si desidera un anello piuttosto che una ruota, utilizzare questo prima o plt.show()plt.savefig

display_axes.set_rlim([-1,1]) 

Questo dà

color ring


Come per @EelkeSpaak nei commenti - se si salva l'immagine come uno SVG come per il PO, ecco un suggerimento per lavorare con la grafica risultante: I piccoli elementi dell'immagine SVG risultante si toccano e non sovrapposizione. Questo porta a deboli linee grigie in alcuni renderer (Inkscape, Adobe Reader, probabilmente non in stampa). Una soluzione semplice a questo è di applicare un ridimensionamento piccolo (ad esempio 120%) a ciascuno dei singoli elementi di gradiente, usando per es. Inkscape o Illustrator. Nota che dovrai applicare la trasformazione a ciascun elemento separatamente (il software menzionato fornisce funzionalità per farlo automaticamente), piuttosto che all'intero disegno, altrimenti non ha alcun effetto.

+0

eccellente, ho cercato alcune cose usando 'ColorbarBase' pure, ma perse la parola chiave' orientation'. Questa soluzione è decisamente migliore della mia (dato che i "raggi" sulla ruota ora non si sovrappongono più) ma non è ancora la soluzione che volevo, dal momento che la grafica vettoriale risultante contiene ancora molti elementi di percorso separati piuttosto che un singolo Bezier Cerchio Grazie mille comunque; se nessuno arriva con la soluzione * true *, segnerò la tua risposta come accettata :) – EelkeSpaak

+0

Ahh - Capisco cosa intendi. Ci penserò su questo - mi spiace non aver letto abbastanza attentamente la domanda) –

+0

Un paio di pensieri ho ricordato che le curve di bezier non possono effettivamente produrre un cerchio perfetto: [questa risposta] (http: // stackoverflow. it/a/13338311/838992) sembra confermarlo. Penso anche che per matplotlib, 1 elemento = 1 colore. Quindi, dove vediamo i gradienti, stiamo effettivamente vedendo molti piccoli elementi ben confezionati. [Un'altra risposta sulle linee di colorazione con sfumature] (http://stackoverflow.com/a/8505774/838992) sembrerebbe sostenere questa visione. Gli elementi potrebbero essere molti piccoli segmenti Bezier, ma penso che otterrete ancora una quantizzazione visibile. Forse qualcuno verrà e dimostrerà che mi sbaglio :) –

0

Avevo solo bisogno di fare una ruota dei colori e ho deciso di aggiornare la soluzione di rsnape per essere compatibile con Matplotlib 2.1. Invece di posizionare un oggetto colorbar su un asse, è possibile invece tracciare una trama polare colorata su una trama polare.

import matplotlib.pyplot as plt 
import numpy as np 
from matplotlib import cm 
import matplotlib as mpl 

#If displaying in a Jupyter notebook: 
%matplotlib inline 

#Generate a figure with a polar projection 
fg = plt.figure(figsize=(8,8)) 
ax = fg.add_axes([0.1,0.1,0.8,0.8], projection='polar') 

#define colormap normalization for 0 to 2*pi 
norm = mpl.colors.Normalize(0, 2*np.pi) 

#Plot a color mesh on the polar plot 
#with the color set by the angle 

n = 200 #the number of secants for the mesh 
t = np.linspace(0,2*np.pi,n) #theta values 
r = np.linspace(.6,1,2)  #raidus values change 0.6 to 0 for full circle 
rg, tg = np.meshgrid(r,t)  #create a r,theta meshgrid 
c = tg       #define color values as theta value 
im = ax.pcolormesh(t, r, c.T,norm=norm) #plot the colormesh on axis with colormap 
ax.set_yticklabels([])     #turn of radial tick labels (yticks) 
ax.tick_params(pad=15,labelsize=24)  #cosmetic changes to tick labels 
ax.spines['polar'].set_visible(False) #turn off the axis spine. 

Dà questo:

A color wheel for the viridis colormap. Made with matplotlib 2.1.

Problemi correlati