如何在matplotlib中添加2D色条或色轮?

6

我正在分析样本的磁化映射。获得梯度及其方向后,我将它们绘制成HSV图像(从-π到π的方向被映射为0到1的色相,而值则是归一化的梯度),并通过img_rgb = mpl.colors.hsv_to_rgb(img_hsv)转换为RGB。

我成功添加了一个HSV色条,使用vmin和vmax,但这并没有显示梯度的大小:

plt.imshow(img_rgb, cmap='hsv', vmin=-180, vmax=180, extent=(0, 100, 0,100))
plt.xlabel('μm')
plt.ylabel('μm')
plt.colorbar()
我的当前绘图
在此输入图片描述 理想情况下,我希望添加一个颜色轮,它编码方向和大小(也许可以作为极坐标图)? 如果不可能,就添加一个二维图,将当前色条延伸到包括梯度大小的x轴。
虽然子图显然是可能的,但它们似乎像一个笨拙的解决方法。有更好的方法吗?
2个回答

4
首先,如果您想同时显示两个不同的参数,可以通过为它们分配两个不同的通道(比如红色和绿色)来实现。这可以通过对两个2D数组进行归一化处理,并将它们类似于此答案中的方式堆叠后,传递给imshow函数来完成。
如果您满意一个正方形的2D颜色映射,那么可以通过创建一个meshgrid并再次堆叠和传递给imshow函数来获取这个颜色映射:
from matplotlib import pyplot as plt
import numpy as np

##generating some  data
x,y = np.meshgrid(
    np.linspace(0,1,100),
    np.linspace(0,1,100),
)
directions = (np.sin(2*np.pi*x)*np.cos(2*np.pi*y)+1)*np.pi
magnitude = np.exp(-(x*x+y*y))


##normalize data:
def normalize(M):
    return (M-np.min(M))/(np.max(M)-np.min(M))

d_norm = normalize(directions)
m_norm = normalize(magnitude)

fig,(plot_ax, bar_ax) = plt.subplots(nrows=1,ncols=2,figsize=(8,4))

plot_ax.imshow(
    np.dstack((d_norm,m_norm, np.zeros_like(directions))),
    aspect = 'auto',
    extent = (0,100,0,100),
)

bar_ax.imshow(
    np.dstack((x, y, np.zeros_like(x))),
    extent = (
        np.min(directions),np.max(directions),
        np.min(magnitude),np.max(magnitude),
    ),
    aspect = 'auto',
    origin = 'lower',
)
bar_ax.set_xlabel('direction')
bar_ax.set_ylabel('magnitude')

plt.show()

结果看起来像这样:

正方形二维色条

原则上,使用极坐标Axes也应该是可行的,但根据此 github 票中的评论,imshow不支持极坐标轴,我无法使imshow填充整个圆盘。 编辑
感谢ImportanceOfBeingErnest和他对另一个问题的回答(关键字color做到了这一点),现在可以在极坐标轴上使用pcolormesh显示二维颜色图。有一些注意事项,最值得注意的是,在theta方向上,colors维度需要比meshgrid小一个,否则颜色图将呈螺旋形状:
fig= plt.figure(figsize=(8,4))
plot_ax = fig.add_subplot(121)
bar_ax = fig.add_subplot(122, projection = 'polar')

plot_ax.imshow(
    np.dstack((d_norm,m_norm, np.zeros_like(directions))),
    aspect = 'auto',
    extent = (0,100,0,100),
)

theta, R = np.meshgrid(
    np.linspace(0,2*np.pi,100),
    np.linspace(0,1,100),
)

t,r = np.meshgrid(
    np.linspace(0,1,99),
    np.linspace(0,1,100),
)    

image = np.dstack((t, r, np.zeros_like(r)))

color = image.reshape((image.shape[0]*image.shape[1],image.shape[2]))

bar_ax.pcolormesh(
    theta,R,
    np.zeros_like(R),
    color = color,
)

bar_ax.set_xticks(np.linspace(0,2*np.pi,5)[:-1])
bar_ax.set_xticklabels(
    ['{:.2}'.format(i) for i in np.linspace(np.min(directions),np.max(directions),5)[:-1]]
)
bar_ax.set_yticks(np.linspace(0,1,5))
bar_ax.set_yticklabels(
    ['{:.2}'.format(i) for i in np.linspace(np.min(magnitude),np.max(magnitude),5)]
)
bar_ax.grid('off')

plt.show()

这将产生这个图像:

工作中的2D色图


第一部分的答案类似可以在这里找到。关于极坐标轴,pcolormesh应该可以正常工作。 - ImportanceOfBeingErnest
@ImportanceOfBeingErnest 对不起,我没有注意到那个答案 - 只需标记为重复。 我尝试了pcolormesh,但它抱怨矩阵的维度(仅允许2D)。 我另一个想法是使用alpha = 0.5,但那看起来不太好看。 - Thomas Kühn
好吧,那个回答没有涉及到极地图。所以我们可以保持这个问题开放。是否可以通过将数组设置为none .set_array(None)来解决pcolormesh的问题,就像这个答案的最后一部分所示? - ImportanceOfBeingErnest
@ImportanceOfBeingErnest 我已经搞定了。感谢你的帮助! - Thomas Kühn

1
我在尝试可视化表面梯度的径向和绝对分量时遇到了类似的问题。
我将梯度的绝对值加上角度转换为颜色,使用hsv(使用色相作为角度,饱和度和价值作为绝对值)。这与磁化图中相同,任何向量场都可以用来替换梯度。以下函数说明了这个想法。完整代码在答案末尾提供。
import matplotlib.colors

# gradabs is the absolute gradient value, 
# gradang is the angle direction, z the vector field
# the gradient was calculated of

max_abs = np.max(gradabs) 

def grad_to_rgb(angle, absolute):
    """Get the rgb value for the given `angle` and the `absolute` value

    Parameters
    ----------
    angle : float
        The angle in radians
    absolute : float
        The absolute value of the gradient

    Returns
    -------
    array_like
        The rgb value as a tuple with values [0..1]
    """
    global max_abs

    # normalize angle
    angle = angle % (2 * np.pi)
    if angle < 0:
        angle += 2 * np.pi

    return matplotlib.colors.hsv_to_rgb((angle / 2 / np.pi, 
                                         absolute / max_abs, 
                                         absolute / max_abs))

# convert to colors via hsv
grad = np.array(list(map(grad_to_rgb, gradang.flatten(), gradabs.flatten())))

# reshape
grad = grad.reshape(tuple(list(z.shape) + [3]))

生成的图形如下。

Resulting image


展示曲面梯度场的完整示例代码如下:
import numpy as np
import matplotlib.colors
import matplotlib.pyplot as plt

r = np.linspace(0, np.pi, num=100)
x, y = np.meshgrid(r, r)
z = np.sin(y) * np.cos(x)

fig = plt.figure()

ax = fig.add_subplot(1, 3, 1, projection='3d')
ax.plot_surface(x, y, z)
# ax.imshow(z)
ax.set_title("Surface")

ax = fig.add_subplot(1, 3, 2)
ax.set_title("Gradient")

# create gradient
grad_y, grad_x = np.gradient(z)

# calculate length
gradabs = np.sqrt(np.square(grad_x) + np.square(grad_y))
max_abs = np.max(gradabs)

# calculate angle component
gradang = np.arctan2(grad_y, grad_x)

def grad_to_rgb(angle, absolute):
    """Get the rgb value for the given `angle` and the `absolute` value

    Parameters
    ----------
    angle : float
        The angle in radians
    absolute : float
        The absolute value of the gradient
    
    Returns
    -------
    array_like
        The rgb value as a tuple with values [0..1]
    """
    global max_abs

    # normalize angle
    angle = angle % (2 * np.pi)
    if angle < 0:
        angle += 2 * np.pi

    return matplotlib.colors.hsv_to_rgb((angle / 2 / np.pi, 
                                         absolute / max_abs, 
                                         absolute / max_abs))

# convert to colors via hsv
grad = np.array(list(map(grad_to_rgb, gradang.flatten(), gradabs.flatten())))

# reshape
grad = grad.reshape(tuple(list(z.shape) + [3]))

ax.imshow(grad)

n = 5
gx, gy = np.meshgrid(np.arange(z.shape[0] / n), np.arange(z.shape[1] / n))
ax.quiver(gx * n, gy * n, grad_x[::n, ::n], grad_y[::n, ::n])

# plot color wheel
# Generate a figure with a polar projection, inspired by
# https://dev59.com/mlwZ5IYBdhLWcg3wBcWz#48253413
ax = fig.add_subplot(1, 3, 3, projection='polar')

n = 200  # the number of secants for the mesh
t = np.linspace(0, 2 * np.pi, n)
r = np.linspace(0, max_abs, n)
rg, tg = np.meshgrid(r, t)
c = np.array(list(map(grad_to_rgb, tg.T.flatten(), rg.T.flatten())))
cv = c.reshape((n, n, 3))

m = ax.pcolormesh(t, r, cv[:,:,1], color=c, shading='auto')
m.set_array(None)
ax.set_yticklabels([])

plt.show()

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