Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DPI-connected bug of imshow when using multiple masked arrays #3057

Closed
Phlya opened this issue May 10, 2014 · 15 comments
Closed

DPI-connected bug of imshow when using multiple masked arrays #3057

Phlya opened this issue May 10, 2014 · 15 comments

Comments

@Phlya
Copy link
Contributor

Phlya commented May 10, 2014

Initial discussion started here: http://stackoverflow.com/questions/23490289/matplotlib-shows-different-figure-than-saves-from-the-show-window

Essentially I need to combine a few arrays to make a whole image I want to produce using imshow. So that the arrays do not overlay each other I use masking (as in the example at StackOverflow) or I set some data to None, so that it is not plotted; I do this:

cmap = matplotlib.cm.get_cmap()
cmap.set_bad('w', 0.0)

See the whole working example:

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

data = np.random.random((3000, 3000))
data[np.tril_indices_from(data, 1)] = None
data2 = np.random.random((3000, 3000))
data2[np.triu_indices_from(data2, -1)] = None

cmap = matplotlib.cm.get_cmap()
cmap.set_bad('w', 0.0)

plt.imshow(data, interpolation='none', cmap=cmap)
plt.imshow(data2, interpolation='none', cmap=cmap)
plt.locator_params(nbins=60)
plt.show()

So the thing is, if I look at such image in an interactive window everything looks perfect (e.g. I zoomed into top-left corner, so that the pixels are visible:
show_zoom_save

If I save the whole image to a file from the Save-dialog of the window, the image gets screwed, so that the pixels are much bigger, than they should (it is a printscreen of Inkscape with opened SVG-file, where I zoomed to approximately the same coordinates):
_046
Same goes to direct saving the image to disk (SVG):
_047
If I use only data, but not data2, the image looks good:
_048
If I save both of them, but change the dpi (e.g. to 900), the image changes and the so-to-say resolution gets better, but the image gets really messy:
_049
You can probably see, that some pixels are distorted. If not, here is a closer zoom into this image:
_050
At the same time you can see, that the white diagonal no more ends and starts in the very corner of the image.

As far as I understand, this is a bug, or at least undocumented behaviour, because I haven't found any mentioning of possibility of such weird effects anywhere. Using such plots (though not randomly generated) is crucial to my research, I would really like this fixed or being pointed out to a solution for this problem.

Please let me know, if you can reproduce such effects and if you need any further information from me.

I use matplotlib 1.3.1 on Ubuntu 14.04 with Python 2.7.6 and Spyder 2.2.5.

@Phlya Phlya changed the title DPI-related bug of imshow when using mutiple masked arrays DPI-connected bug of imshow when using mutiple masked arrays May 10, 2014
@Phlya Phlya changed the title DPI-connected bug of imshow when using mutiple masked arrays DPI-connected bug of imshow when using multiple masked arrays May 10, 2014
@tacaswell
Copy link
Member

This is probably related to several other issues (see bugs connected with #3054).

Try setting the dpi of your viewer to 72 or writing a matplotlib.use_backend('svg') before you import pyplot (which means not interactive windows), but might get around this bug.

@Phlya
Copy link
Contributor Author

Phlya commented May 11, 2014

@tacaswell how do I set the dpi of my viewer to 72?
matplotlib.use('svg') and saving to .svg doesn't help.

@tacaswell
Copy link
Member

You have to make sure you use use (sorry for getting the call wrong) before you import pyplot. iirc something like fig.set_dpi(72).

@Phlya
Copy link
Contributor Author

Phlya commented May 13, 2014

No problem. Yeah, I call use before importing pyplot, I know about that.
Unfortunately setting dpi to 72 does not really help, though visible dpi of the image increases! If I make it even lower (e.g. 36) it gets even better. But still, there are artifacts in the image with distorted pixels etc., and I am not sure if there are all the pixels there that should be.

@tacaswell tacaswell added this to the v1.4.0 milestone May 17, 2014
@tacaswell
Copy link
Member

I don't really understand what is going on here, but

  • you can reproduce this with the Agg backends
  • if you use smaller num- ber of pixels the edges get 'wavy'
    • if you change interpolation='none' -> 'intertolation='nearest'` this works correctly on master.
  • but it still does not work with 3k
  • if you use a smaller number of pixels you can see a pattern of narrow and wide pixels (ex 5 narrow, 1 wide)
    • the pitch of the wide pixel changes if you change the figure size
  • pushing the dpi up even more makes things better

I suspect that there is beating going on between the pixelated data and the rasterization. Some times adjacent pixels in the saved image fall in the same pixel of the input data, but most of the time each saved pixel gets it's own data pixel (see https://en.wikipedia.org/wiki/Moiré_pattern).

I am not sure I understand the diagonal issue, but I think the above explains the non-square pattern. I am inclined to close this as not a bug. Things are behaving correctly and the best they can under the given input parameters.

@Phlya
Copy link
Contributor Author

Phlya commented Jun 2, 2014

@tacaswell Yeah, thank you for such a thorough explanation. I agree it might be happening because of such effects and that even it might not be a bug (though I don't really think so - plotting image of the same resolution with the same dpi, but from a single matrix doesn't cause such weird things), but in that case I would really like to know how to plot such data with imshow in matplotlib. Maybe there is a way to calculate the required dpi or smth else.

@efiring
Copy link
Member

efiring commented Jun 3, 2014

@phyla, is the intention to have an svg file with 9,000,000 pixels in it? With no loss of information, so that when you zoom in, you see each pixel? It will be a big file. When not zoomed in, it will be up to the renderer to decide how to handle its inability to show all the pixels.
It seems like there should be a simple way to achieve this, and it makes sense that interpolation='none' should do the trick--or protest if owing to some limitation, it can't. Evidently, however, this is not the case. A pull request was recently merged, illustrating how 'none' and 'nearest' differ: #2995.
It seems that in order to store an image with a 1:1 correspondence between pixels and the original image array, a calculation of exactly the right dpi for the savefig command is needed. This can only work, however, if the figure has no more than one image, since a single dpi must fit all.
I think we should be able to get around this for svg and pdf.

@Phlya
Copy link
Contributor Author

Phlya commented Jun 3, 2014

@efiring Yes, that is the intention. And it works fine with single image without any fancy dpi calculations, and the file is not too big after that.

@efiring
Copy link
Member

efiring commented Jun 3, 2014

Aha! Superimposing images triggers a compositing operation in the draw method, which is defeating the 'none' interpolation by doing its own calculation of the grid. It is not immediately clear to me whether this can be fixed reasonably easily; if so, it would presumably be only in the special case like yours where the images are identical in grid and extents.
I think the immediate solution for your case would be for you to do the compositing yourself in a numpy array, so that you are only calling imshow once in a given axes.

@Phlya
Copy link
Contributor Author

Phlya commented Jun 3, 2014

What do you mean by compositing in a numpy array? To combine images in a single array? The thing is they might need a different scale of a colormap, so that they are both drawn with the same colour interval, and I would prefer not to change their values, so that it is possible to see the original signal intensity...

@efiring
Copy link
Member

efiring commented Jun 3, 2014

Yes, combine in a single array. If they need to be scaled differently, then use a suitable scaling transformation for each chunk before combining them. You could use a Normalize instance to do this, one instance per chunk, so it would be the same operation as occurs when you call imshow twice. You can get exactly the same result, very easily and efficiently. Check out the colors.Normalize class.

@Phlya
Copy link
Contributor Author

Phlya commented Jun 3, 2014

Thank you for the idea with Normalize, I didn't know about that nice function, should be useful! I will try it out.

@tacaswell
Copy link
Member

re-milestoned to 1.4.x as there is something subtle going on at the bottom of the rendering stack, this is a corner case, and there is an acceptable work-around.

@tacaswell tacaswell modified the milestones: v1.4.x, 1.5.0 Feb 7, 2015
@tacaswell tacaswell modified the milestones: proposed next point release, next point release Jul 17, 2015
@tacaswell
Copy link
Member

Punted as we still have not sorted out what is going on and probably won't get to it in the next week.

However there are now at least 3 issues that I suspect are related #1441 and #4584

@mdboom
Copy link
Member

mdboom commented Dec 30, 2015

Sorry to come so late to the discussion -- you can set the rcParam image.composite_image to False to turn off the compositing of images.

mdboom added a commit to mdboom/matplotlib that referenced this issue Dec 30, 2015
mdboom added a commit to mdboom/matplotlib that referenced this issue Jan 5, 2016
mdboom added a commit to mdboom/matplotlib that referenced this issue Jan 5, 2016
mdboom added a commit to mdboom/matplotlib that referenced this issue Jan 8, 2016
mdboom added a commit to mdboom/matplotlib that referenced this issue Jan 25, 2016
mdboom added a commit to mdboom/matplotlib that referenced this issue Jan 27, 2016
mdboom added a commit to mdboom/matplotlib that referenced this issue Jan 28, 2016
mdboom added a commit to mdboom/matplotlib that referenced this issue Feb 9, 2016
@QuLogic QuLogic modified the milestones: 2.0 (style change major release), 2.1 (next point release) Feb 18, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants