将SVG文件导入Matplotlib图形

34

我喜欢制作高质量的图形,因此尽可能避免使用栅格化图形。

我正在尝试将一个svg文件导入到matplotlib图形中:

import matplotlib.pyplot as plt
earth   = plt.imread('./gfx/earth.svg')
fig, ax = plt.subplots()
im      = ax.imshow(earth)
plt.show()

这对于png格式的图片非常有效。有人能告诉我如何使用svg或至少指出正确的文档吗?

我知道类似的问题已经被问过(但没有得到答案):这里。自那以后有什么变化吗?

P.S. 我知道我可以导出高分辨率的png并实现类似的效果。这不是我正在寻找的解决方案。

这是我想要导入的图像:

Earth_from_above


一个快速的侧记:如果你对在地图上绘制数据感兴趣,那么有一个专门做这个的 cartopy 包 - jadsq
4个回答

34

也许你正在寻找的是svgutils

import svgutils.compose as sc
from IPython.display import SVG # /!\ note the 'SVG' function also in svgutils.compose
import numpy as np

# drawing a random figure on top of your SVG
fig, ax = plt.subplots(1, figsize=(4,4))
ax.plot(np.sin(np.linspace(0,2.*np.pi)), np.cos(np.linspace(0,2.*np.pi)), 'k--', lw=2.)
ax.plot(np.random.randn(20)*.3, np.random.randn(20)*.3, 'ro', label='random sampling')
ax.legend()
ax2 = plt.axes([.2, .2, .2, .2])
ax2.bar([0,1], [70,30])
plt.xticks([0.5,1.5], ['water  ', ' ground'])
plt.yticks([0,50])
plt.title('ratio (%)')
fig.savefig('cover.svg', transparent=True)
# here starts the assembling using svgutils 
sc.Figure("8cm", "8cm", 
    sc.Panel(sc.SVG("./Worldmap_northern.svg").scale(0.405).move(36,29)),
    sc.Panel(sc.SVG("cover.svg"))
    ).save("compose.svg")
SVG('compose.svg')
输出:

输入图像描述


很不幸,我在Jupyter笔记本中也正在做同样的事情,但是我无法得到这个输出。有人知道问题可能出在哪里吗? 我看不到背景。 我的Jupyter笔记本中最终的结果只包含了图表。保存的SVG文件也损坏了。 - Ali AlCapone

8

对于任何在2021年到这里的人...

我建议查看cairosvg
(conda install -c conda-forge cairosvgpip3 install cairosvg)

https://cairosvg.org/

import cairosvg
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO

img_png = cairosvg.svg2png("... the content of the svg file ...")
img = Image.open(BytesIO(img_png))
plt.imshow(img)

我无法导入cairosvg。错误信息: OSError:找不到名为“cairo-2”的库 找不到名为“cairo”的库 找不到名为“libcairo-2”的库 无法加载库'libcairo.so.2':错误代码0x7e 无法加载库'libcairo.2.dylib':错误代码0x7e 无法加载库'libcairo-2.dll':错误代码0x7e - Martin Gardfjell
我安装了这个,但在jupyter notebook中仍然无法使用,出现以下错误:找不到名为"cairo"的库 找不到名为"libcairo-2"的库 无法加载库'libcairo.so.2': 错误0x7e 无法加载库'libcairo.2.dylib': 错误0x7e 无法加载库'libcairo-2.dll': 错误0x7e``` - Ali AlCapone

2

Yann Zerlaut的答案有优点和缺点:

  • 优点:它适用于任何复杂度的SVG图像,因此非常通用。
  • 缺点:SVG图形不是由matplotlib后端处理的,而是将matplotlib图像与SVG图像叠加。

这会导致产生一些伪影。例如,SVG图像中的线宽与matplotlib中的线宽不同。在研究这个问题时,我发现了一些关于为什么matplotlib不支持SVG的答案。

寻找更好的解决方案,我发现了一些值得列出来的解决方案,它们也有优点和缺点。

将SVG路径转换为matplotlib路径的简单解析器

https://matplotlib.org/stable/gallery/showcase/firefox.html#sphx-glr-gallery-showcase-firefox-py

  • 优点:使用matplotlib艺术家绘制SVG路径
  • 缺点:除非您扩展解析器,否则仅适用于简单的SVG
Skunk

https://github.com/whitead/skunk

  • 优点:基本上是Yann Zerlaut的解决方案加强版并且在库中。您可以将SVG绘制到由matplotlib处理的框中,这允许进行高级布局。
  • 缺点:matplotlib不能原生地处理SVG图像,这只是一种巧妙的方法,将一个矢量图形拼接到另一个矢量图形中。

结论

根据这里的讨论,matplotlib没有原生支持导入SVG,因为编写一个完全符合规范的解析器很难,而且matplotlib开发人员可能不想引入外部解析库。

然而,编写一个适用于路径的基本解析器实际上非常容易,正如在matplotlib演示中所看到的那样。对于像OP这样的简单用例,这可能已经足够了。

对于复杂情况,skunk可以很好地自动组合SVG图像。


-15

SVG(可缩放矢量图形)是一种矢量格式,这意味着图像不是由像素组成的,而是由可以任意缩放的相对路径组成的。

作为数值软件,NumPy/Matplotlib只能处理像素图形,无法处理svg。我建议首先将svg文件转换为例如png文件,通过在诸如Inkscape(免费软件)中打开并保存它来实现。然后,在Python中打开导出的png文件。

或者,使用维基媒体提供的png格式文件版本,位于图片信息页面(单击图片右侧的下载按钮)

如果您真的认为需要矢量形式,那么没有办法。您始终可以手动将matplotlib图形叠加到图形上(使用matplotlib Artist在绘图画布上绘制),或通过一些pycairo魔术来完成,并保存该图形。但Matplotlib无法直接处理svg内容。


3
谢谢您的回复。然而,从我的问题中,您可以看出我对这两种格式的特性有所了解。我还明确表示png格式一切正常。此外,matplotlib可处理路径。每次将图像保存为eps或svg格式时,都是在保存矢量图形。大多数图形在保存前是矢量图形,可以任意缩放。 - Sasha
Matplotlib 可能在内部与路径一起工作,就像 svg 一样,但是两个使用路径的东西并不意味着这两个东西可以一起工作。请注意,我还建议分别生成 matplotlib 图形,并在第二个手动或脚本步骤中合并它们。除非您深入了解 Matplotlib 艺术家并准备手动解析 svg 文件,否则我强烈怀疑您能否让 Matplotlib 将文件用作背景。 - rubenvb
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Sasha
3
@Sasha,完成了。请不要过早地进行负评以避免这种情况发生。 - rubenvb
我很困惑,因为我可以从Matplotlib导出svg文件,但我无法读取。 - Alexandre Strube

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接