feat: add dynamic_shape mode and coords to StreamView#112
Merged
tlambert03 merged 18 commits intopymmcore-plus:mainfrom Feb 26, 2026
Merged
feat: add dynamic_shape mode and coords to StreamView#112tlambert03 merged 18 commits intopymmcore-plus:mainfrom
dynamic_shape mode and coords to StreamView#112tlambert03 merged 18 commits intopymmcore-plus:mainfrom
Conversation
… and error checks
…equirement in error message
Merging this PR will degrade performance by 51.58%
Performance Changes
Comparing |
Member
Author
|
with this change, the ndv example reduces to: ...
with create_stream(settings) as stream:
# Create a StreamView. This is an array-like object with dims/coords
# that reflect the stream's current state (by default, live_shape=True).
view = stream.view()
# ndv has built-in support for array-like objects that support the xarray
# dims/coords convention. So we can directly pass our StreamView to an ndv viewer
viewer = ndv.ArrayViewer(view)
viewer.show()
# the only thing we need to connect is the StreamView's coords_changed signal,
# so that the viewer updates when new frames are added and the shape/coords change.
view.coords_changed.connect(viewer.data_wrapper.dims_changed)❤️ the |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #112 +/- ##
==========================================
- Coverage 97.46% 97.43% -0.03%
==========================================
Files 21 21
Lines 2285 2337 +52
==========================================
+ Hits 2227 2277 +50
- Misses 58 60 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…onnect method documentation
live_shape mode to StreamViewlive_shape mode and coords to StreamView
live_shape mode and coords to StreamViewdynamic_shape mode and coords to StreamView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The current default contract of
StreamViewis that itsshapealways reflects the full (expected) dimensions, as defined by theAcquisitionSettings. You can always index into it according to your expectations...But in many cases (such as the ndv example already checked into the repo), a GUI will want some object it can query to know "what are the valid bounds of the data that I can request." The current ndv example (in main) shows how one can manually manage this by connecting to
on('coords_expanded')and then managing how one indexes into the StreamView. But that's a bit of boilerplate that is likely to be very consistent across usage patterns. So:This PR introduces a
dynamic_shapemode to StreamView (viastream.view(dynamic_shape=True)), which changes the semantics ofshape,coords(new), and__len__. It also adds acoordsproperty to all views (dynamic or not). (having bothdimsandcoordsfinishes the x-array like API that's useful in a number of contexts, closes #105).dynamic_shape=False(old)dynamic_shape=True(new default)shape(3, 2, 512, 512)(0, 0, 512, 512)→(1, 1, 512, 512)→ …Frame dims (Y, X) always full.
dims("t", "c", "y", "x")(this is important ... changing ndim is bad )
ndim4— constant__len__30→1→ …coords{"t": range(3), "c": ["DAPI", "GFP"], ...}{"t": range(0), "c": [], ...}→{"t": range(1), "c": ["DAPI"], ...}__getitem__IndexErrorifstrict=True)An additional
strictparameter controls indexing behavior:stream.view(dynamic_shape=True, strict=True)raisesIndexErroron integer indices outside the current "seen" bounds, while slices are always clipped (standard numpy behavior).