Настройка прозрачности на основе значений пикселей в Matplotlib

Я пытаюсь использовать matplotlib для построения некоторых цифр для бумаги, над которой я работаю. У меня есть два набора данных в 2D массивах numpy: растровый растр ascii hillshade, который я могу с удовольствием построить и настроить с помощью:

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()

Что дает:

Hillshade

И второй растр ascii, который определяет свойства реки, текущей по ландшафту. Эти данные могут быть построены таким же образом, как указано выше, однако значения в массиве, которые не соответствуют речной сети, не имеют значения данных -9999. Цель состоит в том, чтобы значения данных не были прозрачными, поэтому значения рек лежат на холме.

Это речные данные, в идеале каждый пиксель, представленный здесь как 0, будет полностью прозрачным.

River data

Проведя некоторые исследования по этому вопросу, похоже, я смогу преобразовать свои данные в массив RGBA и установить альфа-значения только для того, чтобы сделать нежелательные ячейки прозрачными. Однако значения в массиве рек являются поплавками и не могут быть преобразованы (поскольку исходные значения являются целыми точками фигуры), и я полагаю, что функция imshow может принимать только целые числа без знака, если используется формат RGBA.

Есть ли способ ограничить это ограничение? Я надеялся, что могу просто создать кортеж с пиксельным значением и альфа-значением и построить их так, но это не представляется возможным.

У меня также была игра с PIL, чтобы попытаться создать PNG файл речных данных без прозрачного значения данных, однако это, похоже, автоматически масштабирует значения пикселей до 0-255, тем самым теряя значения я необходимо сохранить.

Я был бы рад любому прозрению, которое кто-либо может решить по этой проблеме.

Ответы

Ответ 1

Просто замаскируйте свой "речной" массив.

например

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 радостью примет float для своего формата RGBA. Он просто ожидает, что все будет в диапазоне от 0 до 1.

Ответ 2

Альтернативный способ сделать это с использованием маскированных массивов - установить, как цветовая карта имеет значения отсечения ниже минимума clim (бесстыдно с использованием примера Джо Кингтона):

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 для отсечения сверху и a set_bad для настройки того, как цветовая карта обрабатывает "плохие" значения в данных.

Преимущество такого способа заключается в том, что вы можете изменить свой порог, просто изменив clim на im.set_clim([bot, top])