1

I plot rather complex data with matplotlib's imshow(), so I prefer to first visually inspect if it is all right, before saving. So I usually call plt.show(), see if it is fine, and then manually save it with a GUI dialog in the show() window. And everything was always fine, but recently I started getting a weird thing. When I save the figure I get a very wrong picture, though it looks perfectly fine in the matplotlib's interactive window. If I zoom to a specific location and then save what I see, I get a fine figure.

So, this is the correct one (a small area of the picture, saved with zooming first):enter image description here

And this one is a zoom into approximately the same area of the figure, after I saved it all:approx

For some reason pixels in the second one are much bigger! That is vary bad for me - as you can see, it looses a lot of details in there.

Unfortunately, my code is quite complicated and I wasn't able to reproduce it with some randomly generated data. This problem appeared after I started to plot two triangles of the picture separately: I read my two huge data files with np.loadtxt(), get np.triu(data1) and np.tril(data2), mask zeroes, NAs, -inf and +inf and then plot them on the same axes with plt.imshow(data, interpolation='none', origin='lower', extent=extent). I do lot's of other different things to make it nicer, but I guess it doesn't matter, because it all worked like a charm before.

Please, let me know, if you need to know anything else specific from my code, that could be relevant to this problem.

Phlya
  • 5,726
  • 4
  • 35
  • 54
  • When using `savefig` have you set a `dpi`? It might be that the default is too low. – Ffisegydd May 06 '14 at 09:03
  • I don't use `savefig`, though I actually did try it and it was also bad. I think I set `dpi` to 300, though I am not sure. – Phlya May 06 '14 at 09:17

1 Answers1

1

When you save a figure in png/jpg you are forced to rasterize it, convert it to a finite number of pixels. If you want to keep the full resolution, you have a few options:

  • Use a very high dpi parameter, like 900. Saving the plot will be slow, and many image viewers will take some time to open it, but the information is there and you can always crop it.
  • Save the image data, the exact numbers you used to make the plot. Whenever you need to inspect it, load it in Matplotlib in interactive mode, navigate to your desired corner, and save it.
  • Use SVG: it is a vector graphics format, so you are not limited to pixels.

Here is how to use SVG:

import matplotlib
matplotlib.use('SVG')
import matplotlib.pyplot as plt

# Generate the image
plt.imshow(image, interpolation='none')
plt.savefig('output_image')

Edit:

To save a true SVG you need to use the SVG backend from the beginning, which is unfortunately, incompatible with interactive mode. Some backends, like GTKCairo seem to allow both, but the result is still rasterized, not a true SVG.

This may be a bug in matplotlib, at least, to the best of my knowledge, it is not documented.

Davidmh
  • 3,797
  • 18
  • 35
  • I do always use SVG. And I don't think it is a dpi-related problem, because the pixels are sharp, there is no blurring or anything, they are just bigger, as if each of them consists of many real pixels. – Phlya May 06 '14 at 13:22
  • How big are your images? I am testing with matrices up to 1e4 x 1e4 and I see all the pixels. – Davidmh May 06 '14 at 14:09
  • Could it be a difference in your rc file? Interactive mode doesn't work with SVG backend, so maybe they are using different options. – Davidmh May 06 '14 at 14:09
  • 1e4 is about right. What should I be looking for in my rc file? – Phlya May 06 '14 at 14:23
  • If it is some sort of interpolation, `image.interpolate` (or set `interpolation='none'` in the imshow call). Note `none` != `None`. – Davidmh May 06 '14 at 14:42
  • Yeah, I do set `interpolation='none'` in the code, you can see it in the question. – Phlya May 06 '14 at 14:53
  • Sorry, I missed that. The solution is to use the SVG backend from the beginning and use savefig. See the edit. – Davidmh May 06 '14 at 15:15
  • OK, it works if I set `dpi` to 750. If it is 500 it doesn't help. But why don't I need to specify such high dpi, when I call `imshow()` only once?That is really weird. I am thinking of reporting it as a bug. – Phlya May 08 '14 at 14:49
  • @Ilya if not a bug, it is an unconsistent undocumented behaviour. Go ahead and report it. – Davidmh May 08 '14 at 14:52