fix(segmentation): Allow for manually added label map and contour segmentations to be used as viewport data overlays. (#5562)

This commit is contained in:
Joe Boccanfuso 2025-11-13 07:12:28 -05:00 committed by GitHub
parent 5c44fe06dc
commit 02f6744dab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 25 additions and 15 deletions

View File

@ -233,7 +233,7 @@ class CornerstoneCacheService {
for (const displaySet of displaySets) { for (const displaySet of displaySets) {
const { Modality } = displaySet; const { Modality } = displaySet;
const isParametricMap = Modality === 'PMAP'; 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 // Don't create volumes for the displaySets that have custom load
// function (e.g., SEG, RT, since they rely on the reference volumes // 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 // Parametric maps do not have image ids but they already have volume data
// therefore a new volume should not be created. // therefore a new volume should not be created.
if (!isParametricMap && !isSeg && (!volumeImageIds || !volume)) { if (!isParametricMap && !isSegOrRtstruct && (!volumeImageIds || !volume)) {
volumeImageIds = this._getCornerstoneVolumeImageIds(displaySet, dataSource); volumeImageIds = this._getCornerstoneVolumeImageIds(displaySet, dataSource);
volume = await volumeLoader.createAndCacheVolume(volumeId, { volume = await volumeLoader.createAndCacheVolume(volumeId, {

View File

@ -125,6 +125,7 @@ describe('setUpSegmentationEventHandlers', () => {
it('should create and add display set when segmentation is added and no displaySet exists', () => { it('should create and add display set when segmentation is added and no displaySet exists', () => {
const mockSegmentation = { const mockSegmentation = {
label: 'Test Segmentation Label',
cachedStats: { cachedStats: {
info: 'Test Segmentation Label', info: 'Test Segmentation Label',
}, },
@ -151,12 +152,12 @@ describe('setUpSegmentationEventHandlers', () => {
displaySetInstanceUID: 'test-segmentation-id', displaySetInstanceUID: 'test-segmentation-id',
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
SeriesDescription: mockSegmentation.cachedStats.info, SeriesDescription: mockSegmentation.label,
Modality: 'SEG', Modality: 'SEG',
numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length, numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length,
imageIds: mockSegmentation.representationData.Labelmap.imageIds, imageIds: mockSegmentation.representationData.Labelmap.imageIds,
isOverlayDisplaySet: true, isOverlayDisplaySet: true,
label: mockSegmentation.cachedStats.info, label: mockSegmentation.label,
madeInClient: true, madeInClient: true,
segmentationId: 'test-segmentation-id', segmentationId: 'test-segmentation-id',
isDerived: true, isDerived: true,
@ -165,6 +166,7 @@ describe('setUpSegmentationEventHandlers', () => {
it('should handle displaySet undefined when segmentation is added', () => { it('should handle displaySet undefined when segmentation is added', () => {
const mockSegmentation = { const mockSegmentation = {
label: 'Test Segmentation Label',
cachedStats: { cachedStats: {
info: 'Test Segmentation Label', info: 'Test Segmentation Label',
}, },
@ -190,12 +192,12 @@ describe('setUpSegmentationEventHandlers', () => {
displaySetInstanceUID: 'test-segmentation-id', displaySetInstanceUID: 'test-segmentation-id',
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
SeriesDescription: mockSegmentation.cachedStats.info, SeriesDescription: mockSegmentation.label,
Modality: 'SEG', Modality: 'SEG',
numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length, numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length,
imageIds: mockSegmentation.representationData.Labelmap.imageIds, imageIds: mockSegmentation.representationData.Labelmap.imageIds,
isOverlayDisplaySet: true, isOverlayDisplaySet: true,
label: mockSegmentation.cachedStats.info, label: mockSegmentation.label,
madeInClient: true, madeInClient: true,
segmentationId: 'test-segmentation-id', segmentationId: 'test-segmentation-id',
isDerived: true, isDerived: true,
@ -204,6 +206,7 @@ describe('setUpSegmentationEventHandlers', () => {
it('should handle empty imageIds array', () => { it('should handle empty imageIds array', () => {
const mockSegmentation = { const mockSegmentation = {
label: 'Empty Segmentation',
cachedStats: { cachedStats: {
info: 'Empty Segmentation', info: 'Empty Segmentation',
}, },
@ -228,12 +231,12 @@ describe('setUpSegmentationEventHandlers', () => {
displaySetInstanceUID: 'empty-segmentation-id', displaySetInstanceUID: 'empty-segmentation-id',
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
SeriesDescription: mockSegmentation.cachedStats.info, SeriesDescription: mockSegmentation.label,
Modality: 'SEG', Modality: 'SEG',
numImageFrames: 0, numImageFrames: 0,
imageIds: [], imageIds: [],
isOverlayDisplaySet: true, isOverlayDisplaySet: true,
label: mockSegmentation.cachedStats.info, label: mockSegmentation.label,
madeInClient: true, madeInClient: true,
segmentationId: 'empty-segmentation-id', segmentationId: 'empty-segmentation-id',
isDerived: true, isDerived: true,
@ -242,6 +245,7 @@ describe('setUpSegmentationEventHandlers', () => {
it('should handle different segmentation label values', () => { it('should handle different segmentation label values', () => {
const mockSegmentation = { const mockSegmentation = {
label: 'Custom Label Text',
cachedStats: { cachedStats: {
info: 'Custom Label Text', info: 'Custom Label Text',
}, },
@ -280,6 +284,7 @@ describe('setUpSegmentationEventHandlers', () => {
it('should handle multiple segmentation events', () => { it('should handle multiple segmentation events', () => {
const mockSegmentation1 = { const mockSegmentation1 = {
label: 'Segmentation 1',
cachedStats: { cachedStats: {
info: 'Segmentation 1', info: 'Segmentation 1',
}, },
@ -291,6 +296,7 @@ describe('setUpSegmentationEventHandlers', () => {
}; };
const mockSegmentation2 = { const mockSegmentation2 = {
label: 'Segmentation 2',
cachedStats: { cachedStats: {
info: 'Segmentation 2', info: 'Segmentation 2',
}, },
@ -318,12 +324,12 @@ describe('setUpSegmentationEventHandlers', () => {
displaySetInstanceUID: 'segmentation-1', displaySetInstanceUID: 'segmentation-1',
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
SeriesDescription: mockSegmentation1.cachedStats.info, SeriesDescription: mockSegmentation1.label,
Modality: 'SEG', Modality: 'SEG',
numImageFrames: 1, numImageFrames: 1,
imageIds: mockSegmentation1.representationData.Labelmap.imageIds, imageIds: mockSegmentation1.representationData.Labelmap.imageIds,
isOverlayDisplaySet: true, isOverlayDisplaySet: true,
label: mockSegmentation1.cachedStats.info, label: mockSegmentation1.label,
madeInClient: true, madeInClient: true,
segmentationId: 'segmentation-1', segmentationId: 'segmentation-1',
isDerived: true, isDerived: true,
@ -332,12 +338,12 @@ describe('setUpSegmentationEventHandlers', () => {
displaySetInstanceUID: 'segmentation-2', displaySetInstanceUID: 'segmentation-2',
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
SeriesDescription: mockSegmentation2.cachedStats.info, SeriesDescription: mockSegmentation2.label,
Modality: 'SEG', Modality: 'SEG',
numImageFrames: 2, numImageFrames: 2,
imageIds: mockSegmentation2.representationData.Labelmap.imageIds, imageIds: mockSegmentation2.representationData.Labelmap.imageIds,
isOverlayDisplaySet: true, isOverlayDisplaySet: true,
label: mockSegmentation2.cachedStats.info, label: mockSegmentation2.label,
madeInClient: true, madeInClient: true,
segmentationId: 'segmentation-2', segmentationId: 'segmentation-2',
isDerived: true, isDerived: true,

View File

@ -1,3 +1,5 @@
import { Enums as csToolsEnums } from '@cornerstonejs/tools';
import { import {
setUpSelectedSegmentationsForViewportHandler, setUpSelectedSegmentationsForViewportHandler,
setupSegmentationDataModifiedHandler, setupSegmentationDataModifiedHandler,
@ -28,7 +30,7 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage
} }
const segmentation = segmentationService.getSegmentation(segmentationId); const segmentation = segmentationService.getSegmentation(segmentationId);
const label = segmentation.cachedStats.info; const label = segmentation.label;
const imageIds = const imageIds =
segmentation.representationData?.Labelmap?.imageIds ?? segmentation.representationData?.Labelmap?.imageIds ??
segmentation.representationData?.Contour?.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', SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
SeriesDescription: label, SeriesDescription: label,
Modality: 'SEG', Modality: segmentation.representationData[csToolsEnums.SegmentationRepresentations.Contour]
? 'RTSTRUCT'
: 'SEG',
numImageFrames: imageIds.length, numImageFrames: imageIds.length,
imageIds, imageIds,
isOverlayDisplaySet: true, isOverlayDisplaySet: true,

View File

@ -239,8 +239,8 @@ function modeFactory({ modeConfiguration }) {
leftPanels: [ohif.leftPanel], leftPanels: [ohif.leftPanel],
leftPanelResizable: true, leftPanelResizable: true,
rightPanels: [ rightPanels: [
cornerstone.contourSegmentationPanel,
cornerstone.labelMapSegmentationPanel, cornerstone.labelMapSegmentationPanel,
cornerstone.contourSegmentationPanel,
], ],
rightPanelResizable: true, rightPanelResizable: true,
// leftPanelClosed: true, // leftPanelClosed: true,