基于Matplotlib中像素值设置透明度

35

我正在尝试使用Matplotlib为我正在工作的论文绘制一些图表。我有两组数据,都是2D的numpy数组:一组ascii的山形阴影光栅图,我可以愉快地绘制并使用以下命令进行调整:

import matplotlib.pyplot as pp
import numpy as np

hillshade = np.genfromtxt('hs.asc', delimiter=' ', skip_header=6)[:,:-1]

pp.imshow(hillshade, vmin=0, vmax=255)
pp.gray()
pp.show()

给出的结果为:

山体阴影图

还有第二张ASCII栅格图,用于描述穿过地形的河流特征。可以以与上面相同的方式绘制此数据,但是数组中不对应河网的值被分配为-9999的无数据值。目标是将无数据值设置为透明,以便河流值覆盖山体阴影图。

这是河流数据,这里表示为0的理想情况下每个像素都完全透明。

河流数据

经过一些研究,我发现可以将我的数据转换为RGBA数组,并将α值设置为只使不需要的单元格透明。但是,河流数组中的值是浮点数,不能转换(因为原始值是该图的重点),如果使用RGBA格式,则我认为imshow函数只能使用无符号整数。

是否有任何方法可以解决这个限制?我希望可以简单地创建一个带有像素值和α值的元组,并以这种方式绘制它们,但似乎不可能。

我也尝试使用PIL创建具有无数据值透明的河流数据的PNG文件,但这似乎会自动将像素值缩放为0-255,从而丢失我需要保留的值。

我欢迎任何人对此问题的见解。

4个回答

58

只需对您的 "river" 数组进行掩码处理

例如:

rivers = np.ma.masked_where(rivers == 0, rivers)

这里以快速示例展示如何以此方式叠加两个图:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

# Generate some data...
gray_data = np.arange(10000).reshape(100, 100)

masked_data = np.random.random((100,100))
masked_data = np.ma.masked_where(masked_data < 0.9, masked_data)

# Overlay the two images
fig, ax = plt.subplots()
ax.imshow(gray_data, cmap=cm.gray)
ax.imshow(masked_data, cmap=cm.jet, interpolation='none')
plt.show()

enter image description here

另外,顺便说一句,imshow可以接受浮点数作为其RGBA格式的输入。它只需要在0和1之间的范围内。


1
太棒了!谢谢。我一直希望有这样一个优雅的解决方案,但显然我走错了路。 - sgrieve
2
@MyCarta - 它可以与任意数量的数组一起使用,但您的掩码表达式可能会变得复杂。要将布尔表达式与numpy数组组合,您需要使用&而不是and|而不是or~而不是not等。例如:(a>5) & (b>5)而不是a>5 and b>5。您也无法以相同的方式链接运算符,例如,您需要(x>2) & (x<5)而不是2>x>5 - Joe Kington

20

不使用掩码数组的替代方法是设置颜色映射器如何处理低于 clim 最小值的裁剪值(无耻地使用Joe Kington的示例):

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

# Generate some data...
gray_data = np.arange(10000).reshape(100, 100)

masked_data = np.random.random((100,100))

my_cmap = cm.jet
my_cmap.set_under('k', alpha=0)


# Overlay the two images
fig, ax = plt.subplots()
ax.imshow(gray_data, cmap=cm.gray)
im = ax.imshow(masked_data, cmap=my_cmap, 
          interpolation='none', 
          clim=[0.9, 1])
plt.show()

example

还有一个set_over用于裁剪顶部,以及一个set_bad用于设置颜色映射如何处理数据中的“坏值”。

这种方法的优点是,您可以通过调整im.set_clim([bot, top])来更改阈值。


太好了!这是一个更适合大数组的解决方案。我在@Joe Kington的解决方案中遇到了内存问题。 - PPR

3
另一种选择是将所有应保持透明的单元格设置为np.nan(不确定哪种更有效,我猜基于climtacaswell's answer将是最快的)。例如,可以根据Joe Kington's answer进行调整:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

# Generate some data...
gray_data = np.arange(10000).reshape(100, 100)

masked_data = np.random.random((100,100))
masked_data[np.where(masked_data < 0.9)] = np.nan

# Overlay the two images
fig, ax = plt.subplots()
ax.imshow(gray_data, cmap=cm.gray)
ax.imshow(masked_data, cmap=cm.jet, interpolation='none')
plt.show()

enter image description here

注意,对于dtype=bool类型的数组,您不应该遵循IDE建议的方式,即为了PEP 8(E712)而比较masked_data is True,而应坚持使用masked_data == True进行逐元素比较,否则掩码将失败: 在此输入图片描述

0
另一种策略是导出河流分支网络。这通常是水文流量路由工具中的一个选项。我不确定您在这里使用了哪个工具,但下面是一个示例,使用Python的pysheds库,它需要一个栅格类型的文件,所以使用tiff文件而不是2D numpy数组:
from pysheds.grid import Grid
import matplotlib.pyplot as plt
import numpy as np

# Instantiate grid from raster
grid = Grid.from_raster('test1.tif')
dem = grid.read_raster('test1.tif')

# Fill pits
pit_filled_dem = grid.fill_pits(dem)

# Fill depressions
flooded_dem = grid.fill_depressions(pit_filled_dem)

# Resolve flats and compute flow directions
inflated_dem = grid.resolve_flats(flooded_dem)
fdir = grid.flowdir(inflated_dem)

# Compute accumulation
acc = grid.accumulation(fdir)

# Extract river branch network for accumulations > 1000 units
branches = grid.extract_river_network(fdir, acc > 1000)

# Create fig and axes objects of matplotlib figure
fig, ax = plt.subplots()

# Set limits and aspect and tick formatting
plt.xlim(grid.bbox[0], grid.bbox[2])
plt.ylim(grid.bbox[1], grid.bbox[3])
ax.set_aspect('equal')
ax.ticklabel_format(style='sci', scilimits=(0,0), axis='both')

# Set the axes color to gray to demonstrate non-river pixels 
ax.set_facecolor('lightgray')

# Plot the river branch network using for loop
for branch in branches['features']:
    line = np.asarray(branch['geometry']['coordinates'])
    plt.plot(line[:, 0], line[:, 1], color='k')

River network visualization


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接