fix(cornerstone-dicom-rt): adding caching usage to cornerstone-dicom-rt SOP Class Handler (#5387)

This commit is contained in:
Vinícius Alves de Faria Resende 2025-09-05 09:35:16 -03:00 committed by GitHub
parent 7de1c78fc4
commit d6ea179ac4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 52 additions and 54 deletions

View File

@ -4,7 +4,11 @@ import i18n from '@ohif/i18n';
import { SOPClassHandlerId } from './id'; import { SOPClassHandlerId } from './id';
import loadRTStruct from './loadRTStruct'; 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<string>();
const loadPromises = {}; const loadPromises = {};
@ -113,39 +117,45 @@ function _load(
) { ) {
const { SOPInstanceUID } = rtDisplaySet; const { SOPInstanceUID } = rtDisplaySet;
const { segmentationService } = servicesManager.services; const { segmentationService } = servicesManager.services;
if ( if (
(rtDisplaySet.loading || rtDisplaySet.isLoaded) && (rtDisplaySet.loading || rtDisplaySet.isLoaded) &&
loadPromises[SOPInstanceUID] && loadPromises[SOPInstanceUID] &&
_segmentationExistsInCache(rtDisplaySet, segmentationService) cachedRTStructsSEG.has(rtDisplaySet.displaySetInstanceUID)
) { ) {
return loadPromises[SOPInstanceUID]; return loadPromises[SOPInstanceUID];
} }
rtDisplaySet.loading = true; 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 // 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. // and also return the same promise to any other callers.
loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => { loadPromises[SOPInstanceUID] = new Promise<void>(async (resolve, reject) => {
try {
if (!rtDisplaySet.structureSet) { if (!rtDisplaySet.structureSet) {
const structureSet = await loadRTStruct(extensionManager, rtDisplaySet, headers); const structureSet = await loadRTStruct(extensionManager, rtDisplaySet, headers);
rtDisplaySet.structureSet = structureSet; rtDisplaySet.structureSet = structureSet;
} }
if (createSegmentation) { if (createSegmentation) {
segmentationService await segmentationService.createSegmentationForRTDisplaySet(rtDisplaySet);
.createSegmentationForRTDisplaySet(rtDisplaySet) }
.then(() => {
rtDisplaySet.loading = false;
resolve(); resolve();
}) } catch (error) {
.catch(error => {
rtDisplaySet.loading = false;
reject(error); reject(error);
}); } finally {
} else {
rtDisplaySet.loading = false; rtDisplaySet.loading = false;
resolve();
} }
}); });
@ -187,11 +197,6 @@ function _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
return ReferencedSeriesSequence; return ReferencedSeriesSequence;
} }
function _segmentationExistsInCache() {
// Todo: fix this
return false;
}
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) { function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
const { servicesManager, extensionManager } = params; const { servicesManager, extensionManager } = params;

View File

@ -137,16 +137,12 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
continue; continue;
} }
const isSupported = false;
const ContourSequenceArray = _toArray(ContourSequence); const ContourSequenceArray = _toArray(ContourSequence);
const contourPoints = []; const contourPoints = [];
for (let c = 0; c < ContourSequenceArray.length; c++) { for (const ContourSequenceItem of ContourSequenceArray) {
const { ContourData, NumberOfContourPoints, ContourGeometricType, ContourImageSequence } = const { ContourData, NumberOfContourPoints, ContourGeometricType, ContourImageSequence } =
ContourSequenceArray[c]; ContourSequenceItem;
let isSupported = false;
const points = []; const points = [];
for (let p = 0; p < NumberOfContourPoints * 3; p += 3) { for (let p = 0; p < NumberOfContourPoints * 3; p += 3) {
@ -157,22 +153,18 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
}); });
} }
switch (ContourGeometricType) { const supportedContourTypesMap = new Map([
case 'CLOSED_PLANAR': ['CLOSED_PLANAR', false],
case 'OPEN_PLANAR': ['OPEN_NONPLANAR', false],
case 'POINT': ['OPEN_PLANAR', false],
isSupported = true; ['POINT', true],
]);
break;
default:
continue;
}
contourPoints.push({ contourPoints.push({
numberOfPoints: NumberOfContourPoints, numberOfPoints: NumberOfContourPoints,
points, points,
type: ContourGeometricType, type: ContourGeometricType,
isSupported, isSupported: supportedContourTypesMap.get(ContourGeometricType) ?? false,
}); });
if (ContourImageSequence?.ReferencedSOPInstanceUID) { if (ContourImageSequence?.ReferencedSOPInstanceUID) {
@ -187,8 +179,7 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
StructureSetROISequence, StructureSetROISequence,
RTROIObservationsSequence, RTROIObservationsSequence,
ROIContour, ROIContour,
contourPoints, contourPoints
isSupported
); );
} }
return structureSet; return structureSet;
@ -199,8 +190,7 @@ function _setROIContourMetadata(
StructureSetROISequence, StructureSetROISequence,
RTROIObservationsSequence, RTROIObservationsSequence,
ROIContour, ROIContour,
contourPoints, contourPoints
isSupported
) { ) {
const StructureSetROI = StructureSetROISequence.find( const StructureSetROI = StructureSetROISequence.find(
structureSetROI => structureSetROI.ROINumber === ROIContour.ReferencedROINumber structureSetROI => structureSetROI.ROINumber === ROIContour.ReferencedROINumber
@ -211,9 +201,9 @@ function _setROIContourMetadata(
ROIName: StructureSetROI.ROIName, ROIName: StructureSetROI.ROIName,
ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm, ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm,
ROIDescription: StructureSetROI.ROIDescription, ROIDescription: StructureSetROI.ROIDescription,
isSupported,
contourPoints, contourPoints,
visible: true, visible: true,
colorArray: [],
}; };
_setROIContourDataColor(ROIContour, ROIContourData); _setROIContourDataColor(ROIContour, ROIContourData);

View File

@ -574,10 +574,19 @@ class SegmentationService extends PubSubService {
// find the first image id that contains a referenced SOP instance UID // find the first image id that contains a referenced SOP instance UID
const firstSegmentedSliceImageId = const firstSegmentedSliceImageId =
referencedImageIds?.find(imageId => referencedImageIds?.find(imageId =>
referencedImageIdsWithGeometry.some(referencedId => imageId.includes(referencedId)) referencedImageIdsWithGeometry.some(referencedId =>
imageId.includes(referencedId as string)
)
) || null; ) || null;
rtDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId; 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 // Map ROI contours to RT Struct Data
const allRTStructData = mapROIContoursToRTStructData(structureSet, rtDisplaySetUID); 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 } = {}; const segments: { [segmentIndex: string]: cstTypes.Segment } = {};
let segmentsCachedStats = {}; let segmentsCachedStats = {};