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-порядок растрирования для осей. В совокупности эти изменения сохраняют задумку визуализации и обеспечивают четкость на любом масштабе.

Для рабочих процессов, где просмотрщик или последующий инструмент документа может растрировать при отображении, сохранение векторных штрихов с минимальной толщиной дает устойчивый, публикационный результат без потери ясности.