Skip to content
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

feat: stackviewer v2 #293

Closed
wants to merge 69 commits into from
Closed

Conversation

tlambert03
Copy link
Member

@tlambert03 tlambert03 commented May 4, 2024

new stack viewer, inspired by @wl-stepp's awesome stack viewer, but generalized and abstracted a bit more (works for any array type, such as numpy, xarray), offers various color modes (grayscale, and composite), hopefully has a bit more robust indexing and event system, and abstracts out the vispy part, also providing a pygfx backend (and working on a pure qt backend as well)

needs some changes in superqt pyapp-kit/superqt#242

depends on pymmcore-plus/pymmcore-plus#348

Copy link

codecov bot commented May 4, 2024

Codecov Report

Attention: Patch coverage is 19.25134% with 755 lines in your changes missing coverage. Please review.

Project coverage is 83.94%. Comparing base (7e1512f) to head (f7cddf5).
Report is 1 commits behind head on main.

Current head f7cddf5 differs from pull request most recent head b26e0bf

Please upload reports for the commit b26e0bf to get more accurate results.

Files Patch % Lines
.../pymmcore_widgets/_stack_viewer_v2/_dims_slider.py 22.26% 206 Missing ⚠️
...pymmcore_widgets/_stack_viewer_v2/_stack_viewer.py 20.24% 193 Missing ⚠️
...mcore_widgets/_stack_viewer_v2/_backends/_vispy.py 0.00% 92 Missing ⚠️
...mcore_widgets/_stack_viewer_v2/_backends/_pygfx.py 0.00% 87 Missing ⚠️
.../pymmcore_widgets/_stack_viewer_v2/_lut_control.py 25.00% 57 Missing ⚠️
src/pymmcore_widgets/_stack_viewer_v2/_indexing.py 24.07% 41 Missing ⚠️
...rc/pymmcore_widgets/_stack_viewer_v2/_protocols.py 0.00% 26 Missing ⚠️
.../pymmcore_widgets/_stack_viewer_v2/_save_button.py 34.37% 21 Missing ⚠️
...c/pymmcore_widgets/_stack_viewer_v2/_mda_viewer.py 46.87% 17 Missing ⚠️
...ore_widgets/_stack_viewer_v2/_backends/__init__.py 31.81% 15 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #293      +/-   ##
==========================================
- Coverage   90.26%   83.94%   -6.32%     
==========================================
  Files          76       87      +11     
  Lines        9583    10516     +933     
==========================================
+ Hits         8650     8828     +178     
- Misses        933     1688     +755     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@tlambert03
Copy link
Member Author

tlambert03 commented May 7, 2024

@wl-stepp, when you have a moment, it would be great if you could test this out. I apologize for redoing much of this, a I started to integrate it in a couple projects i found various things I wanted to abstract a little differently and ended up getting carried away (but it was very helpful having your viewer to begin with). The main thing (i think?) that isn't yet re-implemented here is spreading grid images across the canvas, but I'll add that back in shortly. Here are a few bullet points explaining what's different here:

  • The base StackViewer object works now for any nDimensional array, numpy, xarray, etc... i find myself wanting this sort of thing often (and don't want to use napari) and this was very close to the general purpose tool.
  • MDAViewer is the subclass that contains any/all concerns about pymmcore
  • All vispy logic is restricted to a VispyCanvas (and support for pygfx shows that the abstraction can support anything that can paint an image on the screen)
  • The DimsSlider is a more important object whose value (a mapping of dimension name to index) is the source of truth for the current index. The StackViewer itself mostly just acts as an intermediate between the DimsSlider and the canvas: dims changed -> get index value -> slide the data -> send to the canvas
  • The index value also works with slices now (double click on the axis name in the sliders and you'll get a range slider instead) allowing for thick-slice projections (currently just max projections) of a subset of a particular axis
  • All the data-slicing logic is put inside of an isel(data, inde) method. This method A) knows how to index into various datastores, whether they be a pymmcore-plus data handler, numpy array, dask array, xarray, etc... B) is asynchronous to begin with (returns a Future) which may be important in the future if it takes a while to slice data...
  • there is a button that lets you swap between composite and grayscale mode.
  • the space taken by the sliders has been minimized a bit, and LUTs are put inside of a collapsible
  • you can right click on the play button to change the FPS of playback.
  • some changes needed to be made upstream at superqt: fix: fix a number of issues with Labeled and Range Sliders, add LabelsOnHandle mode. pyapp-kit/superqt#242

I'm not sure what code you have that has begun to use experimental.StackViewer ... so I can't tell yet whether you'll be able to mostly drop this in. but have a look at examples/mda_viewer.py here and let me know if you're ok with these changes

(make sure to run pip install -U superqt[iconify,cmap])

@wl-stepp
Copy link
Contributor

however, there is a problem still with iter(Queue()) in that they don't result in an MDASequence with known sizes (i.e. there's no way to know ahead of time what the dimensions are going to look like). So you do see a burst of images, but then indexing through them doesn't work because the underlying datastore itself doesn't work. This means they don't currently work well with the 5Dwriter bases (like OMEZarrWriter) which do currently require some degree of regularity in the dimensions.

I do think we could/should make a data handler that makes zero assumptions about the data it is going to see, and simply stores a sequence of 2D camera planes associated with an index or full MDAEvent. I think this viewer would likely work with that type of thing fine.

Yes, that was my conclusion as well. I think that makes it another issue. I want to change the current EDA implementation that is running on the microscope to use the queue, so I will also think about this.

@tlambert03
Copy link
Member Author

See pymmcore-plus/pymmcore-plus#348

Which this pr is actually now using, and it's working with queues as well (see example mda_viewer_queue)

@tlambert03
Copy link
Member Author

closing in favor of #299, which branched off of this

@tlambert03 tlambert03 closed this Jun 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants