Skip to content

Widget for drawing Regions of Interest (ROIs) as napari Shapes#617

Merged
niksirbi merged 92 commits intomainfrom
napari-roi-widget
Mar 23, 2026
Merged

Widget for drawing Regions of Interest (ROIs) as napari Shapes#617
niksirbi merged 92 commits intomainfrom
napari-roi-widget

Conversation

@niksirbi
Copy link
Copy Markdown
Member

@niksirbi niksirbi commented Jun 18, 2025

Description

What is this PR

  • Bug fix
  • Addition of a new feature
  • Other

Why is this PR needed?

See #378.

What does this PR do?

Adds a new RegionsWidget to 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_layer in layer metadata. It can be created by:

  1. Explicitly clicking 'Add new layer' on the widget
  2. Renaming an existing Shapes layer to a name that starts with "Region".

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:
    • Dropdown to select existing region layers
    • Button to create new region layers
    • Table view displaying regions drawn in the selected layer
  • RegionsTableModel: Qt model wrapping a napari Shapes layer, exposing region names and shape types
  • RegionsTableView: 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:

  • New regions get the default name "Un-named" (editable via the table)
  • Editable region names via double-click in the table
  • Copy-paste support that preserves copied region names
  • Contextual tooltips that update based on widget state
  • Consistent per-layer color styling
  • Proper cleanup of signal connections to prevent memory leaks

Note that this PR doesn't cover conversions between napari Shapes and movement RegionOfInterest (ROI) objects. These have been opened as separate issues and will be tackled in follow-up PRs, see #675 and #676.

References

How has this PR been tested?

Unit tests have been added for RegionsWidget, RegionsTableModel, RegionsTableView, RegionsStyle, and RegionsColorManager. Coverage for regions_widget.py is 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.py to cover the max_frame_idx < 0 case 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:

  • The code has been tested locally
  • Tests have been added to cover all new functionality
  • The documentation has been updated to reflect any changes
  • The code has been formatted with pre-commit

@lochhh lochhh added the GUI Graphical User Interface label Jun 19, 2025
@codecov
Copy link
Copy Markdown

codecov bot commented Jun 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e0fbb2b) to head (3c2c303).
⚠️ Report is 5 commits behind head on main.

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@niksirbi niksirbi force-pushed the napari-roi-widget branch 2 times, most recently from 2a48aaa to 3aab669 Compare July 7, 2025 21:51
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Jul 7, 2025

@niksirbi niksirbi force-pushed the napari-roi-widget branch 2 times, most recently from bc29ead to b0f665e Compare January 13, 2026 17:45
@niksirbi niksirbi changed the title A prototype implementation for an ROI drawing widget Widget for drawing Regions of Interest (ROIs) as napari Shapes Jan 20, 2026
@niksirbi niksirbi marked this pull request as ready for review January 20, 2026 10:36
@niksirbi niksirbi requested a review from sfmig January 20, 2026 10:36
@niksirbi
Copy link
Copy Markdown
Member Author

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.

@niksirbi
Copy link
Copy Markdown
Member Author

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.

@sfmig sfmig force-pushed the napari-roi-widget branch from 3b5af63 to 07ab8c2 Compare February 2, 2026 15:33
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Feb 2, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

Copy link
Copy Markdown
Member

@sfmig sfmig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 movement regions 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_layer flag, 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.

sfmig

This comment was marked as duplicate.

@sfmig
Copy link
Copy Markdown
Member

sfmig commented Mar 10, 2026

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.

niksirbi and others added 12 commits March 18, 2026 18:24
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>
@niksirbi
Copy link
Copy Markdown
Member Author

niksirbi commented Mar 20, 2026

Summary of changes

Thanks 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:

  • default region naming (See questions at the bottom of 'regions names' section). I've adopted napari's base [N] convention for auto-assigned unique names, but there's an open question about whether this should be aligned with the _N suffix convention used by compute_region_occupancy (and relatedly, whether the default name for BaseRegionOfInterest should change from "Un-named region" to region).
  • the documentation I've added (see 'Documentation' section). I've added a "Define regions of interest" section to the GUI guide and a "Developing the napari plugin" section to the contributing guide. Feedback on structure, content, and terminology would be very welcome.

Bidirectional sync region layers and table

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.

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!
I've also documented this 're-entrancy' guard pattern in the contributing guide (see 'Documentation' below).

Creating a movement regions layer

Re the two ways of creating movement ROI layers
...

  • I wonder if the simplest fix is to do away with these two options, and remove the possibility of creating a movement regions 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.

I decided to simplify things by having only one way to create a regions layer: by clicking the 'Add new layer' button. The reason for accommodating the 2nd way was in case someone had previously created a shapes layer independently of the widget, and now wanted to convert that to a 'regions' layer via renaming. It would be a nice-to-have, but I think it's a marginal use-case and not worth the extra machinery and associated confusion.

If you create a regions layer through the button, it will stay as such through renames, as the layer metadata flag will persist. I renamed that metadata flag to plural, i.e. movement_region_layer -> movement_regions_layer . It makes more sense this way, since a layer can hold more than one region, and it's more consistent with the default "regions" name of the layer (I make the latter lowercase to match 'tracks', 'points', 'boxes' layer names).

Region names

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 personally find this name ('Un-named') a bit awkward. How about "untitled"? Or we can follow napari and just use the generic name of the layer (i.e., "Shapes", "Points", so maybe for us "ROIs?" - this relates to the earlier point on nomenclature too).

I've changed this behaviour now.

The default name for newly drawn shapes is region (the singular of the layer name regions). Subsequent shapes get auto-assigned unique names like region [1], region [2] etc., exactly matching how napari layer names behave.

If you copy-paste a shape, it gets 'uniquified' in the same manner: e.g. if I copy-paste a shape named burrow, its duplicate will become burrow [1]; if I copy-paste burrow [1], the new shape will be burrow [2] etc. This also works if multiple shapes are copy-pasted at once.

In effect, all auto-assigned names (at creation or copy) will be unique within the layer by default.

The one place where uniqueness can be violated is when a user manually renames a region by editing the table. For example, a user might rename a region to burrow while another burrow already exists. I decided to allow that, because that action feels like a deliberate choice rather than an auto-assignment. This is also consistent with the movement API, which doesn't prevent creating collections of regions with duplicate names. Moreover, I'm planning for each regions layer to be saved/loaded to/from a GeoJSON file, and nothing in the spec itself (or how we currently validate it in movement) forbids duplicate names.

That said, there is a caveat. Some downstream operations on collections of regions enforce uniqueness. For example, [compute_region_occupancy](https://github.com/neuroinformatics-unit/movement/blob/e0fbb2b31d1a42b0bfde0536b6a7accb84241d43/movement/roi/conditions.py#L11) accepts collections with duplicate names as input, but deals with it accordingly:

When RoIs in regions have identical names, a suffix
will be appended to their name in the form of "_X", where "X" is a number
starting from 0. These numbers are zero-padded depending on the maximum
number of regions with identical names (e.g. if there are 100 RoIs with the
same name, "00" will be appended to the first of them)

Uniqueness in this function's output is necessary because region names become coordinates in the returned xarray.DataArray.

I'm personally fine with the following model for now:

  • Uniqueness is encouraged via auto-assigned names in our GUI, but not strictly required.
  • Downstream functions enforce uniqueness if and as needed, as compute_region_occupancy already does.

The one remaining inconsistency is that auto-assigned unique names in napari use base [N] suffixes, while compute_region_occupancy uses _N suffixes. I see three options:

  1. Keep as-is. The two conventions serve different contexts: [N] is a display convention following napari's own layer naming; _N is a programmatic convention suited to xarray coordinate names.
  2. Adopt the _N style in napari too (e.g. region, region_1, region_2). Note however that compute_region_occupancy renames all duplicates including the original zero-indexed (burrowburrow_0, burrow_1, burrow_2), while napari auto-naming preserves the original's name and only suffixes copies. Full alignment would require revisiting one or both algorithms. Underscore suffixes also look more technical in a UI label context.
  3. Adopt the [N] style in compute_region_occupancy (e.g. burrow, burrow [1], burrow [2]). However "burrow [1]" as an xarray coordinate name requires dict-style access (ds["burrow [1]"]) and would prevent attribute access (ds.burrow_1).

I'm genuinely open to opinions here since am myself split.

On the topic of 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 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 agree with you. This also matches how keypoint labels behave in the Points layer. Moreover, with the bidirectional sync in place, it's easy to determine names by clicking on the shape or on the table row.

  • 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.

I agree this is a good idea, but I'd tied the text colour to the edge colour (as opposed to face). This design decision is already encoded in edge_and_text_color inside RegionsStyle. This is the more opaque colour, made to contrast against the semi-transparent face colour. I've now 'tethered' the text colour to the edge colour, so it follows automatically if the user manually edits the edge colour (which they are now completely free to do, see next section).

Simpler colour management

It feels a bit weird to me that if I change the name of a layer, the color of all the shapes inside it will change. Especially considering that if I want to define a shapes layer as an ROI layer via the layers list, I have to do exactly that (e.g. first name it region so that it gets recognised as an ROI layer, then name it whatever I actually want to name it).

I feel this could be simplified by cycling through a set of colors based on order of creation. You could combine tab20b and tab20c and that should give you 40 different colours (in theory tab20 is also distinct so could be up to 60). If users define more than 40 ROI layers, then the colors with start from the initial one but they can change them manually if required.

I may be missing some complexities here re why this color manager is required tho.

You are right about the RegionsColorManager being over-engineered, and the hash-based colour assignment leading to some unexpected weird behaviours.

I've removed RegionsColorManager entirely. Colours are now assigned by a simple counter (_next_color_idx) on RegionsWidget, cycling through a fixed 10-colour tab10 palette in creation order. The assigned index is stored in layer metadata so it survives re-linking (e.g. switching away and back). This means:

  • Each new regions layer simply gets the next colour in the palette
  • Renaming a layer no longer changes its colour, since the assignment is order-based, not name-based

If the intention is to force users to use the same color for all shapes in one layer, then I guess this is ok but it seems overly limiting? In that case maybe it is more understandable to prevent users from editing colors at all.

If the intention is to use the same color for all ROIs in one layer by default, with the option of customising later, then maybe we don't need this function?

The color_all_shapes method was useful for creating regions layers by renaming existing Shapes layers. I've now eliminated that route, but the method itself remains useful for loading regions layers from GeoJSON files (which is coming soon).

The buggy/confusing behaviours you are describing were stemming from the fact that I was overzealous about controlling colours. And I agree this was overly (and unnecessarily) limiting for users.

I've now re-jigged my whole approach to colouring the layers, so that we mostly let napari's own property auto-copying behaviour (via current*) do its thing, instead of fighting against it (as I had been doing).

The new approach is:

  • When a new regions layer is created by clicking on the 'Add new layer' button (or, in the future, by loading from a GeoJSON file), it gets assigned a colour by a simple counter cycling sequentially through a fixed colormap (by order of creation). That colour will also apply to newly created shapes in that layer.
  • However, if the user manually changes the appearance of a shape (edge colour or face colour), we respect that, and we let napari manage current_* per-layer naturally: if a user recolours a shape, subsequent shapes in that layer inherit the new colour, and switching between layers correctly restores each layer's last-used colour. Shape additions and deletions no longer trigger a style reset.

So, in effect, the colour we assign to each layer only applies until the user decides to change it. After that we respect the user's choice by following default napari behaviour.

Looking forward

  • 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.

Yes, exactly. I've already started playing with the save/load functionality, and the simplest model is for each movement regions layer to be saved into its own GeoJSON file. Similarly, we will be able to load a GeoJSON file as a movement regions layer.

There is not a huge advantage to us supporting multiple layers, other than it's convenient if you want to split your many RoIs into layers (and hence files). For example, for crabs in the field, one might have a layer with all the burrows, and another one marking the boundary of beach/water (or any other useful feature).

  • 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 wan initially very perplexed by this comment, but after playing around with various ways of creating shapes in napari I think I figured it out.

In our widget, regions layers (as of now) can only be created via the "Add new layer" button, which calls viewer.add_shapes() programmatically. This creates a layer with ndim=2 by default, regardless of what other layers are loaded in the viewer. As a result, shape vertices are always stored as (y, x) (no time coordinate) and shapes are visible across all frames.

This is in contrast to a 'vanilla' Shapes layer created through the napari GUI, where layer inherits the viewer's current dimensionality. If a video is loaded, those shapes get (t, y, x) vertices and are sliced per frame, which is likely what you were observing (especially because of the 2nd way of creating region layers via renaming, which is now gone).

So the "propagate to all frames" behaviour you describe is not necessary in our current widget, as there is no frame information attached to each region anyway. This also aligns with how RegionsOfInterest objects work in the movement API, i.e. as static, frame-invariant objects. I agree this would be worth documenting more explicitly, and I'm happy to open a follow-up issue to track it.

Documentation

I've taken your advice to already add some documentation to this PR.

In fact I've added a new Define regions of interest section to the GUI guide, with subsections for:

  • Create a region layer
  • Draw and edit regions
  • Save and load regions (prompts to 'stay tuned')
  • Working with multiple region layers

This new section also include terminology definitions ('shape' vs 'region', 'shape layer' vs 'region layer') and screenshots. I also chose to update all other screenshots in the GUI guide, for 2 reasons:

  1. The old screenshot didn't show the new "Define regions of interest" menu
  2. I also tested this PR with the pre-release of napari v0.7.0 (It works!), and I thought making screenshots with that version will be more 'futureproof'. (there are some minor napari visual differences introduced in v0.7.0)

I've also added a "Developing the napari plugin" section to our contributing guide, which explains the general organisation of our plugin and widgets, and includes a sub-section on "Qt Model/View architecture" (and how it's used for the regions widget). The regions_widget.py module-level docstring now just links to the contributing guide for more info. See 759153c.

Copy link
Copy Markdown
Member

@sfmig sfmig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

@sfmig
Copy link
Copy Markdown
Member

sfmig commented Mar 20, 2026

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 _n if it simplifies the mental model. If I understand correctly, that would mean that in the table and the GeoJSON, regions names that show as burrow, burrow_1, burrow_2,... would show in the output of compute_region_occupancy as burrow_0, burrow_1, burrow_2,... That feels a bit more consistent than if in the UI and GeoJSON they show with square brackets, and the difference at 0 may be ok to handle.

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 (y,x) and (t,y,x) shapes layers, thanks for explaining. I think focusing on time-independent ROIs makes sense for now, but I do feel that adding a time dimension could be useful in the future. For example, if shapes change slightly across the video (small camera movements), you may want to define the ROIs on the first frame and correct them in later frames.

Thanks for the clarifications, it all looks very good for a first version of the ROI widget!

@sonarqubecloud
Copy link
Copy Markdown

@niksirbi
Copy link
Copy Markdown
Member Author

niksirbi commented Mar 23, 2026

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 🎉

@niksirbi niksirbi added this pull request to the merge queue Mar 23, 2026
Merged via the queue into main with commit 3efc8ca Mar 23, 2026
25 checks passed
@niksirbi niksirbi deleted the napari-roi-widget branch March 23, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

GUI Graphical User Interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provide a widget for drawing regions-of-interest (ROIs) in napari

3 participants