2025, Dec 20 00:01
Как избежать размытых линий в SVG из Matplotlib: тонкие штрихи вместо прозрачности
Почему SVG из Matplotlib с тысячами полупрозрачных линий выглядит «мутно», и как это исправить: вместо alpha — минимальный linewidth и запрет растрирования.
Когда вам нужна визуализация плотности публикационного качества для множества случайных линий, сохранение графика из Matplotlib в SVG должно сохранять четкие векторные штрихи. Но при тысячах полупрозрачных линий при масштабировании часто проявляются размытые, «мутные» края. Обычно виновником считают растрирование при экспорте, и переключение соответствующих флагов выглядит бесполезным. Однако причина здесь тоньше.
Воспроизведение проблемы
Следующий фрагмент рисует 1000 случайных линий с низкой прозрачностью (alpha), чтобы проявить распределение. При экспорте в SVG и масштабировании заметна потеря четкости.
import matplotlib.pyplot as plt
import numpy as np
line_total = 1000
np.random.seed(42)
x_grid = np.linspace(0, 10, 100).reshape(1, -1)
y_stack = x_grid * np.random.normal(1, 1, (line_total, 1)) + np.random.normal(0, 1, (line_total, 100))
for y_seq in y_stack:
handle = plt.plot(x_grid.flatten(), y_seq, 'k', alpha=0.01)
handle[0].set_rasterized(False)
plt.savefig('ex.svg')
plt.show()
Что происходит на самом деле
Если открыть получившийся SVG, вы увидите векторные пути, а не растровые картинки. У многих элементов линий присутствуют атрибуты стиля вроде stroke-opacity и stroke-width. Это означает, что файл действительно сохраняет векторную геометрию.
SVG — это текстовый файл, так что его можно открыть в любом текстовом редакторе и проверить, произошло ли растрирование. Можно также открыть его в графическом редакторе, который умеет работать с SVG (например, Inkscape), и посмотреть, как там представлены элементы.
Размытый вид возникает из‑за того, что у каждой линии очень малая прозрачность (например, stroke-opacity: 0.01) сочетается с относительно стандартной толщиной (например, stroke-width: 1.5). При масштабировании в просмотрщиках или последующих инструментах толщина штриха остается прежней, а прозрачность визуально суммируется, создавая мягкое, нечёткое впечатление. Некоторые инструменты к тому же растрируют при отображении, что усиливает эффект.
Решение: перейти от прозрачности к толщине линии
Чтобы сохранить четкость и при этом передать плотность, рисуйте линии полностью непрозрачными, но резко уменьшите толщину линии. Так сохраняется векторная резкость и при этом возникает видимость распределения. Полезно также явно отключить логику растрирования на уровне осей.
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.set_rasterization_zorder(None)
line_total = 1000
np.random.seed(42)
x_grid = np.linspace(0, 10, 100).reshape(1, -1)
y_stack = x_grid * np.random.normal(1, 1, (line_total, 1)) + np.random.normal(0, 1, (line_total, 100))
for y_seq in y_stack:
ax.plot(x_grid.flatten(), y_seq, color='black', linewidth=0.01)
fig.savefig('ex_width.svg', format='svg')
Такое изменение заменяет визуальное смешивание за счет alpha на визуальное «истончение» за счет linewidth. Общее впечатление плотности остается схожим, но в увеличенных областях кромки остаются чистыми, потому что просмотрщик больше не композитит тысячи полупрозрачных штрихов.
Почему это важно
SVG часто — правильный выбор для графиков, которые должны выдерживать сильное масштабирование в редакторах, презентациях или документах. Опора на прозрачность для формирования плотности может выглядеть нормально на экране, но ухудшается при увеличении и в некоторых конвейерах рендеринга. Кодирование «плотности» толщиной линии сохраняет векторную точность во всех инструментах и избегает артефактов, возникающих, когда просмотрщики или редакторы документов растрируют изображение при показе.
Практические выводы
Если в экспортированном SVG с множеством пересекающихся линий вы замечаете «муть», сначала проверьте, действительно ли файл содержит векторную геометрию: откройте его как текст или в редакторе, умеющем работать с элементами SVG. Если линии действительно векторные, значительно уменьшите толщину линии вместо снижения прозрачности. Кроме того, можно указать Matplotlib не растрировать, установив z-порядок растрирования для осей. В совокупности эти изменения сохраняют задумку визуализации и обеспечивают четкость на любом масштабе.
Для рабочих процессов, где просмотрщик или последующий инструмент документа может растрировать при отображении, сохранение векторных штрихов с минимальной толщиной дает устойчивый, публикационный результат без потери ясности.