inline images have low quality - matplotlib

I'm loading a TIF file with scikit-image and displaying it inline in an ipython notebook (version 2.2.0). This works, however, the image is quite small when first displayed, and when I resize it using the draggable handle on the bottom right of the image, it just rescales the image while retaining the resolution of the original, so it's very blurry when enlarged. It's basically as if ipython is converting my image into a thumbnail on the fly.
I've tried using matplotlib's plt.imshow() as well, which has the exact same result. I'm starting the notebook with ipython notebook --pylab inline.
from skimage import io
import matplotlib.pyplot as plt
image_stack = io.MultiImage("my_image.tif")
image = image_stack[0] # it's a multi-page TIF, this gets the first image in the stack
io.imshow(image) # or plt.imshow(image)
io.show() # or plt.show()

To change the "%matplotlib inline" figure resolution on the notebook do:
import matplotlib as mpl
mpl.rcParams['figure.dpi']= dpi
I recommend setting the dpi somewhere between 150 and 300 if you are going to download/print the notebook. Ensure that %matplotlib inline runs before the mpl.rcParams['figure.dpi']= dpi otherwise the magic command resets the dpi to its default value (credits to #fabioedoardoluigialberto for noticing this).
The snipppet below only changes the dpi of figures saved through the savefig method, not of inline generated figures.
import matplotlib as mpl
mpl.rc("savefig", dpi=dpi)

According to https://www.reddit.com/r/IPython/comments/1kg9e2/ipython_tip_for_getting_better_quality_inline/
You could also execute this magic in your cell:
%config InlineBackend.figure_format = 'svg'
The print quality will look significantly better. You can also change svg to retina, to use higher-res PNGs (not as nice). Nevertheless, note that if your picture becomes too complicated, the svg picture will have a much larger size than that of the retina picture

The resolution of inline matplotlib figures is downscaled a bit from what you would see in a GUI window or saved image, presumably to save space in the notebook file. To change it, you can do:
import matplotlib as mpl
mpl.rc("figure", dpi=dpi)
Where dpi is some number that will control the size/resolution of the inline plots. I believe the inline default is 80, and the default elsewhere with matplotlib is 100.
The reason scaling the resulting plot by dragging the handle doesn't work is that the plot is rendered as a png, so scaling it zooms but does not change the intrinsic resolution.

Assuming this is the same thing that happens with iPython notebook (with %matplotlib inline) when you go to drag and resize the image, the fix is fairly simple.
If you just create a figure with a different default size, then the resolution also increases with the size of the default (Change resolution of imshow in ipython). For example:
fig = plt.figure(figsize = (10,10))
ax = fig.add_subplot(111)
ax.imshow(array)
Something like this should increase the resolution of the thing you are trying to plot. This seemed to work for me with your code:
from skimage import io
import matplotlib.pyplot as plt
%matplotlib inline
image_stack = io.MultiImage("my_image.tif")
image = image_stack[0]
fig = plt.figure(figsize= (20,20)) #create an empty figure to plot into with 20x20 size
io.imshow(image)
io.show()

Related

Matplotlib: Get Rid of White Border

I want to get rid of the white border when I save my image to a png in python.
I tried plt.box(on=None), plt.axis('off'). I tried setting the figure's 'frameon' parameter to false.
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
figure(num=None, figsize=(7.965,7.965), dpi=80,facecolor='none',clear=True)
plt.box(on=None)
plt.axis('off')
plt.imshow(Data, cmap='Greys_r', norm=Norm,origin='lower',aspect='auto',interpolation='nearest')
plt.savefig(locationFITSfolder+fitsFile[:-5],transparent=False,bbox=False)
I want there to be no white border to my image. Transparent.
If you change the parameters to the savefig function, you will get the desired output.
Specifically, you must use transparent=True. Note that bbox=False and frameon=False are optional, and only change the width of transparent space around your image.
Adapting from your sample code:
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
#create sample data
import numpy as np
Data = np.random.random([4,4])
figure(num=None, figsize=(7.965,7.965), dpi=80,facecolor='none',clear=True)
plt.box(on=None)
plt.axis('off')
plt.imshow(Data, cmap='Greys_r',origin='lower',aspect='auto',interpolation='nearest')
plt.savefig(locationFITSfolder+fitsFile[:-5],transparent=True)
(sidenote -- you may wish to use os.path.join, .split, and .splitext for file I/O, instead of slicing string names)
This yields the expected image output: (note that the image has transparent borders when you open it in a new tab or download it).

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])

Use a different matplotlibrc for savefig

I am using Jupyter Notebook, with a matplotlibrc style that's consistent with its theme set using jupyterthemes. That plotting style however does not look good if I want to export it to PNG to use it within my other documents.
How do I specify a different matplotlibrc when I do a savefig?
Most matplotlib style settings are applied at the moment the object they apply to is created.
You would hence need to create two different plots, one with the usual style of your notebook and another one with the style from the style file. The latter one would be the one to save.
A decent solution would be to create a plot in a function. You can then call this function within a context, with plt.style.context(<your style>): to give the figure a different style.
import matplotlib.pyplot as plt
def plot():
fig, ax = plt.subplots()
ax.plot([2,3,4], label="label")
ax.legend()
# Plot with general style of the notebook
plot()
# Plot with your chosen style for saved figures
with plt.style.context('ggplot'):
plot()
plt.savefig("dark.png")
#plt.close(plt.gcf()) # if you don't want to show this figure on screen
plt.show()
Relevant here: The matplotlib customizing guide.
Perusing matplotlib/__init__.py reveals a number of functions used for managing rcParams. To update rcParams from a file, use matplotlib.rc_file:
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc_file('/tmp/matplotlibrc')
plt.plot([0,1], [0,10])
plt.savefig('/tmp/out.png')
with /tmp/matplotlibrc containing
lines.linewidth : 10 # line width in points
lines.linestyle : -- # dashed line
yields
PS. In hindsight, having found rc_file, googling shows it is documented here.

Output figure size in Jupyter Notebook

I just upgraded my Jupyter version to 4.2.1 and it seems as though inline figures have gotten a lot larger for the same figsize.
Am I imagining this?
Can I change that without changing the figsize?
You can use top-level matplotlib settings like this:
import matplotlib as mpl
mpl.rcParams['figure.figsize'] = (10,10)
This will change default figure size to 10x10. More on that in the documentation: http://matplotlib.org/users/customizing.html
The scaling of the figure for figures with %matplotlib inline depends on the figure.dpi parameter. So if your figures are to small and you do not want to increase the figsize then you can just set
import matplotlib.pyplot as plt
plt.rcParams['figure.dpi'] = 150 # default for me was 75
I also had the impression that the scaling changed at some point, but I guess this can also appear if you change your screen/resolution.

FuncAnimation does not refresh

I am having problems with FuncAnimation, I am using a slightly modified example http://matplotlib.org/examples/animation/basic_example.html
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def update_line(num, data, line):
data.pop(0)
data.append(np.random.random())
line.set_ydata(data)
return line,
fig1 = plt.figure()
data = [0.0 for i in xrange(100)]
l, = plt.plot(data, 'r-')
plt.ylim(-1, 1)
line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l), interval=50, blit=True)
plt.show()
The problem is that the first line (updated by update_line) stays in background.
If I resize the window (click on the corner of the window and moving the mouse). This first line disappears but now the first line after the resize stays in background.
Is this normal, or I am doing something wrong.
Thanks in advance
If you are not overly worried about speed, remove blit=True and it should work.
Blitting is a way to save time by only re-drawing the bits of the figure that have changed (instead of everything), but is easy to get messed up. If you do not include blit=True all the artists get re-drawn every time.
I recommend reading python matplotlib blit to axes or sides of the figure? and Animating Matplotlib panel - blit leaves old frames behind.