fix: Cornerstone3D 3.0 (#4816)
Co-authored-by: sedghi <ar.sedghi@gmail.com>
This commit is contained in:
1 parent
62abb3ac47
commit
24567c4e92
25 files changed
+326
-342
No files matched your search
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^2.19.14",
|
||||
"@cornerstonejs/core": "^2.19.14",
|
||||
"@cornerstonejs/adapters": "^3.0.1",
|
||||
"@cornerstonejs/core": "^3.0.1",
|
||||
"@kitware/vtk.js": "32.1.1",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^2.19.14",
|
||||
"@cornerstonejs/core": "^2.19.14",
|
||||
"@cornerstonejs/adapters": "^3.0.1",
|
||||
"@cornerstonejs/core": "^3.0.1",
|
||||
"@kitware/vtk.js": "32.1.1",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@@ -324,10 +324,8 @@ const commandsModule = ({
|
||||
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
||||
toolNames?.forEach(toolName => {
|
||||
toolGroup.setToolConfiguration(toolName, {
|
||||
strategySpecificConfiguration: {
|
||||
THRESHOLD: {
|
||||
threshold: value,
|
||||
},
|
||||
threshold: {
|
||||
range: value,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^2.19.14",
|
||||
"@cornerstonejs/core": "^2.19.14",
|
||||
"@cornerstonejs/tools": "^2.19.14",
|
||||
"@cornerstonejs/adapters": "^3.0.1",
|
||||
"@cornerstonejs/core": "^3.0.1",
|
||||
"@cornerstonejs/tools": "^3.0.1",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
+14
-4
@@ -391,7 +391,12 @@ function _getStatusComponent({
|
||||
|
||||
switch (state) {
|
||||
case 1:
|
||||
StatusIcon = () => <Icons.ByName name="status-alert" />;
|
||||
StatusIcon = () => (
|
||||
<Icons.ByName
|
||||
name="status-alert"
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
);
|
||||
|
||||
ToolTipMessage = () => (
|
||||
<div>
|
||||
@@ -402,7 +407,12 @@ function _getStatusComponent({
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
StatusIcon = () => <Icons.ByName name="status-locked" />;
|
||||
StatusIcon = () => (
|
||||
<Icons.ByName
|
||||
name="status-locked"
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
);
|
||||
|
||||
ToolTipMessage = () => (
|
||||
<div>
|
||||
@@ -417,7 +427,7 @@ function _getStatusComponent({
|
||||
case 3:
|
||||
StatusIcon = () => (
|
||||
<Icons.ByName
|
||||
className="text-aqua-pale"
|
||||
className="text-aqua-pale h-4 w-4"
|
||||
name="status-untracked"
|
||||
/>
|
||||
);
|
||||
@@ -439,7 +449,7 @@ function _getStatusComponent({
|
||||
return (
|
||||
<div className="flex h-6 cursor-default text-sm leading-6 text-white">
|
||||
<div className="bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1">
|
||||
<StatusIcon />
|
||||
<StatusIcon className="h-4 w-4" />
|
||||
<span className="ml-1">SR</span>
|
||||
</div>
|
||||
{state === 3 && (
|
||||
|
||||
@@ -85,7 +85,8 @@ export default function hydrateStructuredReport(
|
||||
}
|
||||
});
|
||||
|
||||
const datasetToUse = _mapLegacyDataSet(instance);
|
||||
// Mapping of legacy datasets is now directly handled by adapters module
|
||||
const datasetToUse = instance;
|
||||
|
||||
// Use dcmjs to generate toolState.
|
||||
let storedMeasurementByAnnotationType = MeasurementReport.generateToolState(
|
||||
@@ -237,62 +238,3 @@ export default function hydrateStructuredReport(
|
||||
SeriesInstanceUIDs,
|
||||
};
|
||||
}
|
||||
|
||||
function _mapLegacyDataSet(dataset) {
|
||||
const REPORT = 'Imaging Measurements';
|
||||
const GROUP = 'Measurement Group';
|
||||
const TRACKING_IDENTIFIER = 'Tracking Identifier';
|
||||
|
||||
// Identify the Imaging Measurements
|
||||
const imagingMeasurementContent = toArray(dataset.ContentSequence).find(
|
||||
codeMeaningEquals(REPORT)
|
||||
);
|
||||
|
||||
// Retrieve the Measurements themselves
|
||||
const measurementGroups = toArray(imagingMeasurementContent.ContentSequence).filter(
|
||||
codeMeaningEquals(GROUP)
|
||||
);
|
||||
|
||||
// For each of the supported measurement types, compute the measurement data
|
||||
const measurementData = {};
|
||||
|
||||
const cornerstoneToolClasses = MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
|
||||
|
||||
const registeredToolClasses = [];
|
||||
|
||||
Object.keys(cornerstoneToolClasses).forEach(key => {
|
||||
registeredToolClasses.push(cornerstoneToolClasses[key]);
|
||||
measurementData[key] = [];
|
||||
});
|
||||
|
||||
measurementGroups.forEach((measurementGroup, index) => {
|
||||
const measurementGroupContentSequence = toArray(measurementGroup.ContentSequence);
|
||||
|
||||
const TrackingIdentifierGroup = measurementGroupContentSequence.find(
|
||||
contentItem => contentItem.ConceptNameCodeSequence.CodeMeaning === TRACKING_IDENTIFIER
|
||||
);
|
||||
|
||||
const TrackingIdentifier = TrackingIdentifierGroup.TextValue;
|
||||
|
||||
let [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
|
||||
if (supportedLegacyCornerstoneTags.includes(cornerstoneTag)) {
|
||||
cornerstoneTag = CORNERSTONE_3D_TAG;
|
||||
}
|
||||
|
||||
const mappedTrackingIdentifier = `${cornerstoneTag}:${toolName}`;
|
||||
|
||||
TrackingIdentifierGroup.TextValue = mappedTrackingIdentifier;
|
||||
});
|
||||
|
||||
return dataset;
|
||||
}
|
||||
|
||||
const toArray = function (x) {
|
||||
return Array.isArray(x) ? x : [x];
|
||||
};
|
||||
|
||||
const codeMeaningEquals = codeMeaningName => {
|
||||
return contentItem => {
|
||||
return contentItem.ConceptNameCodeSequence.CodeMeaning === codeMeaningName;
|
||||
};
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
import { adaptersSR } from '@cornerstonejs/adapters';
|
||||
|
||||
const cornerstoneAdapters =
|
||||
adaptersSR.Cornerstone3D.MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
|
||||
|
||||
const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
|
||||
const CORNERSTONE_3D_TAG = adaptersSR.Cornerstone3D.CORNERSTONE_3D_TAG;
|
||||
|
||||
/**
|
||||
* Checks if the given `displaySet`can be rehydrated into the `measurementService`.
|
||||
*
|
||||
* @param {object} displaySet The SR `displaySet` to check.
|
||||
* @param {object[]} mappings The CornerstoneTools 4 mappings to the `measurementService`.
|
||||
* @returns {boolean} True if the SR can be rehydrated into the `measurementService`.
|
||||
*/
|
||||
export default function isRehydratable(displaySet, mappings) {
|
||||
if (!mappings || !mappings.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mappingDefinitions = mappings.map(m => m.annotationType);
|
||||
const { measurements } = displaySet;
|
||||
|
||||
const adapterKeys = Object.keys(cornerstoneAdapters).filter(
|
||||
adapterKey =>
|
||||
typeof cornerstoneAdapters[adapterKey].isValidCornerstoneTrackingIdentifier === 'function'
|
||||
);
|
||||
|
||||
const adapters = [];
|
||||
|
||||
adapterKeys.forEach(key => {
|
||||
if (mappingDefinitions.includes(key)) {
|
||||
// Must have both a dcmjs adapter and a measurementService
|
||||
// Definition in order to be a candidate for import.
|
||||
adapters.push(cornerstoneAdapters[key]);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < measurements.length; i++) {
|
||||
const { TrackingIdentifier } = measurements[i] || {};
|
||||
const hydratable = adapters.some(adapter => {
|
||||
let [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
|
||||
if (supportedLegacyCornerstoneTags.includes(cornerstoneTag)) {
|
||||
cornerstoneTag = CORNERSTONE_3D_TAG;
|
||||
}
|
||||
|
||||
const mappedTrackingIdentifier = `${cornerstoneTag}:${toolName}`;
|
||||
|
||||
return adapter.isValidCornerstoneTrackingIdentifier(mappedTrackingIdentifier);
|
||||
});
|
||||
|
||||
if (hydratable) {
|
||||
return true;
|
||||
}
|
||||
console.log('Measurement is not rehydratable', TrackingIdentifier, measurements[i]);
|
||||
}
|
||||
|
||||
console.log('No measurements found which were rehydratable');
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { adaptersSR } from '@cornerstonejs/adapters';
|
||||
|
||||
const { MeasurementReport } = adaptersSR.Cornerstone3D;
|
||||
|
||||
/**
|
||||
* Checks if the given `displaySet`can be rehydrated into the `measurementService`.
|
||||
*
|
||||
* @param {object} displaySet The SR `displaySet` to check.
|
||||
* @param {object[]} mappings The CornerstoneTools 4 mappings to the `measurementService`.
|
||||
* @returns {boolean} True if the SR can be rehydrated into the `measurementService`.
|
||||
*/
|
||||
export default function isRehydratable(displaySet, mappings) {
|
||||
if (!mappings || !mappings.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const mappingDefinitions = new Set<string>();
|
||||
for (const m of mappings) {
|
||||
mappingDefinitions.add(m.annotationType);
|
||||
}
|
||||
|
||||
const { measurements } = displaySet;
|
||||
|
||||
for (let i = 0; i < measurements.length; i++) {
|
||||
const { TrackingIdentifier } = measurements[i] || {};
|
||||
if (!TrackingIdentifier) {
|
||||
console.warn('No tracking identifier for measurement ', measurements[i]);
|
||||
continue;
|
||||
}
|
||||
const adapter = MeasurementReport.getAdapterForTrackingIdentifier(TrackingIdentifier);
|
||||
const hydratable = adapter && mappingDefinitions.has(adapter.toolType);
|
||||
|
||||
if (hydratable) {
|
||||
return true;
|
||||
}
|
||||
console.log('Measurement is not rehydratable', TrackingIdentifier, measurements[i]);
|
||||
}
|
||||
|
||||
console.log('No measurements found which were rehydratable');
|
||||
return false;
|
||||
}
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/core": "^2.19.14",
|
||||
"@cornerstonejs/tools": "^2.19.14",
|
||||
"@cornerstonejs/core": "^3.0.1",
|
||||
"@cornerstonejs/tools": "^3.0.1",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^2.19.14",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.0.1",
|
||||
"@ohif/core": "3.10.0-beta.113",
|
||||
"@ohif/ui": "3.10.0-beta.113",
|
||||
"dcmjs": "*",
|
||||
@@ -51,10 +51,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^2.19.14",
|
||||
"@cornerstonejs/core": "^2.19.14",
|
||||
"@cornerstonejs/tools": "^2.19.14",
|
||||
"@icr/polyseg-wasm": "^0.4.0",
|
||||
"@cornerstonejs/adapters": "^3.0.1",
|
||||
"@cornerstonejs/core": "^3.0.1",
|
||||
"@cornerstonejs/tools": "^3.0.1",
|
||||
"@cornerstonejs/polymorphic-segmentation": "^3.0.1",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "32.1.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
OrientationMarkerTool,
|
||||
WindowLevelRegionTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
import * as polySeg from '@cornerstonejs/polymorphic-segmentation';
|
||||
|
||||
import CalibrationLineTool from './tools/CalibrationLineTool';
|
||||
import ImageOverlayViewerTool from './tools/ImageOverlayViewerTool';
|
||||
@@ -47,7 +48,11 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
AdvancedMagnifyTool.isAnnotation = false;
|
||||
PlanarFreehandContourSegmentationTool.isAnnotation = false;
|
||||
|
||||
init(configuration);
|
||||
init({
|
||||
addons: {
|
||||
polySeg,
|
||||
},
|
||||
});
|
||||
addTool(PanTool);
|
||||
addTool(WindowLevelTool);
|
||||
addTool(StackScrollTool);
|
||||
|
||||
@@ -2,7 +2,8 @@ import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import { getIsLocked } from './utils/getIsLocked';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { getIsVisible } from './utils/getIsVisible';
|
||||
const Length = {
|
||||
|
||||
const ArrowAnnotate = {
|
||||
toAnnotation: measurement => {},
|
||||
|
||||
/**
|
||||
@@ -172,4 +173,4 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values,
|
||||
};
|
||||
}
|
||||
export default Length;
|
||||
export default ArrowAnnotate;
|
||||
@@ -32,8 +32,8 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "^2.19.14",
|
||||
"@cornerstonejs/tools": "^2.19.14",
|
||||
"@cornerstonejs/core": "^3.0.1",
|
||||
"@cornerstonejs/tools": "^3.0.1",
|
||||
"@ohif/core": "3.10.0-beta.113",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.113",
|
||||
"@ohif/extension-default": "3.10.0-beta.113",
|
||||
|
||||
+9
@@ -89,6 +89,15 @@ function TrackedMeasurementsContextProvider(
|
||||
);
|
||||
|
||||
// Jump to the last tracked measurement - most recent
|
||||
if (!trackedMeasurements?.length) {
|
||||
console.warn(
|
||||
"Didn't find any tracked measurements",
|
||||
measurements,
|
||||
trackedStudy,
|
||||
trackedSeries
|
||||
);
|
||||
return;
|
||||
}
|
||||
const trackedMeasurement = trackedMeasurements[trackedMeasurements.length - 1];
|
||||
const referencedDisplaySetUID = trackedMeasurement.displaySetInstanceUID;
|
||||
|
||||
|
||||
Reference in new issue
Block a user