eps cmyk color matplotlib output - matplotlib

I need to output my plots in EPS with the CMYK color space. Unfortunately this particular format is requested by the journal I am submitting my work to!
This discussion was the only one I could find that has addressed the issue but it is more than 2 years old. I was hoping there might be some updates fixing the problem by now.
All my programming is in Python3 and so far I have been saving my plots in PDF which had no problem. But now that I want to plot EPS there is a problem. For example the code bellow prints the simple plot in .png and .pdf but the .eps output is totally blank!
import numpy as np
import matplotlib.pyplot as plt
X=[1,2,3]
Y=[4,5,6]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(X,Y)
fig.savefig('test.eps')
fig.savefig('test.pdf')
fig.savefig('test.png')
So I have two questions:
How can I fix the eps output?
How can I set the eps output color space to CMYK?
Thanks in advance.

I too have the same problem. One workaround I have found is to save plots as .svg and then use a program like Inkscape to convert to eps. I used to be able to save in .eps without any issues and then lost the ability after an update.
Update I was able to solve this problem for my specific setup by changing a few lines in my .matplotlibrc, so I will post the relevant lines here in the hope that it may be helpful to you as well. Note this requires that you have xpdf and ghostscript already installed.
For me the important one was
##Saving Figures
ps.usedistiller : xpdf
But I also have
path.simplify : True
savefig.format : eps
Now I am able to save directly to .eps and include them in LaTeX'ed journal articles...

Related

How to export svg in matplotlib with correct mm scale

I am trying to export a figure from matplotlib for laser cutting. The figure is plotted with millimeters as the units.
I'm tying to ensure the correct scale by getting the bounding box in inches and then setting the figure size to that value:
import matplotlib.pyplot as plt
ax = plt.subplot(111)
<snipped for brevity...plotting of lines and paths>
x_bound = map(mm_to_inch, ax.get_xbound())
y_bound = map(mm_to_inch, ax.get_ybound())
plt.gcf().set_size_inches(x_bound[1] - x_bound[0], y_bound[1] - y_bound[0])
plt.axis('off')
plt.savefig('{0}.svg'.format(self.name, format='svg'))
The exported .svg is ~2/3rds of the intended scale and I'm not familiar enough with axes and figures to know why. Additionally, there is a black border around the intended geometry. Here is some example output:
.svg output (converted to .png)
How should I remove the black border and scale the .svg correctly?
You probably want to remove the margins around the axes completely,
plt.gcf().subplots_adjust(0,0,1,1)
I might note however that the result may not be precise enough for the application. Definitely also consider creating the figure with a CAD program.
Based off of ImportanceOfBeingErnest's answer and some responses to other stackoverflow questions, the following solution works:
plt.axis('off')
plt.margins(0, 0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
x_bound = map(mm_to_inch, self._ax.get_xbound())
y_bound = map(mm_to_inch, self._ax.get_ybound())
plt.gcf().set_size_inches(x_bound[1] - x_bound[0], y_bound[1] - y_bound[0])

Changing size of MatPlotLib figure with PS backend

I am generating a figure using the PS backend:
import matplotlib
matplotlib.use('ps')
from matplotlib import pyplot as plt
plt.plot((1, 2))
I would like to save the figure:
plt.savefig('test.eps', format='eps', bbox_inches='tight', pad_inches=1.0)
Incidentally, I will make a PNG out of it later (in case it turns out to be relevant to the solution):
gs -q -dSAFER -dBATCH -dNOPAUSE -dUseTrimBox -dEPSCrop -sDEVICE=pngalpha -sOutputFile=test.png test.eps
The result is (PNG viewed in eog):
I would like to change the size of the EPS or the PNG figure, but adding a dpi kwarg to savefig does not change the output. Calling plt.gcf().set_size_inches(10, 10) does nothing as well. How do I make my EPS or PNG file have a higher resolution?
This answer is relevant in that it hints that setting dpi for the ps backend may not be the way to go.
Update
For clarification, it is necessary for me to use the PS backend. At the moment, it is the only one that correctly renders LaTeX colored strings. PGF may or may not do what I want, but I do not have time to research its quirks in depth. I was able to get a hacky solution by adding a -r... argument to gs, but would prefer to control everything from MatPlotLib.
If you create the figure with plt.figure first, you can give the figsize-kwarg. An example:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10, 1)) # figsize in inches
fig.show() # if running in ipython
# plt.show() # if running as a script with "python script_name.py"
To save with a specific dpi, it should be enough with mentioned dpi-kwarg to the figures savefig-method, i.e.
fig.savefig(..., dpi=300)
Note that if you work with plt to save the figure or to plot, it will always work with the active figure, which might not be the figure you expect if you happen to have several of them opened.
UPDATE
1)
Based on the comment by Mad Physicist, the dpi-kwarg apperently does not work with the PS backend. I do not know why.

Remove margins from a matplotlib figure

I'd like to plot a NumPy array using imshow in matplotlib and save it as a JPEG image. However, I can't manage to remove margins/paddings/borders from the image.
My code:
plt.imshow(np.arange(20).reshape(5,4)) ;
plt.axis('off')
plt.savefig('test.jpg', bbox_inches='tight', pad_inches=0, facecolor='black')
I've followed all recommendations that I could find here on Stackoverflow but none of them would help removing uneven white borders (I made them black in this figure) seen below:
setting pad_inches = -1 solved this for me (saving as png).
I suspect the pad_inches=0 was being interpreted as "falsey" and ignored
As it was described in this answer: https://stackoverflow.com/a/26610602/265289, it's important to also call:
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
alongside pad_inches=0. This removes the extra space to the left and bottom of the image.

Save a figure with multiple extensions?

In matplotlib, is there any (especially clever) way to save a figure with multiple extensions?
Use case: I generally want .png's for quick-look examination, uploading to the web, etc. But for publication-quality figures, I want .pdf or .eps files. Often, I want all 3.
It's not hard to do:
for suffix in 'png eps pdf'.split():
pl.savefig(figname+"."+suffix)
but it does involve a lot of rewriting code (since I generally just have savefig(figname+'.png') everywhere right now), and this seems like an easy case for a convenient wrapper function.
If you always do
from matplotlib import pyplot as pl
...
pl.savefig
then you could concievably reassign pl.savefig in one place and it would affect all places.
from matplotlib import pyplot as pl
def save_figs(fn,types=('.pdf',)):
fig = pl.gcf()
for t in types:
fig.savefig(fn+t)
pl.savefig = save_figs
If you usually do
fig=pl.figure()
fig.savefig(...)
then that will require more effort.
You can specify the file type as shown in the documentation for savefig by using format. But as far as I know you can only specify a single format.
While not especially clever, to minimised rewriting code you should write a save_fig function. This can then handle automatically creating appropriate file names and a quality.
def save_fig(fig_name, quality="quick-look"):
if "quick-look" in quality:
# save fig with .png etc
if "pub-qual" in quality:
# save fig with increased dpi etc etc.
Probably not the answer you're looking for as it will require you to rewrite every savefig however I feel this kind of thing can help reduce the amount of repeated code overall.

Aliasing when saving matplotlib filled contour plot to .pdf or .eps

I'm generating a filed contour plot with the matplotlib.pyplot.contourf() function. The arguments in the call to the function are:
contourf(xvec,xvec,w,levels,cmap=matplotlib.cm.jet)
where
xvec = numpy.linspace(-3.,3.,50)
levels = numpy.linspace(-0.01,0.25,100)
and w is my data.
The resulting plot looks pretty good on screen, but when I save to pdf using a call to matplotlib.pyplot.savefig(), the resulting pdf has a lot of aliasing (I think that is what it is) going on. The call to savefig is simply savefig('filename.pdf'). I have tried using the dpi argument, but without luck. A call to matplotlib.get_backend() spits out 'TkAgg'.
I will attach a figure saved as pdf, compared to a figure saved as png (similar to what it looks like on screen) to demonstrate the problem:
png wihtout aliasing: https://dl.dropbox.com/u/6042643/wigner_g0.17.png
pdf with aliasing: https://dl.dropbox.com/u/6042643/wigner_g0.17.pdf
Please let me know if there are any other details I could give to help you give an answer. I should mention that saving as .eps gives similar bad results as saving to pdf. But the pdf shows the problem even clearer. My goal is to end up with a production quality .eps that I can attach to a latex document to be published as a scientific paper. I would be happy with some kind of work around where I save in one format, then convert it, if I can find a way that gives satisfying results.
Best,
Arne
After using the useful answer by #pelson for a while, I finally found a proper solution to this long-standing problem (currently in Matplotlib 3), which does not require multiple calls to contour or rasterizing the figure.
I refer to my original answer here for a more extensive explanation and examples.
In summary, the solution consists of the following lines:
cnt = plt.contourf(x, y, z)
for c in cnt.collections:
c.set_edgecolor("face")
plt.savefig('test.pdf')
I had no idea that contouring in pdf was so bad. You're right, I think the contours are being anti-aliased by the PDF renderers outside of matplotlib. It is for this reason I think you need to be particularly careful which application you use to view the resulting PDF - the best behaviour I have seen is with GIMP, but I'm sure there are plenty of other viewers which perform well.
To fix this problem (when viewing the PDF with GIMP), I was able to "rasterize" the contours produced with matplotlib to avoid the ugly white line problem:
import matplotlib.pyplot as plt
import numpy as np
xs, ys = np.mgrid[0:30, 0:40]
data = (xs - 15) ** 2 + (ys - 20) ** 2 + (np.sin(ys) + 10) ** 2
cs = plt.contourf(xs, ys, data, 60, cmap='jet')
# Rasterize the contour collections
for c in cs.collections:
c.set_rasterized(True)
plt.savefig('test.pdf')
This produced a contour plot which did not exhibit the problems you've shown.
Another alternative, perhaps better, approach, would be to fool the anti-aliasing by putting coloured lines below the contourf.
import matplotlib.pyplot as plt
import numpy as np
xs, ys = np.mgrid[0:30, 0:40]
data = (xs - 15) ** 2 + (ys - 20) ** 2 + (np.sin(ys) + 10) ** 2
# contour the plot first to remove any AA artifacts
plt.contour(xs, ys, data, 60, cmap='jet', lw=0.1)
cs = plt.contourf(xs, ys, data, 60, cmap='jet')
plt.savefig('test.pdf')
I should note that I don't see these problems if I save the figure as a ".ps" rather than a ".pdf" - perhaps that is a third alternative.
Hope this helps you get the paper looking exactly how you want it.