From 18686c2caf13ede3e881303100bd4cc34b8b135f Mon Sep 17 00:00:00 2001 From: Ibrahim <93064150+IbrahimCSAE@users.noreply.github.com> Date: Wed, 29 May 2024 09:12:39 -0400 Subject: [PATCH] feat(measurements): show untracked measurements in measurement panel under additional findings (#4160) --- extensions/cornerstone-dicom-seg/package.json | 4 +-- extensions/cornerstone-dicom-sr/package.json | 6 ++-- .../cornerstone-dynamic-volume/package.json | 6 ++-- extensions/cornerstone/package.json | 10 +++--- .../utils/measurementServiceMappings/Angle.ts | 26 ++++++-------- .../ArrowAnnotate.ts | 20 +++++------ .../Bidirectional.ts | 24 ++++++------- .../measurementServiceMappings/CircleROI.ts | 33 ++++++++--------- .../measurementServiceMappings/CobbAngle.ts | 26 ++++++-------- .../EllipticalROI.ts | 25 ++++++------- .../measurementServiceMappings/Length.ts | 24 ++++++------- .../PlanarFreehandROI.ts | 35 +++++++++++-------- .../utils/measurementServiceMappings/Probe.ts | 29 ++++++++------- .../RectangleROI.ts | 33 ++++++++--------- .../utils/getSOPInstanceAttributes.js | 16 +++++++-- extensions/measurement-tracking/package.json | 4 +-- .../promptSaveReport.js | 8 +++-- .../PanelMeasurementTableTracking/index.tsx | 23 +++++++++--- platform/app/package.json | 2 +- platform/core/package.json | 2 +- .../MeasurementService/MeasurementService.ts | 1 + 21 files changed, 181 insertions(+), 176 deletions(-) diff --git a/extensions/cornerstone-dicom-seg/package.json b/extensions/cornerstone-dicom-seg/package.json index 8e7b062c0..38156a40b 100644 --- a/extensions/cornerstone-dicom-seg/package.json +++ b/extensions/cornerstone-dicom-seg/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^1.74.7", - "@cornerstonejs/core": "^1.74.7", + "@cornerstonejs/adapters": "^1.74.8", + "@cornerstonejs/core": "^1.74.8", "@kitware/vtk.js": "30.4.1", "react-color": "^2.19.3" } diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json index cf6d09eb8..798aadcc7 100644 --- a/extensions/cornerstone-dicom-sr/package.json +++ b/extensions/cornerstone-dicom-sr/package.json @@ -46,9 +46,9 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^1.74.7", - "@cornerstonejs/core": "^1.74.7", - "@cornerstonejs/tools": "^1.74.7", + "@cornerstonejs/adapters": "^1.74.8", + "@cornerstonejs/core": "^1.74.8", + "@cornerstonejs/tools": "^1.74.8", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone-dynamic-volume/package.json b/extensions/cornerstone-dynamic-volume/package.json index de60d3e89..7d12031e4 100644 --- a/extensions/cornerstone-dynamic-volume/package.json +++ b/extensions/cornerstone-dynamic-volume/package.json @@ -42,9 +42,9 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/core": "^1.74.7", - "@cornerstonejs/streaming-image-volume-loader": "^1.74.7", - "@cornerstonejs/tools": "^1.74.7", + "@cornerstonejs/core": "^1.74.8", + "@cornerstonejs/streaming-image-volume-loader": "^1.74.8", + "@cornerstonejs/tools": "^1.74.8", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index 4acf33832..6ea25abff 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -38,7 +38,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.2", - "@cornerstonejs/dicom-image-loader": "^1.74.7", + "@cornerstonejs/dicom-image-loader": "^1.74.8", "@icr/polyseg-wasm": "^0.4.0", "@ohif/core": "3.9.0-beta.23", "@ohif/ui": "3.9.0-beta.23", @@ -55,10 +55,10 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^1.74.7", - "@cornerstonejs/core": "^1.74.7", - "@cornerstonejs/streaming-image-volume-loader": "^1.74.7", - "@cornerstonejs/tools": "^1.74.7", + "@cornerstonejs/adapters": "^1.74.8", + "@cornerstonejs/core": "^1.74.8", + "@cornerstonejs/streaming-image-volume-loader": "^1.74.8", + "@cornerstonejs/tools": "^1.74.8", "@icr/polyseg-wasm": "^0.4.0", "@kitware/vtk.js": "30.4.1", "html2canvas": "^1.4.1", diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Angle.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/Angle.ts index 2e178f2d5..1ef0ecf49 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/Angle.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Angle.ts @@ -36,8 +36,8 @@ const Angle = { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, - CornerstoneViewportService, - viewportId + displaySetService, + annotation ); let displaySet; @@ -48,7 +48,7 @@ const Angle = { SeriesInstanceUID ); } else { - displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points, textBox } = data.handles; @@ -76,11 +76,12 @@ const Angle = { data: data.cachedStats, type: getValueTypeFromToolType(toolName), getReport, + referencedImageId, }; }, }; -function getMappedAnnotations(annotation, DisplaySetService) { +function getMappedAnnotations(annotation, displaySetService) { const { metadata, data } = annotation; const { cachedStats } = data; const { referencedImageId } = metadata; @@ -94,19 +95,14 @@ function getMappedAnnotations(annotation, DisplaySetService) { Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; - if (!referencedImageId) { - throw new Error('Non-acquisition plane measurement mapping not supported'); - } - - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; const { angle } = targetStats; const unit = '\u00B0'; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.ts index 373bd115c..18aee0c23 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.ts @@ -34,8 +34,8 @@ const Length = { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, - cornerstoneViewportService, - viewportId + displaySetService, + annotation ); let displaySet; @@ -46,7 +46,7 @@ const Length = { SeriesInstanceUID ); } else { - displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points, textBox } = data.handles; @@ -64,6 +64,7 @@ const Length = { metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, frameNumber: mappedAnnotations[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, @@ -85,15 +86,14 @@ function getMappedAnnotations(annotation, displaySetService) { const annotations = []; - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; annotations.push({ diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.ts index 6517445d2..d2b1f81f9 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.ts @@ -31,8 +31,8 @@ const Bidirectional = { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, - cornerstoneViewportService, - viewportId + displaySetService, + annotation ); let displaySet; @@ -43,7 +43,7 @@ const Bidirectional = { SeriesInstanceUID ); } else { - displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points, textBox } = data.handles; @@ -63,6 +63,7 @@ const Bidirectional = { metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, frameNumber: mappedAnnotations[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, @@ -89,19 +90,14 @@ function getMappedAnnotations(annotation, displaySetService) { Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; - if (!referencedImageId) { - throw new Error('Non-acquisition plane measurement mapping not supported'); - } - - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; const { length, width, unit } = targetStats; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/CircleROI.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/CircleROI.ts index 88ca76862..1e2e85a3f 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/CircleROI.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/CircleROI.ts @@ -8,7 +8,7 @@ const CircleROI = { toAnnotation: measurement => {}, toMeasurement: ( csToolsEventDetail, - DisplaySetService, + displaySetService, CornerstoneViewportService, getValueTypeFromToolType, customizationService @@ -30,24 +30,24 @@ const CircleROI = { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, - CornerstoneViewportService, - viewportId + displaySetService, + annotation ); let displaySet; if (SOPInstanceUID) { - displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( + displaySet = displaySetService.getDisplaySetForSOPInstanceUID( SOPInstanceUID, SeriesInstanceUID ); } else { - displaySet = DisplaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points, textBox } = data.handles; - const mappedAnnotations = getMappedAnnotations(annotation, DisplaySetService); + const mappedAnnotations = getMappedAnnotations(annotation, displaySetService); const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService); const getReport = () => @@ -62,6 +62,7 @@ const CircleROI = { metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, frameNumber: mappedAnnotations[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, @@ -74,7 +75,7 @@ const CircleROI = { }, }; -function getMappedAnnotations(annotation, DisplaySetService) { +function getMappedAnnotations(annotation, displaySetService) { const { metadata, data } = annotation; const { cachedStats } = data; const { referencedImageId } = metadata; @@ -88,20 +89,14 @@ function getMappedAnnotations(annotation, DisplaySetService) { Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; - if (!referencedImageId) { - // Todo: Non-acquisition plane measurement mapping not supported yet - throw new Error('Non-acquisition plane measurement mapping not supported'); - } - - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/CobbAngle.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/CobbAngle.ts index 631f0d029..91f2c1576 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/CobbAngle.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/CobbAngle.ts @@ -36,8 +36,8 @@ const CobbAngle = { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, - CornerstoneViewportService, - viewportId + displaySetService, + annotation ); let displaySet; @@ -48,7 +48,7 @@ const CobbAngle = { SeriesInstanceUID ); } else { - displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points, textBox } = data.handles; @@ -68,6 +68,7 @@ const CobbAngle = { metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, frameNumber: mappedAnnotations?.[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, @@ -80,7 +81,7 @@ const CobbAngle = { }, }; -function getMappedAnnotations(annotation, DisplaySetService) { +function getMappedAnnotations(annotation, displaySetService) { const { metadata, data } = annotation; const { cachedStats } = data; const { referencedImageId } = metadata; @@ -94,19 +95,14 @@ function getMappedAnnotations(annotation, DisplaySetService) { Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; - if (!referencedImageId) { - throw new Error('Non-acquisition plane measurement mapping not supported'); - } - - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; const { angle } = targetStats; const unit = '\u00B0'; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.ts index c406b9972..54f156abb 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalROI.ts @@ -30,8 +30,8 @@ const EllipticalROI = { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, - cornerstoneViewportService, - viewportId + displaySetService, + annotation ); let displaySet; @@ -42,7 +42,7 @@ const EllipticalROI = { SeriesInstanceUID ); } else { - displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points, textBox } = data.handles; @@ -62,6 +62,7 @@ const EllipticalROI = { metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, frameNumber: mappedAnnotations[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, @@ -88,20 +89,14 @@ function getMappedAnnotations(annotation, displaySetService) { Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; - if (!referencedImageId) { - // Todo: Non-acquisition plane measurement mapping not supported yet - throw new Error('Non-acquisition plane measurement mapping not supported'); - } - - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Length.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/Length.ts index bb1319b24..9ebad2b48 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/Length.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Length.ts @@ -35,8 +35,8 @@ const Length = { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, - cornerstoneViewportService, - viewportId + displaySetService, + annotation ); let displaySet; @@ -47,7 +47,7 @@ const Length = { SeriesInstanceUID ); } else { - displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points, textBox } = data.handles; @@ -67,6 +67,7 @@ const Length = { metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, frameNumber: mappedAnnotations[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, @@ -93,19 +94,14 @@ function getMappedAnnotations(annotation, displaySetService) { Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; - if (!referencedImageId) { - throw new Error('Non-acquisition plane measurement mapping not supported'); - } - - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; const { length, unit = 'mm' } = targetStats; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/PlanarFreehandROI.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/PlanarFreehandROI.ts index d3c837c2a..10c6b9467 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/PlanarFreehandROI.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/PlanarFreehandROI.ts @@ -20,7 +20,7 @@ const PlanarFreehandROI = { */ toMeasurement: ( csToolsEventDetail, - DisplaySetService, + displaySetService, CornerstoneViewportService, getValueTypeFromToolType, customizationService @@ -40,16 +40,16 @@ const PlanarFreehandROI = { } const { SOPInstanceUID, SeriesInstanceUID, frameNumber, StudyInstanceUID } = - getSOPInstanceAttributes(referencedImageId); + getSOPInstanceAttributes(referencedImageId, displaySetService, annotation); let displaySet; if (SOPInstanceUID) { - displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( + displaySet = displaySetService.getDisplaySetForSOPInstanceUID( SOPInstanceUID, SeriesInstanceUID ); } else { - displaySet = DisplaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } return { @@ -62,10 +62,11 @@ const PlanarFreehandROI = { frameNumber, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, label: data.label, - displayText: getDisplayText(annotation, displaySet, customizationService), + displayText: getDisplayText(annotation, displaySet, customizationService, displaySetService), data: data.cachedStats, type: getValueTypeFromToolType(toolName), getReport: () => getColumnValueReport(annotation, customizationService), @@ -122,17 +123,17 @@ function getColumnValueReport(annotation, customizationService) { * @param {Object} displaySet - The display set object. * @returns {string[]} - An array of display text. */ -function getDisplayText(annotation, displaySet, customizationService) { +function getDisplayText(annotation, displaySet, customizationService, displaySetService) { const { PlanarFreehandROI } = customizationService.get('cornerstone.measurements'); const { displayText } = PlanarFreehandROI; const { metadata, data } = annotation; - if (!data.cachedStats || !data.cachedStats[`imageId:${metadata.referencedImageId}`]) { - return []; - } - - const { SOPInstanceUID, frameNumber } = getSOPInstanceAttributes(metadata.referencedImageId); + const { SOPInstanceUID, frameNumber } = getSOPInstanceAttributes( + metadata.referencedImageId, + displaySetService, + annotation + ); const displayTextArray = []; @@ -150,7 +151,13 @@ function getDisplayText(annotation, displaySet, customizationService) { displayTextArray.push(`S: ${SeriesNumber}${instanceText}${frameText}`); } - const stats = data.cachedStats[`imageId:${metadata.referencedImageId}`]; + const stats = + data.cachedStats[`imageId:${metadata.referencedImageId}`] || + Array.from(Object.values(data.cachedStats))[0]; + + if (!stats) { + return displayTextArray; + } const roundValues = values => { if (Array.isArray(values)) { @@ -158,10 +165,10 @@ function getDisplayText(annotation, displaySet, customizationService) { if (isNaN(value)) { return value; } - return utils.roundNumber(value); + return utils.roundNumber(value, 2); }); } - return isNaN(values) ? values : utils.roundNumber(values); + return isNaN(values) ? values : utils.roundNumber(values, 2); }; const findUnitForValue = (displayTextItems, value) => diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts index 554df1842..7d0c89c29 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts @@ -34,8 +34,11 @@ const Probe = { throw new Error('Tool not supported'); } - const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = - getSOPInstanceAttributes(referencedImageId); + const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation + ); let displaySet; @@ -45,7 +48,7 @@ const Probe = { SeriesInstanceUID ); } else { - displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points } = data.handles; @@ -64,6 +67,7 @@ const Probe = { metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, frameNumber: mappedAnnotations?.[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, @@ -76,7 +80,7 @@ const Probe = { }, }; -function getMappedAnnotations(annotation, DisplaySetService) { +function getMappedAnnotations(annotation, displaySetService) { const { metadata, data } = annotation; const { cachedStats } = data; const { referencedImageId } = metadata; @@ -90,19 +94,14 @@ function getMappedAnnotations(annotation, DisplaySetService) { Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; - if (!referencedImageId) { - throw new Error('Non-acquisition plane measurement mapping not supported'); - } - - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; const { value } = targetStats; const unit = 'HU'; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/RectangleROI.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/RectangleROI.ts index fe94bfa27..61b2e1c47 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/RectangleROI.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/RectangleROI.ts @@ -8,7 +8,7 @@ const RectangleROI = { toAnnotation: measurement => {}, toMeasurement: ( csToolsEventDetail, - DisplaySetService, + displaySetService, CornerstoneViewportService, getValueTypeFromToolType, customizationService @@ -30,24 +30,24 @@ const RectangleROI = { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, - CornerstoneViewportService, - viewportId + displaySetService, + annotation ); let displaySet; if (SOPInstanceUID) { - displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( + displaySet = displaySetService.getDisplaySetForSOPInstanceUID( SOPInstanceUID, SeriesInstanceUID ); } else { - displaySet = DisplaySetService.getDisplaySetsForSeries(SeriesInstanceUID); + displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; } const { points, textBox } = data.handles; - const mappedAnnotations = getMappedAnnotations(annotation, DisplaySetService); + const mappedAnnotations = getMappedAnnotations(annotation, displaySetService); const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService); const getReport = () => @@ -62,6 +62,7 @@ const RectangleROI = { metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, + referencedImageId, frameNumber: mappedAnnotations[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, @@ -74,7 +75,7 @@ const RectangleROI = { }, }; -function getMappedAnnotations(annotation, DisplaySetService) { +function getMappedAnnotations(annotation, displaySetService) { const { metadata, data } = annotation; const { cachedStats } = data; const { referencedImageId } = metadata; @@ -88,20 +89,14 @@ function getMappedAnnotations(annotation, DisplaySetService) { Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; - if (!referencedImageId) { - // Todo: Non-acquisition plane measurement mapping not supported yet - throw new Error('Non-acquisition plane measurement mapping not supported'); - } - - const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = - getSOPInstanceAttributes(referencedImageId); - - const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID, - frameNumber + const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes( + referencedImageId, + displaySetService, + annotation ); + const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; + const { SeriesNumber } = displaySet; const { mean, stdDev, max, area, Modality, modalityUnit, areaUnit } = targetStats; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js b/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js index dd6992b83..101ef2117 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js @@ -7,12 +7,24 @@ import * as cornerstone from '@cornerstonejs/core'; * @param {string} imageId The image id of the referenced image * @returns */ -export default function getSOPInstanceAttributes(imageId) { +export default function getSOPInstanceAttributes(imageId, displaySetService, annotation) { if (imageId) { return _getUIDFromImageID(imageId); } - // Todo: implement for volume viewports and use the referencedSeriesInstanceUID + const { metadata } = annotation; + const { volumeId } = metadata; + + const displaySet = displaySetService.getDisplaySetsBy(displaySet => + volumeId.includes(displaySet.uid) + )[0]; + const { StudyInstanceUID, SeriesInstanceUID } = displaySet; + + return { + SOPInstanceUID: undefined, + SeriesInstanceUID, + StudyInstanceUID, + }; } function _getUIDFromImageID(imageId) { diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json index bf67815b4..3fafcae08 100644 --- a/extensions/measurement-tracking/package.json +++ b/extensions/measurement-tracking/package.json @@ -32,8 +32,8 @@ "start": "yarn run dev" }, "peerDependencies": { - "@cornerstonejs/core": "^1.74.7", - "@cornerstonejs/tools": "^1.74.7", + "@cornerstonejs/core": "^1.74.8", + "@cornerstonejs/tools": "^1.74.8", "@ohif/core": "3.9.0-beta.23", "@ohif/extension-cornerstone-dicom-sr": "3.9.0-beta.23", "@ohif/ui": "3.9.0-beta.23", diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptSaveReport.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptSaveReport.js index fd2d69872..3e98bcb2b 100644 --- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptSaveReport.js +++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptSaveReport.js @@ -21,9 +21,11 @@ async function promptSaveReport({ servicesManager, commandsManager, extensionMan const dataSources = extensionManager.getDataSources(); const dataSource = dataSources[0]; const measurements = measurementService.getMeasurements(); - const trackedMeasurements = measurements.filter( - m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID) - ); + const trackedMeasurements = measurements + .filter( + m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID) + ) + .filter(m => m.referencedImageId != null); const SeriesDescription = // isUndefinedOrEmpty diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx index ee7b65405..6ab9b4134 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.tsx @@ -161,14 +161,18 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }: wi }; const displayMeasurementsWithoutFindings = displayMeasurements.filter( - dm => dm.measurementType !== measurementService.VALUE_TYPES.POINT + dm => dm.measurementType !== measurementService.VALUE_TYPES.POINT && dm.referencedImageId ); const additionalFindings = displayMeasurements.filter( - dm => dm.measurementType === measurementService.VALUE_TYPES.POINT + dm => dm.measurementType === measurementService.VALUE_TYPES.POINT && dm.referencedImageId ); + const nonAcquisitionMeasurements = displayMeasurements.filter(dm => dm.referencedImageId == null); + const disabled = - additionalFindings.length === 0 && displayMeasurementsWithoutFindings.length === 0; + additionalFindings.length === 0 && + displayMeasurementsWithoutFindings.length === 0 && + nonAcquisitionMeasurements.length === 0; return ( <> @@ -200,6 +204,15 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }: wi onEdit={onMeasurementItemEditHandler} /> )} + {nonAcquisitionMeasurements.length !== 0 && ( + + )} {!appConfig?.disableEditing && (
@@ -207,7 +220,7 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }: wi t={t} actions={[ { - label: 'Export', + label: 'Download CSV', onClick: exportReport, }, { @@ -266,6 +279,7 @@ function _mapMeasurementToDisplay(measurement, types, displaySetService) { selected, findingSites, finding, + referencedImageId, } = measurement; const firstSite = findingSites?.[0]; @@ -294,6 +308,7 @@ function _mapMeasurementToDisplay(measurement, types, displaySetService) { isActive: selected, finding, findingSites, + referencedImageId, }; } diff --git a/platform/app/package.json b/platform/app/package.json index e523ab332..698dabe2b 100644 --- a/platform/app/package.json +++ b/platform/app/package.json @@ -53,7 +53,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^1.74.7", + "@cornerstonejs/dicom-image-loader": "^1.74.8", "@emotion/serialize": "^1.1.3", "@ohif/core": "3.9.0-beta.23", "@ohif/extension-cornerstone": "3.9.0-beta.23", diff --git a/platform/core/package.json b/platform/core/package.json index 5c8475a3e..8d402801f 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -37,7 +37,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.2", - "@cornerstonejs/dicom-image-loader": "^1.74.7", + "@cornerstonejs/dicom-image-loader": "^1.74.8", "@ohif/ui": "3.9.0-beta.23", "cornerstone-math": "0.1.9", "dicom-parser": "^1.8.21" diff --git a/platform/core/src/services/MeasurementService/MeasurementService.ts b/platform/core/src/services/MeasurementService/MeasurementService.ts index 7f8c97489..35383f89a 100644 --- a/platform/core/src/services/MeasurementService/MeasurementService.ts +++ b/platform/core/src/services/MeasurementService/MeasurementService.ts @@ -60,6 +60,7 @@ const MEASUREMENT_SCHEMA_KEYS = [ 'cachedStats', 'selected', 'textBox', + 'referencedImageId', ]; const EVENTS = {