Problem
I cannot seem to get savefig() to actually save a PNG file without a transparent figure background.
This is having read and tried all the suggestions previously posted, answered, cursed about and also going through the API docs a bunch of times. I've read it all, but still can't get non-transparent figure faces
Background
I'm using matplotlib and savefig to create a PNG file. (env: macos - latest anaconda modules using PY 3.7).
I am trying this out of jupyter however - so hopefully it's not something completely screwed up with only how ipython in jupyter does it - though I don't see how that could be the case
I did read through the previous many posts about the (confusing as hell) nature of savefig doing it's own thing with backgrounds, and did/tried everything as suggested (and as written in the latest savefig api docs).
In particular, I've tried all the following without sucess:
specifying facecolor in the savefig() call (with/without transparency)
savefig.facecolor: white in the style mpl file I'm using
When savefig'ing my figure background is always transparent.
Can anyone tell me what the !##$!# I'm missing here???
Code
Here's what I''m using, which spits out figure with transparent background, regardless of what I do.
In particular the 2nd call below (with savefig(..., transparent=False)) will make the axes not transparent - but the figure itself is still transparent!)
import numpy as np
import matplotlib as mpl
import matplotlib.style as style
a = np.array([-3.2, 0.1, 1.5, 3.3, 8.5])
b = np.array([1.1, 1.8, 1.95, 2.3, 4.3])
labels = ['a', 'bc', 'def', 'g', 'ggghhh']
stylefile = './util/plot_config/aqs_default.mplstyle'
# the file above does contain an entry of:
# savefig.facecolor: white
#
to_res = 1024
dpi = 100
inches = (to_res/dpi, to_res/dpi)
style.use(stylefile)
%matplotlib
fig = mpl.figure.Figure(figsize=inches, dpi=dpi, facecolor='white')
ax = fig.subplots()
for x, y, l in zip(a,b,labels):
ax.scatter(x,y,label=l)
ax.legend()
ax.set_xlabel('Some x')
ax.set_ylabel('Attenuation $\mu$ (cm$^{-1}$)')
ax.set_title('blah', y=1.03)
fig.suptitle('Linearity $\mu$')
# for me, _both_ calls below result in the figure having a transparent background:
fig.savefig('a.png', facecolor=fig.get_facecolor(), transparent=True)
fig.savefig('b.png', facecolor=fig.get_facecolor(), transparent=False)
Unfortunately, it seems that frameon is not supported anymore as of matplotlib 3.3.
I solved the transparency issue by setting facecolor='white', transparent=False options in savefig()
FYI for anyone else with a similiar problem.
The cause (and fix) turned out to be because I was creating a figure with frameon set to False.
I had actually had this set to False in a style file I was using.
Changing frameon to True fixed the problem.
This was confusing and not very obvious at all from any documentation - here is some background on the issue from the MPL github issues:
https://github.com/matplotlib/matplotlib/issues/14339
Related
I recently had to re-install my OS and decided to switch to Python3. With it came updates of my IDE PyCharm and presumably also an update of Matplotlib.
Running a script that worked perfectly fine before, now gives me ugly results with overlapping titles of my subplots.
This is an example code:
import numpy as np
import matplotlib.pyplot as plt
z = np.random.uniform(low=0, high=100, size=(20,4))
fig, axes = plt.subplots(2, 2, constrained_layout=True, sharey=True, sharex=True)
axes[-1, 0].set_xlabel('.\n', color=(0, 0, 0, 0))
axes[-1, 0].set_ylabel('.\n', color=(0, 0, 0, 0))
for s_plot, ax in enumerate(axes.flat):
ax.scatter(x=range(20), y=z[:,s_plot])
fig.suptitle("The Title\nSecond Line\n", fontsize=12)
plt.show()
This produces:
I tried setting constrained_layout to False and also experimented with subplots_adjust, but it does not change the layout of my plots.
I am currently using matplotlib 3.0.2. Was there a major change I have missed? I am puzzled about how to solve this.
Using matplotlib 3.0.2 the plot would look as follows
Using constrained_layout=True
Using constrained_layout=False
Both outcomes are expected. In the case of constrained_layout being used the title appears off-center, because there is more space to the left of the subplots being used by labels than on the right.
I also think this is a problem with pycharm. So for example when I run the code in this standard matplotlib script:
https://matplotlib.org/3.1.1/gallery/subplots_axes_and_figures/figure_title.html
I get this image where the title and suptitle overlap:
However the title and suptitle do not overlap when saved to png.
I have raised an issue with pycharm to hopefully get this resolved:
https://youtrack.jetbrains.com/issue/PY-42545
In the meantime I suggest splitting your editor screen to display the .png file which you can refresh using CTRL+ALT+Y every time you run the code.
When I save a matplotlib figure as a jpeg the tick fonts are pixelated. I'm not sure what is going on or if there is any hack to fix this. Does anyone have any insight?
%matplotlib nbagg
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-1.2,1.2,1000,endpoint=True)
y = np.copy(x)
x,y = np.meshgrid(x,y)
z = -x**2 + y**2 - y**3
fig = plt.figure()
ax = fig.add_subplot(111)
CS = plt.contour(x,y,z, [0,-0.1,0.1], colors=['black','blue', 'gray'])
plt.clabel(CS, fontsize=14, inline=1, fmt='%1.1f', manual=[(-0.15,0), (-0.4,0), (0.25,0.5)])
plt.savefig('plot.png', format='png')
plt.savefig('plot.jpg', format='jpg')
plt.savefig('plot.tiff', format='tiff')
Here is plot.png:
Here is plot.jpg:
Here is the plot.tiff:
I believe this is related to a previous question I had: Anti-aliased Fonts in Animations
As noted above, this situation appears is dependent on the backend used. You can avoid the issue by using:
import matplotlib
matplotlib.use('webagg')
as opposed to:
%matplotlib nbagg
I believe the issue has to do with PIL trying to save a jpeg of a figure with transparency. If you insist on using nbagg, it appears that if you set:
matplotlib.rcParams['nbagg.transparent'] = False
Your jpeg image fonts won't be pixelated and will look nearly identical to the png and tiff files shown in the question. Unfortunately using the rcParams:
matplotlib.rcParams['savefig.transparent'] = False
is not sufficient. It appears that the 'savefig.transparent' rcParam will control the transparency of the plot inside the figure and the 'nbagg.transparent' will control the transparency outside of the figure (ie: axis, ticks, titles, etc..). There is probably an easy work by ensuring the backend forces transparency = False for when saving to file formats that don't support transparency.
Some of the other backends may not support transparency which is why it appears to fix the problem when you change backends.
I will report this to github as a bug.
At the moment I am working with Spyder and doing my plotting with matplotlib. I have two monitors, one for development and another for (data) browsing and other stuff. Since I am doing some calculations and my code often changes, I often (re)execute the code and have a look at the plots to check if the results are valid.
Is there any way to place my matplotlib plots on a second monitor and refresh them from the main monitor?
I have already searched for a solution but could not find anything. It would be really helpful for me!
Here's some additional information:
OS: Ubuntu 14.04 (64 Bit)
Spyder-Version: 2.3.2
Matplotlib-Version: 1.3.1.-1.4.2.
I know it's an old question but I came across a similar problem and found this question. I managed to move my plots to a second display using the QT4Agg backend.
import matplotlib.pyplot as plt
plt.switch_backend('QT4Agg')
# a little hack to get screen size; from here [1]
mgr = plt.get_current_fig_manager()
mgr.full_screen_toggle()
py = mgr.canvas.height()
px = mgr.canvas.width()
mgr.window.close()
# hack end
x = [i for i in range(0,10)]
plt.figure()
plt.plot(x)
figManager = plt.get_current_fig_manager()
# if px=0, plot will display on 1st screen
figManager.window.move(px, 0)
figManager.window.showMaximized()
figManager.window.setFocus()
plt.show()
[1] answer from #divenex: How do you set the absolute position of figure windows with matplotlib?
This has to do with matplotlib, not Spyder. Placing the location of a figure explicitly appears to be one of those things for which there's really just workarounds ... see the answers to the question here. That's an old question, but I'm not sure there's been change since then (any matplotlib devs, feel free to correct me!).
The second monitor shouldn't make any difference, it sounds like the issue is just that the figure is being replaced with a new one.
Fortunately you can update figures you've moved to where you want them pretty easily, by using the object interface specifically, and updating the Axes object without creating a new figure. An example is below:
import matplotlib.pyplot as plt
import numpy as np
# Create the figure and axes, keeping the object references
fig = plt.figure()
ax = fig.add_subplot(111)
p, = ax.plot(np.linspace(0,1))
# First display
plt.show()
# Some time to let you look at the result and move/resize the figure
plt.pause(3)
# Replace the contents of the Axes without making a new window
ax.cla()
p, = ax.plot(2*np.linspace(0,1)**2)
# Since the figure is shown already, use draw() to update the display
plt.draw()
plt.pause(3)
# Or you can get really fancy and simply replace the data in the plot
p.set_data(np.linspace(-1,1), 10*np.linspace(-1,1)**3)
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
plt.draw()
Has anyone encountered this problem using spyder in debug mode (PDB)? It works fine in the interactive mode.
One suggested solution was to use pause(1) instead of show() after imshow(img).
Is there a better way to see my figures in debug mode? If there was, it would be a real Matlab killer!
To answer my own question. Apparently this is bug and the pause(1) is the only way to see plot figures in PDB mode.
The other method is to run the entire program as a script by cutting and pasting it into the command line. This way show() can be used instead of pause(1). The advantage to doing it this way, is one can zoom in on the plot. When using pause(1) this is only possible during the pause.
For example:
import numpy as np
from matplotlib import pyplot as plt
import cv2
file_name = 'myimage.jpg'
img = cv2.imread(file_name)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,150,100,apertureSize = 3)
#display image
plt.figure(1)
plt.imshow(edges)
plt.title('edges of image')
plt.show()
Edit:
I just discovered a nice alternative plotting tool in python called guiqwt.
It works with pdb unlike matplotlib
import numpy as np
from guiqwt.pyplot import *
figure("simple plot")
subplot(1, 2, 1)
plot(x, np.tanh(x+np.sin(12*x)), "g-", label="Tanh")
legend()
subplot(1, 2, 2)
plot(x, np.sinh(x), "r:", label="SinH")
show()
You can get it as part of the included packages in python(x,y) or you can download it from here
Edit2:
I just found out the latest IDE released by Pycharm supports matplotlib much better. You can use plt.imshow(img) and don't even have to use plt.show() to display the image in debug mode
I want to render equations to PNG files and embed them in the HTML documentation of my library. I am already using pylab (matplotlib) in other projects.
I have not found any clues in http://matplotlib.sourceforge.net/users/usetex.html and http://matplotlib.sourceforge.net/users/mathtext.html
When I do
plt.title(r'$\alpha > \beta$')
plt.show()
I get a titled empty figure with axes.
Update:
After doing some research I found, that the easiest way of rendering LaTeX to png is using mathext ( http://code.google.com/p/mathtex/ ).
Suprisingly, I had all requiered libraries to build it from source.
Anyway, thanks to everyone for replies.
Update 2:
I did some testing of mathtex and found it does not support matrices (\begin{pmatrix}) and some other things I need.
So, I'm going to install LaTex (MikTeX).
Update 3:
I installed proTeXt. It's huge, but easy-to-use and fast. IMHO, for now it's the only way of rendering equations.
This worked for me:
# https://gist.github.com/tonyseek/95c90638cf43a87e723b
from cStringIO import StringIO
import matplotlib.pyplot as plt
def render_latex(formula, fontsize=12, dpi=300, format_='svg'):
"""Renders LaTeX formula into image.
"""
fig = plt.figure(figsize=(0.01, 0.01))
fig.text(0, 0, u'${}$'.format(formula), fontsize=fontsize)
buffer_ = StringIO()
fig.savefig(buffer_, dpi=dpi, transparent=True, format=format_, bbox_inches='tight', pad_inches=0.0)
plt.close(fig)
return buffer_.getvalue()
if __name__ == '__main__':
image_bytes = render_latex(
r'\theta=\theta+C(1+\theta-\beta)\sqrt{1-\theta}succ_mul',
fontsize=10, dpi=200, format_='png')
with open('formula.png', 'wb') as image_file:
image_file.write(image_bytes)
(source) If you are using IPython interpreter, it renders all single matplotlib step into a figure window by default.
Thus, plt.title(r'$\alpha > \beta$') in IPython would immediately create a figure even before calling .show(). On the other hand, using terminal/cmd/IDLE won't.
plt.show() would create a figure window whether you're using IPython or not, you want to change that line to:
plt.savefig('filename.png')
Edit:
Okay, I misunderstood your question. As #Li-aung Yip said, you may want to use Sympy for pure equation image. We can still do some trick in matplotlib to achieve what you want though (you may need to readjust or resize accordingly):
import matplotlib.pyplot as plt
#add text
plt.text(0.01, 0.8, r'$\alpha > \beta$',fontsize=50)
#hide axes
fig = plt.gca()
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
plt.draw() #or savefig
This is done by hiding axes ticks and adding text inside the plot figure.
But...this doesn't really "not drawing" a figure :\ Though you can post-process such as cropping the image with PIL.
It sounds like you want to render LaTeX equations to images. See linked question for a variety of ways of doing this with minimal dependencies. (Some even involve matplotlib, I believe.)
Alternately, if you can install LaTeX or rely on LaTeX being installed, you can just use LaTeX itself to render the equation to postscript and thence into an image format.
I fiddled with #warvariuc's answer for a long time in Python 3 and came up with the following solution. It is very similar but has a couple of key differences. First, StringIO and cStringIO are not modules in Py3. Secondly, the equivalent class io.StringIO does not work with at least some versions of MatPlotLib. See this thread: http://matplotlib.1069221.n5.nabble.com/savefig-and-StringIO-error-on-Python3-td44241.html. Basically, the image is binary, so you need to use io.BytesIO. The getvalue() method works just as with StringIO. I took the liberty of using savefig to do the file opening, since it can decide whether you passed in a file name or not for itself:
from io import BytesIO
import matplotlib.pyplot as plt
def renderLatex(formula, fontsize=12, dpi=300, format='svg', file=None):
"""Renders LaTeX formula into image or prints to file.
"""
fig = plt.figure(figsize=(0.01, 0.01))
fig.text(0, 0, u'${}$'.format(formula), fontsize=fontsize)
output = BytesIO() if file is None else file
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=MathTextWarning)
fig.savefig(output, dpi=dpi, transparent=True, format=format,
bbox_inches='tight', pad_inches=0.0, frameon=False)
plt.close(fig)
if file is None:
output.seek(0)
return output
The warning was something that I am pretty sure is related to the figure size. You can remove the enclosing with entirely if you would like. The reason for the seek is to make the "file" readable (best explained here: https://stackoverflow.com/a/8598881/2988730).