fix(cornerstone-dicom-sr): Freehand SR hydration support (#3996)
Co-authored-by: Ibrahim <93064150+IbrahimCSAE@users.noreply.github.com> Co-authored-by: IbrahimCSAE <ibrahim.mdev@gmail.com>
This commit is contained in:
parent
6e5892d202
commit
5645ac1b27
@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.67.0",
|
||||
"@cornerstonejs/core": "^1.67.0",
|
||||
"@cornerstonejs/adapters": "^1.68.1",
|
||||
"@cornerstonejs/core": "^1.68.1",
|
||||
"@kitware/vtk.js": "29.7.0",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.67.0",
|
||||
"@cornerstonejs/core": "^1.67.0",
|
||||
"@cornerstonejs/tools": "^1.67.0",
|
||||
"@cornerstonejs/adapters": "^1.68.1",
|
||||
"@cornerstonejs/core": "^1.68.1",
|
||||
"@cornerstonejs/tools": "^1.68.1",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { SOPClassHandlerName, SOPClassHandlerId } from './id';
|
||||
import { utils, classes, DisplaySetService, Types } from '@ohif/core';
|
||||
import addMeasurement from './utils/addMeasurement';
|
||||
import addDICOMSRDisplayAnnotation from './utils/addDICOMSRDisplayAnnotation';
|
||||
import isRehydratable from './utils/isRehydratable';
|
||||
import { adaptersSR } from '@cornerstonejs/adapters';
|
||||
|
||||
@ -150,13 +150,37 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
|
||||
return [displaySet];
|
||||
}
|
||||
|
||||
function _load(displaySet, servicesManager, extensionManager) {
|
||||
async function _load(displaySet, servicesManager, extensionManager) {
|
||||
const { displaySetService, measurementService } = servicesManager.services;
|
||||
const dataSources = extensionManager.getDataSources();
|
||||
const dataSource = dataSources[0];
|
||||
|
||||
const { ContentSequence } = displaySet.instance;
|
||||
|
||||
async function retrieveBulkData(obj, parentObj = null, key = null) {
|
||||
for (const prop in obj) {
|
||||
if (typeof obj[prop] === 'object' && obj[prop] !== null) {
|
||||
await retrieveBulkData(obj[prop], obj, prop);
|
||||
} else if (Array.isArray(obj[prop])) {
|
||||
await Promise.all(obj[prop].map(item => retrieveBulkData(item, obj, prop)));
|
||||
} else if (prop === 'BulkDataURI') {
|
||||
const value = await dataSource.retrieve.bulkDataURI({
|
||||
BulkDataURI: obj[prop],
|
||||
StudyInstanceUID: displaySet.instance.StudyInstanceUID,
|
||||
SeriesInstanceUID: displaySet.instance.SeriesInstanceUID,
|
||||
SOPInstanceUID: displaySet.instance.SOPInstanceUID,
|
||||
});
|
||||
if (parentObj && key) {
|
||||
parentObj[key] = new Float32Array(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displaySet.isLoaded !== true) {
|
||||
await retrieveBulkData(ContentSequence);
|
||||
}
|
||||
|
||||
displaySet.referencedImages = _getReferencedImagesList(ContentSequence);
|
||||
displaySet.measurements = _getMeasurements(ContentSequence);
|
||||
|
||||
@ -211,7 +235,7 @@ function _checkIfCanAddMeasurementsToDisplaySet(
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newDisplaySet instanceof ImageSet) {
|
||||
if ((!newDisplaySet) instanceof ImageSet) {
|
||||
// This also filters out _this_ displaySet, as it is not an ImageSet.
|
||||
return;
|
||||
}
|
||||
@ -275,7 +299,22 @@ function _checkIfCanAddMeasurementsToDisplaySet(
|
||||
}
|
||||
|
||||
if (_measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frameNumber)) {
|
||||
addMeasurement(measurement, imageId, newDisplaySet.displaySetInstanceUID);
|
||||
const frame =
|
||||
(measurement.coords[0].ReferencedSOPSequence &&
|
||||
measurement.coords[0].ReferencedSOPSequence?.ReferencedFrameNumber) ||
|
||||
1;
|
||||
|
||||
/** Add DICOMSRDisplay annotation for the SR viewport (only) */
|
||||
addDICOMSRDisplayAnnotation(measurement, imageId, frame);
|
||||
|
||||
/** Update measurement properties */
|
||||
measurement.loaded = true;
|
||||
measurement.imageId = imageId;
|
||||
measurement.displaySetInstanceUID = newDisplaySet.displaySetInstanceUID;
|
||||
measurement.ReferencedSOPInstanceUID =
|
||||
measurement.coords[0].ReferencedSOPSequence.ReferencedSOPInstanceUID;
|
||||
measurement.frameNumber = frame;
|
||||
delete measurement.coords;
|
||||
|
||||
unloadedMeasurements.splice(j, 1);
|
||||
}
|
||||
@ -291,7 +330,7 @@ function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frame
|
||||
// Standard. But for now, we will support only one ReferenceFrameNumber.
|
||||
const ReferencedFrameNumber =
|
||||
(measurement.coords[0].ReferencedSOPSequence &&
|
||||
measurement.coords[0].ReferencedSOPSequence[0]?.ReferencedFrameNumber) ||
|
||||
measurement.coords[0].ReferencedSOPSequence?.ReferencedFrameNumber) ||
|
||||
1;
|
||||
|
||||
if (frameNumber && Number(frameNumber) !== Number(ReferencedFrameNumber)) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import getSopClassHandlerModule from './getSopClassHandlerModule';
|
||||
import getHangingProtocolModule, { srProtocol } from './getHangingProtocolModule';
|
||||
import { srProtocol } from './getHangingProtocolModule';
|
||||
import onModeEnter from './onModeEnter';
|
||||
import getCommandsModule from './commandsModule';
|
||||
import preRegistration from './init';
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import {
|
||||
addTool,
|
||||
AngleTool,
|
||||
annotation,
|
||||
ArrowAnnotateTool,
|
||||
@ -9,6 +8,7 @@ import {
|
||||
CircleROITool,
|
||||
LengthTool,
|
||||
PlanarFreehandROITool,
|
||||
RectangleROITool,
|
||||
} from '@cornerstonejs/tools';
|
||||
import DICOMSRDisplayTool from './tools/DICOMSRDisplayTool';
|
||||
import addToolInstance from './utils/addToolInstance';
|
||||
@ -19,24 +19,25 @@ import toolNames from './tools/toolNames';
|
||||
* @param {object} configuration
|
||||
*/
|
||||
export default function init({ configuration = {} }: Types.Extensions.ExtensionParams): void {
|
||||
addTool(DICOMSRDisplayTool);
|
||||
addToolInstance(toolNames.SRLength, LengthTool, {});
|
||||
addToolInstance(toolNames.DICOMSRDisplay, DICOMSRDisplayTool);
|
||||
addToolInstance(toolNames.SRLength, LengthTool);
|
||||
addToolInstance(toolNames.SRBidirectional, BidirectionalTool);
|
||||
addToolInstance(toolNames.SREllipticalROI, EllipticalROITool);
|
||||
addToolInstance(toolNames.SRCircleROI, CircleROITool);
|
||||
addToolInstance(toolNames.SRArrowAnnotate, ArrowAnnotateTool);
|
||||
addToolInstance(toolNames.SRAngle, AngleTool);
|
||||
addToolInstance(toolNames.SRPlanarFreehandROI, PlanarFreehandROITool);
|
||||
addToolInstance(toolNames.SRRectangleROI, RectangleROITool);
|
||||
|
||||
// TODO - fix the SR display of Cobb Angle, as it joins the two lines
|
||||
addToolInstance(toolNames.SRCobbAngle, CobbAngleTool);
|
||||
// TODO - fix the rehydration of Freehand, as it throws an exception
|
||||
// on a missing polyline. The fix is probably in CS3D
|
||||
addToolInstance(toolNames.SRPlanarFreehandROI, PlanarFreehandROITool);
|
||||
|
||||
// Modify annotation tools to use dashed lines on SR
|
||||
const dashedLine = {
|
||||
lineDash: '4,4',
|
||||
};
|
||||
annotation.config.style.setToolGroupToolStyles('SRToolGroup', {
|
||||
[toolNames.DICOMSRDisplay]: dashedLine,
|
||||
SRLength: dashedLine,
|
||||
SRBidirectional: dashedLine,
|
||||
SREllipticalROI: dashedLine,
|
||||
@ -45,6 +46,7 @@ export default function init({ configuration = {} }: Types.Extensions.ExtensionP
|
||||
SRCobbAngle: dashedLine,
|
||||
SRAngle: dashedLine,
|
||||
SRPlanarFreehandROI: dashedLine,
|
||||
SRRectangleROI: dashedLine,
|
||||
global: {},
|
||||
});
|
||||
}
|
||||
|
||||
@ -22,14 +22,14 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
||||
}
|
||||
|
||||
_getTextBoxLinesFromLabels(labels) {
|
||||
// TODO -> max 3 for now (label + shortAxis + longAxis), need a generic solution for this!
|
||||
// TODO -> max 5 for now (label + shortAxis + longAxis), need a generic solution for this!
|
||||
|
||||
const labelLength = Math.min(labels.length, 3);
|
||||
const labelLength = Math.min(labels.length, 5);
|
||||
const lines = [];
|
||||
|
||||
for (let i = 0; i < labelLength; i++) {
|
||||
const labelEntry = labels[i];
|
||||
lines.push(`${_labelToShorthand(labelEntry.label)}${labelEntry.value}`);
|
||||
lines.push(`${_labelToShorthand(labelEntry.label)}: ${labelEntry.value}`);
|
||||
}
|
||||
|
||||
return lines;
|
||||
@ -77,6 +77,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
||||
toolName: this.getToolName(),
|
||||
viewportId: enabledElement.viewport.id,
|
||||
};
|
||||
const { style: annotationStyle } = annotation.config;
|
||||
|
||||
for (let i = 0; i < filteredAnnotations.length; i++) {
|
||||
const annotation = filteredAnnotations[i];
|
||||
@ -87,6 +88,10 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
||||
|
||||
styleSpecifier.annotationUID = annotationUID;
|
||||
|
||||
const groupStyle = annotationStyle.getToolGroupToolStyles(this.toolGroupId)[
|
||||
this.getToolName()
|
||||
];
|
||||
|
||||
const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
|
||||
const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
|
||||
const color =
|
||||
@ -98,6 +103,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
||||
color,
|
||||
lineDash,
|
||||
lineWidth,
|
||||
...groupStyle,
|
||||
};
|
||||
|
||||
Object.keys(renderableData).forEach(GraphicType => {
|
||||
@ -160,6 +166,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
||||
const drawingOptions = {
|
||||
color: options.color,
|
||||
width: options.lineWidth,
|
||||
lineDash: options.lineDash,
|
||||
};
|
||||
let allCanvasCoordinates = [];
|
||||
renderableData.map((data, index) => {
|
||||
@ -307,6 +314,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
|
||||
{
|
||||
color: options.color,
|
||||
width: options.lineWidth,
|
||||
lineDash: options.lineDash,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
import { vec3 } from 'gl-matrix';
|
||||
import { Types, annotation } from '@cornerstonejs/tools';
|
||||
import { metaData, utilities, Types as csTypes } from '@cornerstonejs/core';
|
||||
|
||||
import toolNames from '../tools/toolNames';
|
||||
import SCOORD_TYPES from '../constants/scoordTypes';
|
||||
|
||||
const EPSILON = 1e-4;
|
||||
|
||||
const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
|
||||
|
||||
export default function addMeasurement(measurement, imageId, displaySetInstanceUID) {
|
||||
// TODO -> Render rotated ellipse .
|
||||
export default function addDICOMSRDisplayAnnotation(measurement, imageId, frameNumber) {
|
||||
const toolName = toolNames.DICOMSRDisplay;
|
||||
|
||||
const measurementData = {
|
||||
@ -27,26 +25,25 @@ export default function addMeasurement(measurement, imageId, displaySetInstanceU
|
||||
}
|
||||
|
||||
measurementData.renderableData[GraphicType].push(
|
||||
_getRenderableData(GraphicType, GraphicData, imageId, measurement.TrackingIdentifier)
|
||||
_getRenderableData(GraphicType, GraphicData, imageId)
|
||||
);
|
||||
});
|
||||
|
||||
// Use the metadata provider to grab its imagePlaneModule metadata
|
||||
const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
|
||||
|
||||
const annotationManager = annotation.state.getAnnotationManager();
|
||||
|
||||
// Create Cornerstone3D Annotation from measurement
|
||||
const frameNumber =
|
||||
(measurement.coords[0].ReferencedSOPSequence &&
|
||||
measurement.coords[0].ReferencedSOPSequence[0]?.ReferencedFrameNumber) ||
|
||||
1;
|
||||
|
||||
/**
|
||||
* This annotation (DICOMSRDisplay) is only used by the SR viewport.
|
||||
* This is used before the annotation is hydrated. If hydrated the measurement will be added
|
||||
* to the measurement service and will be available for the other viewports.
|
||||
*/
|
||||
const SRAnnotation: Types.Annotation = {
|
||||
annotationUID: measurement.TrackingUniqueIdentifier,
|
||||
highlighted: false,
|
||||
isLocked: false,
|
||||
invalidated: false,
|
||||
metadata: {
|
||||
FrameOfReferenceUID: imagePlaneModule.frameOfReferenceUID,
|
||||
toolName: toolName,
|
||||
FrameOfReferenceUID: imagePlaneModule.frameOfReferenceUID,
|
||||
referencedImageId: imageId,
|
||||
},
|
||||
data: {
|
||||
@ -58,28 +55,14 @@ export default function addMeasurement(measurement, imageId, displaySetInstanceU
|
||||
TrackingUniqueIdentifier: measurementData.TrackingUniqueIdentifier,
|
||||
renderableData: measurementData.renderableData,
|
||||
},
|
||||
frameNumber: frameNumber,
|
||||
frameNumber,
|
||||
},
|
||||
};
|
||||
|
||||
const annotationManager = annotation.state.getAnnotationManager();
|
||||
annotationManager.addAnnotation(SRAnnotation);
|
||||
|
||||
measurement.loaded = true;
|
||||
measurement.imageId = imageId;
|
||||
measurement.displaySetInstanceUID = displaySetInstanceUID;
|
||||
|
||||
// Remove the unneeded coord now its processed, but keep the SOPInstanceUID.
|
||||
// NOTE: We assume that each SCOORD in the MeasurementGroup maps onto one frame,
|
||||
// It'd be super weird if it didn't anyway as a SCOORD.
|
||||
measurement.ReferencedSOPInstanceUID =
|
||||
measurement.coords[0].ReferencedSOPSequence.ReferencedSOPInstanceUID;
|
||||
measurement.frameNumber = frameNumber;
|
||||
delete measurement.coords;
|
||||
}
|
||||
|
||||
function _getRenderableData(GraphicType, GraphicData, imageId, TrackingIdentifier) {
|
||||
const [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
|
||||
|
||||
function _getRenderableData(GraphicType, GraphicData, imageId) {
|
||||
let renderableData: csTypes.Point3[];
|
||||
|
||||
switch (GraphicType) {
|
||||
@ -125,6 +125,10 @@ function OHIFCornerstoneSRViewport(props) {
|
||||
console.warn('More than one SOPClassUID in the same series is not yet supported.');
|
||||
}
|
||||
|
||||
// if (!srDisplaySet.measurements || !srDisplaySet.measurements.length) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
_getViewportReferencedDisplaySetData(
|
||||
srDisplaySet,
|
||||
newMeasurementSelected,
|
||||
@ -253,12 +257,16 @@ function OHIFCornerstoneSRViewport(props) {
|
||||
* if it is hydrated we don't even use the SR viewport.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!srDisplaySet.isLoaded) {
|
||||
srDisplaySet.load();
|
||||
}
|
||||
const numMeasurements = srDisplaySet.measurements.length;
|
||||
setMeasurementCount(numMeasurements);
|
||||
}, [srDisplaySet]);
|
||||
const loadSR = async () => {
|
||||
if (!srDisplaySet.isLoaded) {
|
||||
await srDisplaySet.load();
|
||||
}
|
||||
const numMeasurements = srDisplaySet.measurements.length;
|
||||
setMeasurementCount(numMeasurements);
|
||||
updateViewport(measurementSelected);
|
||||
};
|
||||
loadSR();
|
||||
}, [dataSource, srDisplaySet]);
|
||||
|
||||
/**
|
||||
* Hook to update the tracking identifiers when the selected measurement changes or
|
||||
@ -275,19 +283,11 @@ function OHIFCornerstoneSRViewport(props) {
|
||||
* Todo: what is this, not sure what it does regarding the react aspect,
|
||||
* it is updating a local variable? which is not state.
|
||||
*/
|
||||
let isLocked = trackedMeasurements?.context?.trackedSeries?.length > 0;
|
||||
const [isLocked, setIsLocked] = useState(trackedMeasurements?.context?.trackedSeries?.length > 0);
|
||||
useEffect(() => {
|
||||
isLocked = trackedMeasurements?.context?.trackedSeries?.length > 0;
|
||||
setIsLocked(trackedMeasurements?.context?.trackedSeries?.length > 0);
|
||||
}, [trackedMeasurements]);
|
||||
|
||||
/**
|
||||
* Data fetching for the SR displaySet, which updates the measurements and
|
||||
* also gets the referenced image displaySet that SR is based on.
|
||||
*/
|
||||
useEffect(() => {
|
||||
updateViewport(measurementSelected);
|
||||
}, [dataSource, srDisplaySet]);
|
||||
|
||||
useEffect(() => {
|
||||
viewportActionCornersService.setComponents([
|
||||
{
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.67.0",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.68.1",
|
||||
"@icr/polyseg-wasm": "^0.4.0",
|
||||
"@ohif/core": "3.8.0-beta.71",
|
||||
"@ohif/ui": "3.8.0-beta.71",
|
||||
@ -55,10 +55,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.67.0",
|
||||
"@cornerstonejs/core": "^1.67.0",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.67.0",
|
||||
"@cornerstonejs/tools": "^1.67.0",
|
||||
"@cornerstonejs/adapters": "^1.68.1",
|
||||
"@cornerstonejs/core": "^1.68.1",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.68.1",
|
||||
"@cornerstonejs/tools": "^1.68.1",
|
||||
"@kitware/vtk.js": "29.7.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lodash.debounce": "4.0.8",
|
||||
|
||||
@ -357,6 +357,10 @@ const connectMeasurementServiceToTools = (
|
||||
imageId = dataSource.getImageIdsForInstance({ instance });
|
||||
}
|
||||
|
||||
/**
|
||||
* This annotation is used by the cornerstone viewport.
|
||||
* This is not the read-only annotation rendered by the SR viewport.
|
||||
*/
|
||||
const annotationManager = annotation.state.getAnnotationManager();
|
||||
annotationManager.addAnnotation({
|
||||
annotationUID: measurement.uid,
|
||||
@ -369,11 +373,16 @@ const connectMeasurementServiceToTools = (
|
||||
referencedImageId: imageId,
|
||||
},
|
||||
data: {
|
||||
/**
|
||||
* Don't remove this destructuring of data here.
|
||||
* This is used to pass annotation specific data forward e.g. contour
|
||||
*/
|
||||
...(data.annotation.data || {}),
|
||||
text: data.annotation.data.text,
|
||||
handles: { ...data.annotation.data.handles },
|
||||
cachedStats: { ...data.annotation.data.cachedStats },
|
||||
label: data.annotation.data.label,
|
||||
frameNumber: frameNumber,
|
||||
frameNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,14 +1,22 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { getDisplayUnit } from './utils';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
/**
|
||||
* Represents a mapping utility for Planar Freehand ROI measurements.
|
||||
*/
|
||||
const PlanarFreehandROI = {
|
||||
toAnnotation: measurement => {},
|
||||
|
||||
/**
|
||||
* Maps cornerstone annotation event data to measurement service format.
|
||||
*
|
||||
* @param {Object} cornerstone Cornerstone event data
|
||||
* @return {Measurement} Measurement instance
|
||||
* @param {Object} csToolsEventDetail Cornerstone event data
|
||||
* @param {DisplaySetService} DisplaySetService Service for managing display sets
|
||||
* @param {CornerstoneViewportService} CornerstoneViewportService Service for managing viewports
|
||||
* @param {Function} getValueTypeFromToolType Function to get value type from tool type
|
||||
* @returns {Measurement} Measurement instance
|
||||
*/
|
||||
toMeasurement: (
|
||||
csToolsEventDetail,
|
||||
@ -16,7 +24,7 @@ const PlanarFreehandROI = {
|
||||
CornerstoneViewportService,
|
||||
getValueTypeFromToolType
|
||||
) => {
|
||||
const { annotation, viewportId } = csToolsEventDetail;
|
||||
const { annotation } = csToolsEventDetail;
|
||||
const { metadata, data, annotationUID } = annotation;
|
||||
|
||||
if (!metadata || !data) {
|
||||
@ -26,19 +34,14 @@ const PlanarFreehandROI = {
|
||||
|
||||
const { toolName, referencedImageId, FrameOfReferenceUID } = metadata;
|
||||
const validToolType = SUPPORTED_TOOLS.includes(toolName);
|
||||
|
||||
if (!validToolType) {
|
||||
throw new Error('Tool not supported');
|
||||
throw new Error(`Tool ${toolName} not supported`);
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
CornerstoneViewportService,
|
||||
viewportId
|
||||
);
|
||||
const { SOPInstanceUID, SeriesInstanceUID, frameNumber, StudyInstanceUID } =
|
||||
getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
let displaySet;
|
||||
|
||||
if (SOPInstanceUID) {
|
||||
displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
@ -48,92 +51,151 @@ const PlanarFreehandROI = {
|
||||
displaySet = DisplaySetService.getDisplaySetsForSeries(SeriesInstanceUID);
|
||||
}
|
||||
|
||||
const { points, textBox } = data.handles;
|
||||
|
||||
const mappedAnnotations = getMappedAnnotations(annotation, DisplaySetService);
|
||||
|
||||
const displayText = getDisplayText(mappedAnnotations);
|
||||
const getReport = () => _getReport(mappedAnnotations, points, FrameOfReferenceUID);
|
||||
|
||||
return {
|
||||
uid: annotationUID,
|
||||
SOPInstanceUID,
|
||||
FrameOfReferenceUID,
|
||||
points,
|
||||
textBox,
|
||||
points: data.contour.polyline,
|
||||
textBox: data.handles.textBox,
|
||||
metadata,
|
||||
frameNumber,
|
||||
referenceSeriesUID: SeriesInstanceUID,
|
||||
referenceStudyUID: StudyInstanceUID,
|
||||
toolName: metadata.toolName,
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
label: data.label,
|
||||
displayText: displayText,
|
||||
data: { ...data, ...data.cachedStats },
|
||||
displayText: getDisplayText(annotation, displaySet),
|
||||
data: data.cachedStats,
|
||||
type: getValueTypeFromToolType(toolName),
|
||||
getReport,
|
||||
getReport: () => getColumnValueReport(annotation),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* It maps an imaging library annotation to a list of simplified annotation properties.
|
||||
*
|
||||
* @param {Object} annotationData
|
||||
* @param {Object} DisplaySetService
|
||||
* @returns
|
||||
*/
|
||||
function getMappedAnnotations(annotationData, DisplaySetService) {
|
||||
const { metadata, data } = annotationData;
|
||||
const { label } = data;
|
||||
const { referencedImageId } = metadata;
|
||||
|
||||
const annotations = [];
|
||||
|
||||
const { SOPInstanceUID: _SOPInstanceUID, SeriesInstanceUID: _SeriesInstanceUID } =
|
||||
getSOPInstanceAttributes(referencedImageId) || {};
|
||||
|
||||
if (!_SOPInstanceUID || !_SeriesInstanceUID) {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
|
||||
_SOPInstanceUID,
|
||||
_SeriesInstanceUID
|
||||
);
|
||||
|
||||
const { SeriesNumber, SeriesInstanceUID } = displaySet;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
SeriesNumber,
|
||||
label,
|
||||
data,
|
||||
});
|
||||
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* TBD
|
||||
* This function is used to convert the measurement data to a format that is suitable for the report generation (e.g. for the csv report).
|
||||
* This function is used to convert the measurement data to a
|
||||
* format that is suitable for report generation (e.g. for the csv report).
|
||||
* The report returns a list of columns and corresponding values.
|
||||
* @param {*} mappedAnnotations
|
||||
* @param {*} points
|
||||
* @param {*} FrameOfReferenceUID
|
||||
* @returns Object representing the report's content for this tool.
|
||||
*
|
||||
* @param {object} annotation
|
||||
* @returns {object} Report's content from this tool
|
||||
*/
|
||||
function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
function getColumnValueReport(annotation) {
|
||||
const columns = [];
|
||||
const values = [];
|
||||
|
||||
return {
|
||||
columns,
|
||||
values,
|
||||
};
|
||||
/** Add type */
|
||||
columns.push('AnnotationType');
|
||||
values.push('Cornerstone:PlanarFreehandROI');
|
||||
|
||||
/** Add cachedStats */
|
||||
const { metadata, data } = annotation;
|
||||
const { mean, stdDev, max, area, unit, areaUnit, perimeter } =
|
||||
data.cachedStats[`imageId:${metadata.referencedImageId}`];
|
||||
columns.push(`Maximum`, `Mean`, `Std Dev`, 'Pixel Unit', `Area`, 'Unit', 'Perimeter');
|
||||
values.push(max, mean, stdDev, unit, area, areaUnit, perimeter);
|
||||
|
||||
/** Add FOR */
|
||||
if (metadata.FrameOfReferenceUID) {
|
||||
columns.push('FrameOfReferenceUID');
|
||||
values.push(metadata.FrameOfReferenceUID);
|
||||
}
|
||||
|
||||
/** Add points */
|
||||
if (data.contour.polyline) {
|
||||
/**
|
||||
* Points has the form of [[x1, y1, z1], [x2, y2, z2], ...]
|
||||
* convert it to string of [[x1 y1 z1];[x2 y2 z2];...]
|
||||
* so that it can be used in the CSV report
|
||||
*/
|
||||
columns.push('points');
|
||||
values.push(data.contour.polyline.map(p => p.join(' ')).join(';'));
|
||||
}
|
||||
|
||||
return { columns, values };
|
||||
}
|
||||
|
||||
function getDisplayText(mappedAnnotations) {
|
||||
return '';
|
||||
/**
|
||||
* Retrieves the display text for an annotation in a display set.
|
||||
*
|
||||
* @param {Object} annotation - The annotation object.
|
||||
* @param {Object} displaySet - The display set object.
|
||||
* @returns {string[]} - An array of display text.
|
||||
*/
|
||||
function getDisplayText(annotation, displaySet) {
|
||||
const { metadata, data } = annotation;
|
||||
|
||||
if (!data.cachedStats || !data.cachedStats[`imageId:${metadata.referencedImageId}`]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { mean, stdDev, max, area, modalityUnit, areaUnit, perimeter } =
|
||||
data.cachedStats[`imageId:${metadata.referencedImageId}`];
|
||||
|
||||
const { SOPInstanceUID, frameNumber } = getSOPInstanceAttributes(metadata.referencedImageId);
|
||||
|
||||
const displayText = [];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
let InstanceNumber;
|
||||
if (instance) {
|
||||
InstanceNumber = instance.InstanceNumber;
|
||||
}
|
||||
|
||||
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
|
||||
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
if (SeriesNumber) {
|
||||
displayText.push(`S: ${SeriesNumber}${instanceText}${frameText}`);
|
||||
}
|
||||
|
||||
if (area) {
|
||||
/**
|
||||
* Add Area
|
||||
* Area sometimes becomes undefined if `preventHandleOutsideImage` is off
|
||||
*/
|
||||
const roundedArea = utils.roundNumber(area || 0, 2);
|
||||
displayText.push(`Area: ${roundedArea} ${getDisplayUnit(areaUnit)}`);
|
||||
}
|
||||
|
||||
if (mean) {
|
||||
if (Array.isArray(mean)) {
|
||||
const meanValues = mean.map(value => utils.roundNumber(value));
|
||||
displayText.push(`Mean: ${meanValues.join(', ')} ${modalityUnit}`);
|
||||
} else {
|
||||
displayText.push(`Mean: ${utils.roundNumber(mean)} ${modalityUnit}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (max) {
|
||||
if (Array.isArray(max)) {
|
||||
const maxValues = max.map(value => utils.roundNumber(value, 2));
|
||||
displayText.push(`Max: ${maxValues.join(', ')} ${modalityUnit}`);
|
||||
} else {
|
||||
displayText.push(`Max: ${utils.roundNumber(max, 2)} ${modalityUnit}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (stdDev) {
|
||||
if (Array.isArray(stdDev)) {
|
||||
const stdDevValues = stdDev.map(value => utils.roundNumber(value));
|
||||
displayText.push(`Std Dev: ${stdDevValues.join(', ')} ${modalityUnit}`);
|
||||
} else {
|
||||
displayText.push(`Std Dev: ${utils.roundNumber(stdDev)} ${modalityUnit}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (perimeter) {
|
||||
if (Array.isArray(perimeter)) {
|
||||
const perimeterValues = perimeter.map(value => utils.roundNumber(value));
|
||||
displayText.push(`Perimeter: ${perimeterValues.join(', ')} ${modalityUnit}`);
|
||||
} else {
|
||||
displayText.push(`Perimeter: ${utils.roundNumber(perimeter)} ${modalityUnit}`);
|
||||
}
|
||||
}
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
export default PlanarFreehandROI;
|
||||
|
||||
@ -32,8 +32,8 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "^1.67.0",
|
||||
"@cornerstonejs/tools": "^1.67.0",
|
||||
"@cornerstonejs/core": "^1.68.1",
|
||||
"@cornerstonejs/tools": "^1.68.1",
|
||||
"@ohif/core": "3.8.0-beta.71",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.8.0-beta.71",
|
||||
"@ohif/ui": "3.8.0-beta.71",
|
||||
|
||||
@ -183,60 +183,69 @@ function TrackedMeasurementsContextProvider(
|
||||
|
||||
// ~~ Listen for changes to ViewportGrid for potential SRs hung in panes when idle
|
||||
useEffect(() => {
|
||||
if (viewports.size > 0) {
|
||||
const activeViewport = viewports.get(activeViewportId);
|
||||
const triggerPromptHydrateFlow = async () => {
|
||||
if (viewports.size > 0) {
|
||||
const activeViewport = viewports.get(activeViewportId);
|
||||
|
||||
if (!activeViewport || !activeViewport?.displaySetInstanceUIDs?.length) {
|
||||
return;
|
||||
if (!activeViewport || !activeViewport?.displaySetInstanceUIDs?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Todo: Getting the first displaySetInstanceUID is wrong, but we don't have
|
||||
// tracking fusion viewports yet. This should change when we do.
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const displaySet = displaySetService.getDisplaySetByUID(
|
||||
activeViewport.displaySetInstanceUIDs[0]
|
||||
);
|
||||
|
||||
if (!displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is an SR produced by our SR SOPClassHandler,
|
||||
// and it hasn't been loaded yet, do that now so we
|
||||
// can check if it can be rehydrated or not.
|
||||
//
|
||||
// Note: This happens:
|
||||
// - If the viewport is not currently an OHIFCornerstoneSRViewport
|
||||
// - If the displaySet has never been hung
|
||||
//
|
||||
// Otherwise, the displaySet will be loaded by the useEffect handler
|
||||
// listening to displaySet changes inside OHIFCornerstoneSRViewport.
|
||||
// The issue here is that this handler in TrackedMeasurementsContext
|
||||
// ends up occurring before the Viewport is created, so the displaySet
|
||||
// is not loaded yet, and isRehydratable is undefined unless we call load().
|
||||
if (
|
||||
displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID &&
|
||||
!displaySet.isLoaded &&
|
||||
displaySet.load
|
||||
) {
|
||||
await displaySet.load();
|
||||
}
|
||||
|
||||
// Magic string
|
||||
// load function added by our sopClassHandler module
|
||||
if (
|
||||
displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID &&
|
||||
displaySet.isRehydratable === true
|
||||
) {
|
||||
console.log('sending event...', trackedMeasurements);
|
||||
sendTrackedMeasurementsEvent('PROMPT_HYDRATE_SR', {
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
SeriesInstanceUID: displaySet.SeriesInstanceUID,
|
||||
viewportId: activeViewportId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Todo: Getting the first displaySetInstanceUID is wrong, but we don't have
|
||||
// tracking fusion viewports yet. This should change when we do.
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const displaySet = displaySetService.getDisplaySetByUID(
|
||||
activeViewport.displaySetInstanceUIDs[0]
|
||||
);
|
||||
|
||||
if (!displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is an SR produced by our SR SOPClassHandler,
|
||||
// and it hasn't been loaded yet, do that now so we
|
||||
// can check if it can be rehydrated or not.
|
||||
//
|
||||
// Note: This happens:
|
||||
// - If the viewport is not currently an OHIFCornerstoneSRViewport
|
||||
// - If the displaySet has never been hung
|
||||
//
|
||||
// Otherwise, the displaySet will be loaded by the useEffect handler
|
||||
// listening to displaySet changes inside OHIFCornerstoneSRViewport.
|
||||
// The issue here is that this handler in TrackedMeasurementsContext
|
||||
// ends up occurring before the Viewport is created, so the displaySet
|
||||
// is not loaded yet, and isRehydratable is undefined unless we call load().
|
||||
if (
|
||||
displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID &&
|
||||
!displaySet.isLoaded &&
|
||||
displaySet.load
|
||||
) {
|
||||
displaySet.load();
|
||||
}
|
||||
|
||||
// Magic string
|
||||
// load function added by our sopClassHandler module
|
||||
if (
|
||||
displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID &&
|
||||
displaySet.isRehydratable === true
|
||||
) {
|
||||
console.log('sending event...', trackedMeasurements);
|
||||
sendTrackedMeasurementsEvent('PROMPT_HYDRATE_SR', {
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
SeriesInstanceUID: displaySet.SeriesInstanceUID,
|
||||
viewportId: activeViewportId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [activeViewportId, sendTrackedMeasurementsEvent, servicesManager.services, viewports]);
|
||||
};
|
||||
triggerPromptHydrateFlow();
|
||||
}, [
|
||||
trackedMeasurements,
|
||||
activeViewportId,
|
||||
sendTrackedMeasurementsEvent,
|
||||
servicesManager.services,
|
||||
viewports,
|
||||
]);
|
||||
|
||||
return (
|
||||
<TrackedMeasurementsContext.Provider
|
||||
|
||||
@ -19,7 +19,7 @@ function PanelStudyBrowserTracking({
|
||||
requestDisplaySetCreationForStudy,
|
||||
dataSource,
|
||||
}) {
|
||||
const { displaySetService, uiDialogService, hangingProtocolService, uiNotificationService } =
|
||||
const { displaySetService, uiDialogService, hangingProtocolService, uiNotificationService, measurementService } =
|
||||
servicesManager.services;
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -340,6 +340,12 @@ function PanelStudyBrowserTracking({
|
||||
sendTrackedMeasurementsEvent('UNTRACK_SERIES', {
|
||||
SeriesInstanceUID: displaySet.SeriesInstanceUID,
|
||||
});
|
||||
const measurements = measurementService.getMeasurements();
|
||||
measurements.forEach(m => {
|
||||
if (m.referenceSeriesUID === displaySet.SeriesInstanceUID) {
|
||||
measurementService.remove(m.uid);
|
||||
}
|
||||
});
|
||||
}}
|
||||
onClickThumbnail={() => {}}
|
||||
onDoubleClickThumbnail={onDoubleClickThumbnailHandler}
|
||||
|
||||
@ -129,6 +129,8 @@ function initSRToolGroup(extensionManager, toolGroupService) {
|
||||
{ toolName: SRToolNames.SRBidirectional },
|
||||
{ toolName: SRToolNames.SREllipticalROI },
|
||||
{ toolName: SRToolNames.SRCircleROI },
|
||||
{ toolName: SRToolNames.SRPlanarFreehandROI },
|
||||
{ toolName: SRToolNames.SRRectangleROI },
|
||||
],
|
||||
enabled: [
|
||||
{
|
||||
|
||||
@ -155,14 +155,6 @@ const moreTools = [
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
}),
|
||||
createButton({
|
||||
id: 'RectangleROI',
|
||||
icon: 'tool-rectangle',
|
||||
label: 'Rectangle',
|
||||
tooltip: 'Rectangle',
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
}),
|
||||
createButton({
|
||||
id: 'CalibrationLine',
|
||||
icon: 'tool-calibration',
|
||||
|
||||
@ -67,6 +67,22 @@ const toolbarButtons: Button[] = [
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
}),
|
||||
createButton({
|
||||
id: 'PlanarFreehandROI',
|
||||
icon: 'tool-freehand-polygon',
|
||||
label: 'Freehand',
|
||||
tooltip: 'Freehand ROI',
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
}),
|
||||
createButton({
|
||||
id: 'RectangleROI',
|
||||
icon: 'tool-rectangle',
|
||||
label: 'Rectangle',
|
||||
tooltip: 'Rectangle ROI',
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
}),
|
||||
createButton({
|
||||
id: 'CircleROI',
|
||||
icon: 'tool-circle',
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.67.0",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.68.1",
|
||||
"@emotion/serialize": "^1.1.3",
|
||||
"@ohif/core": "3.8.0-beta.71",
|
||||
"@ohif/extension-cornerstone": "3.8.0-beta.71",
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.67.0",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.68.1",
|
||||
"@ohif/ui": "3.8.0-beta.71",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "^1.8.21"
|
||||
|
||||
@ -53,6 +53,7 @@ const MEASUREMENT_SCHEMA_KEYS = [
|
||||
'area', // TODO: Add concept names instead (descriptor)
|
||||
'mean',
|
||||
'stdDev',
|
||||
'perimeter',
|
||||
'length',
|
||||
'shortestDiameter',
|
||||
'longestDiameter',
|
||||
|
||||
48
yarn.lock
48
yarn.lock
@ -1582,13 +1582,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
||||
|
||||
"@cornerstonejs/adapters@^1.67.0":
|
||||
version "1.67.0"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.67.0.tgz#84c881a2b1c7bc3cec2ae55df869d82740cc6049"
|
||||
integrity sha512-EnIpdMD7kx42eJYGHsqjahWTZj/ZTkP+5XCWXpLp0BY0p+O53oSV9aYZXvyjd/kWQPr+30L+010zPuE5oQzIxA==
|
||||
"@cornerstonejs/adapters@^1.68.1":
|
||||
version "1.68.1"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.68.1.tgz#9f4fa768f85759bb49e8f252c9a4e5b4acd09b1f"
|
||||
integrity sha512-p4pKMX70/COLf1dSIDUonEBHhSPYklH2OkS9yOmdjOVYzvWxyaQWuczd1V+2cWYanD5WGqRzarl4KpJAK9W+zQ==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs2" "^7.17.8"
|
||||
"@cornerstonejs/tools" "^1.67.0"
|
||||
"@cornerstonejs/tools" "^1.68.1"
|
||||
buffer "^6.0.3"
|
||||
dcmjs "^0.29.8"
|
||||
gl-matrix "^3.4.3"
|
||||
@ -1635,10 +1635,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.67.0":
|
||||
version "1.67.0"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.67.0.tgz#711a915b70b5273d8838b59c8c06c553079b0ee7"
|
||||
integrity sha512-t8VI5acGGu0wN51UzHyEXOjlBjoAJjIVcbBOQejC0yOWfGoy+I9y/h5de41pZyhfbXYNNXA1dHZDDtLi9sauBg==
|
||||
"@cornerstonejs/core@^1.68.1":
|
||||
version "1.68.1"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.68.1.tgz#f5d366924b791b117d77e2fe83cee82dced0f3fa"
|
||||
integrity sha512-F+ptRQ4kYPLS1buDvpQhy3ckdJa4K4B7uz3Ujq3BaEw1AZPB01S6OFw6HtbYs8i+kIi8ZF1fdWvDheO0bpefBQ==
|
||||
dependencies:
|
||||
"@kitware/vtk.js" "29.7.0"
|
||||
comlink "^4.4.1"
|
||||
@ -1646,34 +1646,34 @@
|
||||
gl-matrix "^3.4.3"
|
||||
lodash.clonedeep "4.5.0"
|
||||
|
||||
"@cornerstonejs/dicom-image-loader@^1.67.0":
|
||||
version "1.67.0"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.67.0.tgz#9e70858100821682105f88a927aa4dff96e75cda"
|
||||
integrity sha512-ZhziYYKcci1X11iWA4qOzEVxtVUbDUKqx9ustsAD9vv9P7WfDOI4EHflUhW+X1hIAG5vl7xv6LAG0aRkVnwrrw==
|
||||
"@cornerstonejs/dicom-image-loader@^1.68.1":
|
||||
version "1.68.1"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.68.1.tgz#fd2cda6de4060ade8092a0fc887f32d4ce979684"
|
||||
integrity sha512-xjmp2Izy4KMPUTZVutdnx0TXhFrLmlJP3ZZ54ypjJyKGdv4EIGyDvGmCHkD7G37dYROTbZJVBNtjukCrfvJ1Zg==
|
||||
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.67.0"
|
||||
"@cornerstonejs/core" "^1.68.1"
|
||||
dicom-parser "^1.8.9"
|
||||
pako "^2.0.4"
|
||||
uuid "^9.0.0"
|
||||
|
||||
"@cornerstonejs/streaming-image-volume-loader@^1.67.0":
|
||||
version "1.67.0"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.67.0.tgz#5e22c828e5a5c9932e728314edbfa95abf81982e"
|
||||
integrity sha512-W5+CKSxkLI4BbcdXYVb/M+yeN6Z+G8AWOQISR8qX9NGnK3GR/Id7+9OH+HP6FmIZ4YBXew9/DOqRgEtTtyDZGQ==
|
||||
"@cornerstonejs/streaming-image-volume-loader@^1.68.1":
|
||||
version "1.68.1"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.68.1.tgz#93ab87a0b77717200c7ea35ddb832651f02882de"
|
||||
integrity sha512-4n0oNuf+SNxVfZ6gr5CwSgKpu5f3S3+uFxzRcvUH5DMXwVGxwU1XF8CTFfvEnw9xBn3x6Dg4ckR1a682CyuwXA==
|
||||
dependencies:
|
||||
"@cornerstonejs/core" "^1.67.0"
|
||||
"@cornerstonejs/core" "^1.68.1"
|
||||
comlink "^4.4.1"
|
||||
|
||||
"@cornerstonejs/tools@^1.67.0":
|
||||
version "1.67.0"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.67.0.tgz#47c6459859cc5122f780a7ec67c2b9d41b067b38"
|
||||
integrity sha512-CPFWRF4IePxHV8KoXE6PBY0Q3jw8riJ7ZaJajAzXLpgvTSvIYu0m7tNuFG8iKVppq6KTgSywO4r9VbfcvZXOyw==
|
||||
"@cornerstonejs/tools@^1.68.1":
|
||||
version "1.68.1"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.68.1.tgz#17e177be628bd633391566d8110b2799be7d5068"
|
||||
integrity sha512-fbXIqcgJKa2RUmwLvxbbISSvwkfwZBoKCjtI/j0AMowpV/gpm86Br6/MomNmPQvdfuF5HfQRdBHjZS71Vid1pQ==
|
||||
dependencies:
|
||||
"@cornerstonejs/core" "^1.67.0"
|
||||
"@cornerstonejs/core" "^1.68.1"
|
||||
"@icr/polyseg-wasm" "0.4.0"
|
||||
"@types/offscreencanvas" "2019.7.3"
|
||||
comlink "^4.4.1"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user