Skip to content

Conversation

@Devu-trenser
Copy link
Contributor

@Devu-trenser Devu-trenser commented Oct 24, 2025

Context

Fixes issue:

This merge request refactors the segmentation representation computation and event subscription system.

Changes & Results

This merge request refactors the segmentation representation computation and event subscription system. The goal of this refactor is to:

  • Expose event subscription and removal APIs to OHIF.
  • Centralize listener management for better cleanup and memory safety.
  • Introduced a dedicated segmentationEventManager module to manage segmentation listeners.
  • Implemented a nested Map structure to track listeners per segmentation and representation type
  • Updated computeAndAddRepresentation to use centralized event management instead of directly adding/removing listeners.
  • Added utility functions to:
    • Remove a single representation listener (removeRepresentationListener).
    • Remove all listeners for a segmentation (removeAllSegmentationListeners).

Testing

  • Locally link the OHIF Devu-trenser:fix/restrict-labelmap-to-surface-conversion branch with the branch (Devu-trenser:fix/labelmap-to-surface-conversion)
  • Launch a data in viewer and switch to a volume (MPR) viewport.
  • Draw a segmentation using the brush tool.
  • Switch the layout to a 3D volume viewport.
  • Return to the stack/MPR viewport and continue drawing segments.
  • Verify Labelmap segmentations should not be converted to Surface on each draw if there is no enabled 3d volume viewport.

Checklist

PR

  • My Pull Request title is descriptive, accurate and follows the
    semantic-release format and guidelines.

Code

  • My code has been well-documented (function documentation, inline comments,
    etc.)

Public Documentation Updates

  • The documentation page has been updated as necessary for any public API
    additions or removals.

Tested Environment

  • [] "OS:
  • [] "Node version:
  • [] "Browser:

@jbocce
Copy link
Collaborator

jbocce commented Oct 26, 2025

I tested this PR elaborating on the instructions provided (thank you for such detail).

In summary I locally linked the OHIF master branch with the branch (Devu-trenser:fix/labelmap-to-surface-conversion) with the fix and I found that labelmaps failed to convert to surfaces when switching from MPR to 3D four up. That is to say the third step in the instructions failed.

Here are the specific steps I was using to test this PR...

  1. Checkout the branch Devu-trenser:fix/labelmap-to-surface-conversion
  2. Set up my sandbox for local linking following these instructions
  3. I ran OHIF.
  4. Navigated to http://localhost:3000/segmentation?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5
  5. Launched MPR with the series Body 3.0 CE
  6. Add a segmentation and used the brush tool to segment the coronal view. I added just a single stroke using the brush tool
  7. Launched 3D Four Up
  8. Return to MPR.
  9. Draw more with the brush tool and ensure each draw is NOT converted to a surface.

Unfortunately the testing failed on step 7 with the following exception(s) and failure to create a surface representation...

surfaceComputationStrategies.js:29 Error: Failed to convert labelmap to surface
    at computeSurfaceFromLabelmapSegmentation (surfaceComputationStrategies.js:54:1)
    at async Module.computeSurfaceData (surfaceComputationStrategies.js:22:1)
    at async computeAndAddRepresentation (computeAndAddRepresentation.js:4:1)
    at async Object.render (surfaceDisplay.js:32:1)
webWorkerManager.js:100 Error executing method 'convertLabelmapToSurface' on worker 'polySeg': TypeError: Cannot read properties of null (reading 'instance')
    at Object.convertLabelmapToSurface (vendors-node_modules_cornerstonejs_polymorphic-segmentation_dist_esm_workers_polySegConverter-544fc…:81:38)

Note that I tried the exact steps above using the commit prior to the one for this PR and there was no exception at step 7.

@jbocce jbocce self-requested a review October 26, 2025 23:27
Copy link
Collaborator

@jbocce jbocce left a comment

Choose a reason for hiding this comment

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

Testing failed.

@Devu-trenser
Copy link
Contributor Author

@jbocce
Thank you for the detailed testing and feedback.

I have verified the behavior again using both the latest OHIF master branch and the OHIF branch fix/restrict-labelmap-to-surface-conversion, linked with the cs3d branch Devu-trenser:fix/labelmap-to-surface-conversion.

Following the exact steps mentioned in your comment, I was unable to reproduce the issue and the labelmap to surface conversion worked as expected without any exceptions.

A reference video demonstrating the test workflow and results has been attached for your review.

Could you please confirm if you’re still able to reproduce this issue, or if I might be missing any specific steps in your testing process?

With latest OHIF master:

OSS.main.branch.mp4

With OHIF branch fix/restrict-labelmap-to-surface-conversion:

OSS.fix.branch.mp4

@jbocce
Copy link
Collaborator

jbocce commented Oct 29, 2025

@Devu-trenser, apologies, I am not sure what is wrong with my local system so I had a colleague go through my testing steps and it passed for them. Now I will look more closely at the code.

@Devu-trenser Devu-trenser force-pushed the fix/labelmap-to-surface-conversion branch from 6bd1709 to 26e53b0 Compare October 31, 2025 08:47
* @param options - Additional options for computing the representation.
* @param type - Representation type (e.g., LABELMAP, CONTOUR).
* @param computeFunction - Async function that computes representation data.
* @param updateFunction - Optional function to update UI/state on segmentation change.
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove this param from the doc too please

): (segmentationId: string) => Promise<void> {
const polySeg = getPolySeg();
return (segmentationId: string) =>
polySeg.updateSurfaceData(segmentationId, { viewport }).then();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need the .then() here?

const viewportId = display.render(viewport, representation);
viewportRenderList.push(viewportId);
display.render(viewport, representation).then(() => {
if (!existingRepresentation) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a bit curious for me. From what I can see the display.render also does a addDefaultSegmentationListener via a call to computeAndAddRepresentation. So why do we need this as well?

Copy link
Collaborator

@jbocce jbocce left a comment

Choose a reason for hiding this comment

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

This approach looks good to me. Just a few comments and questions to address. I tested the implementation and it looks great.

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