-
-
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
Reset contrast range when update layer data from magicgui FunctionGui #5136
base: main
Are you sure you want to change the base?
Conversation
This PR solves the problem mentioned here #5127 (comment) (and above in discussions), but I'm not sure if it solves the general problem from #5127 issue. |
Click here to download the docs artifacts |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5136 +/- ##
==========================================
- Coverage 92.81% 92.71% -0.10%
==========================================
Files 623 623
Lines 57347 57352 +5
==========================================
- Hits 53228 53176 -52
- Misses 4119 4176 +57 ☔ View full report in Codecov by Sentry. |
Thanks for working on this @Czaki ! I'm a little bit worried about side effects. Would it be possible that the visualization appears blinking if contrast limits are updated all the time? Also, does this cause a delay? I must admit, I liked the idea by @psobolewskiPhD for adding a reset button more than resetting all the time... |
But this is reset only if new data is set as the function's output. |
I agree that I don't love updating all the time. I understand the use case of updating a layer inplace with a dramatically different image (as in @haesleinhuepf's example of Wouldn't it be better to do this if the |
Actually, maybe only update the range automatically (when |
Aside from the solutions being proposed, we might also want to consider: (1) indicating in the UI that the contrast limit is "out of date" (2) providing a setting to auto-reset the contrast limit cc: @isabela-pf |
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.
If you have a simple widget for summing layers and three layers A, B, C.
For the current solution, if You first execute A+B and then A+C, the effect will be different from just executing A+C, as in the first case the contrast will be calculated based on the result of A+B, in the second it will be based on A+C.
So we have a situation where image presence depends on execution order, and it looks like a bug to me. This PR is fixing this behavior.
At least reset_contrast_limits_range
should be called.
layer.reset_contrast_limits_range() | ||
layer.reset_contrast_limits() |
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 that at least reset_contrast_limits_range
should be called.
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.
Took a look again; I stand by my point, the cost of having this run all the time is too high and not worth it.
The problem is also easily circumvented by setting the auto-contrast
to continuous, which does exactly what we want, and is explicit.
If anything, I would suggest a PR that exposes the autocontrast programmatically somehow?
Why is too high? If the layer is created using a functional widget, the calculation of contrast limit is performed.
Programmatically, the plugin author may just return dict with contrast limits. |
Here's a contrived example explaining why I think it's bad: import numpy as np
import napari
from magicgui import magicgui
from napari.types import ImageData
data = np.random.random((100, 100))
@magicgui(auto_call=True, x={'widget_type': 'Slider'})
def random_change(x) -> ImageData:
data[50, 50] = np.random.rand() * 10
return data
viewer = napari.Viewer()
viewer.window.add_dock_widget(random_change)
viewer.add_image(data) Move the slider around. I think it should be explicitly requested by using auto-contrast |
So your example is an example that updates a single pixel, where my example is when we have generated, a whole new layer each time. I do not see an example of a plugin like yours. But see @haesleinhuepf plugins that are wrappers around |
And your example does not show any problem if we reset only the contrast limit range. |
My point was that there are cases where we make many updates to the data (such as with a slider) and calculating min/max values might be too expensive done that often, unless requested. Or are you saying that is this negligible? |
I'm saying that magic should be more correct than performant.
and much more. But with the current approach, the user has no message that the black screen is because of wrong contrast limits, not because the widget returns arrays of zeros. |
Fair enough, I'm convinced 👍 But would be nice to warn the "experienced user" about the performance issue, somehow. |
Did update documentation will be enough for such warn? |
We discussed this at the napari-eurasia meeting (me, @DragaDoncila, @brisvag, and @Czaki in attendance) and let's just say it was contentious. 😂 Overall, we all agreed that it would be good to have more eyes on this PR, and to think hard about all the possible options to address the issues that this PR means to address:
Some options that were discussed (also above), as alternatives to always updating the contrast:
We'd like to hear from @psobolewskiPhD in particular since he has done a lot of user-facing work and might have encountered this issue before. We also plan to discuss this PR at upcoming community meetings. |
Gah I lost my post. So, getting back to the issue at hand, there are two cases:
So I feel like this is an issue that should be resolved by ensuring the current Now there is no |
With this case it could be simply changed by returning |
No, we are talking about the case when the user has manually and intentionally changed the contrast limits based on the previous output.
All cases of confusion can be cleared up by the implementation using a LayerDataTuple instead of ImageData, so I don't think that's really relevant. What's important is, we are acting on limited information in this case, so we have to make some guesses (despite the Zen of Python), and the questions are:
|
layer.reset_contrast_limits_range() | ||
layer.reset_contrast_limits() |
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.
Maybe this will be a good option to not reset range when someone adjusts contrast limits manually:
layer.reset_contrast_limits_range() | |
layer.reset_contrast_limits() | |
if layer.contrast_limits == layer.contrast_limits_range: | |
layer.reset_contrast_limits_range() | |
layer.reset_contrast_limits() |
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.
Description
Reset contrast limits range and contrast limits when updating layer data from functions that return
ImageData
etc.