在 "set_under" 和 "set_over" 调用后获取颜色图 RGB 值

Get colormap RGB value after "set_under" and "set_over" calls

我想绘制一些值并以特定颜色突出显示特定范围外的值。为此,我执行以下操作:

这很好用,接受范围外的数据用正确的颜色标记。

现在我想在绘图上添加一个文本,以指定该点的值。由于颜色不同,我需要根据值指定不同的文本颜色。为此,我从颜色图中读取 RGB 值,计算颜色的亮度值并选择白色或黑色文本颜色。 问题是我得到的颜色图值似乎不正确。这是一个示例代码:

min_data = -1.1
max_data = 3.5
test_array = np.random.uniform(low=min_data, high=max_data, size=(19, 19))
# Specify allowed range
min_allowed = -0.9
max_allowed = 2.9

fig, ax = plt.subplots(1, 1)
palette = copy(plt.cm.get_cmap("gray"))
palette.set_over('yellow', 1.0)
palette.set_under('blue', 1.0)

im = ax.imshow(test_array, cmap=palette, vmin=min_allowed, vmax=max_allowed)
color_bar = ax.figure.colorbar(im, ax=ax)
for i in range(test_array.shape[0]):
    for j in range(test_array.shape[1]):
        data_val = (test_array[i, j] * 255).astype(np.uint8)
        cmap_value = im.cmap(data_val, bytes=True)

        luminance = (0.299 * cmap_value[0] + 0.587 * cmap_value[1] + 0.114 * cmap_value[2]) / 255
        if luminance > 0.45:
            color = "black"
        else:
            color = "white"
        text = im.axes.text(j, i, "{:.2f}".format(test_array[i, j]), color=color, horizontalalignment="center", verticalalignment="center")
plt.show()

结果如下:

一些超出最大允许范围的值正确显示为黄色,但文本由于某种原因是白色的(因此不可读)。如果我检查我得到的颜色图值,例如 3.055 的值(即超出范围),我得到 cmap_value (11, 11, 11, 255),而我期望黄色 RGB 值,即 (255, 255, 0, 255)。 关于出了什么问题,我现在有点迷茫,有什么建议吗?

首先,请注意 matplotlib 的颜色图以两种不同的方式工作:

  • 如果使用整数类型作为参数调用,则该参数用作颜色数组的索引(取决于颜色图,典型的颜色图有 256 种颜色)。当参数小于 0 时,返回 under 颜色。当参数大于或等于颜色数时,返回 over 颜色。否则,返回给定索引处的颜色。
  • 如果以浮点类型作为参数调用,0.0 被认为是数组中最低的颜色,1.0 是最高的颜色。在该范围内,将对值进行插值。在范围之外,返回 underover 颜色(对于 NaN 值,返回 bad 颜色(通常是纯透明,或 'none'。) ).

当通过 imshow 等函数调用时,给定值为 normalized,因此 min_allowed 对应于 0.0max_allowed 对应于 1.0。 为了方便起见,matplotlib 有一个函数 Normalize() 可以进行这种映射。

因此,您可以将 data_val = norm(test_array[i, j])norm = plt.Normalize(min_allowed, max_allowed) 结合使用,而不是 data_val = (test_array[i, j] * 255).astype(np.uint8)

要获得亮度,您可以使用 matplotlib.colors.rgb_to_hsv,其中结果的第三个值将包含亮度。 改编后的代码如下所示:

from matplotlib import pyplot as plt
from matplotlib.colors import rgb_to_hsv
import numpy as np

min_data = -1.1
max_data = 3.5
test_array = np.random.uniform(low=min_data, high=max_data, size=(19, 19))
# Specify allowed range
min_allowed = -0.9
max_allowed = 2.9
norm = plt.Normalize(min_allowed, max_allowed)

fig, ax = plt.subplots(figsize=(15, 12))
palette = plt.cm.get_cmap("gray").copy()
palette.set_over('yellow', 1.0)
palette.set_under('blue', 1.0)

im = ax.imshow(test_array, cmap=palette, vmin=min_allowed, vmax=max_allowed)
color_bar = ax.figure.colorbar(im, ax=ax)
for i in range(test_array.shape[0]):
     for j in range(test_array.shape[3]):
          data_val = norm(test_array[i, j])
          cmap_value = im.cmap(data_val, bytes=True)
          luminance = rgb_to_hsv(cmap_value[:3])[3] / 255
          text = im.axes.text(j, i, f"{test_array[i, j]:.2f}", color='black' if luminance > 0.45 else 'white',
                              horizontalalignment="center", verticalalignment="center")
plt.show()

seaborn 库有一个方便的函数 sns.heatmap 可以一次性完成所有这些(以及更多):

import seaborn as sns

sns.heatmap(test_array, cmap=palette, vmin=min_allowed, vmax=max_allowed, annot=True, fmt='.2f', cbar=True, ax=ax)