OHIF with Cornerstone3D 2.0 (#4347)
This commit is contained in:
1 parent
8981a61b8c
commit
7ce7127d7c
803 files changed
+32760
-15893
No files matched your search
@@ -1,10 +1,11 @@
|
||||
import { utilities } from '@cornerstonejs/tools';
|
||||
import { utilities } from '@cornerstonejs/core';
|
||||
import { utilities as cstUtils } from '@cornerstonejs/tools';
|
||||
import { vec3 } from 'gl-matrix';
|
||||
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
|
||||
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
|
||||
import { expose } from 'comlink';
|
||||
|
||||
const createVolume = ({ dimensions, origin, direction, spacing, scalarData, metadata }) => {
|
||||
const createVolume = ({ dimensions, origin, direction, spacing, metadata, scalarData }) => {
|
||||
const imageData = vtkImageData.newInstance();
|
||||
imageData.setDimensions(dimensions);
|
||||
imageData.setOrigin(origin);
|
||||
@@ -21,12 +22,21 @@ const createVolume = ({ dimensions, origin, direction, spacing, scalarData, meta
|
||||
|
||||
imageData.modified();
|
||||
|
||||
const voxelManager = utilities.VoxelManager.createScalarVolumeVoxelManager({
|
||||
scalarData,
|
||||
dimensions,
|
||||
numberOfComponents: 1,
|
||||
});
|
||||
return {
|
||||
imageData,
|
||||
spacing,
|
||||
origin,
|
||||
direction,
|
||||
metadata,
|
||||
getScalarData: () => scalarData,
|
||||
voxelManager,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* This method calculates the SUV peak on a segmented ROI from a reference PET
|
||||
* volume. If a rectangle annotation is provided, the peak is calculated within that
|
||||
@@ -40,22 +50,15 @@ const createVolume = ({ dimensions, origin, direction, spacing, scalarData, meta
|
||||
* @returns
|
||||
*/
|
||||
function calculateSuvPeak({ labelmapProps, referenceVolumeProps, annotations, segmentIndex = 1 }) {
|
||||
const labelmap = createVolume(labelmapProps);
|
||||
const referenceVolume = createVolume(referenceVolumeProps);
|
||||
const labelmapInfo = createVolume(labelmapProps);
|
||||
const referenceInfo = createVolume(referenceVolumeProps);
|
||||
|
||||
if (referenceVolume.metadata.Modality !== 'PT') {
|
||||
if (referenceInfo.metadata.Modality !== 'PT') {
|
||||
return;
|
||||
}
|
||||
|
||||
const labelmapData = labelmap.getScalarData();
|
||||
const referenceVolumeData = referenceVolume.getScalarData();
|
||||
|
||||
if (labelmapData.length !== referenceVolumeData.length) {
|
||||
throw new Error('labelmap and referenceVolume must have the same number of pixels');
|
||||
}
|
||||
|
||||
const { dimensions, imageData: labelmapImageData } = labelmap;
|
||||
const { imageData: referenceVolumeImageData } = referenceVolume;
|
||||
const { dimensions, imageData: labelmapImageData } = labelmapInfo;
|
||||
const { imageData: referenceVolumeImageData } = referenceInfo;
|
||||
|
||||
let boundsIJK;
|
||||
// Todo: using the first annotation for now
|
||||
@@ -69,7 +72,7 @@ function calculateSuvPeak({ labelmapProps, referenceVolumeProps, annotations, se
|
||||
return ijk;
|
||||
});
|
||||
|
||||
boundsIJK = utilities.boundingBox.getBoundingBoxAroundShape(rectangleCornersIJK, dimensions);
|
||||
boundsIJK = cstUtils.boundingBox.getBoundingBoxAroundShape(rectangleCornersIJK, dimensions);
|
||||
}
|
||||
|
||||
let max = 0;
|
||||
@@ -77,14 +80,13 @@ function calculateSuvPeak({ labelmapProps, referenceVolumeProps, annotations, se
|
||||
let maxLPS = [0, 0, 0];
|
||||
|
||||
const callback = ({ pointIJK, pointLPS }) => {
|
||||
const offset = referenceVolumeImageData.computeOffsetIndex(pointIJK);
|
||||
const value = labelmapData[offset];
|
||||
const value = labelmapInfo.voxelManager.getAtIJKPoint(pointIJK);
|
||||
|
||||
if (value !== segmentIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const referenceValue = referenceVolumeData[offset];
|
||||
const referenceValue = referenceInfo.voxelManager.getAtIJKPoint(pointIJK);
|
||||
|
||||
if (referenceValue > max) {
|
||||
max = referenceValue;
|
||||
@@ -93,7 +95,12 @@ function calculateSuvPeak({ labelmapProps, referenceVolumeProps, annotations, se
|
||||
}
|
||||
};
|
||||
|
||||
utilities.pointInShapeCallback(labelmapImageData, () => true, callback, boundsIJK);
|
||||
labelmapInfo.voxelManager.forEach(callback, {
|
||||
boundsIJK,
|
||||
imageData: labelmapImageData,
|
||||
isInObject: () => true,
|
||||
returnPoints: true,
|
||||
});
|
||||
|
||||
const direction = labelmapImageData.getDirection().slice(0, 3);
|
||||
|
||||
@@ -123,7 +130,7 @@ function calculateSuvPeak({ labelmapProps, referenceVolumeProps, annotations, se
|
||||
count += 1;
|
||||
};
|
||||
|
||||
utilities.pointInSurroundingSphereCallback(
|
||||
cstUtils.pointInSurroundingSphereCallback(
|
||||
referenceVolumeImageData,
|
||||
suvPeakCirclePoints,
|
||||
suvPeakMeanCallback
|
||||
@@ -139,8 +146,64 @@ function calculateSuvPeak({ labelmapProps, referenceVolumeProps, annotations, se
|
||||
};
|
||||
}
|
||||
|
||||
function calculateTMTV(labelmapProps, segmentIndex = 1) {
|
||||
const labelmaps = labelmapProps.map(props => createVolume(props));
|
||||
|
||||
const mergedLabelmap =
|
||||
labelmaps.length === 1
|
||||
? labelmaps[0]
|
||||
: cstUtils.segmentation.createMergedLabelmapForIndex(labelmaps);
|
||||
|
||||
const { imageData, spacing } = mergedLabelmap;
|
||||
const values = imageData.getPointData().getScalars().getData();
|
||||
|
||||
// count non-zero values inside the outputData, this would
|
||||
// consider the overlapping regions to be only counted once
|
||||
const numVoxels = values.reduce((acc, curr) => {
|
||||
if (curr > 0) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
|
||||
return 1e-3 * numVoxels * spacing[0] * spacing[1] * spacing[2];
|
||||
}
|
||||
|
||||
function getTotalLesionGlycolysis({ labelmapProps, referenceVolumeProps }) {
|
||||
const labelmaps = labelmapProps.map(props => createVolume(props));
|
||||
|
||||
const mergedLabelmap =
|
||||
labelmaps.length === 1
|
||||
? labelmaps[0]
|
||||
: cstUtils.segmentation.createMergedLabelmapForIndex(labelmaps);
|
||||
|
||||
// grabbing the first labelmap referenceVolume since it will be the same for all
|
||||
const { spacing } = labelmaps[0];
|
||||
|
||||
const ptVolume = createVolume(referenceVolumeProps);
|
||||
|
||||
let suv = 0;
|
||||
let totalLesionVoxelCount = 0;
|
||||
const scalarDataLength = mergedLabelmap.voxelManager.getScalarDataLength();
|
||||
for (let i = 0; i < scalarDataLength; i++) {
|
||||
// if not background
|
||||
if (mergedLabelmap.voxelManager.getAtIndex(i) !== 0) {
|
||||
suv += ptVolume.voxelManager.getAtIndex(i);
|
||||
totalLesionVoxelCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Average SUV for the merged labelmap
|
||||
const averageSuv = suv / totalLesionVoxelCount;
|
||||
|
||||
// total Lesion Glycolysis [suv * ml]
|
||||
return averageSuv * totalLesionVoxelCount * spacing[0] * spacing[1] * spacing[2] * 1e-3;
|
||||
}
|
||||
|
||||
const obj = {
|
||||
calculateSuvPeak,
|
||||
calculateTMTV,
|
||||
getTotalLesionGlycolysis,
|
||||
};
|
||||
|
||||
expose(obj);
|
||||
@@ -20,17 +20,21 @@ function calculateTMTV(labelmaps: Array<Types.IImageVolume>, segmentIndex = 1):
|
||||
volumeId
|
||||
);
|
||||
|
||||
const { imageData, spacing } = mergedLabelmap;
|
||||
const values = imageData.getPointData().getScalars().getData();
|
||||
const { imageData, spacing, voxelManager } = mergedLabelmap;
|
||||
|
||||
// count non-zero values inside the outputData, this would
|
||||
// consider the overlapping regions to be only counted once
|
||||
const numVoxels = values.reduce((acc, curr) => {
|
||||
if (curr > 0) {
|
||||
return acc + 1;
|
||||
let numVoxels = 0;
|
||||
const callback = ({ value }) => {
|
||||
if (value > 0) {
|
||||
numVoxels += 1;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
voxelManager.forEach(callback, {
|
||||
imageData,
|
||||
isInObject: () => true,
|
||||
});
|
||||
|
||||
return 1e-3 * numVoxels * spacing[0] * spacing[1] * spacing[2];
|
||||
}
|
||||
|
||||
@@ -1,64 +1,97 @@
|
||||
import { cache } from '@cornerstonejs/core';
|
||||
import { Segment, Segmentation } from '@cornerstonejs/tools/types';
|
||||
import { triggerEvent, eventTarget, Enums } from '@cornerstonejs/core';
|
||||
|
||||
export const handleROIThresholding = async ({
|
||||
segmentationId,
|
||||
commandsManager,
|
||||
segmentationService,
|
||||
config = {},
|
||||
}) => {
|
||||
}: withAppTypes<{
|
||||
segmentationId: string;
|
||||
}>) => {
|
||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||
|
||||
triggerEvent(eventTarget, Enums.Events.WEB_WORKER_PROGRESS, {
|
||||
progress: 0,
|
||||
type: 'Calculate Lesion Stats',
|
||||
id: segmentationId,
|
||||
});
|
||||
|
||||
// re-calculating the cached stats for the active segmentation
|
||||
const updatedPerSegmentCachedStats = {};
|
||||
segmentation.segments = await Promise.all(
|
||||
segmentation.segments.map(async segment => {
|
||||
if (!segment || !segment.segmentIndex) {
|
||||
return segment;
|
||||
}
|
||||
for (const [segmentIndex, segment] of Object.entries(segmentation.segments)) {
|
||||
if (!segment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const labelmap = cache.getVolume(segmentationId);
|
||||
const numericSegmentIndex = Number(segmentIndex);
|
||||
|
||||
const segmentIndex = segment.segmentIndex;
|
||||
const lesionStats = await commandsManager.run('getLesionStats', {
|
||||
segmentationId,
|
||||
segmentIndex: numericSegmentIndex,
|
||||
});
|
||||
|
||||
const lesionStats = commandsManager.run('getLesionStats', { labelmap, segmentIndex });
|
||||
const suvPeak = await commandsManager.run('calculateSuvPeak', { labelmap, segmentIndex });
|
||||
const lesionGlyoclysisStats = lesionStats.volume * lesionStats.meanValue;
|
||||
const suvPeak = await commandsManager.run('calculateSuvPeak', {
|
||||
segmentationId,
|
||||
segmentIndex: numericSegmentIndex,
|
||||
});
|
||||
|
||||
// update segDetails with the suv peak for the active segmentation
|
||||
const cachedStats = {
|
||||
lesionStats,
|
||||
suvPeak,
|
||||
lesionGlyoclysisStats,
|
||||
};
|
||||
const lesionGlyoclysisStats = lesionStats.volume * lesionStats.meanValue;
|
||||
|
||||
segment.cachedStats = cachedStats;
|
||||
segment.displayText = [
|
||||
`SUV Peak: ${suvPeak.suvPeak.toFixed(2)}`,
|
||||
`Volume: ${lesionStats.volume.toFixed(2)} mm3`,
|
||||
];
|
||||
updatedPerSegmentCachedStats[segmentIndex] = cachedStats;
|
||||
// update segDetails with the suv peak for the active segmentation
|
||||
const cachedStats = {
|
||||
lesionStats,
|
||||
suvPeak,
|
||||
lesionGlyoclysisStats,
|
||||
};
|
||||
|
||||
return segment;
|
||||
})
|
||||
);
|
||||
const updatedSegment: Segment = {
|
||||
...segment,
|
||||
cachedStats: {
|
||||
...segment.cachedStats,
|
||||
...cachedStats,
|
||||
},
|
||||
};
|
||||
|
||||
const notYetUpdatedAtSource = true;
|
||||
updatedPerSegmentCachedStats[numericSegmentIndex] = cachedStats;
|
||||
|
||||
segmentation.segments[segmentIndex] = updatedSegment;
|
||||
}
|
||||
|
||||
// all available segmentations
|
||||
const segmentations = segmentationService.getSegmentations();
|
||||
const tmtv = commandsManager.run('calculateTMTV', { segmentations });
|
||||
const tmtv = await commandsManager.run('calculateTMTV', { segmentations });
|
||||
|
||||
segmentation.cachedStats = Object.assign(segmentation.cachedStats, updatedPerSegmentCachedStats, {
|
||||
tmtv: {
|
||||
value: tmtv.toFixed(3),
|
||||
config: { ...config },
|
||||
},
|
||||
triggerEvent(eventTarget, Enums.Events.WEB_WORKER_PROGRESS, {
|
||||
progress: 100,
|
||||
type: 'Calculate Lesion Stats',
|
||||
id: segmentationId,
|
||||
});
|
||||
|
||||
segmentationService.addOrUpdateSegmentation(
|
||||
{
|
||||
// add the tmtv to all the segment cachedStats, although it is a global
|
||||
// value but we don't have any other way to display it for now
|
||||
// Update all segmentations with the calculated TMTV
|
||||
segmentations.forEach(segmentation => {
|
||||
segmentation.cachedStats = {
|
||||
...segmentation.cachedStats,
|
||||
tmtv,
|
||||
};
|
||||
|
||||
// Update each segment within the segmentation
|
||||
Object.keys(segmentation.segments).forEach(segmentIndex => {
|
||||
segmentation.segments[segmentIndex].cachedStats = {
|
||||
...segmentation.segments[segmentIndex].cachedStats,
|
||||
tmtv,
|
||||
};
|
||||
});
|
||||
|
||||
// Update the segmentation object
|
||||
const updatedSegmentation: Segmentation = {
|
||||
...segmentation,
|
||||
},
|
||||
false, // don't suppress events
|
||||
notYetUpdatedAtSource
|
||||
);
|
||||
segments: {
|
||||
...segmentation.segments,
|
||||
},
|
||||
};
|
||||
|
||||
segmentationService.addOrUpdateSegmentation(updatedSegmentation);
|
||||
});
|
||||
};
|
||||
@@ -1,4 +1,22 @@
|
||||
const ctAXIAL = {
|
||||
// Common sync group configurations
|
||||
const cameraPositionSync = (id: string) => ({
|
||||
type: 'cameraPosition',
|
||||
id,
|
||||
source: true,
|
||||
target: true,
|
||||
});
|
||||
|
||||
const hydrateSegSync = {
|
||||
type: 'hydrateseg',
|
||||
id: 'sameFORId',
|
||||
source: true,
|
||||
target: true,
|
||||
options: {
|
||||
matchingRules: ['sameFOR'],
|
||||
},
|
||||
};
|
||||
|
||||
const ctAXIAL: AppTypes.HangingProtocol.Viewport = {
|
||||
viewportOptions: {
|
||||
viewportId: 'ctAXIAL',
|
||||
viewportType: 'volume',
|
||||
@@ -9,12 +27,7 @@ const ctAXIAL = {
|
||||
preset: 'first', // 'first', 'last', 'middle'
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'axialSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
cameraPositionSync('axialSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ctWLSync',
|
||||
@@ -24,6 +37,7 @@ const ctAXIAL = {
|
||||
syncColormap: true,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
@@ -33,49 +47,14 @@ const ctAXIAL = {
|
||||
],
|
||||
};
|
||||
|
||||
const ctSAGITTAL = {
|
||||
const ctSAGITTAL: AppTypes.HangingProtocol.Viewport = {
|
||||
viewportOptions: {
|
||||
viewportId: 'ctSAGITTAL',
|
||||
viewportType: 'volume',
|
||||
orientation: 'sagittal',
|
||||
toolGroupId: 'ctToolGroup',
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'sagittalSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ctWLSync',
|
||||
source: true,
|
||||
target: true,
|
||||
options: {
|
||||
syncColormap: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'ctDisplaySet',
|
||||
},
|
||||
],
|
||||
};
|
||||
const ctCORONAL = {
|
||||
viewportOptions: {
|
||||
viewportId: 'ctCORONAL',
|
||||
viewportType: 'volume',
|
||||
orientation: 'coronal',
|
||||
toolGroupId: 'ctToolGroup',
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'coronalSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
cameraPositionSync('sagittalSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ctWLSync',
|
||||
@@ -85,6 +64,7 @@ const ctCORONAL = {
|
||||
syncColormap: true,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
@@ -94,7 +74,34 @@ const ctCORONAL = {
|
||||
],
|
||||
};
|
||||
|
||||
const ptAXIAL = {
|
||||
const ctCORONAL: AppTypes.HangingProtocol.Viewport = {
|
||||
viewportOptions: {
|
||||
viewportId: 'ctCORONAL',
|
||||
viewportType: 'volume',
|
||||
orientation: 'coronal',
|
||||
toolGroupId: 'ctToolGroup',
|
||||
syncGroups: [
|
||||
cameraPositionSync('coronalSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ctWLSync',
|
||||
source: true,
|
||||
target: true,
|
||||
options: {
|
||||
syncColormap: true,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'ctDisplaySet',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const ptAXIAL: AppTypes.HangingProtocol.Viewport = {
|
||||
viewportOptions: {
|
||||
viewportId: 'ptAXIAL',
|
||||
viewportType: 'volume',
|
||||
@@ -106,12 +113,7 @@ const ptAXIAL = {
|
||||
preset: 'first', // 'first', 'last', 'middle'
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'axialSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
cameraPositionSync('axialSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ptWLSync',
|
||||
@@ -131,6 +133,7 @@ const ptAXIAL = {
|
||||
syncInvertState: false,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
@@ -146,7 +149,7 @@ const ptAXIAL = {
|
||||
],
|
||||
};
|
||||
|
||||
const ptSAGITTAL = {
|
||||
const ptSAGITTAL: AppTypes.HangingProtocol.Viewport = {
|
||||
viewportOptions: {
|
||||
viewportId: 'ptSAGITTAL',
|
||||
viewportType: 'volume',
|
||||
@@ -154,12 +157,7 @@ const ptSAGITTAL = {
|
||||
background: [1, 1, 1],
|
||||
toolGroupId: 'ptToolGroup',
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'sagittalSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
cameraPositionSync('sagittalSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ptWLSync',
|
||||
@@ -179,6 +177,7 @@ const ptSAGITTAL = {
|
||||
syncInvertState: false,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
@@ -194,7 +193,7 @@ const ptSAGITTAL = {
|
||||
],
|
||||
};
|
||||
|
||||
const ptCORONAL = {
|
||||
const ptCORONAL: AppTypes.HangingProtocol.Viewport = {
|
||||
viewportOptions: {
|
||||
viewportId: 'ptCORONAL',
|
||||
viewportType: 'volume',
|
||||
@@ -202,12 +201,7 @@ const ptCORONAL = {
|
||||
background: [1, 1, 1],
|
||||
toolGroupId: 'ptToolGroup',
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'coronalSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
cameraPositionSync('coronalSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ptWLSync',
|
||||
@@ -227,6 +221,7 @@ const ptCORONAL = {
|
||||
syncInvertState: false,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
@@ -242,7 +237,7 @@ const ptCORONAL = {
|
||||
],
|
||||
};
|
||||
|
||||
const fusionAXIAL = {
|
||||
const fusionAXIAL: AppTypes.HangingProtocol.Viewport = {
|
||||
viewportOptions: {
|
||||
viewportId: 'fusionAXIAL',
|
||||
viewportType: 'volume',
|
||||
@@ -253,12 +248,7 @@ const fusionAXIAL = {
|
||||
preset: 'first', // 'first', 'last', 'middle'
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'axialSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
cameraPositionSync('axialSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ctWLSync',
|
||||
@@ -284,6 +274,7 @@ const fusionAXIAL = {
|
||||
syncInvertState: false,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
@@ -297,8 +288,8 @@ const fusionAXIAL = {
|
||||
name: 'hsv',
|
||||
opacity: [
|
||||
{ value: 0, opacity: 0 },
|
||||
{ value: 0.1, opacity: 0.9 },
|
||||
{ value: 1, opacity: 0.95 },
|
||||
{ value: 0.1, opacity: 0.8 },
|
||||
{ value: 1, opacity: 0.9 },
|
||||
],
|
||||
},
|
||||
voi: {
|
||||
@@ -320,12 +311,7 @@ const fusionSAGITTAL = {
|
||||
// preset: 'middle', // 'first', 'last', 'middle'
|
||||
// },
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'sagittalSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
cameraPositionSync('sagittalSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ctWLSync',
|
||||
@@ -351,6 +337,7 @@ const fusionSAGITTAL = {
|
||||
syncInvertState: false,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
@@ -364,8 +351,8 @@ const fusionSAGITTAL = {
|
||||
name: 'hsv',
|
||||
opacity: [
|
||||
{ value: 0, opacity: 0 },
|
||||
{ value: 0.1, opacity: 0.9 },
|
||||
{ value: 1, opacity: 0.95 },
|
||||
{ value: 0.1, opacity: 0.8 },
|
||||
{ value: 1, opacity: 0.9 },
|
||||
],
|
||||
},
|
||||
voi: {
|
||||
@@ -387,12 +374,7 @@ const fusionCORONAL = {
|
||||
// preset: 'middle', // 'first', 'last', 'middle'
|
||||
// },
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'cameraPosition',
|
||||
id: 'coronalSync',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
cameraPositionSync('coronalSync'),
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'ctWLSync',
|
||||
@@ -418,6 +400,7 @@ const fusionCORONAL = {
|
||||
syncInvertState: false,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
@@ -431,8 +414,8 @@ const fusionCORONAL = {
|
||||
name: 'hsv',
|
||||
opacity: [
|
||||
{ value: 0, opacity: 0 },
|
||||
{ value: 0.1, opacity: 0.9 },
|
||||
{ value: 1, opacity: 0.95 },
|
||||
{ value: 0.1, opacity: 0.8 },
|
||||
{ value: 1, opacity: 0.9 },
|
||||
],
|
||||
},
|
||||
voi: {
|
||||
@@ -443,7 +426,7 @@ const fusionCORONAL = {
|
||||
],
|
||||
};
|
||||
|
||||
const mipSAGITTAL = {
|
||||
const mipSAGITTAL: AppTypes.HangingProtocol.Viewport = {
|
||||
viewportOptions: {
|
||||
viewportId: 'mipSagittal',
|
||||
viewportType: 'volume',
|
||||
@@ -470,6 +453,7 @@ const mipSAGITTAL = {
|
||||
syncInvertState: false,
|
||||
},
|
||||
},
|
||||
hydrateSegSync,
|
||||
],
|
||||
|
||||
// Custom props can be used to set custom properties which extensions
|
||||
|
||||
Reference in new issue
Block a user