-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Change pcolormesh snapping (fixes alpha colorbar/grid issues) [AGG] #16090
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
Conversation
{aa:True ec:none} looks worse after? (basically https://github.com/jklymak/contourfIssues?) The tradeoff may be worth it, I'm just wondering whether that's the reason the code got in to start with? |
I tried going back in the blame and couldn't find any explanations for these values/decisions. Probably in some really old initial commits. This way would at least let the user decide what they want to do now. Also, if you increase the mesh size you can see that this addition at least maintains some semblance of alpha, whereas all of the previous plots look like they lose all transparency. Also of note, this is only for flat shading. My guess is that this could be extended to other pathways as well (gouraud, markers, etc...). But, I wanted to at least get the discussion started and see if this is worthwhile or not on a very common use-case (colorbars calling pcolormesh). |
The changes look fine to me, but perhaps we need to check whether some image comparison failures can be fixed by actually setting edgecolor to their previous effective value. |
When I looked at this before the issue was that this was inconsistent between output types as well. So good to check png and PDF |
@jklymak I'm not sure what you mean by inconsistencies? I am only changing the AGG code, so I would assume this is only modifying pdf when the quadmesh calls are rasterized (maybe all the time because there is no specific quadmesh function in the backend_pdf file?) matplotlib/lib/matplotlib/backends/backend_pdf.py Lines 88 to 93 in 45cfd87
Of course, per your nice write-up of contourf issues, there are various antialiasing issues with the viewers themselves that come into play if that's what you're referring to? As a note, a lot of the image tests are actually both png and pdf in the test suite, so those are covered here. A lot of those are different because the colorbars have changed now. I'm also going to ping @mdboom and @efiring as they have commented a lot on this in the past and probably have an inkling if I'm missing something here or should implement this in a different way. As another discussion, the Lines 1053 to 1069 in a6f5a28
|
For interest's sake, I looked at the effect of this on Cartopy. It changes results for 7 tests, 6 of them that are specifically for pcolormesh, plus the colorbar in |
Let me add that I am facing similar issues that I would like to see "fixed". I wonder if there is a temporary fix to this? Thanks |
@nunocalaim, you can try and use a different backend (Cairo for example). I've had success using mplcairo and added an alpha-blended colormap example over there. |
This looks to me like an improvement. In the demo, the only comparison that is slightly worse is aa:True, ec:None, skewed, no alpha. The aa:True, ec:None, with alpha cases are remarkably improved. The desired target should be the ec:None cases because for this sort of plot ec:face doesn't make sense--it is just an attempt to work around the rendering problem. |
@jklymak Can you be responsible for 👍 / 👎 on this change? |
Does the snapping make sense with aa:True? |
Is the snapping the problem with the skewed aa:True, ec:none case? This change is small and seems reasonable to me. CI has lost its artifacts, so not sure how many images this hits... |
I just did a test so I could see the result with and without the snap. Based on the given test case, I see the changes in this PR as a substantial net gain. The penalty in the one case--skewed, aa:true, ec:none--can still be hacked away at the user level by setting edgecolor='face'. This is actually an API cleanup, because it means the user's setting of edgecolor is being respected. |
922636a
to
94c5d4f
Compare
I just pushed up a new commit to update the snap within the collection itself. Not sure if that is actually needed or not, but, now we'll have some artifacts up in CI again. On my local system it was ~40 images. All basically just improvements in pixel positioning. @anntzer, I'm wondering if this is worth updating all of these images if you're going to be working on a new image difference routine this summer in GSOC? This many images would be ideal for something like that... |
If you're willing to wait a couple of months for the new image comparison system (#16447) to come in, I guess there's a reasonable chance that it will and that would certainly be nicer than adding another 40 new baseline images. (Given the current release cycle timescale I guess that would just mean missing 3.3 and going in in 3.4?) So I would prefer waiting, but if others feel strongly about it I'm not going to block the PR over that (note that I haven't reviewed carefully the latest iterations, but looks like there's plenty of other qualified reviewers already on it :-)). |
I just realized that the pcolormesh_alpha test doesn't even fail with this update, when I would have thought it would! matplotlib/lib/matplotlib/tests/test_axes.py Line 1220 in d1f07d3
Because you need to actually request antialiased=True in the pcolormesh call to force the new version. Right now, the pdf version looks good, but the png is atrocious.
Current pngWith this update and with a small update to the gouraud shading too, I can get it to look OK: Possible AGG updateBut, even then there are still some aliased artifacts between the solid regions (lower left panel) that don't look that great. I decided to check mplcairo on this too and it is much nicer (also less than half the saved png size). mplcairoSummaryI'm not in any absolute rush to get this in. I just found this to be a nice update to the defaults so put it out there for feedback and conversation. But, it is really quite confusing (for me) to figure out all of the scanlines and various combinations of snapped, curved, antialiased, rgba/rgba_pre, and how all of those should be put together into the AGG mpl code, so I have no idea how to get it to the "best" possible AGG rendering. To me, this (middle AGG png) still seems like just a half-solution, because it would update a lot of images, but maybe not even to a "correct" state. |
1c9311f
to
4d7b0b6
Compare
I decided to go through and look at all of the tests that were failing and updated all 40+ of the images. Every one of them had a colorbar or pcolormesh in it and all of the changes were essentially pixel snapping and as far as I can tell they all looked like improvements more closely aligning with where the ticks are placed (some previously had a pixel or more offset). Looks like it is passing all of the tests now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should merge this. Hopefully the test image PR mitigates the problem with changing a bunch of images. But I don't think that should hold this up.
Can we just gate the behavior on a (possibly private) rcparam that is disabled by the test suite (except perhaps for a single test that checks the new behavior)? We can always get rid of it once the baseline images project is done, or even just decide to drop it later and put in the new images if we really want to. |
I'm going to be gone for a week or two here without any access to this, but I can look into making some kind of switch in the test suite when I get some time after that. (I didn't add up how large each of the new images are in the repo either to know how much this will balloon everything) The other problem we'll run into though is that matplotlib will be just fine with the rcparam switch, but we'll still likely impact other packages that depend on matplotlib and do image comparisons including colorbars or quadmeshes. Things to do still:
|
We could even expose the rc publically with a note that this is solely to stabilize test images and may be removed in the future (there's precedence for that: text.kerning_factor basically serves a similar purpose). |
060ff1f
to
40194c8
Compare
Thanks for the suggestion about the kerning rcparam. I followed that idea and it seems to do the trick here with everything still passing and no images needing to be updated right away. I added a quick what's new entry clearly demonstrating the improvement: I think this is ready for a proper review and to see if anyone has any other suggestions for a different way to deal with the images issue, or anything else requested for this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a net improvement both in performance and in code consistency and clarity. There are still cases with room for improvement, primarily 2 of the cases with slanted edges and ec=none. (I think ec should be none, so I don't care about the ec='face' cases.) The problem with those remaining cases is probably deeper in the guts of agg, so it shouldn't block this PR.
|
||
Due to how the snapping keyword argument was getting passed to the AGG backend, | ||
previous versions of Matplotlib would appear to show lines between the grid | ||
edges of a mesh with transparency. This version now applies sub-pixel snapping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought this was snapping to full pixels? (Maybe I'm just confused.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, I'm confused now too!... My recollection from reading the AGG documentation a while ago was that it has sub-pixel resolution (I think I recall there was an extra buffer added somewhere for this in the scanlines or similar?). I may have conflated the sub-pixel resolution with the ability to snap at a sub-pixel level.
Here is a write-up on the text pixel positioning (sub-pixel level) http://agg.sourceforge.net/antigrain.com/research/font_rasterization/
I'm not entirely clear if that is applicable to these other geometries though.
Finding documentation on all of this is quite challenging, so if you are pretty sure it is actually to pixels then I can just drop the 'sub'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The quick and dirty solution would be to just write "snapping" without saying snapping to what ;) TBH 1) I'm not super certain here either, I think it's pixel snapping but would need to dig more into it (which I'm not going to do right now) and 2) I don't think that many users will care about the technical details anyways ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion :) I just removed 'sub-pixel' altogether.
1c44544
to
f29553f
Compare
src/_backend_agg.h
Outdated
linewidths, | ||
linestyles, | ||
antialiaseds, | ||
OFFSET_POSITION_FIGURE, | ||
false, | ||
true, // snap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually only enables checking whether the snap flag is set, rather than enabling snapping (I think); perhaps the comment could be slightly clearer? e.g. just replace snap
by check_snap
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is correct. Updated with the suggestion.
…oices from Python, do the logic up there.
…update many baseline images.
… prevent baseline image updates.
f29553f
to
f31c51a
Compare
Lets get this in master and see what it breaks. Thanks @greglucas ! |
Downstream libraries I'm guessing! I only hope it is for the better. |
Are there some we should ping? |
xarray immediately comes to mind. @QuLogic and I can take care of Cartopy. Honestly, anyone who has image comparisons that include colorbars will be impacted by this, and I would guess that is a significant number. |
ping @jhamman @dcherian at xarray @ngoldbaum at yt If you have image comparisons (particularly w/ colorbars) they may start failing on matplotlib master. Either update your images ;-) or you can set Also any big complaints about this, please let us know! |
@munkm re yt image comparison tests ^ |
Just hit this in sunpy image tests, but all looks fine apart from minor colorbar changes 👍 (https://56597-2165383-gh.circle-artifacts.com/0/.tmp/py37-figure-devdeps/figure_test_images/fig_comparison.html and scroll down to a figure with colorbars to see the change if anyone's interested) |
@dstansby thanks! Sorry, I forgot about sunpy in the above. We need a list of packages we know use image tests. |
PR Summary
This fixes longstanding issues with dark or white lines showing up in colorbars and grids when alpha is present.
(#1188 and many other references to similar issues, it is all over stackoverflow as well)
It also appears to fix some grid positioning issues as well: #7341
Consequences
There are more than 40 failing images due to this change (I think all due to pixel offsets in colorbar/pcolormesh image comparisons from what I can tell). These images would all have to be updated. I also suspect that lots of downstream libraries depending on matplotlib would be impacted by this in their image comparisons as well.
I am not an expert on the Agg/rendering machinery, so I welcome feedback/discussion on this.
Timing appears inconsequential (actually faster) during my basic tests of increasing the mesh size, but I haven't done anything rigorous.
Proposed Changes
There are two issues here.
Push the edgecolor logic up into the Python callers (for some reason if antialiasing was set and edgecolors was empty the C++ would override edgecolors. That magic validations should happen upstream if this is desired.
Call the mesh generation code with check_snap set to true.
Comparison results
Before:
After:
Demo code: