-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Bug with colorbar and low DPI output in PDF #6827
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
Comments
The problem is that in the usual case with more than 50 colors in the colormap, the colorbar pcolormesh output is rasterized to eliminate the artifacts that would otherwise appear when the pdf is rendered. Rasterizing at 7 dpi gives terrible resolution, so the colorbar interior doesn't coincide with the vector graphics of its outline. import numpy as np
import pyplot as plt.
X, Y = np.meshgrid([1, 2], [3, 4])
pc = plt.pcolormesh(X, Y, [[1]])
pc.set_rasterized(True) # colorbar does this
plt.savefig('test7r.pdf', dpi=7) test7r.pdf |
@efiring - interestingly, this doesn't affect the main image though. Maybe the colorbar could be rasterized in the same way as imshow rather than using pcolormesh? |
The reason I ran into this problem is that the APLpy package (https://github.com/aplpy/aplpy) basically picks a DPI that matches the imshow resolution to minimize EPS/PDF file size. If this can't be fixed in Matplotlib then the plan B will be to make sure there is a minimum DPI for APLpy figures. |
We can't use imshow because the spacing of the color intervals can be nonuniform. Probably we should use image.PcolorImage (directly, or via Axes.pcolorfast) instead of pcolormesh; but a quick check indicates that this doesn't solve the problem. This looks like a difficult problem to solve; one way or another, rasterization of an artist in the vector backends must be restricted to cases where the dpi is reasonably large. Maybe that logic can be put in the backends. |
Could you use |
No, PcolorImage is a modification of NonUniformImage that has the right property for a colorbar: it colors blocks based on specified boundaries rather than centers. The rendering goes through the same code path. |
I agree that this looks like a problem in placing the rasterized image. The code in backend_mixed.py switches around the figure dpi in a way that I find suspicious, but I don't see an obvious bug there either. |
Could this be yet another integer snapping issue? |
According to #17103, also affects SVG. |
Okay, turns out the issue is rather fundamental to partial rasterization, but I think it can be solved. First, I'll explain the problem in quite some detail to ease discussion (sorry if this is quite stream-of-consciousness-y, feel free to skip to the solution below). I'll use the following example for reproducibility, since SVG XML is easier to read/debug than PDF output for me.
As pointed out by Jouni above, it's important to know that the vector backends draw everything at 72 "dpi", regardless of the actual Anywho, the real issue is how When
Meanwhile, the * Note: there is a subtle bug created by pretending points are pixels....but it doesn't affect us here, and I will open a separate issue about it. |
In summary, in order to respect the Fundamentally, you can't solve the problem of:
We can choose 3/4 basically. Suppose we choose that we want (1), (2) and (4). Then the simplest solution to the current issue seems to be:
Then solves the problem of (3) by simply cropping the color bar by the minimum amount possible. |
I don’t have a ton of sympathy for low dpi plots. However, I actually think a reasonable approach is to use the rasterization dpi as a guide and then make an integer number of pixels fit in the physical space of each artist being rasterized. So if someone requests 100 dpi and has an object that is 1/3 of an inch wide we give them 33 (or 34) pixels So that the resolution for that artist would really be 99dpi. I don’t think anyone using a vector backend needs the rasters to be exactly any dpi whereas they likely would like the objects to line up precisely with non vectorized elements. |
I like this solution. To make sure I'm understanding @jklymak, you're suggesting that I basically just take the existing rasterized This would scale all the component pixels up or down a bit, so the |
dpi need not be an integer, it just happened to be above. If an object is 1.637 inches wide, and dpi is set at 100, then we would use 164 pixels to represent it across 1.637 inches for a practical dpi of 100.18 dpi. |
While I'm fixing this, might as well note for posterity that contrary to what was reported originally, this bug is also present for
produces a figure that only looks a "little" off, but if you highlight the boundary of the actual image in Inkscape, you can see the error is just as bad: The rasterized element |
I don't think its by chance that the bottom left corner aligns? The image origin is placed exactly there, isn't it? |
Oh I guess in this case it is exactly there? I just assumed it was subject to the same integer clipping issues as in the colorbar case (where the bottom corner typically does not align). |
I'd have to look at the code, but there is definitely a floating point x and y and a translate command in there... |
I see, it's a different bug. Unlike the colorbar, the |
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help! |
attn @QuLogic Please re-open if you think this is related to the DPI work you are doing. |
I'm not sure it's related to my work, but probably to @jklymak's since he also added the |
The following example (with Matplotlib 2.0.0b3) causes the colorbar inside the colorbar axes to be offset when outputting to PDF with a low DPI:
This wasn't an issue with Matplotlib 1.4:
and the bug was apparently introduces in Matplotlib 1.5:
The text was updated successfully, but these errors were encountered: