You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
imshow doesn't properly display some images #10072
Bug summary
When using imshow with larger images, you lose pixel resolution for low intensity pixels with lots of nearby zeros (not sure if the intensity or nearby zeros are relevant, or just relative isolation of the pixel). It's like they're being interpolated away, even if interpolation is set to nearest. This is new behavior in version 2.1.
Code for reproduction
Use the attached npy file, which is an image referenced in this test script.
importmatplotlib.pyplotaspltimportnumpyasnpimg=np.load('test_image.npy')
#Display full imageplt.imshow(img, interpolation='nearest')
plt.clim(0, 5)
plt.show()
#Display part of imageplt.imshow(img[200:400,500:700], interpolation='nearest')
plt.clim(0, 5)
plt.show()
Actual outcome
Using matplotlib 2.1.1:
Screenshot from the display of full image (first plot in code above):
Using the zoom tool to zoom in on a region when the full image is plotted:
Plotting roughly the same zoom region initially (second plot in code above):
Expected outcome
It should all look like the third image. Here's the output when the same code is run in version 2.0.2
Screenshot from the display of full image (first plot in code above):
Using the zoom tool to zoom in on a region when the full image is plotted:
Plotting roughly the same zoom region initially (second plot in code above):
Matplotlib version
Operating system: macOS 10.12.6
Matplotlib version: 2.1.1 (broken) and 2.0.2 (working)
One additional note. This doesn't seem to be backend dependent. I first noticed the bug on the wxagg backend. Here's some code for reproducing it there if you want:
If I cast it as a float it works fine, good guess!
I don't understand enough about the inner workings of matplotlib to know why that changed things. Is this something you'll fix going forward, or should I just default to casting to float?
Definitely something that should be fixed (hopefully soon!), but as a work around now casting to float will work.
The root of the issues is we removed a couple of cast-to-float calls (both to save memory and to avoid down-casting float128 values) but apparently now let uint though un cast which means in the normalization step they all end up being 0 or 1 (as it looks like your minimum is 0 and all of the other values are strictly less that the maximum value except the maximum so integer division does it's thing).
Great, thanks! I will look for it in the next release (hopefully). I appreciate the fast response, and all of your hard work, along with the rest of the community.
Oh, hold it, we kind of do, but only if you set both vmin and vmax. The bug here is that vmin=None and then our attempt to make things right fails. Your test above works fine if you set vmin=-2
For pcolor we are rendering individual rectangle patches w for here as for image we resample the image and then pass that to the renderer. In general imshow will be much faster for large arrays.
You can not use vmin and vmax for the rescaling limits because you will not get the interpolation correct as you will be clipping any large values to just above / below the limits.
jklymak, sure I can mask the bad flag, but it's one more step to do and not at all handy if you do not know beforehand what was actually used and you just want to get an impression of the data.
I realized that, if I do the plotting and then re-size the plotting window, using version 2.2.2, it sets back to ok scaling. The same is not true for 2.1.2.
There are still problems if the initial dtype is not float64 and the outlier is negative, consider: x=np.array([[0.1,0.2],[-1.e18,0.3]]).astype("float32");plt.imshow(x,vmin=0,vmax=0.5)
I set both vmin and vmax, but still the result does not look as wanted. In fact, this is even more akward,0.1, 0.2 and -1e18 are coloured as min and 0.3 as maximum.
I still do not see why you "need to do the scaling and unscaling" to the data. If you do not do it, the internal resampling is fine. (?)
Activity
jbhopkins commentedon Dec 21, 2017
One additional note. This doesn't seem to be backend dependent. I first noticed the bug on the wxagg backend. Here's some code for reproducing it there if you want:
tacaswell commentedon Dec 21, 2017
What is the datatype of your input data?
Can you reproduce this with synthetic data?
jbhopkins commentedon Dec 21, 2017
The datatype of the numpy array is uint32 (just realized that's what you meant, so edited this answer).
I haven't tried with synthetic data. If I have a chance, I'll try to generate a dataset and test, but I can't promise I'll have time to do that.
tacaswell commentedon Dec 21, 2017
Does it work if you cast to floats first? My knee-jerk guess is that we have gotten too-careful with preserving input types.
jbhopkins commentedon Dec 21, 2017
If I cast it as a float it works fine, good guess!
I don't understand enough about the inner workings of matplotlib to know why that changed things. Is this something you'll fix going forward, or should I just default to casting to float?
tacaswell commentedon Dec 21, 2017
Definitely something that should be fixed (hopefully soon!), but as a work around now casting to float will work.
The root of the issues is we removed a couple of cast-to-float calls (both to save memory and to avoid down-casting float128 values) but apparently now let uint though un cast which means in the normalization step they all end up being 0 or 1 (as it looks like your minimum is 0 and all of the other values are strictly less that the maximum value except the maximum so integer division does it's thing).
jbhopkins commentedon Dec 21, 2017
Great, thanks! I will look for it in the next release (hopefully). I appreciate the fast response, and all of your hard work, along with the rest of the community.
jklymak commentedon Dec 30, 2017
Pretty sure it was #8966 around line 382 of
image.pyxanfus commentedon Mar 2, 2018
jklymak commentedon Mar 2, 2018
xanfus commentedon Mar 2, 2018
jklymak commentedon Mar 2, 2018
jklymak commentedon Mar 2, 2018
6 remaining items
jklymak commentedon Apr 13, 2018
First, I think its pretty straightforward to do
x = np.ma.masked_greater(x, 1e10)or whatever is adequate to catch your bad flag.But, I agree that if you specify
vmaxinimshowthat it should do a better job of the scaling.@tacaswell the interpolation happens at draw time. Should we not use
vminandvmaxto do the scaling if they are set?jklymak commentedon Apr 13, 2018
Oh, hold it, we kind of do, but only if you set both
vminandvmax. The bug here is thatvmin=Noneand then our attempt to make things right fails. Your test above works fine if you setvmin=-2tacaswell commentedon Apr 13, 2018
For pcolor we are rendering individual rectangle patches w for here as for image we resample the image and then pass that to the renderer. In general
imshowwill be much faster for large arrays.You can not use
vminandvmaxfor the rescaling limits because you will not get the interpolation correct as you will be clipping any large values to just above / below the limits.tacaswell commentedon Apr 13, 2018
ah, that means we just need to make sure the norm has auto-scaled.
mmf1982 commentedon Apr 13, 2018
jklymak, sure I can mask the bad flag, but it's one more step to do and not at all handy if you do not know beforehand what was actually used and you just want to get an impression of the data.
I realized that, if I do the plotting and then re-size the plotting window, using version 2.2.2, it sets back to ok scaling. The same is not true for 2.1.2.
jklymak commentedon Apr 13, 2018
See #11047 for the same fix as before, but this time handling if the norms are
None.@tacaswell, I just used the data min and max rather than relying on a norm autoscaling.
jklymak commentedon Apr 13, 2018
@mmf1982 OK, thats probably because the autoscaling for the norm gets applied at the first draw, so the second draw has the correct info.
mmf1982 commentedon Apr 13, 2018
There are still problems if the initial dtype is not float64 and the outlier is negative, consider:
x=np.array([[0.1,0.2],[-1.e18,0.3]]).astype("float32");plt.imshow(x,vmin=0,vmax=0.5)I set both vmin and vmax, but still the result does not look as wanted. In fact, this is even more akward,0.1, 0.2 and -1e18 are coloured as min and 0.3 as maximum.
I still do not see why you "need to do the scaling and unscaling" to the data. If you do not do it, the internal resampling is fine. (?)
tacaswell commentedon Apr 13, 2018
@mmf1982 See discussions at #8631 #8966, #5718 and https://matplotlib.org/users/dflt_style_changes.html#colormapping-pipeline for more details.
jklymak commentedon Apr 13, 2018
The
dv * 1e7was too big forfloat32. Changed todv * 1e4seems OK, but that value was just empricallly decided. See change to #11047mmf1982 commentedon Apr 16, 2018
Thanks @tacaswell for the references and thanks @jklymak for the fix.