perf(segmentation): make segmentation read 3x faster (#3577)
This commit is contained in:
parent
ec76ea24dd
commit
156c1ba60b
@ -1,11 +1,14 @@
|
|||||||
import vtkMath from '@kitware/vtk.js/Common/Core/Math';
|
|
||||||
|
|
||||||
import { utils } from '@ohif/core';
|
import { utils } from '@ohif/core';
|
||||||
|
import {
|
||||||
|
metaData,
|
||||||
|
cache,
|
||||||
|
triggerEvent,
|
||||||
|
eventTarget,
|
||||||
|
} from '@cornerstonejs/core';
|
||||||
|
import { adaptersSEG, Enums } from '@cornerstonejs/adapters';
|
||||||
|
|
||||||
import { SOPClassHandlerId } from './id';
|
import { SOPClassHandlerId } from './id';
|
||||||
import dcmjs from 'dcmjs';
|
import { dicomlabToRGB } from './utils/dicomlabToRGB';
|
||||||
|
|
||||||
const { DicomMessage, DicomMetaDictionary } = dcmjs.data;
|
|
||||||
|
|
||||||
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.66.4'];
|
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.66.4'];
|
||||||
|
|
||||||
@ -122,13 +125,12 @@ function _load(segDisplaySet, servicesManager, extensionManager, headers) {
|
|||||||
!segDisplaySet.segments ||
|
!segDisplaySet.segments ||
|
||||||
Object.keys(segDisplaySet.segments).length === 0
|
Object.keys(segDisplaySet.segments).length === 0
|
||||||
) {
|
) {
|
||||||
const segments = await _loadSegments(
|
await _loadSegments({
|
||||||
extensionManager,
|
extensionManager,
|
||||||
|
servicesManager,
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
headers
|
headers,
|
||||||
);
|
});
|
||||||
|
|
||||||
segDisplaySet.segments = segments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const suppressEvents = true;
|
const suppressEvents = true;
|
||||||
@ -147,28 +149,65 @@ function _load(segDisplaySet, servicesManager, extensionManager, headers) {
|
|||||||
return loadPromises[SOPInstanceUID];
|
return loadPromises[SOPInstanceUID];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _loadSegments(extensionManager, segDisplaySet, headers) {
|
async function _loadSegments({
|
||||||
|
extensionManager,
|
||||||
|
servicesManager,
|
||||||
|
segDisplaySet,
|
||||||
|
headers,
|
||||||
|
}) {
|
||||||
const utilityModule = extensionManager.getModuleEntry(
|
const utilityModule = extensionManager.getModuleEntry(
|
||||||
'@ohif/extension-cornerstone.utilityModule.common'
|
'@ohif/extension-cornerstone.utilityModule.common'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { segmentationService } = servicesManager.services;
|
||||||
|
|
||||||
const { dicomLoaderService } = utilityModule.exports;
|
const { dicomLoaderService } = utilityModule.exports;
|
||||||
const segArrayBuffer = await dicomLoaderService.findDicomDataPromise(
|
const arrayBuffer = await dicomLoaderService.findDicomDataPromise(
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
null,
|
null,
|
||||||
headers
|
headers
|
||||||
);
|
);
|
||||||
|
|
||||||
const dicomData = DicomMessage.readFile(segArrayBuffer);
|
const cachedReferencedVolume = cache.getVolume(
|
||||||
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
segDisplaySet.referencedVolumeId
|
||||||
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
);
|
||||||
|
|
||||||
if (!Array.isArray(dataset.SegmentSequence)) {
|
if (!cachedReferencedVolume) {
|
||||||
dataset.SegmentSequence = [dataset.SegmentSequence];
|
throw new Error(
|
||||||
|
'Referenced Volume is missing for the SEG, and stack viewport SEG is not supported yet'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const segments = _getSegments(dataset);
|
const { imageIds } = cachedReferencedVolume;
|
||||||
return segments;
|
|
||||||
|
// Todo: what should be defaults here
|
||||||
|
const tolerance = 0.001;
|
||||||
|
const skipOverlapping = true;
|
||||||
|
|
||||||
|
eventTarget.addEventListener(Enums.Events.SEGMENTATION_LOAD_PROGRESS, evt => {
|
||||||
|
const { percentComplete } = evt.detail;
|
||||||
|
segmentationService._broadcastEvent(
|
||||||
|
segmentationService.EVENTS.SEGMENT_LOADING_COMPLETE,
|
||||||
|
{
|
||||||
|
percentComplete,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await adaptersSEG.Cornerstone3D.Segmentation.generateToolState(
|
||||||
|
imageIds,
|
||||||
|
arrayBuffer,
|
||||||
|
metaData,
|
||||||
|
{ skipOverlapping, tolerance, eventTarget, triggerEvent }
|
||||||
|
);
|
||||||
|
|
||||||
|
results.segMetadata.data.forEach((data, i) => {
|
||||||
|
if (i > 0) {
|
||||||
|
data.rgba = dicomlabToRGB(data.RecommendedDisplayCIELabValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(segDisplaySet, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _segmentationExists(segDisplaySet, segmentationService) {
|
function _segmentationExists(segDisplaySet, segmentationService) {
|
||||||
@ -178,114 +217,6 @@ function _segmentationExists(segDisplaySet, segmentationService) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getPixelData(dataset, segments) {
|
|
||||||
let frameSize = Math.ceil((dataset.Rows * dataset.Columns) / 8);
|
|
||||||
let nextOffset = 0;
|
|
||||||
|
|
||||||
Object.keys(segments).forEach(segmentKey => {
|
|
||||||
const segment = segments[segmentKey];
|
|
||||||
segment.numberOfFrames = segment.functionalGroups.length;
|
|
||||||
segment.size = segment.numberOfFrames * frameSize;
|
|
||||||
segment.offset = nextOffset;
|
|
||||||
nextOffset = segment.offset + segment.size;
|
|
||||||
const packedSegment = dataset.PixelData[0].slice(
|
|
||||||
segment.offset,
|
|
||||||
nextOffset
|
|
||||||
);
|
|
||||||
|
|
||||||
segment.pixelData = dcmjs.data.BitArray.unpack(packedSegment);
|
|
||||||
segment.geometry = geometryFromFunctionalGroups(
|
|
||||||
dataset,
|
|
||||||
segment.functionalGroups
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return segments;
|
|
||||||
}
|
|
||||||
|
|
||||||
function geometryFromFunctionalGroups(dataset, perFrame) {
|
|
||||||
let pixelMeasures =
|
|
||||||
dataset.SharedFunctionalGroupsSequence.PixelMeasuresSequence;
|
|
||||||
let planeOrientation =
|
|
||||||
dataset.SharedFunctionalGroupsSequence.PlaneOrientationSequence;
|
|
||||||
let planePosition = perFrame[0].PlanePositionSequence; // TODO: assume sorted frames!
|
|
||||||
|
|
||||||
const geometry = {};
|
|
||||||
|
|
||||||
// NB: DICOM PixelSpacing is defined as Row then Column,
|
|
||||||
// unlike ImageOrientationPatient
|
|
||||||
let spacingBetweenSlices = pixelMeasures.SpacingBetweenSlices;
|
|
||||||
if (!spacingBetweenSlices) {
|
|
||||||
if (pixelMeasures.SliceThickness) {
|
|
||||||
console.log('Using SliceThickness as SpacingBetweenSlices');
|
|
||||||
spacingBetweenSlices = pixelMeasures.SliceThickness;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
geometry.spacing = [
|
|
||||||
pixelMeasures.PixelSpacing[1],
|
|
||||||
pixelMeasures.PixelSpacing[0],
|
|
||||||
spacingBetweenSlices,
|
|
||||||
].map(Number);
|
|
||||||
|
|
||||||
geometry.dimensions = [dataset.Columns, dataset.Rows, perFrame.length].map(
|
|
||||||
Number
|
|
||||||
);
|
|
||||||
|
|
||||||
let orientation = planeOrientation.ImageOrientationPatient.map(Number);
|
|
||||||
const columnStepToPatient = orientation.slice(0, 3);
|
|
||||||
const rowStepToPatient = orientation.slice(3, 6);
|
|
||||||
geometry.planeNormal = [];
|
|
||||||
vtkMath.cross(columnStepToPatient, rowStepToPatient, geometry.planeNormal);
|
|
||||||
|
|
||||||
let firstPosition = perFrame[0].PlanePositionSequence.ImagePositionPatient.map(
|
|
||||||
Number
|
|
||||||
);
|
|
||||||
let lastPosition = perFrame[
|
|
||||||
perFrame.length - 1
|
|
||||||
].PlanePositionSequence.ImagePositionPatient.map(Number);
|
|
||||||
geometry.sliceStep = [];
|
|
||||||
vtkMath.subtract(lastPosition, firstPosition, geometry.sliceStep);
|
|
||||||
vtkMath.normalize(geometry.sliceStep);
|
|
||||||
geometry.direction = columnStepToPatient
|
|
||||||
.concat(rowStepToPatient)
|
|
||||||
.concat(geometry.sliceStep);
|
|
||||||
geometry.origin = planePosition.ImagePositionPatient.map(Number);
|
|
||||||
|
|
||||||
return geometry;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getSegments(dataset) {
|
|
||||||
const segments = {};
|
|
||||||
|
|
||||||
dataset.SegmentSequence.forEach(segment => {
|
|
||||||
const cielab = segment.RecommendedDisplayCIELabValue;
|
|
||||||
const rgba = dcmjs.data.Colors.dicomlab2RGB(cielab).map(x =>
|
|
||||||
Math.round(x * 255)
|
|
||||||
);
|
|
||||||
|
|
||||||
rgba.push(255);
|
|
||||||
const segmentNumber = segment.SegmentNumber;
|
|
||||||
|
|
||||||
segments[segmentNumber] = {
|
|
||||||
color: rgba,
|
|
||||||
functionalGroups: [],
|
|
||||||
offset: null,
|
|
||||||
size: null,
|
|
||||||
pixelData: null,
|
|
||||||
label: segment.SegmentLabel,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// make a list of functional groups per segment
|
|
||||||
dataset.PerFrameFunctionalGroupsSequence.forEach(functionalGroup => {
|
|
||||||
const segmentNumber =
|
|
||||||
functionalGroup.SegmentIdentificationSequence.ReferencedSegmentNumber;
|
|
||||||
segments[segmentNumber].functionalGroups.push(functionalGroup);
|
|
||||||
});
|
|
||||||
|
|
||||||
return _getPixelData(dataset, segments);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||||
const getDisplaySetsFromSeries = instances => {
|
const getDisplaySetsFromSeries = instances => {
|
||||||
return _getDisplaySetsFromSeries(
|
return _getDisplaySetsFromSeries(
|
||||||
|
|||||||
16
extensions/cornerstone-dicom-seg/src/utils/dicomlabToRGB.ts
Normal file
16
extensions/cornerstone-dicom-seg/src/utils/dicomlabToRGB.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import dcmjs from 'dcmjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a CIELAB color to an RGB color using the dcmjs library.
|
||||||
|
* @param cielab - The CIELAB color to convert.
|
||||||
|
* @returns The RGB color as an array of three integers between 0 and 255.
|
||||||
|
*/
|
||||||
|
function dicomlabToRGB(cielab: number[]): number[] {
|
||||||
|
const rgb = dcmjs.data.Colors.dicomlab2RGB(cielab).map(x =>
|
||||||
|
Math.round(x * 255)
|
||||||
|
);
|
||||||
|
|
||||||
|
return rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { dicomlabToRGB };
|
||||||
@ -317,7 +317,6 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
|
|
||||||
setIsHydrated(isHydrated);
|
setIsHydrated(isHydrated);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewportActionBar
|
<ViewportActionBar
|
||||||
@ -344,10 +343,13 @@ function OHIFCornerstoneSEGViewport(props) {
|
|||||||
patientSex: PatientSex || '',
|
patientSex: PatientSex || '',
|
||||||
patientAge: PatientAge || '',
|
patientAge: PatientAge || '',
|
||||||
MRN: PatientID || '',
|
MRN: PatientID || '',
|
||||||
thickness: SliceThickness ? `${parseFloat(SliceThickness).toFixed(2)}mm` : '',
|
thickness: SliceThickness
|
||||||
|
? utils.roundNumber(SliceThickness, 2)
|
||||||
|
: '',
|
||||||
|
thicknessUnits: SliceThickness !== undefined ? 'mm' : '',
|
||||||
spacing:
|
spacing:
|
||||||
SpacingBetweenSlices !== undefined
|
SpacingBetweenSlices !== undefined
|
||||||
? `${parseFloat(SpacingBetweenSlices).toFixed(2)}mm`
|
? utils.roundNumber(SpacingBetweenSlices, 2)
|
||||||
: '',
|
: '',
|
||||||
scanner: ManufacturerModelName || '',
|
scanner: ManufacturerModelName || '',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -44,9 +44,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^1.7.1",
|
"@cornerstonejs/adapters": "^1.9.3",
|
||||||
"@cornerstonejs/core": "^1.7.1",
|
"@cornerstonejs/core": "^1.9.3",
|
||||||
"@cornerstonejs/tools": "^1.7.1",
|
"@cornerstonejs/tools": "^1.9.3",
|
||||||
"classnames": "^2.3.2"
|
"classnames": "^2.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||||
"@cornerstonejs/dicom-image-loader": "^1.7.1",
|
"@cornerstonejs/dicom-image-loader": "^1.9.3",
|
||||||
"@ohif/core": "3.7.0-beta.42",
|
"@ohif/core": "3.7.0-beta.42",
|
||||||
"@ohif/ui": "3.7.0-beta.42",
|
"@ohif/ui": "3.7.0-beta.42",
|
||||||
"dcmjs": "^0.29.6",
|
"dcmjs": "^0.29.6",
|
||||||
@ -52,10 +52,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@cornerstonejs/adapters": "^1.7.1",
|
"@cornerstonejs/adapters": "^1.9.3",
|
||||||
"@cornerstonejs/core": "^1.7.1",
|
"@cornerstonejs/core": "^1.9.3",
|
||||||
"@cornerstonejs/streaming-image-volume-loader": "^1.7.1",
|
"@cornerstonejs/streaming-image-volume-loader": "^1.9.3",
|
||||||
"@cornerstonejs/tools": "^1.7.1",
|
"@cornerstonejs/tools": "^1.9.3",
|
||||||
"@kitware/vtk.js": "27.3.1",
|
"@kitware/vtk.js": "27.3.1",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
|
|||||||
@ -563,11 +563,11 @@ class SegmentationService extends PubSubService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { segments, referencedVolumeId } = segDisplaySet;
|
const { labelmapBufferArray, referencedVolumeId } = segDisplaySet;
|
||||||
|
|
||||||
if (!segments || !referencedVolumeId) {
|
if (!labelmapBufferArray || !referencedVolumeId) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'To create the segmentation from SEG displaySet, the displaySet should be loaded first, you can perform segDisplaySet.load() before calling this method.'
|
'No labelmapBufferArray or referencedVolumeId found for the SEG displaySet'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,88 +593,27 @@ class SegmentationService extends PubSubService {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const [rows, columns] = derivedVolume.dimensions;
|
|
||||||
const derivedVolumeScalarData = derivedVolume.getScalarData();
|
const derivedVolumeScalarData = derivedVolume.getScalarData();
|
||||||
|
|
||||||
const { imageIds } = referencedVolume;
|
const segmentsInfo = segDisplaySet.segMetadata.data;
|
||||||
const sopUIDImageIdIndexMap = imageIds.reduce((acc, imageId, index) => {
|
derivedVolumeScalarData.set(new Uint8Array(labelmapBufferArray[0]));
|
||||||
const { sopInstanceUid } = metaData.get('generalImageModule', imageId);
|
|
||||||
acc[sopInstanceUid] = index;
|
|
||||||
return acc;
|
|
||||||
}, {} as { [sopUID: string]: number });
|
|
||||||
|
|
||||||
const numSegments = Object.keys(segments).length;
|
segmentation.segments = segmentsInfo.map((segmentInfo, segmentIndex) => {
|
||||||
// Note: ideally we could use the TypedArray set method, but since each
|
if (segmentIndex === 0) {
|
||||||
// slice can have multiple segments, we need to loop over each slice and
|
return;
|
||||||
// set the segment value for each segment.
|
|
||||||
let overlappingSegments = false;
|
|
||||||
|
|
||||||
const _segmentInfoUpdate = (segmentInfo, segmentIndex) => {
|
|
||||||
const { pixelData: segPixelData } = segmentInfo;
|
|
||||||
|
|
||||||
let segmentX = 0;
|
|
||||||
let segmentY = 0;
|
|
||||||
let segmentZ = 0;
|
|
||||||
let count = 0;
|
|
||||||
|
|
||||||
for (const [
|
|
||||||
functionalGroupIndex,
|
|
||||||
functionalGroup,
|
|
||||||
] of segmentInfo.functionalGroups.entries()) {
|
|
||||||
const {
|
|
||||||
ReferencedSOPInstanceUID,
|
|
||||||
} = functionalGroup.DerivationImageSequence.SourceImageSequence;
|
|
||||||
|
|
||||||
const imageIdIndex = sopUIDImageIdIndexMap[ReferencedSOPInstanceUID];
|
|
||||||
|
|
||||||
if (imageIdIndex === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const step = rows * columns;
|
|
||||||
|
|
||||||
// we need a faster way to get the pixel data for the current
|
|
||||||
// functional group, which we use typed array view
|
|
||||||
|
|
||||||
const functionGroupPixelData = new Uint8Array(
|
|
||||||
segPixelData.buffer,
|
|
||||||
functionalGroupIndex * step,
|
|
||||||
step
|
|
||||||
);
|
|
||||||
|
|
||||||
const functionalGroupStartIndex = imageIdIndex * step;
|
|
||||||
const functionalGroupEndIndex = (imageIdIndex + 1) * step;
|
|
||||||
|
|
||||||
// Note: this for loop is not optimized, since DICOM SEG stores
|
|
||||||
// each segment as a separate labelmap so if there is a slice
|
|
||||||
// that has multiple segments, we will have to loop over each
|
|
||||||
// segment and we cannot use the TypedArray set method.
|
|
||||||
for (
|
|
||||||
let i = functionalGroupStartIndex, j = 0;
|
|
||||||
i < functionalGroupEndIndex;
|
|
||||||
i++, j++
|
|
||||||
) {
|
|
||||||
if (functionGroupPixelData[j] !== 0) {
|
|
||||||
if (derivedVolumeScalarData[i] !== 0) {
|
|
||||||
overlappingSegments = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
derivedVolumeScalarData[i] = segmentIndex;
|
|
||||||
|
|
||||||
// centroid calculations
|
|
||||||
segmentX += i % columns;
|
|
||||||
segmentY += Math.floor(i / columns) % rows;
|
|
||||||
segmentZ += Math.floor(i / (columns * rows));
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// centroid calculations
|
const {
|
||||||
const x = Math.floor(segmentX / count);
|
SegmentedPropertyCategoryCodeSequence,
|
||||||
const y = Math.floor(segmentY / count);
|
SegmentNumber,
|
||||||
const z = Math.floor(segmentZ / count);
|
SegmentLabel,
|
||||||
|
SegmentAlgorithmType,
|
||||||
|
SegmentAlgorithmName,
|
||||||
|
SegmentedPropertyTypeCodeSequence,
|
||||||
|
rgba,
|
||||||
|
} = segmentInfo;
|
||||||
|
|
||||||
|
const { x, y, z } = segDisplaySet.centroids.get(segmentIndex);
|
||||||
const centerWorld = derivedVolume.imageData.indexToWorld([x, y, z]);
|
const centerWorld = derivedVolume.imageData.indexToWorld([x, y, z]);
|
||||||
|
|
||||||
segmentation.cachedStats = {
|
segmentation.cachedStats = {
|
||||||
@ -691,65 +630,31 @@ class SegmentationService extends PubSubService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const numInitialized = Object.keys(segmentation.cachedStats.segmentCenter)
|
return {
|
||||||
.length;
|
label: SegmentLabel || `Segment ${SegmentNumber}`,
|
||||||
|
segmentIndex: Number(SegmentNumber),
|
||||||
// Calculate percentage completed
|
category: SegmentedPropertyCategoryCodeSequence
|
||||||
const percentComplete = Math.round((numInitialized / numSegments) * 100);
|
? SegmentedPropertyCategoryCodeSequence.CodeMeaning
|
||||||
|
: '',
|
||||||
this._broadcastEvent(EVENTS.SEGMENT_LOADING_COMPLETE, {
|
type: SegmentedPropertyTypeCodeSequence
|
||||||
percentComplete,
|
? SegmentedPropertyTypeCodeSequence.CodeMeaning
|
||||||
numSegments: numSegments,
|
: '',
|
||||||
});
|
algorithmType: SegmentAlgorithmType,
|
||||||
};
|
algorithmName: SegmentAlgorithmName,
|
||||||
|
color: rgba,
|
||||||
const promiseArray = [];
|
opacity: 255,
|
||||||
|
|
||||||
for (const segmentIndex in segments) {
|
|
||||||
const segmentInfo = segments[segmentIndex];
|
|
||||||
|
|
||||||
// Important: we need a non-blocking way to update the segmentation
|
|
||||||
// state, otherwise the UI will freeze and the user will not be able
|
|
||||||
// to interact with the app or progress bars will not be updated.
|
|
||||||
const promise = new Promise<void>((resolve, reject) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
_segmentInfoUpdate(segmentInfo, segmentIndex);
|
|
||||||
resolve();
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
promiseArray.push(promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(promiseArray);
|
|
||||||
|
|
||||||
segmentation.segmentCount = Object.keys(segments).length;
|
|
||||||
segmentation.segments = [null]; // segment 0
|
|
||||||
|
|
||||||
Object.keys(segments).forEach(segmentIndex => {
|
|
||||||
const segmentInfo = segments[segmentIndex];
|
|
||||||
const segIndex = Number(segmentIndex);
|
|
||||||
|
|
||||||
segmentation.segments[segIndex] = {
|
|
||||||
label: segmentInfo.label || `Segment ${segIndex}`,
|
|
||||||
segmentIndex: Number(segmentIndex),
|
|
||||||
color: [
|
|
||||||
segmentInfo.color[0],
|
|
||||||
segmentInfo.color[1],
|
|
||||||
segmentInfo.color[2],
|
|
||||||
],
|
|
||||||
opacity: segmentInfo.color[3],
|
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
isLocked: false,
|
isLocked: false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
segmentation.segmentCount = segmentsInfo.length - 1;
|
||||||
|
|
||||||
segDisplaySet.isLoaded = true;
|
segDisplaySet.isLoaded = true;
|
||||||
|
|
||||||
this._broadcastEvent(EVENTS.SEGMENTATION_LOADING_COMPLETE, {
|
this._broadcastEvent(EVENTS.SEGMENTATION_LOADING_COMPLETE, {
|
||||||
segmentationId,
|
segmentationId,
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
overlappingSegments,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.addOrUpdateSegmentation(segmentation, suppressEvents);
|
return this.addOrUpdateSegmentation(segmentation, suppressEvents);
|
||||||
|
|||||||
@ -30,8 +30,8 @@
|
|||||||
"start": "yarn run dev"
|
"start": "yarn run dev"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@cornerstonejs/core": "^1.7.1",
|
"@cornerstonejs/core": "^1.9.3",
|
||||||
"@cornerstonejs/tools": "^1.7.1",
|
"@cornerstonejs/tools": "^1.9.3",
|
||||||
"@ohif/core": "3.7.0-beta.42",
|
"@ohif/core": "3.7.0-beta.42",
|
||||||
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.42",
|
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.42",
|
||||||
"@ohif/ui": "3.7.0-beta.42",
|
"@ohif/ui": "3.7.0-beta.42",
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
"dev:dcm4chee": "cross-env NODE_ENV=development APP_CONFIG=config/local_dcm4chee.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",
|
"dev:static": "cross-env NODE_ENV=development APP_CONFIG=config/local_static.js webpack serve --config .webpack/webpack.pwa.js",
|
||||||
"dev:viewer": "yarn run dev",
|
"dev:viewer": "yarn run dev",
|
||||||
"preinstall": "node preinstall.js",
|
|
||||||
"start": "yarn run dev",
|
"start": "yarn run dev",
|
||||||
"test:e2e": "cypress open",
|
"test:e2e": "cypress open",
|
||||||
"test:e2e:ci": "percy exec -- cypress run --config video=false --record --browser chrome --spec 'cypress/integration/visual-regression/**/*'",
|
"test:e2e:ci": "percy exec -- cypress run --config video=false --record --browser chrome --spec 'cypress/integration/visual-regression/**/*'",
|
||||||
@ -51,7 +50,7 @@
|
|||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||||
"@cornerstonejs/dicom-image-loader": "^1.7.1",
|
"@cornerstonejs/dicom-image-loader": "^1.9.3",
|
||||||
"@ohif/core": "3.7.0-beta.42",
|
"@ohif/core": "3.7.0-beta.42",
|
||||||
"@ohif/extension-cornerstone": "3.7.0-beta.42",
|
"@ohif/extension-cornerstone": "3.7.0-beta.42",
|
||||||
"@ohif/extension-cornerstone-dicom-rt": "3.7.0-beta.42",
|
"@ohif/extension-cornerstone-dicom-rt": "3.7.0-beta.42",
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||||
"@cornerstonejs/dicom-image-loader": "^1.7.1",
|
"@cornerstonejs/dicom-image-loader": "^1.9.3",
|
||||||
"@ohif/ui": "3.7.0-beta.42",
|
"@ohif/ui": "3.7.0-beta.42",
|
||||||
"cornerstone-math": "0.1.9",
|
"cornerstone-math": "0.1.9",
|
||||||
"dicom-parser": "^1.8.21"
|
"dicom-parser": "^1.8.21"
|
||||||
|
|||||||
@ -70,6 +70,11 @@ class MetadataProvider {
|
|||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
SOPInstanceUID
|
SOPInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!instance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(frameNumber && combineFrameInstance(frameNumber, instance)) || instance
|
(frameNumber && combineFrameInstance(frameNumber, instance)) || instance
|
||||||
);
|
);
|
||||||
|
|||||||
@ -27,6 +27,7 @@ export type ExtensionConfiguration = Record<string, unknown>;
|
|||||||
*/
|
*/
|
||||||
export interface ExtensionParams extends ExtensionConstructor {
|
export interface ExtensionParams extends ExtensionConstructor {
|
||||||
extensionManager: ExtensionManager;
|
extensionManager: ExtensionManager;
|
||||||
|
servicesManager: ServicesManager;
|
||||||
configuration?: ExtensionConfiguration;
|
configuration?: ExtensionConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ export interface Extension {
|
|||||||
getViewportModule?: (p: ExtensionParams) => unknown;
|
getViewportModule?: (p: ExtensionParams) => unknown;
|
||||||
getUtilityModule?: (p: ExtensionParams) => unknown;
|
getUtilityModule?: (p: ExtensionParams) => unknown;
|
||||||
getCustomizationModule?: (p: ExtensionParams) => unknown;
|
getCustomizationModule?: (p: ExtensionParams) => unknown;
|
||||||
|
getSopClassHandlerModule?: (p: ExtensionParams) => unknown;
|
||||||
onModeEnter?: () => void;
|
onModeEnter?: () => void;
|
||||||
onModeExit?: () => void;
|
onModeExit?: () => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ExtensionManager } from '../../extensions';
|
||||||
import { InstanceMetadata } from '../../types';
|
import { InstanceMetadata } from '../../types';
|
||||||
import { PubSubService } from '../_shared/pubSubServiceInterface';
|
import { PubSubService } from '../_shared/pubSubServiceInterface';
|
||||||
import EVENTS from './EVENTS';
|
import EVENTS from './EVENTS';
|
||||||
@ -47,6 +48,7 @@ export default class DisplaySetService extends PubSubService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public activeDisplaySets = [];
|
public activeDisplaySets = [];
|
||||||
|
extensionManager: ExtensionManager;
|
||||||
|
|
||||||
protected activeDisplaySetsMap = new Map<string, DisplaySet>();
|
protected activeDisplaySetsMap = new Map<string, DisplaySet>();
|
||||||
|
|
||||||
|
|||||||
@ -161,3 +161,9 @@ devServer: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Take a look at how other people have integrated OHIF in their react app
|
||||||
|
|
||||||
|
example1: https://github.com/OHIF/Viewers/issues/3371#issuecomment-1630405255
|
||||||
|
:::
|
||||||
|
|||||||
@ -21,26 +21,23 @@ function LoadingIndicatorTotalPercent({
|
|||||||
loadingText = 'Loading...',
|
loadingText = 'Loading...',
|
||||||
targetText = 'segments',
|
targetText = 'segments',
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
percentComplete = percentComplete !== null ? percentComplete : null;
|
const progress = percentComplete;
|
||||||
|
|
||||||
const progress = percentComplete !== null ? percentComplete : null;
|
|
||||||
const totalNumbersText = totalNumbers !== null ? `${totalNumbers}` : '';
|
const totalNumbersText = totalNumbers !== null ? `${totalNumbers}` : '';
|
||||||
const numTargetsLoadedText =
|
const numTargetsLoadedText =
|
||||||
percentComplete !== null
|
percentComplete !== null
|
||||||
? Math.floor((percentComplete * totalNumbers) / 100)
|
? Math.floor((percentComplete * totalNumbers) / 100)
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const textBlock = !totalNumbers ? (
|
const textBlock =
|
||||||
<div className="text-white text-sm">{loadingText}</div>
|
!totalNumbers && percentComplete === null ? (
|
||||||
) : (
|
<div className="text-white text-sm">{loadingText}</div>
|
||||||
<div className="text-white text-sm flex items-baseline space-x-1">
|
) : !totalNumbers && percentComplete !== null ? (
|
||||||
<div>Loaded</div>
|
<div className="text-white text-sm">Loaded {percentComplete}%</div>
|
||||||
<div>{numTargetsLoadedText}</div>
|
) : (
|
||||||
<div>of</div>
|
<div className="text-white text-sm">
|
||||||
<div>{totalNumbersText}</div>
|
Loaded {numTargetsLoadedText} of {totalNumbersText} {targetText}
|
||||||
<div>{targetText}</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingIndicatorProgress
|
<LoadingIndicatorProgress
|
||||||
|
|||||||
@ -19,6 +19,7 @@ function PatientInfo({
|
|||||||
patientAge,
|
patientAge,
|
||||||
MRN,
|
MRN,
|
||||||
thickness,
|
thickness,
|
||||||
|
thicknessUnits,
|
||||||
spacing,
|
spacing,
|
||||||
scanner,
|
scanner,
|
||||||
isOpen,
|
isOpen,
|
||||||
@ -29,7 +30,6 @@ function PatientInfo({
|
|||||||
while (patientAge.charAt(0) === '0') {
|
while (patientAge.charAt(0) === '0') {
|
||||||
patientAge = patientAge.substr(1);
|
patientAge = patientAge.substr(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={showPatientInfoRef}>
|
<div ref={showPatientInfoRef}>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@ -90,7 +90,9 @@ function PatientInfo({
|
|||||||
className={classnames(classes.infoText)}
|
className={classnames(classes.infoText)}
|
||||||
title={thickness}
|
title={thickness}
|
||||||
>
|
>
|
||||||
{thickness}
|
{thicknessUnits
|
||||||
|
? `${thickness}${thicknessUnits}`
|
||||||
|
: `${thickness}`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classnames(classes.row)}>
|
<div className={classnames(classes.row)}>
|
||||||
@ -136,6 +138,7 @@ PatientInfo.propTypes = {
|
|||||||
patientAge: PropTypes.string,
|
patientAge: PropTypes.string,
|
||||||
MRN: PropTypes.string,
|
MRN: PropTypes.string,
|
||||||
thickness: PropTypes.string,
|
thickness: PropTypes.string,
|
||||||
|
thicknessUnits: PropTypes.string,
|
||||||
spacing: PropTypes.string,
|
spacing: PropTypes.string,
|
||||||
scanner: PropTypes.string,
|
scanner: PropTypes.string,
|
||||||
isOpen: PropTypes.bool,
|
isOpen: PropTypes.bool,
|
||||||
|
|||||||
@ -34,6 +34,7 @@ const ViewportActionBar = ({
|
|||||||
patientAge,
|
patientAge,
|
||||||
MRN,
|
MRN,
|
||||||
thickness,
|
thickness,
|
||||||
|
thicknessUnits,
|
||||||
spacing,
|
spacing,
|
||||||
scanner,
|
scanner,
|
||||||
} = patientInformation;
|
} = patientInformation;
|
||||||
@ -169,6 +170,7 @@ const ViewportActionBar = ({
|
|||||||
patientAge={patientAge}
|
patientAge={patientAge}
|
||||||
MRN={MRN}
|
MRN={MRN}
|
||||||
thickness={thickness}
|
thickness={thickness}
|
||||||
|
thicknessUnits={thicknessUnits}
|
||||||
spacing={spacing}
|
spacing={spacing}
|
||||||
scanner={scanner}
|
scanner={scanner}
|
||||||
/>
|
/>
|
||||||
@ -192,6 +194,7 @@ ViewportActionBar.propTypes = {
|
|||||||
patientAge: PropTypes.string.isRequired,
|
patientAge: PropTypes.string.isRequired,
|
||||||
MRN: PropTypes.string.isRequired,
|
MRN: PropTypes.string.isRequired,
|
||||||
thickness: PropTypes.string.isRequired,
|
thickness: PropTypes.string.isRequired,
|
||||||
|
thicknessUnits: PropTypes.string.isRequired,
|
||||||
spacing: PropTypes.string.isRequired,
|
spacing: PropTypes.string.isRequired,
|
||||||
scanner: PropTypes.string.isRequired,
|
scanner: PropTypes.string.isRequired,
|
||||||
}),
|
}),
|
||||||
|
|||||||
57
yarn.lock
57
yarn.lock
@ -1321,9 +1321,9 @@
|
|||||||
regenerator-runtime "^0.13.11"
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
"@babel/runtime@7.17.9", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.6", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.6", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
"@babel/runtime@7.17.9", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.6", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.6", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||||
version "7.22.6"
|
version "7.22.3"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb"
|
||||||
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
|
integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.11"
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
@ -1376,13 +1376,14 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||||
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
||||||
|
|
||||||
"@cornerstonejs/adapters@^1.7.1":
|
"@cornerstonejs/adapters@^1.9.3":
|
||||||
version "1.7.1"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.7.1.tgz#5ae8b510791f5817420ade4b5637f9e84a8ada52"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.9.3.tgz#b1031e26af175ac15c3c975aff89f51c214ec092"
|
||||||
integrity sha512-iwowMUVoNKWZ3PTmYSjnZ2vDThR+OUkvIsSB913A1YhPCl+FVv2Y0JLdueFCYKlTFLnzFa8FOlFwzAOI+AAB+Q==
|
integrity sha512-bjpUq78yupE3+z5qNAS7j27OgCn1BAJQKwcQNY+PBUzTojwg2KpMpG69I+CucSCAxHKDv4waQy4hEyNaiI5JAA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime-corejs2" "^7.17.8"
|
"@babel/runtime-corejs2" "^7.17.8"
|
||||||
dcmjs "^0.29.5"
|
buffer "^6.0.3"
|
||||||
|
dcmjs "^0.29.8"
|
||||||
gl-matrix "^3.4.3"
|
gl-matrix "^3.4.3"
|
||||||
lodash.clonedeep "^4.5.0"
|
lodash.clonedeep "^4.5.0"
|
||||||
ndarray "^1.0.19"
|
ndarray "^1.0.19"
|
||||||
@ -1427,43 +1428,43 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.2.tgz#e96721d56f6ec96f7f95c16321d88cc8467d8d81"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.2.tgz#e96721d56f6ec96f7f95c16321d88cc8467d8d81"
|
||||||
integrity sha512-lgdvBvvNezleY+4pIe2ceUsJzlZe/0PipdeubQ3vZZOz3xxtHHMR1XFCl4fgd8gosR8COHuD7h6q+MwgrwBsng==
|
integrity sha512-lgdvBvvNezleY+4pIe2ceUsJzlZe/0PipdeubQ3vZZOz3xxtHHMR1XFCl4fgd8gosR8COHuD7h6q+MwgrwBsng==
|
||||||
|
|
||||||
"@cornerstonejs/core@^1.7.1":
|
"@cornerstonejs/core@^1.9.3":
|
||||||
version "1.7.1"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.7.1.tgz#38c2fa24cc166b15088a0f8d220ae33e9a80cb7e"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.9.3.tgz#43c04b5c2a929e1f2ffc71d71aa2311dea2b1e7d"
|
||||||
integrity sha512-XS44HcRwXJ81QqEnOjbpstLCrhApzNIvBuV4UM1+qhvUn0fg6LrXEkR/cT4xQ0RhHB9oq+0LPsfm9/X2xxWZAA==
|
integrity sha512-TMgxFe23Rur0k3ISDh0JGkQ6x2xCYFnqON68A+Eovxc7g/sHUZ0tbzXiTd+IaVpuRsaDxspb0fwklPISKTJDuA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@kitware/vtk.js" "27.3.1"
|
"@kitware/vtk.js" "27.3.1"
|
||||||
detect-gpu "^5.0.22"
|
detect-gpu "^5.0.22"
|
||||||
gl-matrix "^3.4.3"
|
gl-matrix "^3.4.3"
|
||||||
lodash.clonedeep "4.5.0"
|
lodash.clonedeep "4.5.0"
|
||||||
|
|
||||||
"@cornerstonejs/dicom-image-loader@^1.7.1":
|
"@cornerstonejs/dicom-image-loader@^1.9.3":
|
||||||
version "1.7.1"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.7.1.tgz#c5ee643bd1c846c1ca7c536f6db3ae26050353fd"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.9.3.tgz#6ec07e8f22ba20c78cab930a1396effc1f86885e"
|
||||||
integrity sha512-U15NsCdjKkU6OKKWvRs6IwBxRqAudE2vZh+fwAWB9tmyjZCHnRs0KgscOVoYQvR5EV7f1gz7777Iz/u8tnSoFQ==
|
integrity sha512-+JRiI5hb9Cuo7yRGg3DzqgXTmNUMn9DAI1ASh721mbqJcPymHBDs+QTfF2bXyAnh4Ijxr+WfCEUSBm4PRPvrAw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cornerstonejs/codec-charls" "^1.2.3"
|
"@cornerstonejs/codec-charls" "^1.2.3"
|
||||||
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
|
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
|
||||||
"@cornerstonejs/codec-openjpeg" "^1.2.2"
|
"@cornerstonejs/codec-openjpeg" "^1.2.2"
|
||||||
"@cornerstonejs/codec-openjph" "^2.4.2"
|
"@cornerstonejs/codec-openjph" "^2.4.2"
|
||||||
"@cornerstonejs/core" "^1.7.1"
|
"@cornerstonejs/core" "^1.9.3"
|
||||||
dicom-parser "^1.8.9"
|
dicom-parser "^1.8.9"
|
||||||
pako "^2.0.4"
|
pako "^2.0.4"
|
||||||
uuid "^9.0.0"
|
uuid "^9.0.0"
|
||||||
|
|
||||||
"@cornerstonejs/streaming-image-volume-loader@^1.7.1":
|
"@cornerstonejs/streaming-image-volume-loader@^1.9.3":
|
||||||
version "1.7.1"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.7.1.tgz#1102cde7136f4804c21b665b6429210545a79087"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.9.3.tgz#1772273bfd59f7f470ba254d9463c2d1f9c22359"
|
||||||
integrity sha512-eIILqRpWodUVX4HgJbWlZhVebsz4gLxZaOtErOvL/knSK3zyb/D+Rsz8RjWbMJsixHp+gQ/Q51q/FYab/RvJrw==
|
integrity sha512-xD1+ZNrYa1FQTgieG11d6jYnuZFH2zpDzg5eigFeZx9e8Vsu9rO1lpkwVWUw1Cf3qhqmsG9PA+BxHQm3ACjwTg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cornerstonejs/core" "^1.7.1"
|
"@cornerstonejs/core" "^1.9.3"
|
||||||
|
|
||||||
"@cornerstonejs/tools@^1.7.1":
|
"@cornerstonejs/tools@^1.9.3":
|
||||||
version "1.7.1"
|
version "1.9.3"
|
||||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.7.1.tgz#7cd589db556ba8a2139fcdaf01b84e03d4b78d2e"
|
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.9.3.tgz#40e967c4bc275b4bf4d38f6c26d3a979779a632b"
|
||||||
integrity sha512-iulzSXInFlcsLTWsn4xCF+dpZEwSWf/UjZBLmVNfTA262bncmE083cn57VEB9mRuFN+doA50/8UJi7uNew+Q5Q==
|
integrity sha512-mesMJi8L0JHebYy7F3+VE+6zRwkv9ZaYw4cwplguTgOq7s7xmINVHomIVkffHUQeUPhwPVmxbjIqeW6O5MkXeg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@cornerstonejs/core" "^1.7.1"
|
"@cornerstonejs/core" "^1.9.3"
|
||||||
lodash.clonedeep "4.5.0"
|
lodash.clonedeep "4.5.0"
|
||||||
lodash.get "^4.4.2"
|
lodash.get "^4.4.2"
|
||||||
|
|
||||||
@ -8219,7 +8220,7 @@ dcmjs@^0.27:
|
|||||||
loglevelnext "^3.0.1"
|
loglevelnext "^3.0.1"
|
||||||
ndarray "^1.0.19"
|
ndarray "^1.0.19"
|
||||||
|
|
||||||
dcmjs@^0.29.5:
|
dcmjs@^0.29.5, dcmjs@^0.29.8:
|
||||||
version "0.29.8"
|
version "0.29.8"
|
||||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.29.8.tgz#3daa5224f8e75b2e5b069590bef26a272741fa44"
|
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.29.8.tgz#3daa5224f8e75b2e5b069590bef26a272741fa44"
|
||||||
integrity sha512-Y0/KZAmT1siVo7eH3KK4ZflEbNi61soUpD0N7lsXMVVJQ6IZkHlaSzb9DtqnEpMs7RJDfvZGr1uXpv1vBBIypQ==
|
integrity sha512-Y0/KZAmT1siVo7eH3KK4ZflEbNi61soUpD0N7lsXMVVJQ6IZkHlaSzb9DtqnEpMs7RJDfvZGr1uXpv1vBBIypQ==
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user