更改plt.imshow()图像的像素颜色
我想用matplotlib的imshow()来画一张图片,然后给一些像素标记上不同的颜色。直接在最开始的数组里改这些像素的值是不行的,因为我需要用到在我使用的颜色映射中没有的颜色。所以我最开始的想法是,在第一张图片上面再画一张生成的数组,大部分地方都遮住,只有需要的像素不遮住,并且这些像素的值可以不同,以便用不同的颜色来表示不同的位置。这在matplotlib的互动查看器里效果很好,但当我保存成文件时,一切都变得扭曲了,可能是因为我在同样的情况下报告的一个bug:https://github.com/matplotlib/matplotlib/issues/3057
有没有其他方法可以改变一些像素的颜色呢?
2 个回答
为了补充Joe的很好的回答,当我们给imshow
提供一组rgba值的数组时,鼠标光标读取到的z值现在会显示rgba值的元组,而不是原来的data
值。
为了解决这个问题,我们可以在原始图像上叠加一张透明的图像。这样,我们就可以同时利用这张透明图像为图形添加一个颜色条:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
data = np.arange(100).reshape(10, 10)
cmap = plt.cm.gray
norm = plt.Normalize(data.min(), data.max())
rgba = cmap(norm(data))
# Set the diagonal to red...
rgba[range(10), range(10), :3] = 1, 0, 0
im = ax.imshow(rgba, interpolation='nearest')
im2 = ax.imshow(data, cmap='gray')
cbar = plt.colorbar(im2, ax=ax)
im2.set_alpha(0.0)
需要注意的是,在调用im2.set_alpha(0.0)
之前创建颜色条是很重要的。如果不这样做,颜色条中的颜色也会变得透明(它们会跟随当前图像的透明度)。
如果无法遵循这个创建顺序,我们可以通过以下方式将颜色条中的颜色的不透明度设置回1:
cbar.set_alpha(1.0)
cbar.draw_all()
你已经提到了一种最简单的方法(在上面叠加另一张图片),但如果这种方法不太符合你的需求,还有其他选择。
方法一 - 手动渲染和合成图像
最直接的方法就是使用颜色映射将你的数组渲染成RGB格式,然后修改你想要的像素。
举个简单的例子:
import numpy as np
import matplotlib.pyplot as plt
data = np.arange(100).reshape(10, 10)
cmap = plt.cm.gray
norm = plt.Normalize(data.min(), data.max())
rgba = cmap(norm(data))
# Set the diagonal to red...
rgba[range(10), range(10), :3] = 1, 0, 0
plt.imshow(rgba, interpolation='nearest')
plt.show()
这种方法的一个缺点是你不能直接调用 fig.colorbar(im)
,因为你传入的是一张已经渲染好的RGB图像。因此,如果你需要一个颜色条,你就得使用一个代理艺术家。最简单的方法是添加一个额外的、不可见的(不是透明的,而是没有绘制的)艺术家,使用 imshow(data, visible=False)
,然后基于这个艺术家来设置颜色映射。举个简单的例子:
import numpy as np
import matplotlib.pyplot as plt
data = np.arange(100).reshape(10, 10)
cmap = plt.cm.gray
norm = plt.Normalize(data.min(), data.max())
rgba = cmap(norm(data))
# Set the diagonal to red
rgba[range(10), range(10), :3] = 1, 0, 0
fig, ax = plt.subplots()
ax.imshow(rgba, interpolation='nearest')
# Add the colorbar using a fake (not shown) image.
im = ax.imshow(data, visible=False, cmap=cmap)
fig.colorbar(im)
plt.show()
使用不可见的 imshow
是创建代理艺术家的最简单方法,但如果你担心速度(或者这可能会触发你提到的渲染错误),你也可以使用任何 ScalarMappable
。ScalarMappable
是一个抽象基类,通常只用来继承以支持颜色条。因为我们不需要绘制任何东西,所以可以直接使用它。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
data = np.arange(100).reshape(10, 10)
cmap = plt.cm.gray
norm = plt.Normalize(data.min(), data.max())
rgba = cmap(norm(data))
# Set the diagonal to red
rgba[range(10), range(10), :3] = 1, 0, 0
fig, ax = plt.subplots()
ax.imshow(rgba, interpolation='nearest')
# Add the colorbar using a ScalarMappable
im = ScalarMappable(norm, cmap)
im.set_array(data)
fig.colorbar(im)
plt.show()
方法二 - 利用 set_bad
、set_over
或 set_under
set_bad
、set_over
和 set_under
方法可以让你标记那些是NaN或超出颜色映射指定范围的像素。
因此,另一种实现你想要的效果的方法是将这些值设置为NaN,并指定NaN的颜色(set_bad
... 默认情况下,对于大多数颜色映射来说,它是透明的)。
如果你有一个整数数组或者需要透明的NaN像素,你也可以类似地利用 set_over
和 set_under
。在这种情况下,你需要在调用 imshow
时手动指定 vmin
或 vmax
。
使用/利用 set_bad
的一个简单例子:
import numpy as np
import matplotlib.pyplot as plt
data = np.arange(100).reshape(10, 10).astype(float)
cmap = plt.cm.gray
cmap.set_bad((1, 0, 0, 1))
# Set the diagonal to NaN
data[range(10), range(10)] = np.nan
plt.imshow(data, cmap=cmap, interpolation='nearest')
plt.show()
这种方法相较于第一种方法的一个优点是绘制颜色条会稍微简单一些。(缺点是这种方法灵活性较差。)
import numpy as np
import matplotlib.pyplot as plt
data = np.arange(100).reshape(10, 10).astype(float)
cmap = plt.cm.gray
cmap.set_bad((1, 0, 0, 1))
# Set the diagonal to NaN
data[range(10), range(10)] = np.nan
plt.imshow(data, cmap=cmap, interpolation='nearest')
plt.colorbar()
plt.show()