Можно ли выполнять аддитивное смешивание с помощью matplotlib?

При работе с перекрывающимися диаграммами разброса с высокой плотностью или линейными графиками разных цветов может быть удобно реализовать схемы аддитивного смешивания, в которых цвета RGB каждого маркера складываются вместе, чтобы получить окончательный цвет на холсте. Это обычная операция в движках 2D и 3D рендеринга.

Однако в Matplotlib я нашел поддержку только смешивания альфа / непрозрачности. Есть ли какой-нибудь обходной способ сделать это, или я застрял на рендеринге в растровое изображение, а затем смешиваю их в какой-нибудь программе рисования?

Изменить: вот пример кода и ручное решение.

Это приведет к двум частично перекрывающимся случайным распределениям:

x1 = randn(1000)
y1 = randn(1000)
x2 = randn(1000) * 5
y2 = randn(1000)
scatter(x1,y1,c='b',edgecolors='none')
scatter(x2,y2,c='r',edgecolors='none')

Это создаст в matplotlib следующее: scatter - no blend

Как видите, есть несколько перекрывающихся синих точек, которые перекрываются красными точками, и мы хотели бы их увидеть. Используя смешивание альфа / непрозрачности в matplotlib, вы можете:

scatter(x1,y1,c='b',edgecolors='none',alpha=0.5)
scatter(x2,y2,c='r',edgecolors='none',alpha=0.5)

Что даст следующее:

scatter - alpha blend (0.5)

Но на самом деле мне нужно следующее:

scatter - аддитивная смесь

Я могу сделать это вручную, визуализируя каждый график независимо в растровое изображение:

xlim = plt.xlim()
ylim = plt.ylim()
scatter(x1,y1,c='b',edgecolors='none')
plt.xlim(xlim)
plt.ylim(ylim)
scatter(x2,y2,c='r',edgecolors='none')
plt.xlim(xlim)
plt.ylim(ylim)
plt.savefig(r'scatter_blue.png',transparent=True)
plt.savefig(r'scatter_red.png',transparent=True)

Это дает мне следующие изображения:

scatter - красный / синий каналы

Что вы можете сделать, так это загрузить их как независимые слои в Paint.NET/PhotoShop/gimp и просто смешать их аддитивно.

Идеально было бы сделать это программно в Matplotlib, так как я буду обрабатывать сотни таких!


person glopes    schedule 02.11.2014    source источник
comment
Самый простой способ - построить двухмерную гистограмму. Пожалуйста, покажите нам пример кода и данных, чтобы мы начали.   -  person Bas Swinckels    schedule 02.11.2014
comment
Спасибо, только что добавили пример кода и шаги для ручного решения.   -  person glopes    schedule 02.11.2014
comment
Спасибо, вопрос лучше, посмотрю, что я могу сделать.   -  person Bas Swinckels    schedule 02.11.2014
comment
Я не думаю, что это режим, который мы поддерживаем «из коробки».   -  person tacaswell    schedule 03.11.2014
comment
Я также не понимаю вашего комментария о «фиксированном цветном фоне», он должен смешиваться с тем, что в настоящее время находится на холсте.   -  person tacaswell    schedule 03.11.2014
comment
Вы правы, я просто проверял, и он смешивается с текущим холстом. Я удалил этот бит из формулировки вопроса, думаю, этого эффекта с альфа-смешиванием просто не добиться. Жаль, что для него нет поддержки, было бы неплохо иметь более точный контроль над параметрами наложения.   -  person glopes    schedule 03.11.2014
comment
Моя реакция коленного рефлекса состоит в том, что это сложнее, чем мы хотели бы понять (даже если AGG поддерживает это из коробки), учитывая трудности, которые у нас возникают с альфа-смешиванием. Если вы действительно этого хотите, я бы посоветовал сделать для этого проблему на github. Не думаю, что это произойдет в ближайшее время, но, по крайней мере, это будет в поле зрения. Если вы создаете проблему, пожалуйста, включите в нее достаточно подробностей, чтобы она стояла отдельно, без ссылки на SO и объясняла, почему эта сложность / работа стоит того.   -  person tacaswell    schedule 03.11.2014


Ответы (2)


Если в результате вам нужно только изображение, вы можете получить буфер холста в виде массива numpy, а затем выполнить смешивание, вот пример:

from matplotlib import pyplot as plt
import numpy as np

fig, ax = plt.subplots()
ax.scatter(x1,y1,c='b',edgecolors='none')
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
ax.patch.set_facecolor("none")
ax.patch.set_edgecolor("none")
fig.canvas.draw()

w, h = fig.canvas.get_width_height()
img = np.frombuffer(fig.canvas.buffer_rgba(), np.uint8).reshape(h, w, -1).copy()

ax.clear()
ax.scatter(x2,y2,c='r',edgecolors='none')
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
ax.patch.set_facecolor("none")
ax.patch.set_edgecolor("none")
fig.canvas.draw()

img2 = np.frombuffer(fig.canvas.buffer_rgba(), np.uint8).reshape(h, w, -1).copy()

img[img[:, :, -1] == 0] = 0
img2[img2[:, :, -1] == 0] = 0

fig.clf()

plt.imshow(np.maximum(img, img2))
plt.subplots_adjust(0, 0, 1, 1)
plt.axis("off")
plt.show()

результат:

введите описание изображения здесь

person HYRY    schedule 03.11.2014
comment
На самом деле я просто проверял функции buffer_rgba (), когда пришел ваш ответ. Да, я думаю, что это лучшее, что на данный момент можно сделать с помощью Matplotlib. По какой-то причине в моей версии Matplotlib конечный результат не такой красивый (нарушена прозрачность), но я уверен, что после небольшой настройки я заставлю его работать сейчас. Спасибо! - person glopes; 03.11.2014
comment
Значит, в настоящее время нет решения для получения векторизованной графики? :( - person PlasmaBinturong; 28.01.2020

Эта функция теперь поддерживается моей серверной частью matplotlib https://github.com/anntzer/mplcairo (только мастер ):

import matplotlib; matplotlib.use("module://mplcairo.qt")
from matplotlib import pyplot as plt
from mplcairo import operator_t
import numpy as np

x1 = np.random.randn(1000)
y1 = np.random.randn(1000)
x2 = np.random.randn(1000) * 5
y2 = np.random.randn(1000)
fig, ax = plt.subplots()
# The figure and axes background must be made transparent.
fig.patch.set(alpha=0)
ax.patch.set(alpha=0)
pc1 = ax.scatter(x1, y1, c='b', edgecolors='none')
pc2 = ax.scatter(x2, y2, c='r', edgecolors='none')
operator_t.ADD.patch_artist(pc2)  # Use additive blending.
plt.show()

введите описание изображения здесь

person antony    schedule 25.03.2019
comment
Это твоя библиотека? Если да, то в своем ответе укажите, что вы являетесь автором. - person DavidG; 25.03.2019