fix: the start/end command in TMTV for the ROIStartEndThreshold tools (#4281)

This commit is contained in:
Celian-abd 2024-07-09 20:49:42 +02:00 committed by GitHub
parent 4b91ce4df9
commit 38c19fab77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 137 additions and 50 deletions

View File

@ -12,9 +12,10 @@ import dicomRTAnnotationExport from './utils/dicomRTAnnotationExport/RTStructure
import { getWebWorkerManager } from '@cornerstonejs/core';
const metadataProvider = classes.MetadataProvider;
const RECTANGLE_ROI_THRESHOLD_MANUAL_TOOL_IDS = [
const ROI_THRESHOLD_MANUAL_TOOL_IDS = [
'RectangleROIStartEndThreshold',
'RectangleROIThreshold',
'CircleROIStartEndThreshold'
];
const LABELMAP = csTools.Enums.SegmentationRepresentations.Labelmap;
@ -204,7 +205,7 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }:
const { referencedVolumeId } = cs.cache.getVolume(segVolumeId);
const annotationUIDs = _getAnnotationsSelectedByToolNames(
RECTANGLE_ROI_THRESHOLD_MANUAL_TOOL_IDS
ROI_THRESHOLD_MANUAL_TOOL_IDS
);
if (annotationUIDs.length === 0) {
@ -291,7 +292,7 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }:
const referencedVolume = cs.cache.getVolume(referencedVolumeId);
const annotationUIDs = _getAnnotationsSelectedByToolNames(
RECTANGLE_ROI_THRESHOLD_MANUAL_TOOL_IDS
ROI_THRESHOLD_MANUAL_TOOL_IDS
);
const annotations = annotationUIDs.map(annotationUID =>
@ -469,38 +470,18 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }:
},
setStartSliceForROIThresholdTool: () => {
const { viewport } = _getActiveViewportsEnabledElement();
const { focalPoint, viewPlaneNormal } = viewport.getCamera();
const { focalPoint } = viewport.getCamera();
const selectedAnnotationUIDs = _getAnnotationsSelectedByToolNames(
RECTANGLE_ROI_THRESHOLD_MANUAL_TOOL_IDS
ROI_THRESHOLD_MANUAL_TOOL_IDS
);
const annotationUID = selectedAnnotationUIDs[0];
const annotation = csTools.annotation.state.getAnnotation(annotationUID);
const { handles } = annotation.data;
const { points } = handles;
// get the current slice Index
const sliceIndex = viewport.getCurrentImageIdIndex();
annotation.data.startSlice = sliceIndex;
// distance between camera focal point and each point on the rectangle
const newPoints = points.map(point => {
const distance = vec3.create();
vec3.subtract(distance, focalPoint, point);
// distance in the direction of the viewPlaneNormal
const distanceInViewPlane = vec3.dot(distance, viewPlaneNormal);
// new point is current point minus distanceInViewPlane
const newPoint = vec3.create();
vec3.scaleAndAdd(newPoint, point, viewPlaneNormal, distanceInViewPlane);
return newPoint;
//
});
handles.points = newPoints;
// set the current focalpoint
annotation.data.startCoordinate = focalPoint;
// IMPORTANT: invalidate the toolData for the cached stat to get updated
// and re-calculate the projection points
annotation.invalidated = true;
@ -510,16 +491,16 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }:
const { viewport } = _getActiveViewportsEnabledElement();
const selectedAnnotationUIDs = _getAnnotationsSelectedByToolNames(
RECTANGLE_ROI_THRESHOLD_MANUAL_TOOL_IDS
ROI_THRESHOLD_MANUAL_TOOL_IDS
);
const annotationUID = selectedAnnotationUIDs[0];
const annotation = csTools.annotation.state.getAnnotation(annotationUID);
// get the current slice Index
const sliceIndex = viewport.getCurrentImageIdIndex();
annotation.data.endSlice = sliceIndex;
// get the current focalpoint
const focalPointToEnd = viewport.getCamera().focalPoint;
annotation.data.endCoordinate = focalPointToEnd;
// IMPORTANT: invalidate the toolData for the cached stat to get updated
// and re-calculate the projection points
@ -535,7 +516,7 @@ const commandsModule = ({ servicesManager, commandsManager, extensionManager }:
Object.keys(stateManager.annotations).forEach(frameOfReferenceUID => {
const forAnnotations = stateManager.annotations[frameOfReferenceUID];
const ROIAnnotations = RECTANGLE_ROI_THRESHOLD_MANUAL_TOOL_IDS.reduce(
const ROIAnnotations = ROI_THRESHOLD_MANUAL_TOOL_IDS.reduce(
(annotations, toolName) => [...annotations, ...(forAnnotations[toolName] ?? [])],
[]
);

View File

@ -1,4 +1,4 @@
import { addTool, RectangleROIStartEndThresholdTool } from '@cornerstonejs/tools';
import { addTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool } from '@cornerstonejs/tools';
import measurementServiceMappingsFactory from './utils/measurementServiceMappings/measurementServiceMappingsFactory';
@ -15,8 +15,9 @@ export default function init({ servicesManager }) {
servicesManager.services;
addTool(RectangleROIStartEndThresholdTool);
addTool(CircleROIStartEndThresholdTool);
const { RectangleROIStartEndThreshold } = measurementServiceMappingsFactory(
const { RectangleROIStartEndThreshold, CircleROIStartEndThreshold } = measurementServiceMappingsFactory(
measurementService,
displaySetService,
cornerstoneViewportService
@ -34,4 +35,12 @@ export default function init({ servicesManager }) {
RectangleROIStartEndThreshold.toAnnotation,
RectangleROIStartEndThreshold.toMeasurement
);
measurementService.addMapping(
csTools3DVer1MeasurementSource,
'CircleROIStartEndThreshold',
CircleROIStartEndThreshold.matchingCriteria,
CircleROIStartEndThreshold.toAnnotation,
CircleROIStartEndThreshold.toMeasurement
);
}

View File

@ -0,0 +1,67 @@
import SUPPORTED_TOOLS from './constants/supportedTools';
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
const CircleROIStartEndThreshold = {
toAnnotation: (measurement, definition) => { },
/**
* Maps cornerstone annotation event data to measurement service format.
*
* @param {Object} cornerstone Cornerstone event data
* @return {Measurement} Measurement instance
*/
toMeasurement: (csToolsEventDetail, displaySetService, cornerstoneViewportService) => {
const { annotation, viewportId } = csToolsEventDetail;
const { metadata, data, annotationUID } = annotation;
if (!metadata || !data) {
console.warn('Length tool: Missing metadata or data');
return null;
}
const { toolName, referencedImageId, FrameOfReferenceUID } = metadata;
const validToolType = SUPPORTED_TOOLS.includes(toolName);
if (!validToolType) {
throw new Error('Tool not supported');
}
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
referencedImageId,
cornerstoneViewportService,
viewportId
);
let displaySet;
if (SOPInstanceUID) {
displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID,
SeriesInstanceUID
);
} else {
displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID);
}
const { cachedStats } = data;
return {
uid: annotationUID,
SOPInstanceUID,
FrameOfReferenceUID,
// points,
metadata,
referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID,
toolName: metadata.toolName,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
label: metadata.label,
// displayText: displayText,
data: data.cachedStats,
type: 'CircleROIStartEndThreshold',
// getReport,
};
},
};
export default CircleROIStartEndThreshold;

View File

@ -1,4 +1,5 @@
import RectangleROIStartEndThreshold from './RectangleROIStartEndThreshold';
import CircleROIStartEndThreshold from './CircleROIStartEndThreshold'
const measurementServiceMappingsFactory = (
measurementService,
@ -20,6 +21,20 @@ const measurementServiceMappingsFactory = (
},
],
},
CircleROIStartEndThreshold: {
toAnnotation: CircleROIStartEndThreshold.toAnnotation,
toMeasurement: csToolsAnnotation =>
CircleROIStartEndThreshold.toMeasurement(
csToolsAnnotation,
displaySetService,
cornerstoneViewportService
),
matchingCriteria: [
{
valueType: measurementService.VALUE_TYPES.ROI_THRESHOLD_MANUAL,
},
],
},
};
};

View File

@ -1492,6 +1492,7 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@cornerstonejs/adapters@^1.81.6", "@cornerstonejs/adapters@^1.82.0":
version "1.82.0"
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.82.0.tgz#4c7ae6341ad7bcf5cf0213e5252fbac009da23b6"
@ -1530,10 +1531,10 @@
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
"@cornerstonejs/core@^1.78.1":
version "1.78.1"
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.78.1.tgz#e7db1afd573dd631e0ad4157706deb59844ca1b7"
integrity sha512-BfClVMEPM6IwU4TlAEh305jZOwK+xQBgf5jQywBea1DKf5KPYUFhnZcrxaWlZ44ldlpvsGBHZhDuTpdwjYszBg==
"@cornerstonejs/core@^1.78.1", "@cornerstonejs/core@^1.81.3":
version "1.81.3"
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.81.3.tgz#76deaee4a2cce24db5695cb87857accea5645263"
integrity sha512-Uievs/wBpw20Xj4B+8UEjb/qe+cmSfz2oWfQzBANcWoqI5pXD77evXE0s6hORCMr4yFUmTIMbGnWwXT2fF2bYw==
dependencies:
"@kitware/vtk.js" "30.4.1"
comlink "^4.4.1"
@ -1553,15 +1554,15 @@
lodash.clonedeep "4.5.0"
"@cornerstonejs/dicom-image-loader@^1.77.6":
version "1.78.1"
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.78.1.tgz#d8eef8f65e7069bef26a0f569a2d89bf7c48c9de"
integrity sha512-S37NlGD3FPrwE9qQcLVQA7pyzRlAIO+XBS7y2AgHWgXlHysRlJP+s4BWdLuwz7+Xh6cVh5g6mj/g+z51zep2kQ==
version "1.81.3"
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.81.3.tgz#8d9165b351954999bbc566d811a9eedd2363d55c"
integrity sha512-QFqr0Jq9DWvoBB1rTwI6gL4fc3pawpnG6ebDSZpz+uPmG/QcGYaeGNT+Szrcu2V/q1ByOU/Jxn8ZG5Nuo+kD6A==
dependencies:
"@cornerstonejs/codec-charls" "^1.2.3"
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
"@cornerstonejs/codec-openjpeg" "^1.2.2"
"@cornerstonejs/codec-openjph" "^2.4.5"
"@cornerstonejs/core" "^1.78.1"
"@cornerstonejs/core" "^1.81.3"
dicom-parser "^1.8.9"
pako "^2.0.4"
uuid "^9.0.0"
@ -5476,10 +5477,10 @@
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@^20.12.12":
version "20.14.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.5.tgz#fe35e3022ebe58b8f201580eb24e1fcfc0f2487d"
integrity sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==
"@types/node@*":
version "20.14.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420"
integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==
dependencies:
undici-types "~5.26.4"
@ -5500,6 +5501,13 @@
dependencies:
undici-types "~5.26.4"
"@types/node@^20.12.12":
version "20.14.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.5.tgz#fe35e3022ebe58b8f201580eb24e1fcfc0f2487d"
integrity sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==
dependencies:
undici-types "~5.26.4"
"@types/normalize-package-data@^2.4.0":
version "2.4.4"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901"
@ -5727,9 +5735,9 @@
source-map "^0.6.0"
"@types/webxr@^0.5.5":
version "0.5.16"
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.16.tgz#28955aa2d1197d1ef0b9826ae0f7e68f7eca0601"
integrity sha512-0E0Cl84FECtzrB4qG19TNTqpunw0F1YF0QZZnFMF6pDw1kNKJtrlTKlVB34stGIsHbZsYQ7H0tNjPfZftkHHoA==
version "0.5.19"
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.19.tgz#463a27bc06ff1c0a0c997e86b48bf24c5f50a4af"
integrity sha512-4hxA+NwohSgImdTSlPXEqDqqFktNgmTXQ05ff1uWam05tNGroCMp4G+4XVl6qWm1p7GQ/9oD41kAYsSssF6Mzw==
"@types/ws@^8.2.2", "@types/ws@^8.5.5":
version "8.5.10"
@ -12536,13 +12544,20 @@ is-ci@^2.0.0:
dependencies:
ci-info "^2.0.0"
is-core-module@^2.1.0, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1:
is-core-module@^2.1.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1:
version "2.13.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
dependencies:
hasown "^2.0.0"
is-core-module@^2.13.0:
version "2.14.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1"
integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==
dependencies:
hasown "^2.0.2"
is-data-view@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f"