Matplotlib绘图保存为.ps/.eps格式时失去透明度

54

我在尝试保存一些带有透明椭圆的绘图时遇到了问题,如果我尝试使用 .ps/.eps 扩展名保存它们,就会出现问题。

这是以 .png 格式保存的绘图: png

如果我选择将其保存为 .ps/.eps,它会变成这样: ps

我的解决方法是使用 ImageMagick 将原始 png 转换为 ps。唯一的问题是,png 格式的图像大小约为 90k,在转换后大小只有不到 4M 。这并不好,因为我有很多这样的图像,而且编译我的 LaTeX 文档需要太长时间。有没有人有解决这个问题的办法?


你最终解决了这个问题,而没有使用任何模仿吗? - Srivatsan
8个回答

36
问题在于eps不支持本地透明度。有几个选择:
  1. 将图像光栅化并嵌入eps文件(如@Molly所建议的)或导出为pdf并使用某些外部工具进行转换(如gs)(通常也依赖于光栅化)。

  2. “模拟”透明度,给出在给定背景下看起来像透明的颜色。

我曾在matplotlib邮件列表中肯定讨论过这个问题,并得到了光栅化的建议,但这不可行,因为你要么得到像素化的图像,要么得到巨大的图像。当放入例如出版物时,它们的缩放效果也不太好。
我个人使用第二种方法,虽然不是最理想的,但我认为足够好了。我编写了一个小的python脚本,实现了这篇SO帖子中的算法,以获得具有给定透明度的颜色的坚实RGB表示。 编辑 在您的情况下,尝试使用zorder关键字对绘制的部分进行排序。尝试使用zorder=10来绘制蓝色椭圆形、zorder=11来绘制绿色椭圆形和zorder=12来绘制hexbins。
这样,蓝色应该在所有内容下面,然后是绿色椭圆形,最后是hexbins。并且即使使用纯色也可以阅读图表。如果您喜欢png中的蓝色和绿色阴影,可以尝试使用mimic_alpha.py进行调整。 编辑2

如果你百分之百确定必须使用 eps,那么我想到了几个解决方法(比你的绘图要丑陋):

  1. 只需在六边形的上方绘制椭圆形边框。
  2. 获取每个六边形的中心和振幅(可能会丢弃所有零箱),并使用与 hexbin 中相同的颜色映射制作 散点图 ,并根据需要调整标记大小和形状。您可能需要在此基础上重新绘制椭圆形边框。

谢谢,我试过你的mimic_alpha.py代码了,但是你没有办法透过它看到我生成的散点图。颜色搭配得很好,但仍然不透明。 - astromax
是的,它模仿了颜色,但返回标准的RGB颜色。无论如何,如果您必须使用eps,那么没有太多可以做的事情。我几次使用了mimic_alpha(我认为主要是与fill_between一起),通过在阴影区域之上使用标准线条来增强限制。 - Francesco Montesano
@astromax:我已经编辑了答案,添加了可能解决你问题的方案。 - Francesco Montesano
我也试过了 - 问题是,尽管hexbin有许多零箱,但hexbin图跨越整个范围。在这种情况下,zorder大于椭圆的hexbin完全阻挡了它们。 - astromax
@astromax 我明白了。这真的很烦人。我会编辑答案并提供一个可能的解决方法。 - Francesco Montesano

30

另一种选择是将它们保存为PDF文件。

savefig('myfigure.pdf')

如果使用pdflatex,那就可以工作了,如果这就是你需要使用eps而不是svg的原因。


1
确实,这是正确的答案。我不知道有哪些2020年的出版物不接受PDF格式的图表。 - Luke Davis

14

在保存前,您可以对图形进行光栅化处理,以保留eps文件中的透明度:

ax.set_rasterized(True)
plt.savefig('rasterized_fig.eps')

1
非常有用,唯一的问题是它太大了,有33M。我有很多这样的文件,它们不能那么大。 - astromax
2
另外,它似乎将绘图的背景涂成了黑色,因此覆盖了x和y轴标签。你有什么想法为什么会这样? - astromax

11

我遇到了同样的问题。为了避免光栅化,您可以将图像保存为pdf格式,然后在终端上(至少在类Unix系统中)运行以下命令:

pdftops -eps my.pdf my.eps

这将生成一个.eps文件作为输出。


3
我通过以下方法解决了这个问题: 1) 在定义图形区域时添加 set_rasterization_zorder(1) :
fxsize=16
fysize=8
f = figure(num=None, figsize=(fxsize, fysize), dpi=180, facecolor='w',
edgecolor='k')
plt.subplots_adjust(
left    = (18/25.4)/fxsize, 
bottom  = (13/25.4)/fysize, 
right   = 1 - (8/25.4)/fxsize, 
top     = 1 - (8/25.4)/fysize)
subplots_adjust(hspace=0,wspace=0.1)
#f.suptitle('An overall title', size=20)
gs0 = gridspec.GridSpec(1, 2)

gs11 = gridspec.GridSpecFromSubplotSpec(1, 1, subplot_spec=gs0[0])

ax110 = plt.Subplot(f, gs11[0,0])
f.add_subplot(ax110)

ax110.set_rasterization_zorder(1)

2) 在绘图中的每个alpha=任意数字的zorder=0:

ax110.scatter(xs1,ys1  , marker='o', color='gray'  , s=1.5,zorder=0,alpha=0.3)#, label=label_bg)

最后,在保存时设置rasterized=True:

P.savefig(str(PLOTFILENAME)+'.eps', rasterized=True)

请注意,使用transparent关键字对savefig进行操作时,可能无法按预期工作,因为在透明背景上具有alpha<1的RGBA颜色将与具有alpha=1的RGB颜色呈现相同。


2

如上所述,如果您不想失去分辨率,最好和最简单的选择是将图像栅格化。

f = plt.figure()
f.set_rasterized(True)

ax = f.add_subplot(111)

ax.set_rasterized(True)
f.savefig('figure_name.eps',rasterized=True,dpi=300)

通过dpi选项,您可以管理尺寸。实际上,您还可以在应用光栅化之前调整所需的zorder:

ax.set_rasterization_zorder(0)

注意:在使用plt.subplot和plt.subplot2grid函数时,保持f.set_rasterized(True)是很重要的。否则,在.eps文件中,标签和刻度区域将不会出现。

1
我的解决方案是将图形导出为.eps格式,例如加载到Inkscape中,然后取消组合图形,选择我想要设置透明度的对象,仅在“填充和描边”选项卡中编辑填充的不透明度即可。
如果您想稍后调整它,可以将文件保存为.svg格式,或者将图像导出为出版物。

1
如果您正在使用latex编写学术论文,我建议您导出.pdf 文件而不是.eps文件。.pdf格式完美支持透明度,并具有良好的压缩效率,最重要的是可以在Adobe Illustrator中轻松编辑。
如果您想进一步编辑图形(不是编辑数据!我的意思是为了好看),可以在Adobe Acrobat中打开导出的图形 - 编辑 - 将元素复制到Adobe Illustrator中。这两个软件都可以完美处理所有内容。
我很高兴使用这种方法。一切清晰、可编辑且文件体积小。希望可以帮到您。

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