@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.
Files changed (45) hide show
  1. package/dist/esm/config.d.ts +7 -0
  2. package/dist/esm/enums/WorkerTypes.d.ts +2 -1
  3. package/dist/esm/enums/WorkerTypes.js +1 -0
  4. package/dist/esm/index.d.ts +2 -2
  5. package/dist/esm/index.js +2 -2
  6. package/dist/esm/tools/annotation/BidirectionalTool.d.ts +3 -0
  7. package/dist/esm/tools/annotation/BidirectionalTool.js +39 -1
  8. package/dist/esm/tools/index.d.ts +2 -1
  9. package/dist/esm/tools/index.js +2 -1
  10. package/dist/esm/tools/segmentation/SegmentBidirectionalTool.d.ts +27 -0
  11. package/dist/esm/tools/segmentation/SegmentBidirectionalTool.js +253 -0
  12. package/dist/esm/tools/segmentation/strategies/compositions/ensureImageVolume.js +5 -8
  13. package/dist/esm/tools/segmentation/strategies/utils/getStrategyData.js +5 -10
  14. package/dist/esm/types/CalculatorTypes.d.ts +21 -3
  15. package/dist/esm/types/ToolSpecificAnnotationTypes.d.ts +34 -0
  16. package/dist/esm/utilities/contours/generateContourSetsFromLabelmap.js +1 -0
  17. package/dist/esm/utilities/math/basic/BasicStatsCalculator.js +88 -13
  18. package/dist/esm/utilities/registerComputeWorker.js +4 -1
  19. package/dist/esm/utilities/segmentation/VolumetricCalculator.js +12 -2
  20. package/dist/esm/utilities/segmentation/computeMetabolicStats.d.ts +8 -0
  21. package/dist/esm/utilities/segmentation/computeMetabolicStats.js +58 -0
  22. package/dist/esm/utilities/segmentation/contourAndFindLargestBidirectional.js +7 -5
  23. package/dist/esm/utilities/segmentation/createMergedLabelmapForIndex.js +10 -2
  24. package/dist/esm/utilities/segmentation/findLargestBidirectional.d.ts +5 -0
  25. package/dist/esm/utilities/segmentation/findLargestBidirectional.js +1 -1
  26. package/dist/esm/utilities/segmentation/getOrCreateImageVolume.d.ts +3 -0
  27. package/dist/esm/utilities/segmentation/getOrCreateImageVolume.js +18 -0
  28. package/dist/esm/utilities/segmentation/getOrCreateSegmentationVolume.d.ts +2 -1
  29. package/dist/esm/utilities/segmentation/getOrCreateSegmentationVolume.js +5 -1
  30. package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentation.d.ts +1 -0
  31. package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentation.js +34 -0
  32. package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentationVolume.d.ts +1 -0
  33. package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentationVolume.js +20 -0
  34. package/dist/esm/utilities/segmentation/getSegmentLargestBidirectional.d.ts +5 -0
  35. package/dist/esm/utilities/segmentation/getSegmentLargestBidirectional.js +54 -0
  36. package/dist/esm/utilities/segmentation/getStatistics.js +50 -108
  37. package/dist/esm/utilities/segmentation/index.d.ts +5 -1
  38. package/dist/esm/utilities/segmentation/index.js +5 -1
  39. package/dist/esm/utilities/segmentation/isLineInSegment.d.ts +13 -1
  40. package/dist/esm/utilities/segmentation/isLineInSegment.js +20 -12
  41. package/dist/esm/utilities/segmentation/segmentContourAction.js +1 -0
  42. package/dist/esm/utilities/segmentation/utilsForWorker.d.ts +38 -0
  43. package/dist/esm/utilities/segmentation/utilsForWorker.js +125 -0
  44. package/dist/esm/workers/computeWorker.js +336 -38
  45. package/package.json +4 -4
@@ -1,53 +1,18 @@
1
- import { cache, utilities, getWebWorkerManager, eventTarget, Enums, triggerEvent, metaData, } from '@cornerstonejs/core';
2
- import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
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 ensureSegmentationVolume from '../../tools/segmentation/strategies/compositions/ensureSegmentationVolume';
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(eventTarget, 0);
22
- const segmentation = getSegmentation(segmentationId);
23
- const { representationData } = segmentation;
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 = Labelmap.volumeId;
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 = getStrategyData({
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: imageScalarData,
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 workerManager.executeTask('compute', 'calculateSegmentsStatisticsVolume', {
56
+ const stats = await getWebWorkerManager().executeTask('compute', 'calculateSegmentsStatisticsVolume', {
97
57
  segmentationInfo,
98
58
  imageInfo,
99
59
  indices,
100
60
  mode,
101
61
  });
102
- triggerWorkerProgress(eventTarget, 100);
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(eventTarget, 0);
153
- const segmentationInfo = [];
154
- const imageInfo = [];
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(eventTarget, 100);
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
- 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, getStatistics, validateLabelmap, computeStackLabelmapFromVolume, computeVolumeLabelmapFromStack, };
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
- 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, getStatistics, validateLabelmap, computeStackLabelmapFromVolume, computeVolumeLabelmapFromStack, };
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 createIsInSegment(segVolumeId, segmentIndex, containedSegmentIndices) {
23
- const vol = cache.getVolume(segVolumeId);
24
- if (!vol) {
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 = vol.imageData.worldToIndex(point).map(Math.round);
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) => vol.imageData.worldToIndex(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
- export { createIsInSegment, isLineInSegment };
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
+ };