From 02f6744dab993f16cf2516fd5f657e0743e3fa42 Mon Sep 17 00:00:00 2001 From: Joe Boccanfuso <109477394+jbocce@users.noreply.github.com> Date: Thu, 13 Nov 2025 07:12:28 -0500 Subject: [PATCH] fix(segmentation): Allow for manually added label map and contour segmentations to be used as viewport data overlays. (#5562) --- .../CornerstoneCacheService.ts | 4 +-- .../setUpSegmentationEventHandlers.test.ts | 26 ++++++++++++------- .../utils/setUpSegmentationEventHandlers.ts | 8 ++++-- modes/segmentation/src/index.tsx | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts b/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts index fe14ade92..8940a17cd 100644 --- a/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts +++ b/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts @@ -233,7 +233,7 @@ class CornerstoneCacheService { for (const displaySet of displaySets) { const { Modality } = displaySet; const isParametricMap = Modality === 'PMAP'; - const isSeg = Modality === 'SEG'; + const isSegOrRtstruct = Modality === 'SEG' || Modality === 'RTSTRUCT'; // Don't create volumes for the displaySets that have custom load // function (e.g., SEG, RT, since they rely on the reference volumes @@ -276,7 +276,7 @@ class CornerstoneCacheService { // Parametric maps do not have image ids but they already have volume data // therefore a new volume should not be created. - if (!isParametricMap && !isSeg && (!volumeImageIds || !volume)) { + if (!isParametricMap && !isSegOrRtstruct && (!volumeImageIds || !volume)) { volumeImageIds = this._getCornerstoneVolumeImageIds(displaySet, dataSource); volume = await volumeLoader.createAndCacheVolume(volumeId, { diff --git a/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.test.ts b/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.test.ts index 0f4117ebf..90e611217 100644 --- a/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.test.ts +++ b/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.test.ts @@ -125,6 +125,7 @@ describe('setUpSegmentationEventHandlers', () => { it('should create and add display set when segmentation is added and no displaySet exists', () => { const mockSegmentation = { + label: 'Test Segmentation Label', cachedStats: { info: 'Test Segmentation Label', }, @@ -151,12 +152,12 @@ describe('setUpSegmentationEventHandlers', () => { displaySetInstanceUID: 'test-segmentation-id', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', - SeriesDescription: mockSegmentation.cachedStats.info, + SeriesDescription: mockSegmentation.label, Modality: 'SEG', numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length, imageIds: mockSegmentation.representationData.Labelmap.imageIds, isOverlayDisplaySet: true, - label: mockSegmentation.cachedStats.info, + label: mockSegmentation.label, madeInClient: true, segmentationId: 'test-segmentation-id', isDerived: true, @@ -165,6 +166,7 @@ describe('setUpSegmentationEventHandlers', () => { it('should handle displaySet undefined when segmentation is added', () => { const mockSegmentation = { + label: 'Test Segmentation Label', cachedStats: { info: 'Test Segmentation Label', }, @@ -190,12 +192,12 @@ describe('setUpSegmentationEventHandlers', () => { displaySetInstanceUID: 'test-segmentation-id', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', - SeriesDescription: mockSegmentation.cachedStats.info, + SeriesDescription: mockSegmentation.label, Modality: 'SEG', numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length, imageIds: mockSegmentation.representationData.Labelmap.imageIds, isOverlayDisplaySet: true, - label: mockSegmentation.cachedStats.info, + label: mockSegmentation.label, madeInClient: true, segmentationId: 'test-segmentation-id', isDerived: true, @@ -204,6 +206,7 @@ describe('setUpSegmentationEventHandlers', () => { it('should handle empty imageIds array', () => { const mockSegmentation = { + label: 'Empty Segmentation', cachedStats: { info: 'Empty Segmentation', }, @@ -228,12 +231,12 @@ describe('setUpSegmentationEventHandlers', () => { displaySetInstanceUID: 'empty-segmentation-id', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', - SeriesDescription: mockSegmentation.cachedStats.info, + SeriesDescription: mockSegmentation.label, Modality: 'SEG', numImageFrames: 0, imageIds: [], isOverlayDisplaySet: true, - label: mockSegmentation.cachedStats.info, + label: mockSegmentation.label, madeInClient: true, segmentationId: 'empty-segmentation-id', isDerived: true, @@ -242,6 +245,7 @@ describe('setUpSegmentationEventHandlers', () => { it('should handle different segmentation label values', () => { const mockSegmentation = { + label: 'Custom Label Text', cachedStats: { info: 'Custom Label Text', }, @@ -280,6 +284,7 @@ describe('setUpSegmentationEventHandlers', () => { it('should handle multiple segmentation events', () => { const mockSegmentation1 = { + label: 'Segmentation 1', cachedStats: { info: 'Segmentation 1', }, @@ -291,6 +296,7 @@ describe('setUpSegmentationEventHandlers', () => { }; const mockSegmentation2 = { + label: 'Segmentation 2', cachedStats: { info: 'Segmentation 2', }, @@ -318,12 +324,12 @@ describe('setUpSegmentationEventHandlers', () => { displaySetInstanceUID: 'segmentation-1', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', - SeriesDescription: mockSegmentation1.cachedStats.info, + SeriesDescription: mockSegmentation1.label, Modality: 'SEG', numImageFrames: 1, imageIds: mockSegmentation1.representationData.Labelmap.imageIds, isOverlayDisplaySet: true, - label: mockSegmentation1.cachedStats.info, + label: mockSegmentation1.label, madeInClient: true, segmentationId: 'segmentation-1', isDerived: true, @@ -332,12 +338,12 @@ describe('setUpSegmentationEventHandlers', () => { displaySetInstanceUID: 'segmentation-2', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', - SeriesDescription: mockSegmentation2.cachedStats.info, + SeriesDescription: mockSegmentation2.label, Modality: 'SEG', numImageFrames: 2, imageIds: mockSegmentation2.representationData.Labelmap.imageIds, isOverlayDisplaySet: true, - label: mockSegmentation2.cachedStats.info, + label: mockSegmentation2.label, madeInClient: true, segmentationId: 'segmentation-2', isDerived: true, diff --git a/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.ts b/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.ts index bd255ae1b..c7104081f 100644 --- a/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.ts +++ b/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.ts @@ -1,3 +1,5 @@ +import { Enums as csToolsEnums } from '@cornerstonejs/tools'; + import { setUpSelectedSegmentationsForViewportHandler, setupSegmentationDataModifiedHandler, @@ -28,7 +30,7 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage } const segmentation = segmentationService.getSegmentation(segmentationId); - const label = segmentation.cachedStats.info; + const label = segmentation.label; const imageIds = segmentation.representationData?.Labelmap?.imageIds ?? segmentation.representationData?.Contour?.imageIds; @@ -39,7 +41,9 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', SeriesDescription: label, - Modality: 'SEG', + Modality: segmentation.representationData[csToolsEnums.SegmentationRepresentations.Contour] + ? 'RTSTRUCT' + : 'SEG', numImageFrames: imageIds.length, imageIds, isOverlayDisplaySet: true, diff --git a/modes/segmentation/src/index.tsx b/modes/segmentation/src/index.tsx index dcb398e3f..18eae732e 100644 --- a/modes/segmentation/src/index.tsx +++ b/modes/segmentation/src/index.tsx @@ -239,8 +239,8 @@ function modeFactory({ modeConfiguration }) { leftPanels: [ohif.leftPanel], leftPanelResizable: true, rightPanels: [ - cornerstone.contourSegmentationPanel, cornerstone.labelMapSegmentationPanel, + cornerstone.contourSegmentationPanel, ], rightPanelResizable: true, // leftPanelClosed: true,