-
-
Notifications
You must be signed in to change notification settings - Fork 420
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
Minimal changes to support affine composition #2855
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2855 +/- ##
==========================================
+ Coverage 82.90% 82.92% +0.02%
==========================================
Files 500 500
Lines 41977 41992 +15
==========================================
+ Hits 34801 34822 +21
+ Misses 7176 7170 -6
Continue to review full report at Codecov.
|
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.
Looks great! Left only minor comments. Maybe @jni can look over before merge, but I think this is good to go! We should add a comment to the release notes under the API change section when this goes out describing the change in behavior
napari/layers/base/base.py
Outdated
self._update_dims() | ||
self.events.affine() | ||
|
||
def _coerce_affine(self, affine): |
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.
Can coerce_affine
be a method in our layer_utils.py
that also takes an ndim
kwarg rather than live on the object? I guess it's a little stylistic, but we've tended to prefer having more simple utils functions rather than functions on classes when the function does a simple computation like this
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.
Sounds good. Another alternative is in transform_utils
, where I put some coerce_*
functions for the transform components like rotate
in a WIP 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.
yup, that sounds like a good place for it
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.
Done. I actually put it in layer_utils
because putting it in transform_utils
introduced a circular dependency I couldn't easily break.
napari/layers/base/base.py
Outdated
@@ -407,6 +389,22 @@ def editable(self, editable): | |||
self._set_editable(editable=editable) | |||
self.events.editable() | |||
|
|||
@property | |||
def data2world_transform(self) -> Affine: |
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.
Very nice!
Awesome PR! Posting some suggestions from the relevant Zulip discussion and adding some comments about the PR itself:
If we can address those two points (again, |
Thanks for the review @jni and @sofroniewn! @jni: As for (1), I actually didn't notice A. I think we might be able to resolve (A) by making The simplest resolution for (B) is to include Alternatively, to solve both these issues we could define As for (2), agreed that |
👍 A+ to this. Actually
Can you elaborate on this? What do you mean that it doesn't "care"? If I give you some coordinates and run them through the transform, they definitely will come out different when there's a translation vs not, right??? Thinking about this, I don't think I like the idea of including world2grid. As seen in my PR above, at least one use case for exposing these things is to be able to move coordinates between layers — which will be impossible if the grid is included, since by definition the grid separates the layers' data in space. So, I think we should:
Regarding the intermediate space, I'm not into abbreviations — it's why we call things |
Thanks for the comments @jni !
By it doesn't care, I meant that after my changes here all current usages of I think I'm good with your proposals, my main two issues being the following.
As I'm still new here, it's hard for me to put these issues in context, so additional comments would be welcome. My sense is that it might just be worth exposing them, seeing how they work out as we and others use them, and deprecate/refactor if needed later. |
I agreed with both these points. See #1690 for a long time issue of getting the grid information off the layers - it really doesn't belong there are all, so deprecating something to add something in with grid seems like I step in the wrong direction. I think i'd prefer to get this in without exposing new things publicly as much as possible, and then as we continue our refactoring
I think this might just be easiest and would let us merge this PR and defer this until after #1690 and other improvements are made |
Just one minor comment:
I don't know which bit you think is overengineered, certainly this could be true of several "clever" parts of it, but I don't think overall it is overengineered. The transformation chain is a fundamental part of rendering the data and if anything, we will need to get even more sophisticated if we want napari to e.g. be able to display geographic data using nonlinear projections. Which we do. 😜 Not least because we will also want to be able to display e.g. nonlinear distortions in ssTEM slice data. Once you start mixing that with e.g. correlated light data in the same space, you really need the transform chain, a very clear definition of your spaces, and both forward and inverse transforms. |
But other than that yes, I agree that the above discussion does not fall under "minimal changes to support affine composition" 😂 and am happy to keep things very simple and private as a start! Just didn't want to codify 'world to world' or inconsistent naming. 🙏 |
How about we make |
Thanks @jni and @sofroniewn for the comments again. I followed up by making I left
I meant that its over engineered with respect to its current internal usage within napari, but I can see how it could be useful externally when trying to implement things like a non-linear transform on top. Given the conversation here and in PR 2793, it seems like the interface still needs a bit more thought and I'm happy to work on that over the next few weeks. |
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.
Ok, this looks great @andy-sweet! I'm approving now and think we should merge as is.
I think next steps are to think about some of the performance considerations, particularly anything coupled to mouse movements as we're still doing the decompositions from before.
We can also think about removing grid information from layers and exposing a full public transform API in followups too
Description
This changes the behavior of
Layer
so that theaffine
property represents an extra affine transform that is applied on top of the transform defined by thescale
,translate
,rotate
,shear
properties. Previously,affine
would cause those other parameters to be ignored.The composition of all the transform properties is defined in the order
affine * (rotate * shear * scale + translate)
and can be accessed through the newdata2world_transform
property.For example, this was the old behavior:
and this is the new behavior:
Type of change
This is a breaking change for anyone who was passing both the Layer's
affine
parameter and any of the other transform parameters likescale
ortranslate
. In particular, anyone that needs the complete data-to-world coordinate transform, should access the newdata2world_transform
property instead of the individual layer transform properties. I think I fixed all the internal usages like this, but it would be great if people could check any missing ones.References
Closes #2163.
It's probably worth following up on this PR to make some optimizations (e.g. removing the affine matrix decomposition to access properties like scale) and some possible simplifications to the
transforms
package. Some of these potential changes can be seen in the more dramatic, draft PR 2793, but I think it's worth splitting off this to capture the change in behavior in a more minimal PR first.How has this been tested?
Final checklist:
trans.
to make them localizable.For more information see our translations guide.
I'm planning to write up some general documentation on napari's coordinate system, but I don't think that blocks this PR. I udpated the docstrings to describe the new behavior.