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

tight_layout flips images when making plots without displaying them #12134

Closed
galenlynch opened this issue Sep 16, 2018 · 15 comments
Closed

tight_layout flips images when making plots without displaying them #12134

galenlynch opened this issue Sep 16, 2018 · 15 comments
Labels
status: needs clarification Issues that need more information to resolve.
Milestone

Comments

@galenlynch
Copy link
Contributor

Bug report

I am trying to make a bunch of figures on a cluster. In the process of doing so, I have discovered that images are flipped upside down when using tight_layout on GridSpec objects that do not contain the image.

However, this bug only happens when interactive mode is off. Even more strangely, it is not inverted if the figure has already been saved prior to calling tight_layout.

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

x, y = np.ogrid[0:6, 0:7]
imdata = x + y

# Two column figure (with simple gridspecs for this example)
gs_left = GridSpec(1, 1, left = 0.05, right = 0.48)
gs_right = GridSpec(1, 1, left = 0.55, right = 0.98)

f = plt.figure()

# Add the image to the right column
im_ax = f.add_subplot(gs_right[0])
im_ax.imshow(imdata, extent = (0, 10, 0, 10), origin = 'upper', aspect = 'auto')

# Uncommenting the following line makes both figures correct
#f.savefig("image_ok")

# Call tight layout on the OTHER grid spec
gs_left.tight_layout(f, rect = [0.05, 0.05, 0.45, 0.95])

f.savefig("image_inverted")

Actual outcome

The image is inverted. The dark portion of the image should be on the bottom left.

image_inverted

Expected outcome

image_ok

Matplotlib version

  • Operating system: Fedora 28
  • Matplotlib version: 2.2.3
  • Matplotlib backend: GTK3Cairo, I think maybe agg with non-interactive?
  • Python version: 3.6.6

Matplotlib was installed from Fedora's package manager.

@jklymak
Copy link
Member

jklymak commented Sep 16, 2018

I don't think your code above is correct. You aren't adding gs_left.

Regardless, I can't reproduce. I get the Actual Outcome with or without the call to tight layout, both on master and v2.2.3.

The only way I get your "Expected" is to set origin='lower'.

@jklymak jklymak added the status: needs clarification Issues that need more information to resolve. label Sep 16, 2018
@galenlynch
Copy link
Contributor Author

galenlynch commented Sep 16, 2018

Thanks for taking a look. The above code reflects when I see this behavior: I add the image to the right gridspec, and call tight_layout on the left. The output images above are from running the script above from my machine, though you are right I forgot to include the line that makes the empty axis in gs_left. When i have the chance I'll also try it on conda python.

@galenlynch
Copy link
Contributor Author

Here is the script that I used to generate the figures above, with the call to gs_left.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

plt.ioff()
x, y = np.ogrid[0:6, 0:7]
imdata = x + y

gs_left = GridSpec(1, 1, left = 0.05, right = 0.48)
gs_right = GridSpec(1, 1, left = 0.55, right = 0.98)

f = plt.figure()
im_ax = f.add_subplot(gs_right[0])
other_ax = f.add_subplot(gs_left[0])
im_ax.imshow(imdata, extent = (0, 10, 0, 10), origin = 'upper', aspect = 'auto')
# f.savefig("image_ok")

gs_left.tight_layout(f, rect = [0.05, 0.05, 0.45, 0.95])

f.savefig("image_inverted")

@jklymak I do agree that from the documentation, I should need origin='lower' but I have found that at least with interactive mode, I have to use origin = 'upper' to get my desired result.

@jklymak
Copy link
Member

jklymak commented Sep 16, 2018

The above does not show the error. What backend are you using (print(matplotlib.get_backend())? I assume you are calling plt.show or running from ipython, or?

@ImportanceOfBeingErnest
Copy link
Member

There is a relatively new tutorial about imshow's origin and extent available here. This shows that what is called "actual outcome" above, is indeed expected for extent = (0, 10, 0, 10), origin = 'upper'.

So if anything is wrong, then it would the "expected outcome". And indeed using the cairo backend ("TkCairo" in my case, since I don't have GTK available), the image is inverted.

What then happens seems that if you use tight_layout, cairo backend will for some reason fall back to agg and the expected outcome with the dark color in the upper left corner is produced, together with a warning UserWarning: tight_layout : falling back to Agg renderer.

So in total there are two potential issues:

  • *Cairo backend mixes up origin = 'lower' and 'upper' (it just reverses their meaning it seems)
  • tight_layout will fall back to agg for some reason.

@ImportanceOfBeingErnest
Copy link
Member

The "cairo flipping images" issue is been reported in #8798 and fixed in #11724. But it has not been backported to 2.2.x.

@galenlynch
Copy link
Contributor Author

galenlynch commented Sep 16, 2018

My backend is GTK3Cairo. @ImportanceOfBeingErnest is right that I get the agg warning when calling tight from python when interactive mode is off, so given the issues that he mentioned, it seems like cairo images being upside down would explain everything that I've experienced. However, I do not get the agg warning when using interactive mode, and therefore see consistently wrong behavior. To me, the "bug" is mostly that it's inconsistent... I've been pulling my hair out trying to figure out why images in my figures appear one way when plotting them without interactive mode, and are inverted when I use interactive mode. It took my a while to even narrow it down to the tight_layout call.

I've read the tutorial a bunch of times, but it didn't seem to be accurate (didn't know about #8798) so I chalked it up to some version difference or something. I will say that the API for imshow is rather complicated once you start caring about where the image is in an axis.

So it seems like the final verdict is that this is due to #11724 not being backported to my version of matplotlib? Should I close the issue or leave it open as a petition to backport the fix so I don't have to plot twice to get consistently oriented images?

@galenlynch
Copy link
Contributor Author

Oh and @jklymak I was looking at the generated file. I get different behavior when I use interactive mode (which I guess is the equivalent of plt.show()?).

@jklymak
Copy link
Member

jklymak commented Sep 16, 2018

This is still confusing. Running the code above as a script should not use gtk3cairo. And the bug in #8798 shouldn’t have anything to do w tight_layout. I’d also have thought that saving interactively should still have run through agg but maybe by that point the image is already incorrect. Anyhow the problem has been fixed in master. I don’t see any reason it couldn’t be back ported to 2.2.4.

@ImportanceOfBeingErnest
Copy link
Member

I suppose the user's backend rcParam is simply set to gtk3cairo?!
And then, it's a combination of both, I guess. Without the tight_layout you would probably consistently get the wrong behaviour. So appart from the flipping issue, there is also the question why tight_layout would switch the renderer and whether this is expected.
(There is also another warning about tight_layout not being able to process the gridspecs, so I'm not even sure if it would be extected to work in this case.)

@galenlynch
Copy link
Contributor Author

@jklymak If I just start a vanilla python shell and look at the backend, it's already gtk3cairo. Poking around, I realized that I had set it to that (and forgot about it) in my matplotlibrc file. Is there a reason to not use that backend? I did mention it as my backend in the original bug report. I just removed the matplotlibrc.

I would definitely appreciate the bug fix being ported! If at all possible, I would like to continue to use my distro's version of matplotlib, which is currently 2.2.4. I've had pretty bad experiences trying to use conda, or other bolt-on python package mangers, on Fedora. Matplotlib installed with conda on Fedora is particularly problematic, maybe because of wayland. I invariably run into library compatibility issues resulting in segfaults, missing symbols, etc...

@ImportanceOfBeingErnest Yes, I believe I was just getting consistently wrong behavior, which I'm ok with to be honest.

As for the other warning, when I run into this problem when I'm making real figures, it only sometimes gives me a warning about tight_layout not being able to process the gridspecs. Often when it does give me a warning, it will still has the desired result of re-sizing subplots to make room for labels and ticks. Maybe I'm just experiencing alarm fatigue, but came to the conclusion that it was safe to ignore.

@ImportanceOfBeingErnest
Copy link
Member

The probability to find some bug in the cairo backends is currently a little larger than for the agg backends. It seems the cairo backends do not get the same love as the agg backends when it comes to maintainance.
On the other hand the agg backends currently show some really bad issues with marker and tick positionning, which are not present in the cairo case. Having both available is hence a good idea.

As of today, the latest release of matplotlib is 2.2.3. If the bug fix was backported, it would appear in the (yet to be released) version 2.2.4. There will however be a version 3.0 released pretty soonish, which will contain this fix. The main difference is that 2.x is python2 compatible, while 3.x is python3 only. In order to get the fix you will need to use a new version anyways; whether that'll be 2.2.4 or 3.0 or the current development version from github will depend on your other requirements.

@galenlynch
Copy link
Contributor Author

Whoops, I meant 2.2.3 -- that's the version that I'm using.

Ok I'll keep in mind that I should try different backends when I run into issues down the road.

Thanks again for both of your help!

@jklymak
Copy link
Member

jklymak commented Sep 27, 2018

I tried to auto-backport #11724, but is says there is a conflict. If someone wants to take up the backport I don't see any problem w/ that, but I don't have the bandwidth...

@jklymak
Copy link
Member

jklymak commented Sep 27, 2018

Note that 3.0 is released and should fix this bug, so I'll close..

@jklymak jklymak closed this as completed Sep 27, 2018
@QuLogic QuLogic added this to the v3.0 milestone Sep 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs clarification Issues that need more information to resolve.
Projects
None yet
Development

No branches or pull requests

4 participants