From d6ea179ac43a47d4011b8dcaf59d79d99382d6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Alves=20de=20Faria=20Resende?= Date: Fri, 5 Sep 2025 09:35:16 -0300 Subject: [PATCH] fix(cornerstone-dicom-rt): adding caching usage to cornerstone-dicom-rt SOP Class Handler (#5387) --- .../src/getSopClassHandlerModule.ts | 55 ++++++++++--------- .../cornerstone-dicom-rt/src/loadRTStruct.js | 34 ++++-------- .../SegmentationService.ts | 17 +++--- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/extensions/cornerstone-dicom-rt/src/getSopClassHandlerModule.ts b/extensions/cornerstone-dicom-rt/src/getSopClassHandlerModule.ts index 85b87b722..45564d7df 100644 --- a/extensions/cornerstone-dicom-rt/src/getSopClassHandlerModule.ts +++ b/extensions/cornerstone-dicom-rt/src/getSopClassHandlerModule.ts @@ -4,7 +4,11 @@ import i18n from '@ohif/i18n'; import { SOPClassHandlerId } from './id'; import loadRTStruct from './loadRTStruct'; -const sopClassUids = ['1.2.840.10008.5.1.4.1.1.481.3']; +const { sopClassDictionary } = utils; + +const sopClassUids = [sopClassDictionary.RTStructureSetStorage]; + +const cachedRTStructsSEG = new Set(); const loadPromises = {}; @@ -113,39 +117,45 @@ function _load( ) { const { SOPInstanceUID } = rtDisplaySet; const { segmentationService } = servicesManager.services; + if ( (rtDisplaySet.loading || rtDisplaySet.isLoaded) && loadPromises[SOPInstanceUID] && - _segmentationExistsInCache(rtDisplaySet, segmentationService) + cachedRTStructsSEG.has(rtDisplaySet.displaySetInstanceUID) ) { return loadPromises[SOPInstanceUID]; } rtDisplaySet.loading = true; + const { unsubscribe } = segmentationService.subscribe( + segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE, + (evt: { rtDisplaySet: { displaySetInstanceUID: string } }) => { + if (evt.rtDisplaySet?.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) { + cachedRTStructsSEG.add(rtDisplaySet.displaySetInstanceUID); + unsubscribe(); + } + } + ); + // We don't want to fire multiple loads, so we'll wait for the first to finish // and also return the same promise to any other callers. - loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => { - if (!rtDisplaySet.structureSet) { - const structureSet = await loadRTStruct(extensionManager, rtDisplaySet, headers); + loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => { + try { + if (!rtDisplaySet.structureSet) { + const structureSet = await loadRTStruct(extensionManager, rtDisplaySet, headers); + rtDisplaySet.structureSet = structureSet; + } - rtDisplaySet.structureSet = structureSet; - } + if (createSegmentation) { + await segmentationService.createSegmentationForRTDisplaySet(rtDisplaySet); + } - if (createSegmentation) { - segmentationService - .createSegmentationForRTDisplaySet(rtDisplaySet) - .then(() => { - rtDisplaySet.loading = false; - resolve(); - }) - .catch(error => { - rtDisplaySet.loading = false; - reject(error); - }); - } else { - rtDisplaySet.loading = false; resolve(); + } catch (error) { + reject(error); + } finally { + rtDisplaySet.loading = false; } }); @@ -187,11 +197,6 @@ function _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence( return ReferencedSeriesSequence; } -function _segmentationExistsInCache() { - // Todo: fix this - return false; -} - function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) { const { servicesManager, extensionManager } = params; diff --git a/extensions/cornerstone-dicom-rt/src/loadRTStruct.js b/extensions/cornerstone-dicom-rt/src/loadRTStruct.js index 0e9ab3841..72fcdb56b 100644 --- a/extensions/cornerstone-dicom-rt/src/loadRTStruct.js +++ b/extensions/cornerstone-dicom-rt/src/loadRTStruct.js @@ -137,16 +137,12 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet, continue; } - const isSupported = false; - const ContourSequenceArray = _toArray(ContourSequence); const contourPoints = []; - for (let c = 0; c < ContourSequenceArray.length; c++) { + for (const ContourSequenceItem of ContourSequenceArray) { const { ContourData, NumberOfContourPoints, ContourGeometricType, ContourImageSequence } = - ContourSequenceArray[c]; - - let isSupported = false; + ContourSequenceItem; const points = []; for (let p = 0; p < NumberOfContourPoints * 3; p += 3) { @@ -157,22 +153,18 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet, }); } - switch (ContourGeometricType) { - case 'CLOSED_PLANAR': - case 'OPEN_PLANAR': - case 'POINT': - isSupported = true; - - break; - default: - continue; - } + const supportedContourTypesMap = new Map([ + ['CLOSED_PLANAR', false], + ['OPEN_NONPLANAR', false], + ['OPEN_PLANAR', false], + ['POINT', true], + ]); contourPoints.push({ numberOfPoints: NumberOfContourPoints, points, type: ContourGeometricType, - isSupported, + isSupported: supportedContourTypesMap.get(ContourGeometricType) ?? false, }); if (ContourImageSequence?.ReferencedSOPInstanceUID) { @@ -187,8 +179,7 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet, StructureSetROISequence, RTROIObservationsSequence, ROIContour, - contourPoints, - isSupported + contourPoints ); } return structureSet; @@ -199,8 +190,7 @@ function _setROIContourMetadata( StructureSetROISequence, RTROIObservationsSequence, ROIContour, - contourPoints, - isSupported + contourPoints ) { const StructureSetROI = StructureSetROISequence.find( structureSetROI => structureSetROI.ROINumber === ROIContour.ReferencedROINumber @@ -211,9 +201,9 @@ function _setROIContourMetadata( ROIName: StructureSetROI.ROIName, ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm, ROIDescription: StructureSetROI.ROIDescription, - isSupported, contourPoints, visible: true, + colorArray: [], }; _setROIContourDataColor(ROIContour, ROIContourData); diff --git a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts index 457da8219..f1c7310a7 100644 --- a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts +++ b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts @@ -574,10 +574,19 @@ class SegmentationService extends PubSubService { // find the first image id that contains a referenced SOP instance UID const firstSegmentedSliceImageId = referencedImageIds?.find(imageId => - referencedImageIdsWithGeometry.some(referencedId => imageId.includes(referencedId)) + referencedImageIdsWithGeometry.some(referencedId => + imageId.includes(referencedId as string) + ) ) || null; rtDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId; + + if (!structureSet.ROIContours?.length) { + throw new Error( + 'The structureSet does not contain any ROIContours. Please ensure the structureSet is loaded first.' + ); + } + // Map ROI contours to RT Struct Data const allRTStructData = mapROIContoursToRTStructData(structureSet, rtDisplaySetUID); @@ -600,12 +609,6 @@ class SegmentationService extends PubSubService { }, }; - if (!structureSet.ROIContours?.length) { - throw new Error( - 'The structureSet does not contain any ROIContours. Please ensure the structureSet is loaded first.' - ); - } - const segments: { [segmentIndex: string]: cstTypes.Segment } = {}; let segmentsCachedStats = {};