feat(measurements): show untracked measurements in measurement panel under additional findings (#4160)

This commit is contained in:
Ibrahim 2024-05-29 09:12:39 -04:00 committed by GitHub
parent 07eb3c527d
commit 18686c2caf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 181 additions and 176 deletions

View File

@ -46,8 +46,8 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^1.74.7", "@cornerstonejs/adapters": "^1.74.8",
"@cornerstonejs/core": "^1.74.7", "@cornerstonejs/core": "^1.74.8",
"@kitware/vtk.js": "30.4.1", "@kitware/vtk.js": "30.4.1",
"react-color": "^2.19.3" "react-color": "^2.19.3"
} }

View File

@ -46,9 +46,9 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^1.74.7", "@cornerstonejs/adapters": "^1.74.8",
"@cornerstonejs/core": "^1.74.7", "@cornerstonejs/core": "^1.74.8",
"@cornerstonejs/tools": "^1.74.7", "@cornerstonejs/tools": "^1.74.8",
"classnames": "^2.3.2" "classnames": "^2.3.2"
} }
} }

View File

@ -42,9 +42,9 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/core": "^1.74.7", "@cornerstonejs/core": "^1.74.8",
"@cornerstonejs/streaming-image-volume-loader": "^1.74.7", "@cornerstonejs/streaming-image-volume-loader": "^1.74.8",
"@cornerstonejs/tools": "^1.74.7", "@cornerstonejs/tools": "^1.74.8",
"classnames": "^2.3.2" "classnames": "^2.3.2"
} }
} }

View File

@ -38,7 +38,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2",
"@cornerstonejs/codec-openjph": "^2.4.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", "@icr/polyseg-wasm": "^0.4.0",
"@ohif/core": "3.9.0-beta.23", "@ohif/core": "3.9.0-beta.23",
"@ohif/ui": "3.9.0-beta.23", "@ohif/ui": "3.9.0-beta.23",
@ -55,10 +55,10 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^1.74.7", "@cornerstonejs/adapters": "^1.74.8",
"@cornerstonejs/core": "^1.74.7", "@cornerstonejs/core": "^1.74.8",
"@cornerstonejs/streaming-image-volume-loader": "^1.74.7", "@cornerstonejs/streaming-image-volume-loader": "^1.74.8",
"@cornerstonejs/tools": "^1.74.7", "@cornerstonejs/tools": "^1.74.8",
"@icr/polyseg-wasm": "^0.4.0", "@icr/polyseg-wasm": "^0.4.0",
"@kitware/vtk.js": "30.4.1", "@kitware/vtk.js": "30.4.1",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",

View File

@ -36,8 +36,8 @@ const Angle = {
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
CornerstoneViewportService, displaySetService,
viewportId annotation
); );
let displaySet; let displaySet;
@ -48,7 +48,7 @@ const Angle = {
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points, textBox } = data.handles; const { points, textBox } = data.handles;
@ -76,11 +76,12 @@ const Angle = {
data: data.cachedStats, data: data.cachedStats,
type: getValueTypeFromToolType(toolName), type: getValueTypeFromToolType(toolName),
getReport, getReport,
referencedImageId,
}; };
}, },
}; };
function getMappedAnnotations(annotation, DisplaySetService) { function getMappedAnnotations(annotation, displaySetService) {
const { metadata, data } = annotation; const { metadata, data } = annotation;
const { cachedStats } = data; const { cachedStats } = data;
const { referencedImageId } = metadata; const { referencedImageId } = metadata;
@ -94,19 +95,14 @@ function getMappedAnnotations(annotation, DisplaySetService) {
Object.keys(cachedStats).forEach(targetId => { Object.keys(cachedStats).forEach(targetId => {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
throw new Error('Non-acquisition plane measurement mapping not supported'); referencedImageId,
} displaySetService,
annotation
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
getSOPInstanceAttributes(referencedImageId);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
const { angle } = targetStats; const { angle } = targetStats;
const unit = '\u00B0'; const unit = '\u00B0';

View File

@ -34,8 +34,8 @@ const Length = {
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
cornerstoneViewportService, displaySetService,
viewportId annotation
); );
let displaySet; let displaySet;
@ -46,7 +46,7 @@ const Length = {
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points, textBox } = data.handles; const { points, textBox } = data.handles;
@ -64,6 +64,7 @@ const Length = {
metadata, metadata,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
frameNumber: mappedAnnotations[0]?.frameNumber || 1, frameNumber: mappedAnnotations[0]?.frameNumber || 1,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
@ -85,15 +86,14 @@ function getMappedAnnotations(annotation, displaySetService) {
const annotations = []; const annotations = [];
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
getSOPInstanceAttributes(referencedImageId); referencedImageId,
displaySetService,
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( annotation
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
annotations.push({ annotations.push({

View File

@ -31,8 +31,8 @@ const Bidirectional = {
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
cornerstoneViewportService, displaySetService,
viewportId annotation
); );
let displaySet; let displaySet;
@ -43,7 +43,7 @@ const Bidirectional = {
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points, textBox } = data.handles; const { points, textBox } = data.handles;
@ -63,6 +63,7 @@ const Bidirectional = {
metadata, metadata,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
frameNumber: mappedAnnotations[0]?.frameNumber || 1, frameNumber: mappedAnnotations[0]?.frameNumber || 1,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
@ -89,19 +90,14 @@ function getMappedAnnotations(annotation, displaySetService) {
Object.keys(cachedStats).forEach(targetId => { Object.keys(cachedStats).forEach(targetId => {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
throw new Error('Non-acquisition plane measurement mapping not supported'); referencedImageId,
} displaySetService,
annotation
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
getSOPInstanceAttributes(referencedImageId);
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
const { length, width, unit } = targetStats; const { length, width, unit } = targetStats;

View File

@ -8,7 +8,7 @@ const CircleROI = {
toAnnotation: measurement => {}, toAnnotation: measurement => {},
toMeasurement: ( toMeasurement: (
csToolsEventDetail, csToolsEventDetail,
DisplaySetService, displaySetService,
CornerstoneViewportService, CornerstoneViewportService,
getValueTypeFromToolType, getValueTypeFromToolType,
customizationService customizationService
@ -30,24 +30,24 @@ const CircleROI = {
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
CornerstoneViewportService, displaySetService,
viewportId annotation
); );
let displaySet; let displaySet;
if (SOPInstanceUID) { if (SOPInstanceUID) {
displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = DisplaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points, textBox } = data.handles; const { points, textBox } = data.handles;
const mappedAnnotations = getMappedAnnotations(annotation, DisplaySetService); const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService); const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService);
const getReport = () => const getReport = () =>
@ -62,6 +62,7 @@ const CircleROI = {
metadata, metadata,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
frameNumber: mappedAnnotations[0]?.frameNumber || 1, frameNumber: mappedAnnotations[0]?.frameNumber || 1,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
@ -74,7 +75,7 @@ const CircleROI = {
}, },
}; };
function getMappedAnnotations(annotation, DisplaySetService) { function getMappedAnnotations(annotation, displaySetService) {
const { metadata, data } = annotation; const { metadata, data } = annotation;
const { cachedStats } = data; const { cachedStats } = data;
const { referencedImageId } = metadata; const { referencedImageId } = metadata;
@ -88,20 +89,14 @@ function getMappedAnnotations(annotation, DisplaySetService) {
Object.keys(cachedStats).forEach(targetId => { Object.keys(cachedStats).forEach(targetId => {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
// Todo: Non-acquisition plane measurement mapping not supported yet referencedImageId,
throw new Error('Non-acquisition plane measurement mapping not supported'); displaySetService,
} annotation
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
getSOPInstanceAttributes(referencedImageId);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats; const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats;

View File

@ -36,8 +36,8 @@ const CobbAngle = {
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
CornerstoneViewportService, displaySetService,
viewportId annotation
); );
let displaySet; let displaySet;
@ -48,7 +48,7 @@ const CobbAngle = {
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points, textBox } = data.handles; const { points, textBox } = data.handles;
@ -68,6 +68,7 @@ const CobbAngle = {
metadata, metadata,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
frameNumber: mappedAnnotations?.[0]?.frameNumber || 1, frameNumber: mappedAnnotations?.[0]?.frameNumber || 1,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
@ -80,7 +81,7 @@ const CobbAngle = {
}, },
}; };
function getMappedAnnotations(annotation, DisplaySetService) { function getMappedAnnotations(annotation, displaySetService) {
const { metadata, data } = annotation; const { metadata, data } = annotation;
const { cachedStats } = data; const { cachedStats } = data;
const { referencedImageId } = metadata; const { referencedImageId } = metadata;
@ -94,19 +95,14 @@ function getMappedAnnotations(annotation, DisplaySetService) {
Object.keys(cachedStats).forEach(targetId => { Object.keys(cachedStats).forEach(targetId => {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
throw new Error('Non-acquisition plane measurement mapping not supported'); referencedImageId,
} displaySetService,
annotation
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
getSOPInstanceAttributes(referencedImageId);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
const { angle } = targetStats; const { angle } = targetStats;
const unit = '\u00B0'; const unit = '\u00B0';

View File

@ -30,8 +30,8 @@ const EllipticalROI = {
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
cornerstoneViewportService, displaySetService,
viewportId annotation
); );
let displaySet; let displaySet;
@ -42,7 +42,7 @@ const EllipticalROI = {
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points, textBox } = data.handles; const { points, textBox } = data.handles;
@ -62,6 +62,7 @@ const EllipticalROI = {
metadata, metadata,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
frameNumber: mappedAnnotations[0]?.frameNumber || 1, frameNumber: mappedAnnotations[0]?.frameNumber || 1,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
@ -88,20 +89,14 @@ function getMappedAnnotations(annotation, displaySetService) {
Object.keys(cachedStats).forEach(targetId => { Object.keys(cachedStats).forEach(targetId => {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
// Todo: Non-acquisition plane measurement mapping not supported yet referencedImageId,
throw new Error('Non-acquisition plane measurement mapping not supported'); displaySetService,
} annotation
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
getSOPInstanceAttributes(referencedImageId);
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats; const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats;

View File

@ -35,8 +35,8 @@ const Length = {
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
cornerstoneViewportService, displaySetService,
viewportId annotation
); );
let displaySet; let displaySet;
@ -47,7 +47,7 @@ const Length = {
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points, textBox } = data.handles; const { points, textBox } = data.handles;
@ -67,6 +67,7 @@ const Length = {
metadata, metadata,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
frameNumber: mappedAnnotations[0]?.frameNumber || 1, frameNumber: mappedAnnotations[0]?.frameNumber || 1,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
@ -93,19 +94,14 @@ function getMappedAnnotations(annotation, displaySetService) {
Object.keys(cachedStats).forEach(targetId => { Object.keys(cachedStats).forEach(targetId => {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
throw new Error('Non-acquisition plane measurement mapping not supported'); referencedImageId,
} displaySetService,
annotation
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
getSOPInstanceAttributes(referencedImageId);
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
const { length, unit = 'mm' } = targetStats; const { length, unit = 'mm' } = targetStats;

View File

@ -20,7 +20,7 @@ const PlanarFreehandROI = {
*/ */
toMeasurement: ( toMeasurement: (
csToolsEventDetail, csToolsEventDetail,
DisplaySetService, displaySetService,
CornerstoneViewportService, CornerstoneViewportService,
getValueTypeFromToolType, getValueTypeFromToolType,
customizationService customizationService
@ -40,16 +40,16 @@ const PlanarFreehandROI = {
} }
const { SOPInstanceUID, SeriesInstanceUID, frameNumber, StudyInstanceUID } = const { SOPInstanceUID, SeriesInstanceUID, frameNumber, StudyInstanceUID } =
getSOPInstanceAttributes(referencedImageId); getSOPInstanceAttributes(referencedImageId, displaySetService, annotation);
let displaySet; let displaySet;
if (SOPInstanceUID) { if (SOPInstanceUID) {
displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = DisplaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
return { return {
@ -62,10 +62,11 @@ const PlanarFreehandROI = {
frameNumber, frameNumber,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
label: data.label, label: data.label,
displayText: getDisplayText(annotation, displaySet, customizationService), displayText: getDisplayText(annotation, displaySet, customizationService, displaySetService),
data: data.cachedStats, data: data.cachedStats,
type: getValueTypeFromToolType(toolName), type: getValueTypeFromToolType(toolName),
getReport: () => getColumnValueReport(annotation, customizationService), getReport: () => getColumnValueReport(annotation, customizationService),
@ -122,17 +123,17 @@ function getColumnValueReport(annotation, customizationService) {
* @param {Object} displaySet - The display set object. * @param {Object} displaySet - The display set object.
* @returns {string[]} - An array of display text. * @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 { PlanarFreehandROI } = customizationService.get('cornerstone.measurements');
const { displayText } = PlanarFreehandROI; const { displayText } = PlanarFreehandROI;
const { metadata, data } = annotation; const { metadata, data } = annotation;
if (!data.cachedStats || !data.cachedStats[`imageId:${metadata.referencedImageId}`]) { const { SOPInstanceUID, frameNumber } = getSOPInstanceAttributes(
return []; metadata.referencedImageId,
} displaySetService,
annotation
const { SOPInstanceUID, frameNumber } = getSOPInstanceAttributes(metadata.referencedImageId); );
const displayTextArray = []; const displayTextArray = [];
@ -150,7 +151,13 @@ function getDisplayText(annotation, displaySet, customizationService) {
displayTextArray.push(`S: ${SeriesNumber}${instanceText}${frameText}`); 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 => { const roundValues = values => {
if (Array.isArray(values)) { if (Array.isArray(values)) {
@ -158,10 +165,10 @@ function getDisplayText(annotation, displaySet, customizationService) {
if (isNaN(value)) { if (isNaN(value)) {
return 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) => const findUnitForValue = (displayTextItems, value) =>

View File

@ -34,8 +34,11 @@ const Probe = {
throw new Error('Tool not supported'); throw new Error('Tool not supported');
} }
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
getSOPInstanceAttributes(referencedImageId); referencedImageId,
displaySetService,
annotation
);
let displaySet; let displaySet;
@ -45,7 +48,7 @@ const Probe = {
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points } = data.handles; const { points } = data.handles;
@ -64,6 +67,7 @@ const Probe = {
metadata, metadata,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
frameNumber: mappedAnnotations?.[0]?.frameNumber || 1, frameNumber: mappedAnnotations?.[0]?.frameNumber || 1,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
@ -76,7 +80,7 @@ const Probe = {
}, },
}; };
function getMappedAnnotations(annotation, DisplaySetService) { function getMappedAnnotations(annotation, displaySetService) {
const { metadata, data } = annotation; const { metadata, data } = annotation;
const { cachedStats } = data; const { cachedStats } = data;
const { referencedImageId } = metadata; const { referencedImageId } = metadata;
@ -90,19 +94,14 @@ function getMappedAnnotations(annotation, DisplaySetService) {
Object.keys(cachedStats).forEach(targetId => { Object.keys(cachedStats).forEach(targetId => {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
throw new Error('Non-acquisition plane measurement mapping not supported'); referencedImageId,
} displaySetService,
annotation
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
getSOPInstanceAttributes(referencedImageId);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
const { value } = targetStats; const { value } = targetStats;
const unit = 'HU'; const unit = 'HU';

View File

@ -8,7 +8,7 @@ const RectangleROI = {
toAnnotation: measurement => {}, toAnnotation: measurement => {},
toMeasurement: ( toMeasurement: (
csToolsEventDetail, csToolsEventDetail,
DisplaySetService, displaySetService,
CornerstoneViewportService, CornerstoneViewportService,
getValueTypeFromToolType, getValueTypeFromToolType,
customizationService customizationService
@ -30,24 +30,24 @@ const RectangleROI = {
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
CornerstoneViewportService, displaySetService,
viewportId annotation
); );
let displaySet; let displaySet;
if (SOPInstanceUID) { if (SOPInstanceUID) {
displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
SeriesInstanceUID SeriesInstanceUID
); );
} else { } else {
displaySet = DisplaySetService.getDisplaySetsForSeries(SeriesInstanceUID); displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
} }
const { points, textBox } = data.handles; const { points, textBox } = data.handles;
const mappedAnnotations = getMappedAnnotations(annotation, DisplaySetService); const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService); const displayText = getDisplayText(mappedAnnotations, displaySet, customizationService);
const getReport = () => const getReport = () =>
@ -62,6 +62,7 @@ const RectangleROI = {
metadata, metadata,
referenceSeriesUID: SeriesInstanceUID, referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID, referenceStudyUID: StudyInstanceUID,
referencedImageId,
frameNumber: mappedAnnotations[0]?.frameNumber || 1, frameNumber: mappedAnnotations[0]?.frameNumber || 1,
toolName: metadata.toolName, toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID, displaySetInstanceUID: displaySet.displaySetInstanceUID,
@ -74,7 +75,7 @@ const RectangleROI = {
}, },
}; };
function getMappedAnnotations(annotation, DisplaySetService) { function getMappedAnnotations(annotation, displaySetService) {
const { metadata, data } = annotation; const { metadata, data } = annotation;
const { cachedStats } = data; const { cachedStats } = data;
const { referencedImageId } = metadata; const { referencedImageId } = metadata;
@ -88,20 +89,14 @@ function getMappedAnnotations(annotation, DisplaySetService) {
Object.keys(cachedStats).forEach(targetId => { Object.keys(cachedStats).forEach(targetId => {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(
// Todo: Non-acquisition plane measurement mapping not supported yet referencedImageId,
throw new Error('Non-acquisition plane measurement mapping not supported'); displaySetService,
} annotation
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
getSOPInstanceAttributes(referencedImageId);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID,
frameNumber
); );
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
const { SeriesNumber } = displaySet; const { SeriesNumber } = displaySet;
const { mean, stdDev, max, area, Modality, modalityUnit, areaUnit } = targetStats; const { mean, stdDev, max, area, Modality, modalityUnit, areaUnit } = targetStats;

View File

@ -7,12 +7,24 @@ import * as cornerstone from '@cornerstonejs/core';
* @param {string} imageId The image id of the referenced image * @param {string} imageId The image id of the referenced image
* @returns * @returns
*/ */
export default function getSOPInstanceAttributes(imageId) { export default function getSOPInstanceAttributes(imageId, displaySetService, annotation) {
if (imageId) { if (imageId) {
return _getUIDFromImageID(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) { function _getUIDFromImageID(imageId) {

View File

@ -32,8 +32,8 @@
"start": "yarn run dev" "start": "yarn run dev"
}, },
"peerDependencies": { "peerDependencies": {
"@cornerstonejs/core": "^1.74.7", "@cornerstonejs/core": "^1.74.8",
"@cornerstonejs/tools": "^1.74.7", "@cornerstonejs/tools": "^1.74.8",
"@ohif/core": "3.9.0-beta.23", "@ohif/core": "3.9.0-beta.23",
"@ohif/extension-cornerstone-dicom-sr": "3.9.0-beta.23", "@ohif/extension-cornerstone-dicom-sr": "3.9.0-beta.23",
"@ohif/ui": "3.9.0-beta.23", "@ohif/ui": "3.9.0-beta.23",

View File

@ -21,9 +21,11 @@ async function promptSaveReport({ servicesManager, commandsManager, extensionMan
const dataSources = extensionManager.getDataSources(); const dataSources = extensionManager.getDataSources();
const dataSource = dataSources[0]; const dataSource = dataSources[0];
const measurements = measurementService.getMeasurements(); const measurements = measurementService.getMeasurements();
const trackedMeasurements = measurements.filter( const trackedMeasurements = measurements
m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID) .filter(
); m => trackedStudy === m.referenceStudyUID && trackedSeries.includes(m.referenceSeriesUID)
)
.filter(m => m.referencedImageId != null);
const SeriesDescription = const SeriesDescription =
// isUndefinedOrEmpty // isUndefinedOrEmpty

View File

@ -161,14 +161,18 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }: wi
}; };
const displayMeasurementsWithoutFindings = displayMeasurements.filter( const displayMeasurementsWithoutFindings = displayMeasurements.filter(
dm => dm.measurementType !== measurementService.VALUE_TYPES.POINT dm => dm.measurementType !== measurementService.VALUE_TYPES.POINT && dm.referencedImageId
); );
const additionalFindings = displayMeasurements.filter( 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 = const disabled =
additionalFindings.length === 0 && displayMeasurementsWithoutFindings.length === 0; additionalFindings.length === 0 &&
displayMeasurementsWithoutFindings.length === 0 &&
nonAcquisitionMeasurements.length === 0;
return ( return (
<> <>
@ -200,6 +204,15 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }: wi
onEdit={onMeasurementItemEditHandler} onEdit={onMeasurementItemEditHandler}
/> />
)} )}
{nonAcquisitionMeasurements.length !== 0 && (
<MeasurementTable
title="Non-tracked"
data={nonAcquisitionMeasurements}
servicesManager={servicesManager}
onClick={jumpToImage}
onEdit={onMeasurementItemEditHandler}
/>
)}
</div> </div>
{!appConfig?.disableEditing && ( {!appConfig?.disableEditing && (
<div className="flex justify-center p-4"> <div className="flex justify-center p-4">
@ -207,7 +220,7 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }: wi
t={t} t={t}
actions={[ actions={[
{ {
label: 'Export', label: 'Download CSV',
onClick: exportReport, onClick: exportReport,
}, },
{ {
@ -266,6 +279,7 @@ function _mapMeasurementToDisplay(measurement, types, displaySetService) {
selected, selected,
findingSites, findingSites,
finding, finding,
referencedImageId,
} = measurement; } = measurement;
const firstSite = findingSites?.[0]; const firstSite = findingSites?.[0];
@ -294,6 +308,7 @@ function _mapMeasurementToDisplay(measurement, types, displaySetService) {
isActive: selected, isActive: selected,
finding, finding,
findingSites, findingSites,
referencedImageId,
}; };
} }

View File

@ -53,7 +53,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2",
"@cornerstonejs/codec-openjph": "^2.4.5", "@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", "@emotion/serialize": "^1.1.3",
"@ohif/core": "3.9.0-beta.23", "@ohif/core": "3.9.0-beta.23",
"@ohif/extension-cornerstone": "3.9.0-beta.23", "@ohif/extension-cornerstone": "3.9.0-beta.23",

View File

@ -37,7 +37,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2",
"@cornerstonejs/codec-openjph": "^2.4.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", "@ohif/ui": "3.9.0-beta.23",
"cornerstone-math": "0.1.9", "cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21" "dicom-parser": "^1.8.21"

View File

@ -60,6 +60,7 @@ const MEASUREMENT_SCHEMA_KEYS = [
'cachedStats', 'cachedStats',
'selected', 'selected',
'textBox', 'textBox',
'referencedImageId',
]; ];
const EVENTS = { const EVENTS = {