Widget for drawing Regions of Interest (ROIs) as napari Shapes#617
Widget for drawing Regions of Interest (ROIs) as napari Shapes#617
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #617 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 38 39 +1
Lines 2284 3053 +769
==========================================
+ Hits 2284 3053 +769 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
2a48aaa to
3aab669
Compare
|
bc29ead to
b0f665e
Compare
c0a33d1 to
3b5af63
Compare
|
During Friday's Community Call, we discussed enforcing unique region names within a regions layer. This could be added, but I will wait for a PR review and implement it together with other suggestions that will come out of the review. |
|
An edge case I came across today while demoing the feature: if one duplicates an entire regions layer, the new layers is added to the layer selection dropdown, but it isn't automatically selected. |
3b5af63 to
07ab8c2
Compare
|
sfmig
left a comment
There was a problem hiding this comment.
Thanks for this @niksirbi, it'll be nice to soon be able to enjoy this feature.
I leave you some comments here re the UI experience and some more general comments too.
Re the regions' names:
- I think it would be more clear if regions in the same layer have unique names. This is in line with napari and how it behaves re layers (we could even follow their format for avoiding duplicate names using numbers in square brackets). Right now, a new shape in a layer will take the name of the last added shape, which feels a bit odd. Especially if you copy-paste, it can be confusing if all copies have the same name.
- I would not display the region name by default. The text is often not very high contrast anyways and the line for the text is thin... What do you think?
- I think it would make sense if the text color follows changes in face color (note that users cannot change text color via the UI). Right now, if you change the face color, the text color stays and there is an odd mismatch.
Re updates on the regions table:
- Clicking on the shape does not highlight the row in the table for me. I think this would be very useful to have if possible, as it can be hard to map shapes to rows if there are too many of them.
- Similarly, clicking on a regions layer in the layer list does not update the dropdown nor the table. I think this bidirectionality would add a lot of clarity.
Re the two ways of creating movement ROI layers
- I initially liked it but then found myself in a couple of confusing spots.
- I found it confusing that if I create a layer via the layers list, call it "regions" and then call it something else, it will still be added to the dropdown. You mention it in the PR description but it still surprised me.
- I think the docs should be super clear about this. Also maybe about the keyword for region names being case insensitive.
- I wonder if the simplest fix is to do away with these two options, and remove the possibility of creating a
movementregions layer via a specific name. I liked the idea initially but now I feel it adds confusion and there is not a big gain for it. - Another option could be to expose the
movement_region_layerflag, as that may make it more clear. Ideally in the layer controls but I think that napari doesn't support much customisation there.
More generally, I think it would be useful for users and contributors if we agree from the start in some nomenclature, to distinguish between napari "regular regions" and our movement regions. For example (just for clarity, I don't particularly like it):
- a shapes layer is a type of napari layer
- a shapes layer contains regions
- an ROI layer is a
movement-specific type of shapes layer - ROIs are the polygons, ellipses, etc drawn in an ROI layer
Re docs updates, I think when we release this it would make sense to have a brief intro in the GUI guide, even if it is just a clarification of the nomenclature for example. We can signal that saving and loading are upcoming features too.
Looking forward a bit, I was wondering two aspects:
- Re grouping regions in layers, apart from the organising benefit, is the plan that one layer is exportable as one JSON file? I was thinking what other advantages this grouping could have.
- Should we allow users to define ROIs for all frames? Right now the shapes only exist in the single frame we define them in. I think we should support a "propagate to all frames" button, that expands those ROIs defined at frame f to all frames. Then users can tweak them individually if needed. Let me know thoughts, it feels like a nice followup PR but if we go for it we should document it in an issue.
I mark this PR as approved as I trust your judgment to implement the suggestions, but feel free to req-request if you'd like a second look from me.
|
Looking forward a bit, I was wondering two aspects:
|
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RegionsStyle.opacity was defined but never applied. Now set on the layer so the face alpha (0.25) is not further compounded by napari's default layer-level opacity multiplier (0.7). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When drawing interactively, napari fires events in the order
data("adding") -> set_data -> data("added") and propagates the
selected shape's properties to the new shape. The set_data handler
was processing the new shape first (without overriding the propagated
name), so renaming e.g. "region" to "burrow" caused subsequent drawn
shapes to be named "burrow [1]" instead of "region [N]".
Add a guard flag (_adding_shape) that is set on "adding" and cleared
on "added", causing the set_data handler to defer to the data handler
which correctly applies DEFAULT_REGION_NAME. Also rename the parameter
assign_default_to_new -> use_default_name for clarity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
for more information, see https://pre-commit.ci
Summary of changesThanks for the amazing and details review @sfmig! Here's a summary of all the important changes I made based on your review, for future reference. The two areas I'd like feedback on are:
Bidirectional sync region layers and table
These were excellent suggestions. I was initially afraid of 'closing the loop' on these sync events, to avoid runaway circular callback. But Claude helped me do this safely with 're-entrancy' if guards. Now, selecting a shape also highlights the corresponding table row and vice versa. This works for single-shape selection (if user selects multiple shapes at once, we just don't highlight). Also, clicking a regions layer also automatically updates the dropdown now. It works beautifully for me and makes the UX much more intuitive! Creating a
|
sfmig
left a comment
There was a problem hiding this comment.
Hi @niksirbi!
I had a look at the documentation updates and left some small suggestions. It looks very good though.
The "Developing the napari plugin" section is great and I think it will be a very good reference for us too in the future. Nice that you did that while it was still fresh in your mind!
|
Below some response to your summary of changes. Re default region naming: if it simplifies our and users' lives downstream, I say we go with underscore. It is in any case intuitive enough. Re Bidirectional sync region layers and table: from the demo it seems to work very nicely and the docs on the potential gotchas are very nice and clear. Re Creating a movement regions layer: I like the simplified approach, and I agree with your thinking. We could also consider doing away with the button and have it at the bottom of the dropdown, like Octron does, but that could be a future PR to further simplify the UI. I also like this simplicity: "If you create a regions layer through the button, it will stay as such through renames, as the layer metadata flag will persist". I think all this rework will make our lives way easier when contributing to this bit in the future. I also agree with the naming of the attribute. Re Region names: "In effect, all auto-assigned names (at creation or copy) will be unique within the layer by default." - this sounds very sensible. I would have enforced that also for user input, but I see your point. And also thinking of a case with many "burrows" it may be helpful to not force users to make them all unique names, and just do that programmatically later. We can leave as is and see if this becomes an issue. Re [n] vs _n: I think I would vote for Re aligning the region-of-interest GUI and API: "I wonder whether we should also change the default name for BaseRegionOfInterest from "Un-named region" to just region" - I had not noticed this. Being internal it feels less important so maybe we can leave as is? Re linking edge and text colour: I think this is a nice and more readable than what I suggested. Re colour management: I like the simplicity and playing with napari rather than fighting against it :D. Re "Renaming a layer no longer changes its colour, since the assignment is order-based, not name-based" - this is much more intuitive I think. Re saving: I like that one layer -> one GeoJSON. It gives users implicitly flexibility on how to export/organise their files. Re ROIs along depth (i.e. time dimension): I wasn't aware about the difference between Thanks for the clarifications, it all looks very good for a first version of the ROI widget! |
Co-authored-by: sfmig <33267254+sfmig@users.noreply.github.com>
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
|
|
Thanks @sfmig. I've implemented your suggestions about the docs and have also changed default region name formatting to use underscores (instead of spaces and square brackets). I'm merging this now 🎉 |





Description
What is this PR
Why is this PR needed?
See #378.
What does this PR do?
Adds a new
RegionsWidgetto the napari plugin for interactively defining and managing regions of interest. The widget uses Qt's Model/View architecture to display regions drawn in napari Shapes layers.The concept of a "region layer" is key to all of this. A region layer is a Shapes layer that is marked with
movement_region_layerin layer metadata. It can be created by:Once a region layer has been created it remains as such, even through subsequent renames (until deleted).
Users can create one or many region layers, and can draw/edit multiple shapes per region layer. This is useful in situations with many regions, where it would make sense to group them into categories.
New shapes are auto-assigned the default name "Un-named", which can be edited via the table interface.
Key components added:
RegionsWidget: Main widget coordinating the UI, with:RegionsTableModel: Qt model wrapping a napari Shapes layer, exposing region names and shape typesRegionsTableView: Table view with bidirectional selection sync (clicking a row selects the shape in napari and vice versa)RegionsStyle: Style dataclass for consistent region appearance (semi-transparent faces, opaque edges/text)RegionsColorManager: Assigns deterministic colors to region layers based on layer name (sequential for default names, hash-based for custom names)Features:
Note that this PR doesn't cover conversions between
napariShapes andmovementRegionOfInterest (ROI) objects. These have been opened as separate issues and will be tackled in follow-up PRs, see #675 and #676.References
napariShapes layer and our ROI classes #676How has this PR been tested?
Unit tests have been added for
RegionsWidget,RegionsTableModel,RegionsTableView,RegionsStyle, andRegionsColorManager. Coverage forregions_widget.pyis complete, with only 2 unreachable defensive code lines excluded via# pragma: no cover(these handle edge cases that napari's property management currently prevents from occurring in practice).Additional tests were added for
loader_widgets.pyto cover themax_frame_idx < 0case when adding empty shapes layers.Integration tests make more sense once the full workflow exists (load data → draw regions → edit names → save regions), so I'm deferring these for now.
How should this be reviewed
This PR contains quite a lot of lines of code, but most of them are within the 2 new files, and a lot of it is Qt boilerplate.
For me it would be most useful to get feedback on the UI/UX of the new widget. Try using it from the perspective of a user, and break it if you can. The video below shows the envisioned workflow. Keep in mind that I use some shortcuts like Cmd + C and Cmd + V for copy-pasting shapes, and Delete for removing them. All the native napari Shape controls should work as expected.
napari-regions-widget-compressed.mp4
I'm slightly worried that I may have over-engineered this feature, so if you come across things/behaviours that are not needed, feel free to point them out.
Note
I've written the widget tests with the help of Claude, as the commit history shows.
It's my first time experimenting with Claude and I did my best to understand the code it wrote and verify everything. Please review the PR as usual, and do point out anything that doesn't make sense.
Is this a breaking change?
No.
Does this PR require an update to the documentation?
The GUI user guide will be updated once #675 and #676 are also addressed. The new widget is not very useful as is, without the ability to save the regions (alongside their names) to file.
Checklist: