Matplotlib:使用特殊值绘制图像¶
日期 | 2006-01-22(最后修改),2006-01-22(创建) |
---|
图像绘制需要数据、颜色映射和归一化。一个常见的需求是使用指定颜色显示缺失数据或其他值。以下代码展示了如何实现这一点。
代码创建了一个新的 Colormap 子类和一个 norm 子类。
初始化接受一个值、颜色对的字典。数据被假定为已归一化(除了保留的哨兵值)。哨兵值处的 RGB 值被替换为指定的颜色。
该类以标准方式对数据进行归一化,但有一个细微差别。接受一个“ignore”参数。忽略的值需要从归一化中排除,这样它们就不会扭曲结果。
我使用了一个不太好的算法,明确地对数据进行排序,并使用第一个非哨兵值来定义最小值和最大值。这可能可以改进,但对于我的目的来说,它简单且足够。然后对数据进行归一化,包括哨兵值。最后,哨兵值被替换。
在 [ ]
from matplotlib.colors import Colormap, normalize
import matplotlib.numerix as nx
from types import IntType, FloatType, ListType
class SentinelMap(Colormap):
def __init__(self, cmap, sentinels={}):
# boilerplate stuff
self.N = cmap.N
self.name = 'SentinelMap'
self.cmap = cmap
self.sentinels = sentinels
for rgb in sentinels.values():
if len(rgb)!=3:
raise ValueError('sentinel color must be RGB')
def __call__(self, scaledImageData, alpha=1):
# assumes the data is already normalized (ignoring sentinels)
# clip to be on the safe side
rgbaValues = self.cmap(nx.clip(scaledImageData, 0.,1.))
#replace sentinel data with sentinel colors
for sentinel,rgb in self.sentinels.items():
r,g,b = rgb
rgbaValues[:,:,0] = nx.where(scaledImageData==sentinel, r, rgbaValues[:,:,0])
rgbaValues[:,:,1] = nx.where(scaledImageData==sentinel, g, rgbaValues[:,:,1])
rgbaValues[:,:,2] = nx.where(scaledImageData==sentinel, b, rgbaValues[:,:,2])
rgbaValues[:,:,3] = nx.where(scaledImageData==sentinel, alpha, rgbaValues[:,:,3])
return rgbaValues
class SentinelNorm(normalize):
"""
Leave the sentinel unchanged
"""
def __init__(self, ignore=[], vmin=None, vmax=None):
self.vmin=vmin
self.vmax=vmax
if type(ignore) in [IntType, FloatType]:
self.ignore = [ignore]
else:
self.ignore = list(ignore)
def __call__(self, value):
vmin = self.vmin
vmax = self.vmax
if type(value) in [IntType, FloatType]:
vtype = 'scalar'
val = array([value])
else:
vtype = 'array'
val = nx.asarray(value)
# if both vmin is None and vmax is None, we'll automatically
# norm the data to vmin/vmax of the actual data, so the
# clipping step won't be needed.
if vmin is None and vmax is None:
needs_clipping = False
else:
needs_clipping = True
if vmin is None or vmax is None:
rval = nx.ravel(val)
#do this if sentinels (values to ignore in data)
if self.ignore:
sortValues=nx.sort(rval)
if vmin is None:
# find the lowest non-sentinel value
for thisVal in sortValues:
if thisVal not in self.ignore:
vmin=thisVal #vmin is the lowest non-sentinel value
break
else:
vmin=0.
if vmax is None:
for thisVal in sortValues[::-1]:
if thisVal not in self.ignore:
vmax=thisVal #vmax is the greatest non-sentinel value
break
else:
vmax=0.
else:
if vmin is None: vmin = min(rval)
if vmax is None: vmax = max(rval)
if vmin > vmax:
raise ValueError("minvalue must be less than or equal to maxvalue")
elif vmin==vmax:
return 0.*value
else:
if needs_clipping:
val = nx.clip(val,vmin, vmax)
result = (1.0/(vmax-vmin))*(val-vmin)
# replace sentinels with original (non-normalized) values
for thisIgnore in self.ignore:
result = nx.where(val==thisIgnore,thisIgnore,result)
if vtype == 'scalar':
result = result[0]
return result
if __name__=="__main__":
import pylab
import matplotlib.colors
n=100
# create a random array
X = nx.mlab.rand(n,n)
cmBase = pylab.cm.jet
# plot it array as an image
pylab.figure(1)
pylab.imshow(X, cmap=cmBase, interpolation='nearest')
# define the sentinels
sentinel1 = -10
sentinel2 = 10
# replace some data with sentinels
X[int(.1*n):int(.2*n), int(.5*n):int(.7*n)] = sentinel1
X[int(.6*n):int(.8*n), int(.2*n):int(.3*n)] = sentinel2
# define the colormap and norm
rgb1 = (0.,0.,0.)
rgb2 = (1.,0.,0.)
cmap = SentinelMap(cmBase, sentinels={sentinel1:rgb1,sentinel2:rgb2,})
norm = SentinelNorm(ignore=[sentinel1,sentinel2])
# plot with the modified colormap and norm
pylab.figure(2)
pylab.imshow(X, cmap = cmap, norm=norm, interpolation='nearest')
pylab.show()
如果前面的代码从提示符运行,则会生成两个图像。第一个是随机数据的原始图像。第二个图像是通过将某些块设置为哨兵值,然后以特定颜色绘制哨兵值而修改的数据。下面显示了一个示例结果。
章节作者:AndrewStraw
附件