使用Python/Matplotlib绘制基于色图的(极坐标)颜色轮。

21

我想用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([])

自行尝试创建色轮

但是,这个尝试有两个严重的缺点。

首先,将结果保存为矢量图(figure_1.svg)时,颜色轮由621个不同的形状组成,对应于正在绘制的不同(x,y)值。虽然结果看起来像一个圆,但实际上不是真正的圆。我非常希望使用一个真正的圆,由几个路径点和它们之间的Bezier曲线定义,例如matplotlib.patches.Circle。这对我来说似乎是“正确”的方法,并且结果看起来更漂亮(没有条纹,更好的渐变,更好的抗锯齿)。

其次(相关性更强),最终绘制的标记(前几个标记)2*pi与前几个重叠。在像素渲染中很难看到,但如果你放大基于矢量的渲染,你可以清楚地看到最后的圆盘重叠在前几个圆盘上。

我尝试使用不同的标记(.|),但它们都无法解决第二个问题。

底线: 在Python/Matplotlib中我能够画一个用适当的矢量/Bezier曲线定义的圆,并且边缘颜色是根据colormap(或者任意颜色渐变)定义的吗?

3个回答

21

我发现一种方法是生成一个颜色映射,然后将其投影到极坐标轴上。这是一个工作示例——虽然其中包含了一个可怕的hack(已经有清晰的注释)。我相信有一种方法可以调整限制或者(更难)编写自己的Transform来解决它,但我还没有完全做到。我认为对Normalize的调用边界会做到这一点,但显然不行。

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

这将产生:

colorwheel direct from matplotlib

如果您想要一个环而不是一个轮,使用此代码放在plt.show()plt.savefig之前。

display_axes.set_rlim([-1,1])

这个给出:

彩色环


根据评论中的@EelkeSpaak所述——如果您将图形保存为SVG(与OP相同),则以下是使用生成的图形的提示:结果的SVG图像的小元素相互接触且不重叠。这会在某些渲染器(Inkscape,Adobe Reader,可能不会出现在打印中)中产生微弱的灰色线条。解决此问题的简单方法是对每个单独的渐变元素应用小比例尺(例如120%),例如使用Inkscape或Illustrator。请注意,您必须对每个元素分别应用变换(所提到的软件提供自动完成此操作的功能),而不是应用于整个图纸,否则它没有效果。


非常好,我也尝试使用ColorbarBase来做一些东西,但是错过了orientation关键字。这个解决方案肯定比我的好(因为现在轮子上的“辐条”不再重叠),但它仍然不是我想要的解决方案,因为生成的矢量图仍然包含许多单独的路径元素,而不是一个单一的贝塞尔曲线圆。无论如何,非常感谢;如果没有人提出真正的解决方案,我将把你的答案标记为已接受 :) - EelkeSpaak
啊 - 我明白你的意思了。我会考虑一下的 - 对不起,我没有仔细阅读问题 ;) - J Richard Snape
一些想法。我记得贝塞尔曲线实际上不能产生完美的圆:这个答案似乎支持这种观点。我还认为对于matplotlib,1个元素=1个颜色。所以我们看到的渐变其实是很多紧密排列的小元素。另一个有关用渐变着色线条的答案似乎支持这种观点。这些元素可能是许多小的贝塞尔段,但我认为你仍然会看到一些可见的量化效应。也许有人会来证明我错了 :) - J Richard Snape
1
另一个提示。生成的SVG图像的小元素是相互接触且不重叠的。这会导致某些渲染器(如Inkscape、Adobe Reader,可能不会在打印中出现)出现淡灰色线条。解决方法很简单,就是对每个单独的渐变元素应用小的缩放(例如120%),使用Inkscape或Illustrator等软件即可。请注意,您必须将变换分别应用于每个元素(所提到的软件提供了自动执行此操作的功能),而不是整个图形,否则它将没有效果。 - EelkeSpaak
1
@Greedo 仔细查看我的代码后,可能是那个已注释的“恶意黑客”不再起作用了。我在那里更改了一个内部变量 - 这是你不应该做的事情,尽管它已经工作了2年,但看起来我的运气已经用完了。这就是这种黑客的问题 - 库的“底层”行为可能会在没有警告的情况下发生变化。如果我有时间,我会更新答案。 - J Richard Snape
显示剩余4条评论

10

我只需要制作一个颜色轮,决定更新rsnape的解决方案以使其与matplotlib 2.1兼容。不必在坐标轴上放置一个colorbar对象,您可以在极坐标图中绘制一个极向着色网格。

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)        #radius 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.

它呈现出这个效果:

使用matplotlib 2.1制作的viridis色标的颜色轮。


0

基于第二个答案绘制HSV色轮,并在色轮中更改红色位置

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
colormap = plt.get_cmap('hsv')
norm = mpl.colors.Normalize(start, end)

s = np.pi*65/180
e = np.pi*65/180+2*np.pi

n = 1000  #the number of secants for the mesh
t = np.linspace(s,e,n)   #theta values
r = np.linspace(0,1,2)        #radius 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, cmap=colormap)  #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.
ax.set_thetagrids(np.linspace(0, 360, 12, endpoint=False), 
                  ['0','30', '60', '90', '120', '150','180','210','240','270', '300','330'])
ax.grid(False)

在此输入图片描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接