Skip to content

WIP: add check for incoming updated ROIs#136

Open
jo-mueller wants to merge 6 commits intoome:mainfrom
jo-mueller:sync-ROIs
Open

WIP: add check for incoming updated ROIs#136
jo-mueller wants to merge 6 commits intoome:mainfrom
jo-mueller:sync-ROIs

Conversation

@jo-mueller
Copy link
Collaborator

@jo-mueller jo-mueller commented Jan 15, 2026

Fixes #135

Hi @psobolewskiPhD ,

I started working on a feature that expands the download/upload functionality of the ROI widget. As you recently also noted, clicking the Upload button for an existing image and an attached shapes layer will also upload all the ROIs twice over. So what we'd actually want would be some sort of synchronization of ROIs.

I started implementing this and I think I have it working for the download so far:

15.01.2026_13.48.50_REC.mp4

This also works with the upload, but I think there's a bug to be ironed out in omero: When the upload button is clicked, the new code gets all roi/shape ids from the remote and checks them against the shape/roi ids in the layer.features. A new shape shouldn't have an roi/shape id yet, so these entries are None. The uploader then only uploads ROIs from the shapes/points/labels layer, if the roi_id and shape_ids are None. Vice versa, if there are roi_ids on the remote that do not exist locally, these are removed remotely upon clicking the upload button.

The napari "bug": When I draw a new shape in an existing shapes layer, the corresponding entry in the features table is automatically filled with the values from layer.feature_defaults I force-set feature_defaults to None because anything else is obviously problematic here, but it seems like simply selecting any given shape in the layer overrides the feature_defaults to the values of the given shape. Is this intended behavior in napari? I guess it makes sense when duplicating/copying a shape...but in this case it's really tricky :/

@jo-mueller jo-mueller added enhancement New feature or request help wanted Extra attention is needed labels Jan 15, 2026
@psobolewskiPhD
Copy link
Collaborator

Syncing sounds pretty nice, but are we maybe overcomplicating?

layer.feature_defaults

I'm not too familiar with this machinery, probably need to open a thread on zulip, maybe Lorenzo would know.

For the time being or in general: would it suffice to have an Add to OMERO ROIs button and Replace OMERO ROIs button? 🤔

@jo-mueller
Copy link
Collaborator Author

Syncing sounds pretty nice, but are we maybe overcomplicating?

Good question...Overriding would also be possible and not hard to put in. In that case, maybe overriding should be the default (and only?) behavior? I'm not sure in which scenario you would want to download ROIs, do something with them and then append them to the existing ROIs on Omero 🤔

The motivation for syncing came mostly from the experience that the upload of big ROIs in 3D can be painfully slow and I was hoping to keep the overhead of such transactions small. I.e., if you would want to download some annotations, inspect/correct them and just push your changes.

I was actually considering hashing the ROI values to check for changes in the ROIs but kept away from it because I thought I'd probably be overkill :D

TL;DR: I can also change it to overriding, but in that case I'd rather always override remote/local ROIs.

@Tom-TBT
Copy link

Tom-TBT commented Jan 20, 2026

I'm not sure in which scenario you would want to download ROIs, do something with them and then append them to the existing ROIs on Omero

Scenario in this direction:

  • ROIs in a certain version on OMERO "ground truth by user A"
  • user B connects to napari and download the ground truth of user A.
  • user B made changes, that's the "ground truth by user B"
  • We want to keep both version (or at least the ones that were corrected), to compare later on agreement in annotation (normally ground truth is made from scratch, but here that could be a "correction"

I was actually considering hashing the ROI values to check for changes in the ROIs but kept away from it because I thought I'd probably be overkill :D

I thought of that too writting this. I think for the least that could prompt a message:
"two ROIs have been modified from the server version, do you want to update them or upload new roi?"

Also having tagging of ROI mechanics could help handling these "duplicated" ROIs in the iviewer (I need to resume to work on this, right now just a working branch of mine from the hackathon https://github.com/Tom-TBT/omero-iviewer/tree/tags-on-roi)

@jo-mueller
Copy link
Collaborator Author

jo-mueller commented Jan 20, 2026

I thought of that too writting this. I think for the least that could prompt a message:
"two ROIs have been modified from the server version, do you want to update them or upload new roi?"

Maybe we add a config dropdown to the widget with two three options:

  • Replace
  • Update
  • Append

@Tom-TBT
Copy link

Tom-TBT commented Jan 28, 2026

Hi Johannes,
testing this PR, I end up with empty ROIs (seems that the shapes aren't attached to the ROIs).

In the ivewer I shows me I have 7 ROIs, but none are listed. I looked at the JSON response from the server and there were indeed no shape; Reloading the shapes of the image with napari-omero also did not gave me the shapes.

Is it only on my side and maybe a misconfiguration?

@jo-mueller
Copy link
Collaborator Author

jo-mueller commented Jan 28, 2026

Hah, I think I also encountered this at some point. Maaaybe it's when shapes are deleted but not the ROIs they're part of?

What's the configuration you start with/workflow you do? Empty image > download to napari > annotate > upload? Or vice versa? I think I haven't properly implemented it for the upload yet, only for the download

@Tom-TBT
Copy link

Tom-TBT commented Jan 28, 2026

Oh ok I understood it wrong from the start. I was looking into the upload of ROIs, napari→OMERO
Which I see issue with, but I'm having hard time reproducing what I saw.

  • Once it cleared the shapes of my ROIs (I had 5 of different types)
  • Once the upload uploaded a new empty ROI
  • Now the upload doesn't do anything
  • And finally a crash saying roi_id is not defined

But if I'm focussing now on the direction OMERO→napari, things seems to work fine. But I don't understand then the nuance between Replace and Update. In the direction napari→OMERO, it makes sense because ROIs are identified from their ID (and can be linked to annotation). But does it have a meaning in napari world?

@jo-mueller
Copy link
Collaborator Author

Thanks for the description, I'll try and reproduce.

But I don't understand then the nuance between Replace and Update.

The outcome of both should be the same. They are just different in how many ROIs are loaded back and forth. For Update, only the changed ROIs should be updated, the rest should stay the same. For Replace, all are downloaded again.

Whereas the directions are concerned: When the plugin downloads an ROI/shape from OMERO into napari, all the roi_ids and shape_ids are stored in a pandas table that's attached to the respective layer. I.e., if a point layer is created, you can inspect point_layer.features and find a pandas dataframe which holds each point's respective roi_id and shape_id. If a point is deleted locally, comparison will show that it exists remotely, but not locally. Vice versa, if a new point is added/drawn locally, the roi_id and shape_id value for this column will be unset.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request help wanted Extra attention is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discussion: handling pull/push of annotations

3 participants