diff --git a/extensions/cornerstone-dicom-seg/package.json b/extensions/cornerstone-dicom-seg/package.json
index ca5125ac9..4cc90eab4 100644
--- a/extensions/cornerstone-dicom-seg/package.json
+++ b/extensions/cornerstone-dicom-seg/package.json
@@ -44,7 +44,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/tools": "^1.19.4",
+ "@cornerstonejs/tools": "^1.20.1",
+ "@cornerstonejs/adapters": "^1.20.1",
+ "@kitware/vtk.js": "27.3.1",
"react-color": "^2.19.3"
}
}
diff --git a/extensions/cornerstone-dicom-seg/src/commandsModule.ts b/extensions/cornerstone-dicom-seg/src/commandsModule.ts
index 7cffb83f7..1926ee9f1 100644
--- a/extensions/cornerstone-dicom-seg/src/commandsModule.ts
+++ b/extensions/cornerstone-dicom-seg/src/commandsModule.ts
@@ -2,9 +2,16 @@ import dcmjs from 'dcmjs';
import { createReportDialogPrompt } from '@ohif/extension-default';
import { ServicesManager, Types } from '@ohif/core';
import { cache, metaData } from '@cornerstonejs/core';
-import { segmentation as cornerstoneToolsSegmentation } from '@cornerstonejs/tools';
-import { adaptersSEG, helpers } from '@cornerstonejs/adapters';
-import { DicomMetadataStore } from '@ohif/core';
+import {
+ segmentation as cornerstoneToolsSegmentation,
+ Enums as cornerstoneToolsEnums,
+} from '@cornerstonejs/tools';
+import { adaptersRT, helpers, adaptersSEG } from '@cornerstonejs/adapters';
+import { classes, DicomMetadataStore } from '@ohif/core';
+
+import vtkImageMarchingSquares from '@kitware/vtk.js/Filters/General/ImageMarchingSquares';
+import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
+import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
import {
updateViewportsForSegmentationRendering,
@@ -12,12 +19,20 @@ import {
getTargetViewport,
} from './utils/hydrationUtils';
+const { datasetToBlob } = dcmjs.data;
+
const {
Cornerstone3D: {
Segmentation: { generateLabelMaps2DFrom3D, generateSegmentation },
},
} = adaptersSEG;
+const {
+ Cornerstone3D: {
+ RTSS: { generateRTSSFromSegmentations },
+ },
+} = adaptersRT;
+
const { downloadDICOMData } = helpers;
const commandsModule = ({
@@ -348,6 +363,40 @@ const commandsModule = ({
return naturalizedReport;
},
+ /**
+ * Converts segmentations into RTSS for download.
+ * This sample function retrieves all segentations and passes to
+ * cornerstone tool adapter to convert to DICOM RTSS format. It then
+ * converts dataset to downloadable blob.
+ *
+ */
+ downloadRTSS: ({ segmentationId }) => {
+ const segmentations = segmentationService.getSegmentation(segmentationId);
+ const vtkUtils = {
+ vtkImageMarchingSquares,
+ vtkDataArray,
+ vtkImageData,
+ };
+
+ const RTSS = generateRTSSFromSegmentations(
+ segmentations,
+ classes.MetadataProvider,
+ DicomMetadataStore,
+ cache,
+ cornerstoneToolsEnums,
+ vtkUtils
+ );
+
+ try {
+ const reportBlob = datasetToBlob(RTSS);
+
+ //Create a URL for the binary.
+ const objectUrl = URL.createObjectURL(reportBlob);
+ window.location.assign(objectUrl);
+ } catch (e) {
+ console.warn(e);
+ }
+ },
};
const definitions = {
@@ -372,6 +421,9 @@ const commandsModule = ({
storeSegmentation: {
commandFn: actions.storeSegmentation,
},
+ downloadRTSS: {
+ commandFn: actions.downloadRTSS,
+ },
};
return {
diff --git a/extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx b/extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx
index 870c1d454..381fa7cc5 100644
--- a/extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx
+++ b/extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx
@@ -1,7 +1,7 @@
import { createReportAsync } from '@ohif/extension-default';
import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
-import { SegmentationGroupTable } from '@ohif/ui';
+import { SegmentationGroupTable, LegacyButtonGroup, LegacyButton } from '@ohif/ui';
import callInputDialog from './callInputDialog';
import callColorPickerDialog from './colorPickerDialog';
@@ -215,6 +215,12 @@ export default function PanelSegmentation({
}
};
+ const onSegmentationDownloadRTSS = segmentationId => {
+ commandsManager.runCommand('downloadRTSS', {
+ segmentationId,
+ });
+ };
+
return (
<>
@@ -227,6 +233,7 @@ export default function PanelSegmentation({
onSegmentationClick={onSegmentationClick}
onSegmentationDelete={onSegmentationDelete}
onSegmentationDownload={onSegmentationDownload}
+ onSegmentationDownloadRTSS={onSegmentationDownloadRTSS}
storeSegmentation={storeSegmentation}
onSegmentationEdit={onSegmentationEdit}
onSegmentClick={onSegmentClick}
diff --git a/extensions/cornerstone-dicom-seg/src/panels/SegmentationToolbox.tsx b/extensions/cornerstone-dicom-seg/src/panels/SegmentationToolbox.tsx
index 4be0ed1b3..f2d580cff 100644
--- a/extensions/cornerstone-dicom-seg/src/panels/SegmentationToolbox.tsx
+++ b/extensions/cornerstone-dicom-seg/src/panels/SegmentationToolbox.tsx
@@ -10,9 +10,9 @@ const TOOL_TYPES = {
SPHERE_BRUSH: 'SphereBrush',
CIRCULAR_ERASER: 'CircularEraser',
SPHERE_ERASER: 'SphereEraser',
- CIRCLE_SCISSOR: 'CircleScissor',
- RECTANGLE_SCISSOR: 'RectangleScissor',
- SPHERE_SCISSOR: 'SphereScissor',
+ CIRCLE_SHAPE: 'CircleScissor',
+ RECTANGLE_SHAPE: 'RectangleScissor',
+ SPHERE_SHAPE: 'SphereScissor',
THRESHOLD_CIRCULAR_BRUSH: 'ThresholdCircularBrush',
THRESHOLD_SPHERE_BRUSH: 'ThresholdSphereBrush',
};
@@ -31,7 +31,7 @@ const initialState = {
brushSize: 15,
mode: 'CircularEraser', // Can be 'CircularEraser' or 'SphereEraser'
},
- Scissors: {
+ Shapes: {
brushSize: 15,
mode: 'CircleScissor', // E.g., 'CircleScissor', 'RectangleScissor', or 'SphereScissor'
},
@@ -301,24 +301,24 @@ function SegmentationToolbox({ servicesManager, extensionManager }) {
],
},
{
- name: 'Scissor',
- icon: 'icon-tool-scissor',
+ name: 'Shapes',
+ icon: 'icon-tool-shape',
disabled: !toolsEnabled,
active:
- state.activeTool === TOOL_TYPES.CIRCLE_SCISSOR ||
- state.activeTool === TOOL_TYPES.RECTANGLE_SCISSOR ||
- state.activeTool === TOOL_TYPES.SPHERE_SCISSOR,
- onClick: () => setToolActive(TOOL_TYPES.CIRCLE_SCISSOR),
+ state.activeTool === TOOL_TYPES.CIRCLE_SHAPE ||
+ state.activeTool === TOOL_TYPES.RECTANGLE_SHAPE ||
+ state.activeTool === TOOL_TYPES.SPHERE_SHAPE,
+ onClick: () => setToolActive(TOOL_TYPES.CIRCLE_SHAPE),
options: [
{
name: 'Mode',
type: 'radio',
- value: state.Scissors.mode,
- id: 'scissor-mode',
+ value: state.Shapes.mode,
+ id: 'shape-mode',
values: [
- { value: TOOL_TYPES.CIRCLE_SCISSOR, label: 'Circle' },
- { value: TOOL_TYPES.RECTANGLE_SCISSOR, label: 'Rectangle' },
- { value: TOOL_TYPES.SPHERE_SCISSOR, label: 'Sphere' },
+ { value: TOOL_TYPES.CIRCLE_SHAPE, label: 'Circle' },
+ { value: TOOL_TYPES.RECTANGLE_SHAPE, label: 'Rectangle' },
+ { value: TOOL_TYPES.SPHERE_SHAPE, label: 'Sphere' },
],
onChange: value => setToolActive(value),
},
diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json
index f25fa2c2f..4dde7495a 100644
--- a/extensions/cornerstone-dicom-sr/package.json
+++ b/extensions/cornerstone-dicom-sr/package.json
@@ -44,9 +44,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^1.19.4",
- "@cornerstonejs/core": "^1.19.4",
- "@cornerstonejs/tools": "^1.19.4",
+ "@cornerstonejs/adapters": "^1.20.1",
+ "@cornerstonejs/core": "^1.20.1",
+ "@cornerstonejs/tools": "^1.20.1",
"classnames": "^2.3.2"
}
}
diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json
index f6e6440ac..cdaccb4f4 100644
--- a/extensions/cornerstone/package.json
+++ b/extensions/cornerstone/package.json
@@ -36,7 +36,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.19.4",
+ "@cornerstonejs/dicom-image-loader": "^1.20.1",
"@ohif/core": "3.7.0-beta.101",
"@ohif/ui": "3.7.0-beta.101",
"dcmjs": "^0.29.6",
@@ -52,10 +52,10 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^1.19.4",
- "@cornerstonejs/core": "^1.19.4",
- "@cornerstonejs/streaming-image-volume-loader": "^1.19.4",
- "@cornerstonejs/tools": "^1.19.4",
+ "@cornerstonejs/adapters": "^1.20.1",
+ "@cornerstonejs/core": "^1.20.1",
+ "@cornerstonejs/streaming-image-volume-loader": "^1.20.1",
+ "@cornerstonejs/tools": "^1.20.1",
"@kitware/vtk.js": "27.3.1",
"html2canvas": "^1.4.1",
"lodash.debounce": "4.0.8",
diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json
index 62762098d..ad0734231 100644
--- a/extensions/measurement-tracking/package.json
+++ b/extensions/measurement-tracking/package.json
@@ -30,8 +30,8 @@
"start": "yarn run dev"
},
"peerDependencies": {
- "@cornerstonejs/core": "^1.19.4",
- "@cornerstonejs/tools": "^1.19.4",
+ "@cornerstonejs/core": "^1.20.1",
+ "@cornerstonejs/tools": "^1.20.1",
"@ohif/core": "3.7.0-beta.101",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.101",
"@ohif/ui": "3.7.0-beta.101",
diff --git a/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/RTSSReport.js b/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/RTSSReport.js
deleted file mode 100644
index afcab5604..000000000
--- a/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/RTSSReport.js
+++ /dev/null
@@ -1,235 +0,0 @@
-import AnnotationToPointData from './measurements/AnnotationToPointData';
-import dcmjs from 'dcmjs';
-import { DicomMetadataStore } from '@ohif/core';
-
-const { DicomMetaDictionary } = dcmjs.data;
-
-export default class RTSSReport {
- constructor() {}
-
- /**
- * Convert handles to RTSSReport report object containing the dcmjs dicom dataset.
- *
- * Note: The tool data needs to be formatted in a specific way, and currently
- * it is limited to the RectangleROIStartEndTool in the Cornerstone.
- *
- * @param annotations Array of Cornerstone tool annotation data
- * @param metadataProvider Metadata provider
- * @param options report generation options
- * @returns Report object containing the dataset
- */
- static generateReport(annotations, metadataProvider, options) {
- let dataset = initializeDataset(annotations, metadataProvider);
-
- annotations.forEach((annotation, index) => {
- const ContourSequence = AnnotationToPointData.convert(
- annotation,
- index,
- metadataProvider,
- options
- );
-
- dataset.StructureSetROISequence.push(
- getStructureSetModule(annotation, index, metadataProvider)
- );
-
- dataset.ROIContourSequence.push(ContourSequence);
- dataset.RTROIObservationsSequence.push(
- getRTROIObservationsSequence(annotation, index, metadataProvider)
- );
-
- // ReferencedSeriesSequence
- // Todo: handle more than one series
- dataset.ReferencedSeriesSequence = getReferencedSeriesSequence(
- annotation,
- index,
- metadataProvider
- );
-
- // ReferencedFrameOfReferenceSequence
- dataset.ReferencedFrameOfReferenceSequence = getReferencedFrameOfReferenceSequence(
- annotation,
- metadataProvider,
- dataset
- );
- });
-
- const fileMetaInformationVersionArray = new Uint8Array(2);
- fileMetaInformationVersionArray[1] = 1;
-
- const _meta = {
- FileMetaInformationVersion: {
- Value: [fileMetaInformationVersionArray.buffer],
- vr: 'OB',
- },
- TransferSyntaxUID: {
- Value: ['1.2.840.10008.1.2.1'],
- vr: 'UI',
- },
- ImplementationClassUID: {
- Value: [DicomMetaDictionary.uid()], // TODO: could be git hash or other valid id
- vr: 'UI',
- },
- ImplementationVersionName: {
- Value: ['dcmjs'],
- vr: 'SH',
- },
- };
-
- dataset._meta = _meta;
-
- return dataset;
- }
-
- /**
- * Generate Cornerstone tool state from dataset
- * @param {object} dataset dataset
- * @param {object} hooks
- * @param {function} hooks.getToolClass Function to map dataset to a tool class
- * @returns
- */
- static generateToolState(dataset, hooks = {}) {
- // Todo
- console.warn('RTSSReport.generateToolState not implemented');
- }
-}
-
-function initializeDataset(annotations, metadataProvider) {
- const rtSOPInstanceUID = DicomMetaDictionary.uid();
-
- // get the first annotation data
- const { referencedImageId: imageId, FrameOfReferenceUID } = annotations[0].metadata;
-
- const { studyInstanceUID } = metadataProvider.get('generalSeriesModule', imageId);
-
- const patientModule = getPatientModule(imageId, metadataProvider);
- const rtSeriesModule = getRTSeriesModule(imageId, metadataProvider);
-
- return {
- StructureSetROISequence: [],
- ROIContourSequence: [],
- RTROIObservationsSequence: [],
- ReferencedSeriesSequence: [],
- ReferencedFrameOfReferenceSequence: [],
- ...patientModule,
- ...rtSeriesModule,
- StudyInstanceUID: studyInstanceUID,
- SOPClassUID: '1.2.840.10008.5.1.4.1.1.481.3', // RT Structure Set Storage
- SOPInstanceUID: rtSOPInstanceUID,
- Manufacturer: 'dcmjs',
- Modality: 'RTSTRUCT',
- FrameOfReferenceUID,
- PositionReferenceIndicator: '',
- StructureSetLabel: '',
- StructureSetName: '',
- ReferringPhysicianName: '',
- OperatorsName: '',
- StructureSetDate: DicomMetaDictionary.date(),
- StructureSetTime: DicomMetaDictionary.time(),
- };
-}
-
-function getPatientModule(imageId, metadataProvider) {
- const generalSeriesModule = metadataProvider.get('generalSeriesModule', imageId);
- const generalStudyModule = metadataProvider.get('generalStudyModule', imageId);
- const patientStudyModule = metadataProvider.get('patientStudyModule', imageId);
- const patientModule = metadataProvider.get('patientModule', imageId);
- const patientDemographicModule = metadataProvider.get('patientDemographicModule', imageId);
-
- return {
- Modality: generalSeriesModule.modality,
- PatientID: patientModule.patientId,
- PatientName: patientModule.patientName,
- PatientBirthDate: '',
- PatientAge: patientStudyModule.patientAge,
- PatientSex: patientDemographicModule.patientSex,
- PatientWeight: patientStudyModule.patientWeight,
- StudyDate: generalStudyModule.studyDate,
- StudyTime: generalStudyModule.studyTime,
- StudyID: 'ToDo',
- AccessionNumber: generalStudyModule.accessionNumber,
- };
-}
-
-function getReferencedFrameOfReferenceSequence(toolData, metadataProvider, dataset) {
- const { referencedImageId: imageId, FrameOfReferenceUID } = toolData.metadata;
- const instance = metadataProvider.get('instance', imageId);
- const { SeriesInstanceUID } = instance;
-
- const { ReferencedSeriesSequence } = dataset;
-
- return [
- {
- FrameOfReferenceUID,
- RTReferencedStudySequence: [
- {
- ReferencedSOPClassUID: dataset.SOPClassUID,
- ReferencedSOPInstanceUID: dataset.SOPInstanceUID,
- RTReferencedSeriesSequence: [
- {
- SeriesInstanceUID,
- ContourImageSequence: [...ReferencedSeriesSequence[0].ReferencedInstanceSequence],
- },
- ],
- },
- ],
- },
- ];
-}
-
-function getReferencedSeriesSequence(toolData, index, metadataProvider) {
- // grab imageId from toolData
- const { referencedImageId: imageId } = toolData.metadata;
- const instance = metadataProvider.get('instance', imageId);
- const { SeriesInstanceUID, StudyInstanceUID } = instance;
-
- const ReferencedSeriesSequence = [];
- if (SeriesInstanceUID) {
- const series = DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID);
-
- const ReferencedSeries = {
- SeriesInstanceUID,
- ReferencedInstanceSequence: [],
- };
-
- series.instances.forEach(instance => {
- const { SOPInstanceUID, SOPClassUID } = instance;
- ReferencedSeries.ReferencedInstanceSequence.push({
- ReferencedSOPClassUID: SOPClassUID,
- ReferencedSOPInstanceUID: SOPInstanceUID,
- });
- });
-
- ReferencedSeriesSequence.push(ReferencedSeries);
- }
-
- return ReferencedSeriesSequence;
-}
-
-function getRTSeriesModule(imageId, metadataProvider) {
- return {
- SeriesInstanceUID: DicomMetaDictionary.uid(), // generate a new series instance uid
- SeriesNumber: '99', // Todo:: what should be the series number?
- };
-}
-
-function getStructureSetModule(toolData, index, metadataProvider) {
- const { FrameOfReferenceUID } = toolData.metadata;
-
- return {
- ROINumber: index + 1,
- ROIName: `Todo: name ${index + 1}`,
- ROIDescription: `Todo: description ${index + 1}`,
- ROIGenerationAlgorithm: 'Todo: algorithm',
- ReferencedFrameOfReferenceUID: FrameOfReferenceUID,
- };
-}
-
-function getRTROIObservationsSequence(toolData, index, metadataProvider) {
- return {
- ObservationNumber: index + 1,
- ReferencedROINumber: index + 1,
- RTROIInterpretedType: 'Todo: type',
- ROIInterpreter: 'Todo: interpreter',
- };
-}
diff --git a/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/dicomRTAnnotationExport.js b/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/dicomRTAnnotationExport.js
index 999b37add..df4aa9c3c 100644
--- a/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/dicomRTAnnotationExport.js
+++ b/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/dicomRTAnnotationExport.js
@@ -1,12 +1,16 @@
-import RTSSReport from './RTSSReport';
import dcmjs from 'dcmjs';
-import { classes } from '@ohif/core';
+import { classes, DicomMetadataStore } from '@ohif/core';
+import { adaptersSEG } from '@cornerstonejs/adapters';
const { datasetToBlob } = dcmjs.data;
const metadataProvider = classes.MetadataProvider;
export default function dicomRTAnnotationExport(annotations) {
- const dataset = RTSSReport.generateReport(annotations, metadataProvider);
+ const dataset = adaptersSEG.Cornerstone3D.RTStruct.RTSS.generateRTSSFromAnnotations(
+ annotations,
+ metadataProvider,
+ DicomMetadataStore
+ );
const reportBlob = datasetToBlob(dataset);
//Create a URL for the binary.
diff --git a/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/measurements/AnnotationToPointData.js b/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/measurements/AnnotationToPointData.js
deleted file mode 100644
index d1d67cebf..000000000
--- a/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/measurements/AnnotationToPointData.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import RectangleROIStartEndThreshold from './RectangleROIStartEndThreshold';
-
-function validateAnnotation(annotation) {
- if (!annotation?.data) {
- throw new Error('Tool data is empty');
- }
-
- if (!annotation.metadata || annotation.metadata.referenceImageId) {
- throw new Error('Tool data is not associated with any imageId');
- }
-}
-
-class AnnotationToPointData {
- constructor() {}
-
- static convert(annotation, index, metadataProvider) {
- validateAnnotation(annotation);
-
- const { toolName } = annotation.metadata;
- const toolClass = AnnotationToPointData.TOOL_NAMES[toolName];
-
- if (!toolClass) {
- throw new Error(`Unknown tool type: ${toolName}, cannot convert to RTSSReport`);
- }
-
- // Each toolData should become a list of contours, ContourSequence
- // contains a list of contours with their pointData, their geometry
- // type and their length.
- const ContourSequence = toolClass.getContourSequence(annotation, metadataProvider);
-
- // Todo: random rgb color for now, options should be passed in
- const color = [
- Math.floor(Math.random() * 255),
- Math.floor(Math.random() * 255),
- Math.floor(Math.random() * 255),
- ];
-
- return {
- ReferencedROINumber: index + 1,
- ROIDisplayColor: color,
- ContourSequence,
- };
- }
-
- static register(toolClass) {
- AnnotationToPointData.TOOL_NAMES[toolClass.toolName] = toolClass;
- }
-}
-
-AnnotationToPointData.TOOL_NAMES = {};
-AnnotationToPointData.register(RectangleROIStartEndThreshold);
-
-export default AnnotationToPointData;
diff --git a/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/measurements/RectangleROIStartEndThreshold.js b/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/measurements/RectangleROIStartEndThreshold.js
deleted file mode 100644
index c3b86a627..000000000
--- a/extensions/tmtv/src/utils/dicomRTAnnotationExport/RTStructureSet/measurements/RectangleROIStartEndThreshold.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// comment
-class RectangleROIStartEndThreshold {
- constructor() {}
-
- static getContourSequence(toolData, metadataProvider) {
- const { data } = toolData;
- const { projectionPoints, projectionPointsImageIds } = data.cachedStats;
-
- return projectionPoints.map((point, index) => {
- const ContourData = getPointData(point);
- const ContourImageSequence = getContourImageSequence(
- projectionPointsImageIds[index],
- metadataProvider
- );
-
- return {
- NumberOfContourPoints: ContourData.length / 3,
- ContourImageSequence,
- ContourGeometricType: 'CLOSED_PLANAR',
- ContourData,
- };
- });
- }
-}
-
-RectangleROIStartEndThreshold.toolName = 'RectangleROIStartEndThreshold';
-
-function getPointData(points) {
- // Since this is a closed contour, the order of the points is important.
- // re-order the points to be in the correct order clockwise
- // Spread to make sure Float32Arrays are converted to arrays
- const orderedPoints = [...points[0], ...points[1], ...points[3], ...points[2]];
- const pointsArray = orderedPoints.flat();
-
- // reduce the precision of the points to 2 decimal places
- const pointsArrayWithPrecision = pointsArray.map(point => {
- return point.toFixed(2);
- });
-
- return pointsArrayWithPrecision;
-}
-
-function getContourImageSequence(imageId, metadataProvider) {
- const sopCommon = metadataProvider.get('sopCommonModule', imageId);
-
- return {
- ReferencedSOPClassUID: sopCommon.sopClassUID,
- ReferencedSOPInstanceUID: sopCommon.sopInstanceUID,
- };
-}
-export default RectangleROIStartEndThreshold;
diff --git a/package.json b/package.json
index 97fb2c48b..dceeec8b3 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"build:package-all": "lerna run build:package --parallel --stream",
"build:package-all-1": "lerna run build:package-1 --parallel --stream",
"dev": "lerna run dev:viewer --stream",
+ "dev:no:cache": "lerna run dev:no:cache --stream",
"dev:project": ".scripts/dev.sh",
"dev:orthanc": "lerna run dev:orthanc --stream",
"dev:dcm4chee": "lerna run dev:dcm4chee --stream",
diff --git a/platform/app/package.json b/platform/app/package.json
index 5136bf221..bbeaea1e7 100644
--- a/platform/app/package.json
+++ b/platform/app/package.json
@@ -26,6 +26,7 @@
"build:viewer:demo": "cross-env NODE_ENV=production APP_CONFIG=config/demo.js HTML_TEMPLATE=rollbar.html QUICK_BUILD=false yarn run build",
"build": "node --max_old_space_size=4096 ./../../node_modules/webpack/bin/webpack.js --progress --config .webpack/webpack.pwa.js",
"dev": "cross-env NODE_ENV=development webpack serve --config .webpack/webpack.pwa.js",
+ "dev:no:cache": "cross-env NODE_ENV=development webpack serve --no-cache --config .webpack/webpack.pwa.js",
"dev:orthanc": "cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack serve --config .webpack/webpack.pwa.js",
"dev:dcm4chee": "cross-env NODE_ENV=development APP_CONFIG=config/local_dcm4chee.js webpack serve --config .webpack/webpack.pwa.js",
"dev:static": "cross-env NODE_ENV=development APP_CONFIG=config/local_static.js webpack serve --config .webpack/webpack.pwa.js",
@@ -50,7 +51,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.19.4",
+ "@cornerstonejs/dicom-image-loader": "^1.20.1",
"@ohif/core": "3.7.0-beta.101",
"@ohif/extension-cornerstone": "3.7.0-beta.101",
"@ohif/extension-cornerstone-dicom-rt": "3.7.0-beta.101",
diff --git a/platform/core/package.json b/platform/core/package.json
index 57812d99c..e42b0d736 100644
--- a/platform/core/package.json
+++ b/platform/core/package.json
@@ -35,7 +35,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.19.4",
+ "@cornerstonejs/dicom-image-loader": "^1.20.1",
"@ohif/ui": "3.7.0-beta.101",
"cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21"
diff --git a/platform/ui/src/assets/icons/icon-tool-brush.svg b/platform/ui/src/assets/icons/icon-tool-brush.svg
index 095613710..7f78d720a 100644
--- a/platform/ui/src/assets/icons/icon-tool-brush.svg
+++ b/platform/ui/src/assets/icons/icon-tool-brush.svg
@@ -1,15 +1,13 @@
-
\ No newline at end of file
diff --git a/platform/ui/src/assets/icons/icon-tool-eraser.svg b/platform/ui/src/assets/icons/icon-tool-eraser.svg
index 35e29a5dd..5c72e4c37 100644
--- a/platform/ui/src/assets/icons/icon-tool-eraser.svg
+++ b/platform/ui/src/assets/icons/icon-tool-eraser.svg
@@ -1,16 +1,14 @@
-
+
icon-tool-eraser
-
-
-
-
-
-
-
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/platform/ui/src/assets/icons/icon-tool-shape.svg b/platform/ui/src/assets/icons/icon-tool-shape.svg
new file mode 100644
index 000000000..132f7b58a
--- /dev/null
+++ b/platform/ui/src/assets/icons/icon-tool-shape.svg
@@ -0,0 +1,13 @@
+
+
+ icon-tool-shape
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/ui/src/assets/icons/icon-tool-threshold.svg b/platform/ui/src/assets/icons/icon-tool-threshold.svg
index 9a518c5b3..d1bcaa76e 100644
--- a/platform/ui/src/assets/icons/icon-tool-threshold.svg
+++ b/platform/ui/src/assets/icons/icon-tool-threshold.svg
@@ -1,21 +1,17 @@
-
+
icon-tool-threshold
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/platform/ui/src/components/Icon/getIcon.js b/platform/ui/src/components/Icon/getIcon.js
index 9604964a4..d09de1e75 100644
--- a/platform/ui/src/components/Icon/getIcon.js
+++ b/platform/ui/src/components/Icon/getIcon.js
@@ -137,6 +137,7 @@ import iconMoreMenu from './../../assets/icons/icon-more-menu.svg';
import iconToolBrush from './../../assets/icons/icon-tool-brush.svg';
import iconToolEraser from './../../assets/icons/icon-tool-eraser.svg';
import iconToolScissor from './../../assets/icons/icon-tool-scissor.svg';
+import iconToolShape from './../../assets/icons/icon-tool-shape.svg';
import iconToolThreshold from './../../assets/icons/icon-tool-threshold.svg';
/** Old OHIF */
@@ -282,6 +283,7 @@ const ICONS = {
'icon-tool-brush': iconToolBrush,
'icon-tool-eraser': iconToolEraser,
'icon-tool-scissor': iconToolScissor,
+ 'icon-tool-shape': iconToolShape,
'icon-tool-threshold': iconToolThreshold,
/** Old OHIF */
diff --git a/platform/ui/src/components/SegmentationGroupTable/SegmentationDropDownRow.tsx b/platform/ui/src/components/SegmentationGroupTable/SegmentationDropDownRow.tsx
index 0095c9767..4fc5b188d 100644
--- a/platform/ui/src/components/SegmentationGroupTable/SegmentationDropDownRow.tsx
+++ b/platform/ui/src/components/SegmentationGroupTable/SegmentationDropDownRow.tsx
@@ -10,6 +10,7 @@ function SegmentationDropDownRow({
onToggleSegmentationVisibility,
onSegmentationEdit,
onSegmentationDownload,
+ onSegmentationDownloadRTSS,
storeSegmentation,
onSegmentationDelete,
onSegmentationAdd,
@@ -40,6 +41,7 @@ function SegmentationDropDownRow({
alignment="left"
itemsClassName="text-primary-active"
showBorders={false}
+ maxCharactersPerLine={30}
list={[
...(!disableEditing
? [
@@ -79,11 +81,17 @@ function SegmentationDropDownRow({
: []),
...[
{
- title: 'Download',
+ title: 'Download DICOM SEG',
onClick: () => {
onSegmentationDownload(activeSegmentation.id);
},
},
+ {
+ title: 'Download DICOM RTSTRUCT',
+ onClick: () => {
+ onSegmentationDownloadRTSS(activeSegmentation.id);
+ },
+ },
],
]}
>
@@ -149,6 +157,7 @@ SegmentationDropDownRow.propTypes = {
onToggleSegmentationVisibility: PropTypes.func,
onSegmentationEdit: PropTypes.func,
onSegmentationDownload: PropTypes.func,
+ onSegmentationDownloadRTSS: PropTypes.func,
storeSegmentation: PropTypes.func,
onSegmentationDelete: PropTypes.func,
onSegmentationAdd: PropTypes.func,
diff --git a/platform/ui/src/components/SegmentationGroupTable/SegmentationGroupTable.tsx b/platform/ui/src/components/SegmentationGroupTable/SegmentationGroupTable.tsx
index a247326e3..295a79514 100644
--- a/platform/ui/src/components/SegmentationGroupTable/SegmentationGroupTable.tsx
+++ b/platform/ui/src/components/SegmentationGroupTable/SegmentationGroupTable.tsx
@@ -22,6 +22,7 @@ const SegmentationGroupTable = ({
onSegmentationClick,
onSegmentationDelete,
onSegmentationDownload,
+ onSegmentationDownloadRTSS,
storeSegmentation,
// segment handlers
onSegmentClick,
@@ -112,6 +113,7 @@ const SegmentationGroupTable = ({
onSegmentationDelete={onSegmentationDelete}
onSegmentationEdit={onSegmentationEdit}
onSegmentationDownload={onSegmentationDownload}
+ onSegmentationDownloadRTSS={onSegmentationDownloadRTSS}
storeSegmentation={storeSegmentation}
onSegmentationAdd={onSegmentationAdd}
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
@@ -188,6 +190,7 @@ SegmentationGroupTable.propTypes = {
onSegmentationClick: PropTypes.func.isRequired,
onSegmentationDelete: PropTypes.func.isRequired,
onSegmentationDownload: PropTypes.func.isRequired,
+ onSegmentationDownloadRTSS: PropTypes.func.isRequired,
storeSegmentation: PropTypes.func.isRequired,
onSegmentClick: PropTypes.func.isRequired,
onSegmentAdd: PropTypes.func.isRequired,
@@ -217,6 +220,7 @@ SegmentationGroupTable.defaultProps = {
onSegmentationClick: () => {},
onSegmentationDelete: () => {},
onSegmentationDownload: () => {},
+ onSemgnetationDownloadRTSS: () => {},
storeSegmentation: () => {},
onSegmentClick: () => {},
onSegmentAdd: () => {},
diff --git a/yarn.lock b/yarn.lock
index c5ea684ce..4b89db159 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1482,10 +1482,10 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
-"@cornerstonejs/adapters@^1.19.4":
- version "1.19.4"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.19.4.tgz#65b5f83f709faf2b62ba87effde7579a034fcfd6"
- integrity sha512-ulirXb8Jx2zWRviXKbumU1K4J9q+nTRCAeG8SONZFFWlWTSwWWPNyQfOHDb3SE/KcupVk4gKzIjd+r0cC+cPdw==
+"@cornerstonejs/adapters@^1.20.1":
+ version "1.20.1"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.20.1.tgz#3240a9af56f7508069c1e50fd1ceaa016d3df0e2"
+ integrity sha512-iEKhw8+/ll0I7bblbO82XNlW8DjdrtNO4M3lTnUj/lBTvt4pk0QK2KM+/ILcaSMizwgZ7ZANlOK06J4cTtINdw==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
buffer "^6.0.3"
@@ -1534,43 +1534,43 @@
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.2.tgz#e96721d56f6ec96f7f95c16321d88cc8467d8d81"
integrity sha512-lgdvBvvNezleY+4pIe2ceUsJzlZe/0PipdeubQ3vZZOz3xxtHHMR1XFCl4fgd8gosR8COHuD7h6q+MwgrwBsng==
-"@cornerstonejs/core@^1.19.4":
- version "1.19.4"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.19.4.tgz#7d527f151a1487955e7817b8113ab4c9ad2f7231"
- integrity sha512-vVV3I6SzMbP4JqqRp49fBmVSr44l7qOXrO/gb6J3cY/EmwuUdFeSQdmN6ggouaQ45fhKPqPGcyHNO/iww9BR3Q==
+"@cornerstonejs/core@^1.20.1":
+ version "1.20.1"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.20.1.tgz#0d8765b1f9e7a9314a29286d59e5cf7ece3bee2f"
+ integrity sha512-83Ebs35XSNMhbTXDP3V3tTDjM00Qh3o/eQGDu4cHUq0Cu2nVYXTQeIlaQkLHffoqUDKJh3PzbQCDRYtodJ/vhQ==
dependencies:
"@kitware/vtk.js" "27.3.1"
detect-gpu "^5.0.22"
gl-matrix "^3.4.3"
lodash.clonedeep "4.5.0"
-"@cornerstonejs/dicom-image-loader@^1.19.4":
- version "1.19.4"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.19.4.tgz#778d933f8531233382080c3afea45854e7cf92b5"
- integrity sha512-1+Dt+FR12pfbybPwOVJONx2KcoHbTyp8zCxrbmpRUxivwbq55SMExQa4ef8fFL97/+Hb9euczdA+PO9bbcD3Fw==
+"@cornerstonejs/dicom-image-loader@^1.20.1":
+ version "1.20.1"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.20.1.tgz#01d112f626a6a3bf01c5fc44f2d32061e5c4b835"
+ integrity sha512-PpRykX3Y3D8LRyNg9vG8hh+CmvUKB8Q8cJ5R1XY0lSv7jsnB1hW2NGA8L9JfKalkUxAugC/nPEwRG6sf9c5z0A==
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.2"
- "@cornerstonejs/core" "^1.19.4"
+ "@cornerstonejs/core" "^1.20.1"
dicom-parser "^1.8.9"
pako "^2.0.4"
uuid "^9.0.0"
-"@cornerstonejs/streaming-image-volume-loader@^1.19.4":
- version "1.19.4"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.19.4.tgz#d2a7afcd673ba112d851e95d2895109c3490cdce"
- integrity sha512-Y+g6hdM3cTsdjKjsgy/H19/qEveRqSeT/Og/mCi7AjKe/UeS+EB3vqA7GfkkyGkM2x/U/+F+5RkyzFCXPA2nOw==
+"@cornerstonejs/streaming-image-volume-loader@^1.20.1":
+ version "1.20.1"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.20.1.tgz#e5053a7bfe9967882115054b50142f36df8f468a"
+ integrity sha512-E36rlnLDE4EjCPf1f9tB5LQdxtwAHHJuTSdZoBvcEbKOG/EzlNr1bVfHoJoMkPX8aTGfO6Y7dNP57IyqqEY3Bw==
dependencies:
- "@cornerstonejs/core" "^1.19.4"
+ "@cornerstonejs/core" "^1.20.1"
-"@cornerstonejs/tools@^1.19.4":
- version "1.19.4"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.19.4.tgz#c0d97c40c3d3b0c13dd359eef703de394994c719"
- integrity sha512-BVlmi1LuLH4Q5ZYGAPJT/p7t9p8NGLrux1eBGx7LmDe7G2ZOsOWU3kGkbGAzVtb/d0HB9eBV3cruA0+xRwmpMA==
+"@cornerstonejs/tools@^1.20.1":
+ version "1.20.1"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.20.1.tgz#7242ce2cdc25fe8367c855f67f329be1f235ae64"
+ integrity sha512-0tPvnS7RNkVkZWmUOG/CnPfXRAdU0P9oBGtEC3t2h8uBAiCX/VUxBiXOKkbsnmzkEj1LDElCqLONSHqCvz35tg==
dependencies:
- "@cornerstonejs/core" "^1.19.4"
+ "@cornerstonejs/core" "^1.20.1"
lodash.clonedeep "4.5.0"
lodash.get "^4.4.2"