@cornerstonejs/tools 3.5.3 → 3.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/config.d.ts +7 -0
- package/dist/esm/enums/WorkerTypes.d.ts +2 -1
- package/dist/esm/enums/WorkerTypes.js +1 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/tools/annotation/BidirectionalTool.d.ts +3 -0
- package/dist/esm/tools/annotation/BidirectionalTool.js +39 -1
- package/dist/esm/tools/index.d.ts +2 -1
- package/dist/esm/tools/index.js +2 -1
- package/dist/esm/tools/segmentation/SegmentBidirectionalTool.d.ts +27 -0
- package/dist/esm/tools/segmentation/SegmentBidirectionalTool.js +253 -0
- package/dist/esm/tools/segmentation/strategies/compositions/ensureImageVolume.js +5 -8
- package/dist/esm/tools/segmentation/strategies/utils/getStrategyData.js +5 -10
- package/dist/esm/types/CalculatorTypes.d.ts +21 -3
- package/dist/esm/types/ToolSpecificAnnotationTypes.d.ts +34 -0
- package/dist/esm/utilities/contours/generateContourSetsFromLabelmap.js +1 -0
- package/dist/esm/utilities/math/basic/BasicStatsCalculator.js +88 -13
- package/dist/esm/utilities/registerComputeWorker.js +4 -1
- package/dist/esm/utilities/segmentation/VolumetricCalculator.js +12 -2
- package/dist/esm/utilities/segmentation/computeMetabolicStats.d.ts +8 -0
- package/dist/esm/utilities/segmentation/computeMetabolicStats.js +58 -0
- package/dist/esm/utilities/segmentation/contourAndFindLargestBidirectional.js +7 -5
- package/dist/esm/utilities/segmentation/createMergedLabelmapForIndex.js +10 -2
- package/dist/esm/utilities/segmentation/findLargestBidirectional.d.ts +5 -0
- package/dist/esm/utilities/segmentation/findLargestBidirectional.js +1 -1
- package/dist/esm/utilities/segmentation/getOrCreateImageVolume.d.ts +3 -0
- package/dist/esm/utilities/segmentation/getOrCreateImageVolume.js +18 -0
- package/dist/esm/utilities/segmentation/getOrCreateSegmentationVolume.d.ts +2 -1
- package/dist/esm/utilities/segmentation/getOrCreateSegmentationVolume.js +5 -1
- package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentation.d.ts +1 -0
- package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentation.js +34 -0
- package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentationVolume.d.ts +1 -0
- package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentationVolume.js +20 -0
- package/dist/esm/utilities/segmentation/getSegmentLargestBidirectional.d.ts +5 -0
- package/dist/esm/utilities/segmentation/getSegmentLargestBidirectional.js +54 -0
- package/dist/esm/utilities/segmentation/getStatistics.js +50 -108
- package/dist/esm/utilities/segmentation/index.d.ts +5 -1
- package/dist/esm/utilities/segmentation/index.js +5 -1
- package/dist/esm/utilities/segmentation/isLineInSegment.d.ts +13 -1
- package/dist/esm/utilities/segmentation/isLineInSegment.js +20 -12
- package/dist/esm/utilities/segmentation/segmentContourAction.js +1 -0
- package/dist/esm/utilities/segmentation/utilsForWorker.d.ts +38 -0
- package/dist/esm/utilities/segmentation/utilsForWorker.js +125 -0
- package/dist/esm/workers/computeWorker.js +336 -38
- package/package.json +4 -4
|
@@ -1,53 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import VolumetricCalculator from './VolumetricCalculator';
|
|
4
|
-
import { getStrategyData } from '../../tools/segmentation/strategies/utils/getStrategyData';
|
|
1
|
+
import { utilities, getWebWorkerManager } from '@cornerstonejs/core';
|
|
2
|
+
import { triggerWorkerProgress, getSegmentationDataForWorker, prepareVolumeStrategyDataForWorker, prepareStackDataForWorker, getImageReferenceInfo, } from './utilsForWorker';
|
|
5
3
|
import { getPixelValueUnitsImageId } from '../getPixelValueUnits';
|
|
6
|
-
import
|
|
7
|
-
import ensureImageVolume from '../../tools/segmentation/strategies/compositions/ensureImageVolume';
|
|
8
|
-
import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
|
|
9
|
-
import { registerComputeWorker } from '../registerComputeWorker';
|
|
4
|
+
import VolumetricCalculator from './VolumetricCalculator';
|
|
10
5
|
import { WorkerTypes } from '../../enums';
|
|
6
|
+
import { registerComputeWorker } from '../registerComputeWorker';
|
|
11
7
|
const radiusForVol1 = Math.pow((3 * 1000) / (4 * Math.PI), 1 / 3);
|
|
12
|
-
const workerManager = getWebWorkerManager();
|
|
13
|
-
const triggerWorkerProgress = (eventTarget, progress) => {
|
|
14
|
-
triggerEvent(eventTarget, Enums.Events.WEB_WORKER_PROGRESS, {
|
|
15
|
-
progress,
|
|
16
|
-
type: WorkerTypes.COMPUTE_STATISTICS,
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
8
|
async function getStatistics({ segmentationId, segmentIndices, mode = 'collective', }) {
|
|
20
9
|
registerComputeWorker();
|
|
21
|
-
triggerWorkerProgress(
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const { Labelmap } = representationData;
|
|
25
|
-
if (!Labelmap) {
|
|
26
|
-
console.debug('No labelmap found for segmentation', segmentationId);
|
|
10
|
+
triggerWorkerProgress(WorkerTypes.COMPUTE_STATISTICS, 0);
|
|
11
|
+
const segData = getSegmentationDataForWorker(segmentationId, segmentIndices);
|
|
12
|
+
if (!segData) {
|
|
27
13
|
return;
|
|
28
14
|
}
|
|
29
|
-
const segVolumeId =
|
|
30
|
-
const segImageIds = Labelmap.imageIds;
|
|
31
|
-
const operationData = {
|
|
32
|
-
segmentationId,
|
|
33
|
-
volumeId: segVolumeId,
|
|
34
|
-
imageIds: segImageIds,
|
|
35
|
-
};
|
|
36
|
-
let reconstructableVolume = false;
|
|
37
|
-
if (segImageIds) {
|
|
38
|
-
const refImageIds = segImageIds.map((imageId) => {
|
|
39
|
-
const image = cache.getImage(imageId);
|
|
40
|
-
return image.referencedImageId;
|
|
41
|
-
});
|
|
42
|
-
reconstructableVolume = utilities.isValidVolume(refImageIds);
|
|
43
|
-
}
|
|
44
|
-
let indices = segmentIndices;
|
|
45
|
-
if (!indices) {
|
|
46
|
-
indices = [getActiveSegmentIndex(segmentationId)];
|
|
47
|
-
}
|
|
48
|
-
else if (!Array.isArray(indices)) {
|
|
49
|
-
indices = [indices, 255];
|
|
50
|
-
}
|
|
15
|
+
const { operationData, segVolumeId, segImageIds, reconstructableVolume, indices, } = segData;
|
|
51
16
|
const { refImageId, modalityUnitOptions } = getImageReferenceInfo(segVolumeId, segImageIds);
|
|
52
17
|
const unit = getPixelValueUnitsImageId(refImageId, modalityUnitOptions);
|
|
53
18
|
const stats = reconstructableVolume
|
|
@@ -66,13 +31,7 @@ async function getStatistics({ segmentationId, segmentIndices, mode = 'collectiv
|
|
|
66
31
|
return stats;
|
|
67
32
|
}
|
|
68
33
|
async function calculateVolumeStatistics({ operationData, indices, unit, mode, }) {
|
|
69
|
-
const strategyData =
|
|
70
|
-
operationData,
|
|
71
|
-
strategy: {
|
|
72
|
-
ensureSegmentationVolumeFor3DManipulation: ensureSegmentationVolume.ensureSegmentationVolumeFor3DManipulation,
|
|
73
|
-
ensureImageVolumeFor3DManipulation: ensureImageVolume.ensureImageVolumeFor3DManipulation,
|
|
74
|
-
},
|
|
75
|
-
});
|
|
34
|
+
const strategyData = prepareVolumeStrategyDataForWorker(operationData);
|
|
76
35
|
const { segmentationVoxelManager, imageVoxelManager, segmentationImageData, imageData, } = strategyData;
|
|
77
36
|
const spacing = segmentationImageData.getSpacing();
|
|
78
37
|
const { boundsIJK: boundsOrig } = segmentationVoxelManager;
|
|
@@ -80,26 +39,27 @@ async function calculateVolumeStatistics({ operationData, indices, unit, mode, }
|
|
|
80
39
|
return VolumetricCalculator.getStatistics({ spacing });
|
|
81
40
|
}
|
|
82
41
|
const segmentationScalarData = segmentationVoxelManager.getCompleteScalarDataArray();
|
|
83
|
-
const imageScalarData = imageVoxelManager.getCompleteScalarDataArray();
|
|
84
42
|
const segmentationInfo = {
|
|
85
43
|
scalarData: segmentationScalarData,
|
|
86
44
|
dimensions: segmentationImageData.getDimensions(),
|
|
87
45
|
spacing: segmentationImageData.getSpacing(),
|
|
88
46
|
origin: segmentationImageData.getOrigin(),
|
|
47
|
+
direction: segmentationImageData.getDirection(),
|
|
89
48
|
};
|
|
90
49
|
const imageInfo = {
|
|
91
|
-
scalarData:
|
|
50
|
+
scalarData: imageVoxelManager.getCompleteScalarDataArray(),
|
|
92
51
|
dimensions: imageData.getDimensions(),
|
|
93
52
|
spacing: imageData.getSpacing(),
|
|
94
53
|
origin: imageData.getOrigin(),
|
|
54
|
+
direction: imageData.getDirection(),
|
|
95
55
|
};
|
|
96
|
-
const stats = await
|
|
56
|
+
const stats = await getWebWorkerManager().executeTask('compute', 'calculateSegmentsStatisticsVolume', {
|
|
97
57
|
segmentationInfo,
|
|
98
58
|
imageInfo,
|
|
99
59
|
indices,
|
|
100
60
|
mode,
|
|
101
61
|
});
|
|
102
|
-
triggerWorkerProgress(
|
|
62
|
+
triggerWorkerProgress(WorkerTypes.COMPUTE_STATISTICS, 100);
|
|
103
63
|
if (mode === 'collective') {
|
|
104
64
|
return processSegmentationStatistics({
|
|
105
65
|
stats,
|
|
@@ -123,6 +83,18 @@ async function calculateVolumeStatistics({ operationData, indices, unit, mode, }
|
|
|
123
83
|
return finalStats;
|
|
124
84
|
}
|
|
125
85
|
}
|
|
86
|
+
const updateStatsArray = (stats, newStat) => {
|
|
87
|
+
if (!stats.array) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const existingIndex = stats.array.findIndex((stat) => stat.name === newStat.name);
|
|
91
|
+
if (existingIndex !== -1) {
|
|
92
|
+
stats.array[existingIndex] = newStat;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
stats.array.push(newStat);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
126
98
|
const processSegmentationStatistics = ({ stats, unit, spacing, segmentationImageData, imageVoxelManager, }) => {
|
|
127
99
|
stats.mean.unit = unit;
|
|
128
100
|
stats.max.unit = unit;
|
|
@@ -144,42 +116,39 @@ const processSegmentationStatistics = ({ stats, unit, spacing, segmentationImage
|
|
|
144
116
|
value: mean.value,
|
|
145
117
|
unit,
|
|
146
118
|
};
|
|
119
|
+
stats.peakPoint = {
|
|
120
|
+
name: 'peakLPS',
|
|
121
|
+
label: 'Peak SUV Point',
|
|
122
|
+
value: testMax.pointLPS ? [...testMax.pointLPS] : null,
|
|
123
|
+
unit: null,
|
|
124
|
+
};
|
|
125
|
+
updateStatsArray(stats, stats.peakValue);
|
|
126
|
+
updateStatsArray(stats, stats.peakPoint);
|
|
147
127
|
}
|
|
148
128
|
}
|
|
129
|
+
if (stats.volume && stats.mean) {
|
|
130
|
+
const mtv = stats.volume.value;
|
|
131
|
+
const suvMean = stats.mean.value;
|
|
132
|
+
stats.lesionGlycolysis = {
|
|
133
|
+
name: 'lesionGlycolysis',
|
|
134
|
+
label: 'Lesion Glycolysis',
|
|
135
|
+
value: mtv * suvMean,
|
|
136
|
+
unit: `${stats.volume.unit}·${unit}`,
|
|
137
|
+
};
|
|
138
|
+
updateStatsArray(stats, stats.lesionGlycolysis);
|
|
139
|
+
}
|
|
149
140
|
return stats;
|
|
150
141
|
};
|
|
151
142
|
async function calculateStackStatistics({ segImageIds, indices, unit, mode }) {
|
|
152
|
-
triggerWorkerProgress(
|
|
153
|
-
const segmentationInfo =
|
|
154
|
-
const
|
|
155
|
-
for (const segImageId of segImageIds) {
|
|
156
|
-
const segImage = cache.getImage(segImageId);
|
|
157
|
-
const segPixelData = segImage.getPixelData();
|
|
158
|
-
const segVoxelManager = segImage.voxelManager;
|
|
159
|
-
const segSpacing = [segImage.rowPixelSpacing, segImage.columnPixelSpacing];
|
|
160
|
-
const refImageId = segImage.referencedImageId;
|
|
161
|
-
const refImage = cache.getImage(refImageId);
|
|
162
|
-
const refPixelData = refImage.getPixelData();
|
|
163
|
-
const refVoxelManager = refImage.voxelManager;
|
|
164
|
-
const refSpacing = [refImage.rowPixelSpacing, refImage.columnPixelSpacing];
|
|
165
|
-
segmentationInfo.push({
|
|
166
|
-
scalarData: segPixelData,
|
|
167
|
-
dimensions: segVoxelManager.dimensions,
|
|
168
|
-
spacing: segSpacing,
|
|
169
|
-
});
|
|
170
|
-
imageInfo.push({
|
|
171
|
-
scalarData: refPixelData,
|
|
172
|
-
dimensions: refVoxelManager.dimensions,
|
|
173
|
-
spacing: refSpacing,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
const stats = await workerManager.executeTask('compute', 'calculateSegmentsStatisticsStack', {
|
|
143
|
+
triggerWorkerProgress(WorkerTypes.COMPUTE_STATISTICS, 0);
|
|
144
|
+
const { segmentationInfo, imageInfo } = prepareStackDataForWorker(segImageIds);
|
|
145
|
+
const stats = await getWebWorkerManager().executeTask('compute', 'calculateSegmentsStatisticsStack', {
|
|
177
146
|
segmentationInfo,
|
|
178
147
|
imageInfo,
|
|
179
148
|
indices,
|
|
180
149
|
mode,
|
|
181
150
|
});
|
|
182
|
-
triggerWorkerProgress(
|
|
151
|
+
triggerWorkerProgress(WorkerTypes.COMPUTE_STATISTICS, 100);
|
|
183
152
|
const spacing = segmentationInfo[0].spacing;
|
|
184
153
|
const segmentationImageData = segmentationInfo[0];
|
|
185
154
|
const imageVoxelManager = imageInfo[0].voxelManager;
|
|
@@ -207,7 +176,7 @@ async function calculateStackStatistics({ segImageIds, indices, unit, mode }) {
|
|
|
207
176
|
}
|
|
208
177
|
}
|
|
209
178
|
function getSphereStats(testMax, radiusIJK, segData, imageVoxels, spacing) {
|
|
210
|
-
const { pointIJK: centerIJK } = testMax;
|
|
179
|
+
const { pointIJK: centerIJK, pointLPS: centerLPS } = testMax;
|
|
211
180
|
if (!centerIJK) {
|
|
212
181
|
return;
|
|
213
182
|
}
|
|
@@ -237,31 +206,4 @@ function getSphereStats(testMax, radiusIJK, segData, imageVoxels, spacing) {
|
|
|
237
206
|
});
|
|
238
207
|
return VolumetricCalculator.getStatistics({ spacing });
|
|
239
208
|
}
|
|
240
|
-
function getImageReferenceInfo(segVolumeId, segImageIds) {
|
|
241
|
-
let refImageId;
|
|
242
|
-
let modalityUnitOptions;
|
|
243
|
-
if (segVolumeId) {
|
|
244
|
-
const segmentationVolume = cache.getVolume(segVolumeId);
|
|
245
|
-
const referencedVolumeId = segmentationVolume.referencedVolumeId;
|
|
246
|
-
const volume = cache.getVolume(referencedVolumeId);
|
|
247
|
-
if (volume?.imageIds?.length > 0) {
|
|
248
|
-
refImageId = volume.imageIds[0];
|
|
249
|
-
}
|
|
250
|
-
modalityUnitOptions = {
|
|
251
|
-
isPreScaled: Object.keys(volume.scaling || {}).length > 0,
|
|
252
|
-
isSuvScaled: Boolean(volume.scaling?.PT),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
else if (segImageIds?.length) {
|
|
256
|
-
const segImage = cache.getImage(segImageIds[0]);
|
|
257
|
-
refImageId = segImage.referencedImageId;
|
|
258
|
-
const refImage = cache.getImage(refImageId);
|
|
259
|
-
const scalingModule = metaData.get('scalingModule', refImageId);
|
|
260
|
-
modalityUnitOptions = {
|
|
261
|
-
isPreScaled: Boolean(refImage.preScale?.scaled),
|
|
262
|
-
isSuvScaled: typeof scalingModule?.preScale?.scaled === 'number',
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
return { refImageId, modalityUnitOptions };
|
|
266
|
-
}
|
|
267
209
|
export default getStatistics;
|
|
@@ -22,8 +22,12 @@ import * as growCut from './growCut';
|
|
|
22
22
|
import * as LabelmapMemo from './createLabelmapMemo';
|
|
23
23
|
import IslandRemoval from './islandRemoval';
|
|
24
24
|
import getOrCreateSegmentationVolume from './getOrCreateSegmentationVolume';
|
|
25
|
+
import getOrCreateImageVolume from './getOrCreateImageVolume';
|
|
25
26
|
import getStatistics from './getStatistics';
|
|
26
27
|
import * as validateLabelmap from './validateLabelmap';
|
|
27
28
|
import { computeStackLabelmapFromVolume } from '../../stateManagement/segmentation/helpers/computeStackLabelmapFromVolume';
|
|
28
29
|
import { computeVolumeLabelmapFromStack } from '../../stateManagement/segmentation/helpers/computeVolumeLabelmapFromStack';
|
|
29
|
-
|
|
30
|
+
import { getReferenceVolumeForSegmentationVolume } from './getReferenceVolumeForSegmentationVolume';
|
|
31
|
+
import { getSegmentLargestBidirectional } from './getSegmentLargestBidirectional';
|
|
32
|
+
import { computeMetabolicStats } from './computeMetabolicStats';
|
|
33
|
+
export { thresholdVolumeByRange, createMergedLabelmapForIndex, createLabelmapVolumeForViewport, rectangleROIThresholdVolumeByRange, triggerSegmentationRender, triggerSegmentationRenderBySegmentationId, floodFill, getBrushSizeForToolGroup, setBrushSizeForToolGroup, getBrushThresholdForToolGroup, setBrushThresholdForToolGroup, VolumetricCalculator, SegmentStatsCalculator, thresholdSegmentationByRange, contourAndFindLargestBidirectional, createBidirectionalToolData, segmentContourAction, invalidateBrushCursor, getUniqueSegmentIndices, getSegmentIndexAtWorldPoint, getSegmentIndexAtLabelmapBorder, getHoveredContourSegmentationAnnotation, getBrushToolInstances, growCut, LabelmapMemo, IslandRemoval, getOrCreateSegmentationVolume, getOrCreateImageVolume, getStatistics, validateLabelmap, computeStackLabelmapFromVolume, computeVolumeLabelmapFromStack, getReferenceVolumeForSegmentationVolume, getSegmentLargestBidirectional, computeMetabolicStats, };
|
|
@@ -22,8 +22,12 @@ import * as growCut from './growCut';
|
|
|
22
22
|
import * as LabelmapMemo from './createLabelmapMemo';
|
|
23
23
|
import IslandRemoval from './islandRemoval';
|
|
24
24
|
import getOrCreateSegmentationVolume from './getOrCreateSegmentationVolume';
|
|
25
|
+
import getOrCreateImageVolume from './getOrCreateImageVolume';
|
|
25
26
|
import getStatistics from './getStatistics';
|
|
26
27
|
import * as validateLabelmap from './validateLabelmap';
|
|
27
28
|
import { computeStackLabelmapFromVolume } from '../../stateManagement/segmentation/helpers/computeStackLabelmapFromVolume';
|
|
28
29
|
import { computeVolumeLabelmapFromStack } from '../../stateManagement/segmentation/helpers/computeVolumeLabelmapFromStack';
|
|
29
|
-
|
|
30
|
+
import { getReferenceVolumeForSegmentationVolume } from './getReferenceVolumeForSegmentationVolume';
|
|
31
|
+
import { getSegmentLargestBidirectional } from './getSegmentLargestBidirectional';
|
|
32
|
+
import { computeMetabolicStats } from './computeMetabolicStats';
|
|
33
|
+
export { thresholdVolumeByRange, createMergedLabelmapForIndex, createLabelmapVolumeForViewport, rectangleROIThresholdVolumeByRange, triggerSegmentationRender, triggerSegmentationRenderBySegmentationId, floodFill, getBrushSizeForToolGroup, setBrushSizeForToolGroup, getBrushThresholdForToolGroup, setBrushThresholdForToolGroup, VolumetricCalculator, SegmentStatsCalculator, thresholdSegmentationByRange, contourAndFindLargestBidirectional, createBidirectionalToolData, segmentContourAction, invalidateBrushCursor, getUniqueSegmentIndices, getSegmentIndexAtWorldPoint, getSegmentIndexAtLabelmapBorder, getHoveredContourSegmentationAnnotation, getBrushToolInstances, growCut, LabelmapMemo, IslandRemoval, getOrCreateSegmentationVolume, getOrCreateImageVolume, getStatistics, validateLabelmap, computeStackLabelmapFromVolume, computeVolumeLabelmapFromStack, getReferenceVolumeForSegmentationVolume, getSegmentLargestBidirectional, computeMetabolicStats, };
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import type { Types } from '@cornerstonejs/core';
|
|
2
2
|
import { vec3 } from 'gl-matrix';
|
|
3
|
+
import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
3
4
|
export default function isLineInSegment(point1: Types.Point3, point2: Types.Point3, isInSegment: any): boolean;
|
|
5
|
+
declare function createIsInSegmentMetadata({ dimensions, imageData, voxelManager, segmentIndex, containedSegmentIndices, }: {
|
|
6
|
+
dimensions: number[];
|
|
7
|
+
imageData: vtkImageData;
|
|
8
|
+
voxelManager: Types.IVoxelManager<number>;
|
|
9
|
+
segmentIndex: number;
|
|
10
|
+
containedSegmentIndices?: Set<number>;
|
|
11
|
+
}): {
|
|
12
|
+
testCenter: (point1: any, point2: any) => boolean;
|
|
13
|
+
toIJK: (point: any) => vec3;
|
|
14
|
+
testIJK: (ijk: any) => boolean;
|
|
15
|
+
};
|
|
4
16
|
declare function createIsInSegment(segVolumeId: string, segmentIndex: number, containedSegmentIndices?: Set<number>): {
|
|
5
17
|
testCenter: (point1: any, point2: any) => boolean;
|
|
6
18
|
toIJK: (point: any) => vec3;
|
|
7
19
|
testIJK: (ijk: any) => boolean;
|
|
8
20
|
};
|
|
9
|
-
export { createIsInSegment, isLineInSegment };
|
|
21
|
+
export { createIsInSegment, createIsInSegmentMetadata, isLineInSegment };
|
|
@@ -19,25 +19,19 @@ export default function isLineInSegment(point1, point2, isInSegment) {
|
|
|
19
19
|
}
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
|
-
function
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
console.warn(`No volume found for ${segVolumeId}`);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const voxelManager = vol.voxelManager;
|
|
29
|
-
const width = vol.dimensions[0];
|
|
30
|
-
const pixelsPerSlice = width * vol.dimensions[1];
|
|
22
|
+
function createIsInSegmentMetadata({ dimensions, imageData, voxelManager, segmentIndex, containedSegmentIndices, }) {
|
|
23
|
+
const width = dimensions[0];
|
|
24
|
+
const pixelsPerSlice = width * dimensions[1];
|
|
31
25
|
return {
|
|
32
26
|
testCenter: (point1, point2) => {
|
|
33
27
|
const point = vec3.add(vec3.create(), point1, point2).map((it) => it / 2);
|
|
34
|
-
const ijk =
|
|
28
|
+
const ijk = imageData.worldToIndex(point).map(Math.round);
|
|
35
29
|
const [i, j, k] = ijk;
|
|
36
30
|
const index = i + j * width + k * pixelsPerSlice;
|
|
37
31
|
const value = voxelManager.getAtIndex(index);
|
|
38
32
|
return value === segmentIndex || containedSegmentIndices?.has(value);
|
|
39
33
|
},
|
|
40
|
-
toIJK: (point) =>
|
|
34
|
+
toIJK: (point) => imageData.worldToIndex(point),
|
|
41
35
|
testIJK: (ijk) => {
|
|
42
36
|
const [i, j, k] = ijk;
|
|
43
37
|
const index = Math.round(i) + Math.round(j) * width + Math.round(k) * pixelsPerSlice;
|
|
@@ -46,4 +40,18 @@ function createIsInSegment(segVolumeId, segmentIndex, containedSegmentIndices) {
|
|
|
46
40
|
},
|
|
47
41
|
};
|
|
48
42
|
}
|
|
49
|
-
|
|
43
|
+
function createIsInSegment(segVolumeId, segmentIndex, containedSegmentIndices) {
|
|
44
|
+
const vol = cache.getVolume(segVolumeId);
|
|
45
|
+
if (!vol) {
|
|
46
|
+
console.warn(`No volume found for ${segVolumeId}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
return createIsInSegmentMetadata({
|
|
50
|
+
dimensions: vol.dimensions,
|
|
51
|
+
imageData: vol.imageData,
|
|
52
|
+
voxelManager: vol.voxelManager,
|
|
53
|
+
segmentIndex,
|
|
54
|
+
containedSegmentIndices,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export { createIsInSegment, createIsInSegmentMetadata, isLineInSegment };
|
|
@@ -6,6 +6,7 @@ import BidirectionalTool from '../../tools/annotation/BidirectionalTool';
|
|
|
6
6
|
import { getSegmentations } from '../../stateManagement/segmentation/getSegmentations';
|
|
7
7
|
import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
|
|
8
8
|
export default function segmentContourAction(element, configuration) {
|
|
9
|
+
console.warn('Deprecation Alert: There is a new getSegmentLargestBidirectional function that handles volume, stack and individual segment cases properly. This function is deprecated and will be removed in a future version.');
|
|
9
10
|
const { data: configurationData } = configuration;
|
|
10
11
|
const enabledElement = getEnabledElement(element);
|
|
11
12
|
const segment = (configurationData.getSegment || defaultGetSegment)(enabledElement, configurationData);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const triggerWorkerProgress: (workerType: any, progress: any) => void;
|
|
2
|
+
export declare const getSegmentationDataForWorker: (segmentationId: any, segmentIndices: any) => {
|
|
3
|
+
operationData: {
|
|
4
|
+
segmentationId: any;
|
|
5
|
+
volumeId: string;
|
|
6
|
+
imageIds: string[];
|
|
7
|
+
};
|
|
8
|
+
segVolumeId: string;
|
|
9
|
+
segImageIds: string[];
|
|
10
|
+
reconstructableVolume: boolean;
|
|
11
|
+
indices: any;
|
|
12
|
+
};
|
|
13
|
+
export declare const prepareVolumeStrategyDataForWorker: (operationData: any) => {
|
|
14
|
+
segmentationImageData: any;
|
|
15
|
+
segmentationScalarData: any;
|
|
16
|
+
imageScalarData: any;
|
|
17
|
+
segmentationVoxelManager: any;
|
|
18
|
+
imageVoxelManager: any;
|
|
19
|
+
imageData: any;
|
|
20
|
+
};
|
|
21
|
+
export declare const prepareImageInfo: (imageVoxelManager: any, imageData: any) => {
|
|
22
|
+
scalarData: any;
|
|
23
|
+
dimensions: any;
|
|
24
|
+
spacing: any;
|
|
25
|
+
origin: any;
|
|
26
|
+
direction: any;
|
|
27
|
+
};
|
|
28
|
+
export declare const prepareStackDataForWorker: (segImageIds: any) => {
|
|
29
|
+
segmentationInfo: any[];
|
|
30
|
+
imageInfo: any[];
|
|
31
|
+
};
|
|
32
|
+
export declare const getImageReferenceInfo: (segVolumeId: any, segImageIds: any) => {
|
|
33
|
+
refImageId: any;
|
|
34
|
+
modalityUnitOptions: {
|
|
35
|
+
isPreScaled: boolean;
|
|
36
|
+
isSuvScaled: boolean;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { cache, utilities, eventTarget, Enums, triggerEvent, metaData, } from '@cornerstonejs/core';
|
|
2
|
+
import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
|
|
3
|
+
import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
|
|
4
|
+
import { getStrategyData } from '../../tools/segmentation/strategies/utils/getStrategyData';
|
|
5
|
+
import ensureSegmentationVolume from '../../tools/segmentation/strategies/compositions/ensureSegmentationVolume';
|
|
6
|
+
import ensureImageVolume from '../../tools/segmentation/strategies/compositions/ensureImageVolume';
|
|
7
|
+
export const triggerWorkerProgress = (workerType, progress) => {
|
|
8
|
+
triggerEvent(eventTarget, Enums.Events.WEB_WORKER_PROGRESS, {
|
|
9
|
+
progress,
|
|
10
|
+
type: workerType,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
export const getSegmentationDataForWorker = (segmentationId, segmentIndices) => {
|
|
14
|
+
const segmentation = getSegmentation(segmentationId);
|
|
15
|
+
const { representationData } = segmentation;
|
|
16
|
+
const { Labelmap } = representationData;
|
|
17
|
+
if (!Labelmap) {
|
|
18
|
+
console.debug('No labelmap found for segmentation', segmentationId);
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const segVolumeId = Labelmap.volumeId;
|
|
22
|
+
const segImageIds = Labelmap.imageIds;
|
|
23
|
+
const operationData = {
|
|
24
|
+
segmentationId,
|
|
25
|
+
volumeId: segVolumeId,
|
|
26
|
+
imageIds: segImageIds,
|
|
27
|
+
};
|
|
28
|
+
let reconstructableVolume = false;
|
|
29
|
+
if (segImageIds) {
|
|
30
|
+
const refImageIds = segImageIds.map((imageId) => {
|
|
31
|
+
const image = cache.getImage(imageId);
|
|
32
|
+
return image.referencedImageId;
|
|
33
|
+
});
|
|
34
|
+
reconstructableVolume = utilities.isValidVolume(refImageIds);
|
|
35
|
+
}
|
|
36
|
+
let indices = segmentIndices;
|
|
37
|
+
if (!indices) {
|
|
38
|
+
indices = [getActiveSegmentIndex(segmentationId)];
|
|
39
|
+
}
|
|
40
|
+
else if (!Array.isArray(indices)) {
|
|
41
|
+
indices = [indices, 255];
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
operationData,
|
|
45
|
+
segVolumeId,
|
|
46
|
+
segImageIds,
|
|
47
|
+
reconstructableVolume,
|
|
48
|
+
indices,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export const prepareVolumeStrategyDataForWorker = (operationData) => {
|
|
52
|
+
return getStrategyData({
|
|
53
|
+
operationData,
|
|
54
|
+
strategy: {
|
|
55
|
+
ensureSegmentationVolumeFor3DManipulation: ensureSegmentationVolume.ensureSegmentationVolumeFor3DManipulation,
|
|
56
|
+
ensureImageVolumeFor3DManipulation: ensureImageVolume.ensureImageVolumeFor3DManipulation,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
export const prepareImageInfo = (imageVoxelManager, imageData) => {
|
|
61
|
+
const imageScalarData = imageVoxelManager.getCompleteScalarDataArray();
|
|
62
|
+
return {
|
|
63
|
+
scalarData: imageScalarData,
|
|
64
|
+
dimensions: imageData.getDimensions(),
|
|
65
|
+
spacing: imageData.getSpacing(),
|
|
66
|
+
origin: imageData.getOrigin(),
|
|
67
|
+
direction: imageData.getDirection(),
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
export const prepareStackDataForWorker = (segImageIds) => {
|
|
71
|
+
const segmentationInfo = [];
|
|
72
|
+
const imageInfo = [];
|
|
73
|
+
for (const segImageId of segImageIds) {
|
|
74
|
+
const segImage = cache.getImage(segImageId);
|
|
75
|
+
const segPixelData = segImage.getPixelData();
|
|
76
|
+
const { origin, direction, spacing, dimensions } = utilities.getImageDataMetadata(segImage);
|
|
77
|
+
segmentationInfo.push({
|
|
78
|
+
scalarData: segPixelData,
|
|
79
|
+
dimensions,
|
|
80
|
+
spacing,
|
|
81
|
+
origin,
|
|
82
|
+
direction,
|
|
83
|
+
});
|
|
84
|
+
const refImageId = segImage.referencedImageId;
|
|
85
|
+
if (refImageId) {
|
|
86
|
+
const refImage = cache.getImage(refImageId);
|
|
87
|
+
const refPixelData = refImage.getPixelData();
|
|
88
|
+
const refVoxelManager = refImage.voxelManager;
|
|
89
|
+
const refSpacing = [
|
|
90
|
+
refImage.rowPixelSpacing,
|
|
91
|
+
refImage.columnPixelSpacing,
|
|
92
|
+
];
|
|
93
|
+
imageInfo.push({
|
|
94
|
+
scalarData: refPixelData,
|
|
95
|
+
dimensions: refVoxelManager
|
|
96
|
+
? refVoxelManager.dimensions
|
|
97
|
+
: [refImage.columns, refImage.rows, 1],
|
|
98
|
+
spacing: refSpacing,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { segmentationInfo, imageInfo };
|
|
103
|
+
};
|
|
104
|
+
export const getImageReferenceInfo = (segVolumeId, segImageIds) => {
|
|
105
|
+
let refImageId;
|
|
106
|
+
if (segVolumeId) {
|
|
107
|
+
const segmentationVolume = cache.getVolume(segVolumeId);
|
|
108
|
+
const imageIds = segmentationVolume.imageIds;
|
|
109
|
+
const cachedImage = cache.getImage(imageIds[0]);
|
|
110
|
+
if (cachedImage) {
|
|
111
|
+
refImageId = cachedImage.referencedImageId;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (segImageIds?.length) {
|
|
115
|
+
const segImage = cache.getImage(segImageIds[0]);
|
|
116
|
+
refImageId = segImage.referencedImageId;
|
|
117
|
+
}
|
|
118
|
+
const refImage = cache.getImage(refImageId);
|
|
119
|
+
const scalingModule = metaData.get('scalingModule', refImageId);
|
|
120
|
+
const modalityUnitOptions = {
|
|
121
|
+
isPreScaled: Boolean(refImage.preScale?.scaled),
|
|
122
|
+
isSuvScaled: typeof scalingModule?.suvbw === 'number',
|
|
123
|
+
};
|
|
124
|
+
return { refImageId, modalityUnitOptions };
|
|
125
|
+
};
|