【问题标题】:Configure matplotlib colorbar to match 3D surface values配置 matplotlib 颜色条以匹配 3D 表面值
【发布时间】:2019-09-30 09:54:41
【问题描述】:

我正在尝试将快速傅里叶变换的结果显示为来自 2D 矩阵(形状:2048x1024)的 3D 表面,并且侧面的颜色条应与我的值匹配。 显示工作正常,但颜色条的“颜色”和图形的颜色不匹配。我应该如何配置颜色条和图表以匹配?

我尝试根据我的 FFT 结果设置 vminvmax 值,但随后图表的颜色完全错误。 我也试过clim(vmin, vmax)函数或.set_clim(vmin, vmax)方法,但问题还是一样。

这是我的代码: rp 是一个 2048x1024 矩阵。

import numpy as np
from pylab import *
from mpl_toolkits.mplot3d import Axes3D


figure(2, figsize=(9.6, 7.2))
ax1 = gca(projection='3d')
X = np.arange(0, 1024)
Y = np.arange(0, 2048)
X, Y = np.meshgrid(X, Y)
Z = 20 * np.log10(abs(rp))
# Plot the surface.
surf1 = ax1.plot_surface(X, Y, Z, cmap='jet', antialiased=False, vmin=np.min(Z), vmax=np.max(Z))
# Add a color bar which maps values to colors.
colorbar(surf1)
title('3D frequency profile')

show()

这是我在没有为颜色条配置 vminvmax 值的情况下得到的结果。最大颜色(红色)与最大尖峰不匹配,并且颜色条的最大值和最小值与图表中的最小值和最大值不匹配(注意:min=-24 和 max=145)。

first_figure

这是我设置vminvmax 时得到的结果。所有的图表都是大致相同的阴影,而我应该有两个红色的长矛。

second_figure

有什么想法吗?

【问题讨论】:

  • 为什么不使用imshow? 3d 图通常难以解释。还有JET is generally considered harmful
  • @SamMason imshow 显示 2D 图像可能就足够了,但我的要求要求 3D 图显示信号的功率(Z 轴)。我尝试了不同的cmap 值,例如bwrPuOr,但问题仍然存在。关于颜色图而不是jet的任何建议?

标签: python matplotlib colorbar


【解决方案1】:

我想你只是想在 Matplotlib 中使用 Mappable 的东西,比如:

import matplotlib.pyplot as plt

mappable = plt.cm.ScalarMappable()
mappable.set_array(Z)

然后从该可映射对象中使用cmapnorm 调用plot_surface,例如:

ax.plot_surface(X, Y, Z, cmap=mappable.cmap, norm=mappable.norm, linewidth=0, antialiased=False)

然后您可以将此映射用于colorbar

plt.colorbar(mappable)

这应该使用默认的viridis colormap颜色映射,但可以手动指定为:

mappable = plt.cm.ScalarMappable(cmap=plt.cm.viridis)

Viridis 是一个不错的“感知统一”颜色图,请参阅上面的视频,了解为什么 Matlab 的 jet 几乎总是一个糟糕的选择,以及为什么它比更现代的默认 parula 更好

这似乎只是将 Z 中的数据重新用于“垂直”高度和颜色,因此我建议使用 imshow,例如:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

X, Y = np.meshgrid(
    np.linspace(-2, 2, 128),
    np.linspace(-2, 2, 128),
)
Z = np.exp(-(X ** 2 + Y ** 2)) * 3 + 5

mappable = plt.cm.ScalarMappable(cmap=plt.cm.viridis)
mappable.set_array(Z)
mappable.set_clim(5, 8) # optional

fig = plt.figure(figsize=(10,4))

ax1 = fig.add_subplot(121, projection='3d')
ax1.plot_surface(X, Y, Z, cmap=mappable.cmap, norm=mappable.norm, linewidth=0, antialiased=False)

ax2 = fig.add_subplot(122)
ax2.imshow(Z, cmap=mappable.cmap, norm=mappable.norm, extent=(
    np.min(X), np.max(X), np.min(Y), np.max(Y)), interpolation='none')

plt.colorbar(mappable)
plt.tight_layout()

给我:

我发现第二个非 3D 情节让我更容易看到正在发生的事情

【讨论】:

  • 我完全同意你的观点,我只能使用imshow 情节。另外我没有任何错误。我真正的问题是关于颜色条的配置,因为最大颜色是黄色,对吧?所以我在 3D 图上的“两个”长矛应该是黄色的,但不是。如果我没有指定 vminvmax 值,看起来最大值就在边缘,这是错误的。因此,要么我的配置被破坏,要么我在这里遇到了 matplotlib 的错误。顺便谢谢你的帮助!
【解决方案2】:

我终于找到了我的问题的原因。问题是图形的colormap 的步幅(这是它的分辨率,如果我理解得很好 - cstride and rstride documentation)。更准确地说,它使用cstride * rstride 的表面来平均表面上信号的能量,并使地图的颜色对应于这个平均值。我的长矛真的很薄,这就是为什么它们没有处于良好的阴影中。 我必须用较小的值指定参数cstriderstride。因为它们的默认值为 10。

所以现在我有:

figure(1, figsize=(12, 6))
X = np.arange(0, 1024)
Y = np.arange(0, 2048)
X, Y = np.meshgrid(X, Y)
Z = 20 * np.log10(abs(rp))
mappable = cm.ScalarMappable()
mappable.set_array(Z)
suptitle('3D-plot and 2D-plot of frequency profile')
ax1 = subplot(121, projection='3d')
ax1.plot_surface(X, Y, Z, cmap=mappable.cmap, norm=mappable.norm, linewidth=0,
                 antialiased=False, cstride=10, rstride=1)
ax2 = subplot(122)
ax2.imshow(Z, cmap=mappable.cmap, norm=mappable.norm,
           extent=(np.min(X), np.max(X), np.min(Y), np.max(Y)),
           interpolation=None)
colorbar(mappable)

show()

注意:我可以保留 cstride 的默认值,因为我只需要更改 row 步幅。但它可能对其他人有帮助。

这是我得到的结果,它符合我想要的。

NB2:小心减小步幅,输入数据越大,脚本越慢。

【讨论】:

    猜你喜欢
    • 2013-08-14
    • 1970-01-01
    • 2011-05-29
    • 2017-09-11
    • 2018-05-13
    • 1970-01-01
    • 2020-02-01
    • 2016-05-16
    • 2018-08-24
    相关资源
    最近更新 更多