如何确保绘制NumPy图像数组时相同像素值始终显示同一颜色
问题描述
我正在对图像序列进行像素处理,遇到一个问题:当绘制包含不同像素值范围的图像时,相同像素值会显示不同颜色。
例如,我有一张包含6种不同像素值(0、2、3、4、5、6)的图像:
代码示例1
import numpy as np import matplotlib.pyplot as plt import cv2 image = np.zeros(shape=[256, 256], dtype=np.uint8) cv2.circle(image, center=(50, 50), radius=10, color=(2, 2), thickness= -1) cv2.circle(image, center=(100, 100), radius=10, color=(3, 3), thickness= -1) cv2.circle(image, center=(150, 150), radius=10, color=(4, 4), thickness= -1) cv2.circle(image, center=(75, 200), radius=10, color=(5, 5), thickness= -1) cv2.circle(image, center=(200, 89), radius=10, color=(6, 6), thickness= -1) plt.imshow(image) plt.show()
输出1

当我向图像中添加两个像素值为7和12的图形后,原有所有像素的颜色都发生了改变:
代码示例2
image = np.zeros(shape=[256, 256], dtype=np.uint8) cv2.circle(image, center=(50, 50), radius=10, color=(2, 2), thickness= -1) cv2.circle(image, center=(100, 100), radius=10, color=(3, 3), thickness= -1) cv2.circle(image, center=(150, 150), radius=10, color=(4, 4), thickness= -1) cv2.circle(image, center=(75, 200), radius=10, color=(5, 5), thickness= -1) cv2.circle(image, center=(200, 89), radius=10, color=(6, 6), thickness= -1) cv2.circle(image, center=(21, 230), radius=5, color=(7, 7), thickness= -1) cv2.circle(image, center=(149, 250), radius=5, color=(12, 12), thickness= -1) plt.imshow(image) plt.show()
输出2

请问如何确保相同像素值无论图像中存在其他何种像素,都能显示同一颜色?
解决方案
问题出在matplotlib.pyplot.imshow()的默认行为上:它会自动将当前图像的像素值归一化到0-1区间——把图像里的最小像素值对应到颜色映射的起点,最大值对应到终点。当图像的像素值范围变化时,这个归一化的基准也跟着变,导致相同像素值的显示颜色发生偏移。
下面是两种可靠的解决方法:
方法1:固定颜色映射的数值范围
调用imshow()时,通过vmin和vmax参数指定全局的像素值范围,比如你所有图像中可能出现的最小像素是0,最大是12,就固定这个范围:
plt.imshow(image, vmin=0, vmax=12) plt.show()
这样无论单张图像里有没有包含0或12,所有像素都会按照0-12的区间映射颜色,相同像素值的显示效果完全一致。
方法2:使用离散颜色映射(适合分类场景)
如果你的像素值是类别标签(比如0代表背景,2-12是不同的目标类别),推荐用离散颜色映射,直接给每个像素值绑定固定颜色:
import matplotlib.colors as mcolors # 列出所有可能出现的像素值 all_pixels = [0, 2, 3, 4, 5, 6, 7, 12] # 给每个像素值分配对应颜色(可根据需求自定义) color_list = ['#000000', '#0000FF', '#008000', '#FF0000', '#00FFFF', '#FF00FF', '#FFFF00', '#FFA500'] # 创建离散颜色映射 custom_cmap = mcolors.ListedColormap(color_list) # 创建边界归一化,确保每个像素值对应唯一颜色区间 custom_norm = mcolors.BoundaryNorm([val - 0.5 for val in all_pixels] + [all_pixels[-1] + 0.5], custom_cmap.N) plt.imshow(image, cmap=custom_cmap, norm=custom_norm) plt.show()
这种方式彻底脱离了像素值的数值大小影响,每个指定的像素值都会显示你设定的固定颜色,完全不受其他像素的干扰。
内容的提问来源于stack exchange,提问作者Deep
相关产品推荐
相关产品推荐

