Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
PGF Backend: Support interpolation='none' #6792
Conversation
mdboom
added the
needs_review
label
Jul 18, 2016
Re-added the skewing test to the demo and two more transformations. Images and dashed rectangle targets match up nicely for all transforms both in the PDF and PGF backend. Ready to merge from my side. |
|
Shouldn't a Actually, I rather prefer your example to what I wrote in #6673 :). Small remark: it seems that you could remove l. 33 Besides, it looks like the weird offset between the image and extent rectangle (see #6673) is still present when I plot the new version of the example: @tacaswell Should I open a dedicated issue for the latter odd behavior, as it tends to deviate from the original PR? |
I don't know, it was removed by @mdboom in 1384c7c. You tell me :)
Oh, didn't notice. Will amend my commit.
That's because the rectangle lines are centered on the left and bottom pixel boundaries rather than the pixel centers. This is consistent within matplotlib. The way the image extents are currently chosen (
I think red doesn't look nice with the new default colormap, that's why I changed it from blue (the new default color for the plot) to yellow. I'd be fine either way. |
Amended my comment to remove the unused |
|
@f0k Thank you for amending the example! I will then close my redundant PR #6673. About the red color: I only used it for me. I agree that blue was a bit difficult to see, and if you prefer to replace it with yellow, it's fine for me. About the offset between the extent rectangle and the image, if it's normal behavior, then everything is alright (I was just scared it might be a small bug), and indeed it's unclear to me that it's really worth the hassle to offset the rectangle by half pixel. For the PGF backend part, I apologize but I don't know enough about backends to review it. |
afvincent
referenced
this pull request
Jul 19, 2016
Closed
Docstring fix & small adjustements in `demo_affine_image` #6673
I retracted this, I'm not sure it should be this way, but I guess the problem lies outside the demo code. It's not the business of our PRs then.
I liked the outline showing the rotation, we could think about adding this here as well (does it make sense for the other transformations?). But probably this should still be a separate PR -- I extended the demo merely to check whether the modified PGF backend works correctly, not to make the demo more instructive.
Let's try to summon @pwuertz for this! (He's the original author of the PGF backend, not sure how much he's still involved in matplotlib.) |
|
Ok, reviewed the changes to the PGF backend. One small detail (just an opinion, feel free to ignore it if you don't agree): My main concern before was the half-pixel/pixel alignment, but if your extended demo indeed shows that this is a matplotlib-wide issue and not a backend-to-backend inconsistency it shouldn't hold up a merge of this PR. Better alignment is a job for another day then. In conclusion: Nice work! Thanks for tracking down the details for making this possible. |
This was not done for cleverness. If we remove lines 647--650, the translation is applied before the affine transform. This was correct for matplotlib 1.5, but is wrong for matplotlib 2.0. Integrating it in the transformation seemed easier than moving the
It's definitely consistent between backends. |
|
@pwuertz: Moved I can merge this into the second commit before merging if needed, I just separated it for now so @pwuertz can have a look. |
|
Ok, the code looks really good now. Unfortunately the new image handling is causing trouble on my machine. When I save the demo figure to PDF using the modified backend_pgf the images are missing completely (Xelatex from Ubuntu 16.04 Texlive). How did you set up your tests so far? Pdf, xe, lualatex? Version? Os? Tex distribution? |
Booh. Thanks for testing!
Ubuntu 14.04, pdflatex ( |
|
I'm seeing good results with pdflatex and lualatex, only xelatex fails. Your initial version with the translation merged into the matrix works however. Might be a bug in xelatex, but I guess it's not worth figuring it out and instead use your first approach again. |
tacaswell
added this to the
2.0 (style change major release)
milestone
Jul 20, 2016
|
@f0k A doc PR to write down your hard won (sorry) understanding of how the image handling works would be appreciated! |
|
@f0k This block here is based on your last commit and works with all 3 texsystems. # reference the image in the pgf picture
writeln(self.fh, r"\begin{pgfscope}")
self._print_pgf_clip(gc)
f = 1. / self.dpi # from display coords to inch
writeln(self.fh, r"\pgfsys@transformshift{%fin}{%fin}" % (x * f, y * f))
if transform is not None:
tr1, tr2, tr3, tr4, tr5, tr6 = transform.frozen().to_values()
w = h = 1. / f # scale is already included in the transform
writeln(self.fh, r"\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}" % (tr1 * f, tr2 * f, tr3 * f, tr4 * f, tr5 * f, tr6 * f))
interp = str(transform is None).lower() # interpolation in PDF reader
writeln(self.fh, r"\pgftext[left,bottom]{\pgfimage[interpolate=%s,width=%fin,height=%fin]{%s}}" % (interp, w * f, h * f, fname_img))
writeln(self.fh, r"\end{pgfscope}") |
Great, thanks for figuring it out! Shall we really use both transformshift and transformcm at the same time, or change to: # reference the image in the pgf picture
writeln(self.fh, r"\begin{pgfscope}")
self._print_pgf_clip(gc)
f = 1. / self.dpi # from display coords to inch
if transform is None:
writeln(self.fh, r"\pgfsys@transformshift{%fin}{%fin}" % (x * f, y * f))
w, h = w * f, h * f
else:
tr1, tr2, tr3, tr4, tr5, tr6 = transform.frozen().to_values()
writeln(self.fh, r"\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}" % (tr1 * f, tr2 * f, tr3 * f, tr4 * f, (tr5 + x) * f, (tr6 + y) * f))
w = h = 1 # scale is already included in the transform
interp = str(transform is None).lower() # interpolation in PDF reader
writeln(self.fh, r"\pgftext[left,bottom]{\pgfimage[interpolate=%s,width=%fin,height=%fin]{%s}}" % (interp, w, h, fname_img))
writeln(self.fh, r"\end{pgfscope}")For one, this will use either transformshift or transformcm, and secondly, it uses the case distinction to set the image height and width directly to
My knowledge is still incomplete... the PDF backend has: def get_image_magnification(self):
return self.image_dpi/72.0This probably changes the units of the translation and the transform, or maybe just one of them. I can see if I can improve the base class docstrings with what I know, though. |
|
@f0k Agreed |
Amended the commits to use my latest proposed code (which uses the
Actually, the docs were not that bad. Part of the problem was that I had started with matplotlib 1.5.2, which had a different API and less documentation. |
|
If you want you can also take down the #TODO note in |
Did so. Wasn't sure whether the |
f0k
commented on the diff
Jul 20, 2016
| """ | ||
| - Draw the image instance into the current axes; | ||
| + Draw an RGBA image. |
f0k
Contributor
|
f0k
and 3 others
commented on an outdated diff
Jul 20, 2016
| *y* | ||
| - the distance from the origin. That is, if origin is | ||
| - upper, y is the distance from top. If origin is lower, y | ||
| - is the distance from bottom | ||
| + the distance in pixels from the bottom side of the canvas. |
f0k
Contributor
|
f0k
commented on an outdated diff
Jul 20, 2016
| *im* | ||
| An NxMx4 array of RGBA pixels (of dtype uint8). | ||
| - *trans* | ||
| - If the concrete backend is written such that | ||
| - `option_scale_image` returns `True`, an affine | ||
| - transformation may also be passed to `draw_image`. The | ||
| - backend should apply the transformation to the image | ||
| - before applying the translation of `x` and `y`. | ||
| + *transform* | ||
| + If and only if the concrete backend is written such that | ||
| + :meth:`option_scale_image` returns ``True``, an affine | ||
| + transformation *may* be passed to :meth:`draw_image`. It takes the | ||
| + form of a :class:`~matplotlib.transforms.Affine2DBase` instance, | ||
| + with translation given in pixels. Note that this transformation | ||
| + does not override `x` and `y`, and has to be applied *before* | ||
| + translating the result by `x` and `y` (this can be accomplished | ||
| + by adding `x` and `y` to the translation defined by `transform`). |
f0k
Contributor
|
|
Travis throws an error for Python 3.4, PYTHON_ARGS=-OO now:
Is this anything that could possibly have been caused by this PR, specifically by the last commit which changed the docstring and one argument name? Everything passed fine before. Shall we just restart Travis and hope for the best? |
tacaswell
commented on the diff
Jul 26, 2016
| @@ -508,30 +508,31 @@ def get_image_magnification(self): | ||
| """ | ||
| return 1.0 | ||
| - def draw_image(self, gc, x, y, im, trans=None): | ||
| + def draw_image(self, gc, x, y, im, transform=None): |
tacaswell
Owner
|
tacaswell
and 1 other
commented on an outdated diff
Jul 26, 2016
| - fig, ax1 = plt.subplots(1, 1) | ||
| +if 1: |
f0k
Contributor
|
|
@tacaswell, @mdboom, let me know if anything else is needed. The Travis failure seems unrelated. |
mdboom
and 3 others
commented on an outdated diff
Aug 2, 2016
| *im* | ||
| An NxMx4 array of RGBA pixels (of dtype uint8). | ||
| - *trans* | ||
| - If the concrete backend is written such that | ||
| - `option_scale_image` returns `True`, an affine | ||
| - transformation may also be passed to `draw_image`. The | ||
| - backend should apply the transformation to the image | ||
| - before applying the translation of `x` and `y`. | ||
| + *transform* | ||
| + If and only if the concrete backend is written such that | ||
| + :meth:`option_scale_image` returns ``True``, an affine | ||
| + transformation *may* be passed to :meth:`draw_image`. It takes the | ||
| + form of a :class:`~matplotlib.transforms.Affine2DBase` instance, | ||
| + with translation given in pixels. Note that this transformation |
mdboom
Owner
|
mdboom
and 2 others
commented on an outdated diff
Aug 2, 2016
| f = 1. / self.dpi # from display coords to inch | ||
| - writeln(self.fh, r"\pgftext[at=\pgfqpoint{%fin}{%fin},left,bottom]{\pgfimage[interpolate=true,width=%fin,height=%fin]{%s}}" % (x * f, y * f, w * f, h * f, fname_img)) | ||
| + if transform is None: | ||
| + writeln(self.fh, r"\pgfsys@transformshift{%fin}{%fin}" % (x * f, y * f)) | ||
| + w, h = w * f, h * f | ||
| + else: | ||
| + tr1, tr2, tr3, tr4, tr5, tr6 = transform.frozen().to_values() | ||
| + writeln(self.fh, r"\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}" % (tr1 * f, tr2 * f, tr3 * f, tr4 * f, (tr5 + x) * f, (tr6 + y) * f)) | ||
| + w = h = 1 # scale is already included in the transform | ||
| + interp = str(transform is None).lower() # interpolation in PDF reader | ||
| + writeln(self.fh, r"\pgftext[left,bottom]{\pgfimage[interpolate=%s,width=%fin,height=%fin]{%s}}" % (interp, w, h, fname_img)) |
f0k
Contributor
|
|
Looks good aside from my comments above. |
|
Anything left do be done? It's good to merge from my perspective. |
|
Did you address @mdboom's comments? I don't see any updates to api_changes.rst. |
I commented on the comment: #6792 (comment) |
|
@f0k Thanks for bearing with us while we wordsmith these details. |
|
I've changed all three occurrences of
Thank you. While I appreciate paying attention to details, it's certainly not very motivating to iterate that long on documenting a single method. It would have helped if somebody more familiar with the internals had taken the time to make a well-informed and consistent suggestion for the docstring. Anyway, I'm thankful matplotlib exists, is in active development and accepts contributions! |
|
@f0k Thanks for digging into a subtle corner of the code base. Hopefully we will hear from you again! |
tacaswell
merged commit 6eeb109
into matplotlib:master
Aug 27, 2016
tacaswell
removed the
needs_review
label
Aug 27, 2016
tacaswell
added a commit
that referenced
this pull request
Aug 27, 2016
|
|
tacaswell |
e7d7a49
|
|
backported to v2.x as e7d7a49 |
f0k
deleted the
f0k:pgf-transform branch
Aug 27, 2016
|
Great, thanks for merging! |

f0k commentedJul 18, 2016
As discussed in #6740, the PGF backend currently doesn't support
interpolation='none', which means all raster graphics are interpolated prior to saving them. This adds full support for affine image transforms.In the old backend, it took me 10 minutes to implement this from what I already had in #6740: https://gist.github.com/f0k/f8c87ffd82488c2ed303989592bf4ebd/eeaa54c8b98f84fd2f80caf4305034fc1eeeb76f#file-backend_mypgf-py-L61-L62
For the new backend, things seem to have changed quite a bit, but documentation is still lacking. It took me an hour of fiddling around. Now the code produces the same result as the PDF backend for the affine transform demo in its current version, which does not include the skewing test any more.
Closes #6740.