通过将不透明度设置为数据梯度,在 Matplotlib imshow 中创建地图着色问题

Issue creating map shading in Matplotlib imshow by setting opacity to data gradient

我正在尝试通过计算数据的梯度并使用它来设置 alpha 值来为某些数据的地图添加阴影。

我首先加载我的数据(不幸的是,我无法共享这些数据,因为它正被用于许多正在准备的手稿中。编辑 - 2020 年 12 月:已发表的论文可以开放获取在勘探地球物理学家协会 library, and the data is available with an accompanying Jupyter Notebook):

import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
from pylab import imread, imshow, gray, mean
import matplotlib.colors as cl
%matplotlib inline 

data = np.loadtxt('data.txt')
plt.imshow(data, cmap='cubehelix')
plt.show()

给我一张数据图:

然后我计算总水平梯度并将其归一化以用于着色:

dx,dy = np.gradient(data, 1, 1)
tdx=np.sqrt(dx*dx + dy*dy)
tdx_n=(tdx-tdx.min())/(tdx.max()-tdx.min())
tdx_n=1-tdx_n

这看起来符合我的预期:

plt.imshow(tdx_n[4:-3,4:-3],  cmap='bone')
plt.show()

要创建阴影效果,我想我会从数据图中获取颜色,然后用渐变替换不透明度,以便具有与渐变成比例的深色阴影,如下所示:

img_array = plt.get_cmap('cubehelix')(data[4:-3,4:-3])
img_array[..., 3] = (tdx_n[4:-3,4:-3]) 
plt.imshow(img_array)
plt.show()

但是结果不是我所期望的:

这就是我要找的(在 Matlab 中创建,colormap 不同):

关于如何修改我的代码有什么建议吗?

更新

使用 运行 Novitsky 的方法,使用 titusjan 的答案中建议的代码,我得到了这个结果:

这给出了我正在寻找的效果。在阴影方面,虽然我喜欢 titusjan 自己关于使用 HSV 的建议,它给出了这个结果: .

但是,我无法将颜色图设置为 cubehelix,即使我要求它:

from matplotlib.colors import rgb_to_hsv, hsv_to_rgb
hsv = rgb_to_hsv(img_array[:, :, :3])
hsv[:, :, 2] = tdx_n
rgb = hsv_to_rgb(hsv)
plt.imshow(rgb[4:-3,4:-3], cmap='cubehelix')
plt.show()

首先,Matplotlib 包含一个 hill shading 实现。这通过将梯度与特定角度的光源进行比较来计算强度。所以这并不完全是你正在实施的,但接近甚至可能会产生更好的结果。

运行 Novitsky another hill shading implementation 在颜色和强度值合并的方式上与 Matplotlib 不同。我不知道哪个更好,但值得一看。

也许结合颜色和强度的最佳方法是使用 gouraud 着色,它用于 3D 计算机图形。我自己的方法是将强度放在图像的 HSV 颜色的 value 层中。

我认为我不同意您将强度(tdx_n 在您的情况下)放置在图像的 alpha 层中的方法。这意味着在梯度较低的地方,图像将是透明的,您会看到之前绘制的数据。我认为这就是您的屏幕截图中发生的情况。

此外,我认为您需要在通过 cmap 传递数据之前对其进行标准化,就像对强度进行标准化一样:

data_n=(data-data.min())/(data.max()-data.min())
img_array = plt.get_cmap('cubehelix')(data_n)

然后我们可以使用 运行 Novitsky 的方法将颜色与强度合并:

rgb = img_array[:, :, :3]

# form an rgb eqvivalent of intensity
d = tdx_n.repeat(3).reshape(rgb.shape)

# simulate illumination based on pegtop algorithm.
rgb = 2 * d * rgb + (rgb ** 2) * (1 - 2 * d)

plt.imshow(rgb[4:-3,4:-3])
plt.show()

或者你可以按照我过去的做法,将强度放在HSV三元组的层中。

from matplotlib.colors import rgb_to_hsv, hsv_to_rgb

hsv = rgb_to_hsv(img_array[:, :, :3])
hsv[:, :, 2] = tdx_n
rgb = hsv_to_rgb(hsv)
plt.imshow(rgb[4:-3,4:-3])
plt.show()

编辑 2015-05-23:

你的问题促使我完成了一年前开始的山体阴影实施。我已经把它放在 Github here 上了。

它使用了类似于高氏着色的混合机制,用于 3D 计算机图形。它在下面标记为 RGB 混合。我认为这是最好的混合算法,当颜色接近黑色时,HSV 混合会给出错误的结果(注意 HSV 图像中心的蓝色,它不存在于未着色的数据中)。

RGB 混合也是最简单的算法,它只是将强度与 RGB 三元组相乘(它添加了一个额外的长度为 1 的维度以允许在乘法中进行广播)。

rgb = img_array[:, :, :3]
tdx_n_exp = np.expand_dims(tdx_n, axis=2) 
result = tdx_n_exp * rgb
plt.imshow(result[4:-3,4:-3])