@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
@@ -13,6 +13,9 @@ function createBasicStatsState(storePointData) {
13
13
  minLPS: null,
14
14
  runMean: [0],
15
15
  m2: [0],
16
+ m3: [0],
17
+ m4: [0],
18
+ allValues: [[]],
16
19
  pointsInShape: storePointData ? PointsManager.create3(1024) : null,
17
20
  };
18
21
  }
@@ -25,6 +28,9 @@ function basicStatsCallback(state, newValue, pointLPS = null, pointIJK = null) {
25
28
  state.sum.push(state.sum[0], state.sum[0]);
26
29
  state.runMean.push(0, 0);
27
30
  state.m2.push(state.m2[0], state.m2[0]);
31
+ state.m3.push(state.m3[0], state.m3[0]);
32
+ state.m4.push(state.m4[0], state.m4[0]);
33
+ state.allValues.push([], []);
28
34
  }
29
35
  if (state?.pointsInShape && pointLPS) {
30
36
  state.pointsInShape.push(pointLPS);
@@ -33,46 +39,82 @@ function basicStatsCallback(state, newValue, pointLPS = null, pointIJK = null) {
33
39
  state.count += 1;
34
40
  state.max.forEach((it, idx) => {
35
41
  const value = newArray[idx];
42
+ state.allValues[idx].push(value);
43
+ const n = state.count;
36
44
  const delta = value - state.runMean[idx];
45
+ const delta_n = delta / n;
46
+ const term1 = delta * delta_n * (n - 1);
37
47
  state.sum[idx] += value;
38
- state.runMean[idx] += delta / state.count;
39
- const delta2 = value - state.runMean[idx];
40
- state.m2[idx] += delta * delta2;
48
+ state.runMean[idx] += delta_n;
49
+ state.m4[idx] +=
50
+ term1 * delta_n * delta_n * (n * n - 3 * n + 3) +
51
+ 6 * delta_n * delta_n * state.m2[idx] -
52
+ 4 * delta_n * state.m3[idx];
53
+ state.m3[idx] += term1 * delta_n * (n - 2) - 3 * delta_n * state.m2[idx];
54
+ state.m2[idx] += term1;
41
55
  if (value < state.min[idx]) {
42
56
  state.min[idx] = value;
43
57
  if (idx === 0) {
44
- state.minIJK = pointIJK;
45
- state.minLPS = pointLPS;
58
+ state.minIJK = pointIJK ? [...pointIJK] : null;
59
+ state.minLPS = pointLPS ? [...pointLPS] : null;
46
60
  }
47
61
  }
48
62
  if (value > state.max[idx]) {
49
63
  state.max[idx] = value;
50
64
  if (idx === 0) {
51
- state.maxIJK = pointIJK;
52
- state.maxLPS = pointLPS;
65
+ state.maxIJK = pointIJK ? [...pointIJK] : null;
66
+ state.maxLPS = pointLPS ? [...pointLPS] : null;
53
67
  }
54
68
  }
55
69
  });
56
70
  }
71
+ function calculateMedian(values) {
72
+ if (values.length === 0) {
73
+ return 0;
74
+ }
75
+ const sorted = [...values].sort((a, b) => a - b);
76
+ const mid = Math.floor(sorted.length / 2);
77
+ if (sorted.length % 2 === 0) {
78
+ return (sorted[mid - 1] + sorted[mid]) / 2;
79
+ }
80
+ else {
81
+ return sorted[mid];
82
+ }
83
+ }
57
84
  function basicGetStatistics(state, unit) {
58
85
  const mean = state.sum.map((sum) => sum / state.count);
59
86
  const stdDev = state.m2.map((squaredDiffSum) => Math.sqrt(squaredDiffSum / state.count));
87
+ const skewness = state.m3.map((m3, idx) => {
88
+ const variance = state.m2[idx] / state.count;
89
+ if (variance === 0) {
90
+ return 0;
91
+ }
92
+ return m3 / (state.count * Math.pow(variance, 1.5));
93
+ });
94
+ const kurtosis = state.m4.map((m4, idx) => {
95
+ const variance = state.m2[idx] / state.count;
96
+ if (variance === 0) {
97
+ return 0;
98
+ }
99
+ return m4 / (state.count * variance * variance) - 3;
100
+ });
101
+ const median = state.allValues.map((values) => calculateMedian(values));
60
102
  const named = {
61
103
  max: {
62
104
  name: 'max',
63
105
  label: 'Max Pixel',
64
106
  value: state.max.length === 1 ? state.max[0] : state.max,
65
107
  unit,
66
- pointIJK: state.maxIJK,
67
- pointLPS: state.maxLPS,
108
+ pointIJK: state.maxIJK ? [...state.maxIJK] : null,
109
+ pointLPS: state.maxLPS ? [...state.maxLPS] : null,
68
110
  },
69
111
  min: {
70
112
  name: 'min',
71
113
  label: 'Min Pixel',
72
114
  value: state.min.length === 1 ? state.min[0] : state.min,
73
115
  unit,
74
- pointIJK: state.minIJK,
75
- pointLPS: state.minLPS,
116
+ pointIJK: state.minIJK ? [...state.minIJK] : null,
117
+ pointLPS: state.minLPS ? [...state.minLPS] : null,
76
118
  },
77
119
  mean: {
78
120
  name: 'mean',
@@ -88,14 +130,44 @@ function basicGetStatistics(state, unit) {
88
130
  },
89
131
  count: {
90
132
  name: 'count',
91
- label: 'Pixel Count',
133
+ label: 'Voxel Count',
92
134
  value: state.count,
93
135
  unit: null,
94
136
  },
137
+ median: {
138
+ name: 'median',
139
+ label: 'Median',
140
+ value: median.length === 1 ? median[0] : median,
141
+ unit,
142
+ },
143
+ skewness: {
144
+ name: 'skewness',
145
+ label: 'Skewness',
146
+ value: skewness.length === 1 ? skewness[0] : skewness,
147
+ unit: null,
148
+ },
149
+ kurtosis: {
150
+ name: 'kurtosis',
151
+ label: 'Kurtosis',
152
+ value: kurtosis.length === 1 ? kurtosis[0] : kurtosis,
153
+ unit: null,
154
+ },
155
+ maxLPS: {
156
+ name: 'maxLPS',
157
+ label: 'Max LPS',
158
+ value: state.maxLPS ? Array.from(state.maxLPS) : null,
159
+ unit: null,
160
+ },
161
+ minLPS: {
162
+ name: 'minLPS',
163
+ label: 'Min LPS',
164
+ value: state.minLPS ? Array.from(state.minLPS) : null,
165
+ unit: null,
166
+ },
95
167
  pointsInShape: state.pointsInShape,
96
168
  array: [],
97
169
  };
98
- named.array.push(named.max, named.mean, named.stdDev, named.stdDev, named.count);
170
+ named.array.push(named.min, named.max, named.mean, named.stdDev, named.median, named.skewness, named.kurtosis, named.count, named.maxLPS, named.minLPS);
99
171
  const store = state.pointsInShape !== null;
100
172
  const freshState = createBasicStatsState(store);
101
173
  state.max = freshState.max;
@@ -108,6 +180,9 @@ function basicGetStatistics(state, unit) {
108
180
  state.minLPS = freshState.minLPS;
109
181
  state.runMean = freshState.runMean;
110
182
  state.m2 = freshState.m2;
183
+ state.m3 = freshState.m3;
184
+ state.m4 = freshState.m4;
185
+ state.allValues = freshState.allValues;
111
186
  state.pointsInShape = freshState.pointsInShape;
112
187
  return named;
113
188
  }
@@ -1,4 +1,5 @@
1
1
  import { getWebWorkerManager } from '@cornerstonejs/core';
2
+ import { getConfig } from '../config';
2
3
  let registered = false;
3
4
  export function registerComputeWorker() {
4
5
  if (registered) {
@@ -12,9 +13,11 @@ export function registerComputeWorker() {
12
13
  });
13
14
  };
14
15
  const workerManager = getWebWorkerManager();
16
+ const config = getConfig();
17
+ const computeWorkerConfig = config.computeWorker;
15
18
  const options = {
16
19
  maxWorkerInstances: 1,
17
- autoTerminateOnIdle: {
20
+ autoTerminateOnIdle: computeWorkerConfig?.autoTerminateOnIdle ?? {
18
21
  enabled: true,
19
22
  idleTimeThreshold: 2000,
20
23
  },
@@ -14,13 +14,22 @@ function volumetricStatsCallback(state, data) {
14
14
  (length >= TEST_MAX_LOCATIONS && value < maxIJKs[0].value)) {
15
15
  return;
16
16
  }
17
+ const dataCopy = {
18
+ value: data.value,
19
+ pointLPS: data.pointLPS
20
+ ? [data.pointLPS[0], data.pointLPS[1], data.pointLPS[2]]
21
+ : undefined,
22
+ pointIJK: data.pointIJK
23
+ ? [data.pointIJK[0], data.pointIJK[1], data.pointIJK[2]]
24
+ : undefined,
25
+ };
17
26
  if (!length || value >= maxIJKs[length - 1].value) {
18
- maxIJKs.push(data);
27
+ maxIJKs.push(dataCopy);
19
28
  }
20
29
  else {
21
30
  for (let i = 0; i < length; i++) {
22
31
  if (value <= maxIJKs[i].value) {
23
- maxIJKs.splice(i, 0, data);
32
+ maxIJKs.splice(i, 0, dataCopy);
24
33
  break;
25
34
  }
26
35
  }
@@ -42,6 +51,7 @@ function volumetricGetStatistics(state, stats, options) {
42
51
  : stats.count.value * volumeScale,
43
52
  unit: volumeUnit,
44
53
  name: 'volume',
54
+ label: 'Volume',
45
55
  };
46
56
  stats.maxIJKs = state.maxIJKs.filter((entry) => entry.pointIJK !== undefined);
47
57
  stats.array.push(stats.volume);
@@ -0,0 +1,8 @@
1
+ import type { NamedStatistics } from '../../types';
2
+ declare function computeMetabolicStats({ segmentationIds, segmentIndex, }: {
3
+ segmentationIds: string[];
4
+ segmentIndex: number;
5
+ }): Promise<NamedStatistics | {
6
+ [segmentIndex: number]: NamedStatistics;
7
+ }>;
8
+ export { computeMetabolicStats };
@@ -0,0 +1,58 @@
1
+ import { utilities, getWebWorkerManager } from '@cornerstonejs/core';
2
+ import { triggerWorkerProgress } from './utilsForWorker';
3
+ import { WorkerTypes } from '../../enums';
4
+ import { registerComputeWorker } from '../registerComputeWorker';
5
+ import createMergedLabelmapForIndex from './createMergedLabelmapForIndex';
6
+ import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
7
+ import getOrCreateSegmentationVolume from './getOrCreateSegmentationVolume';
8
+ import { getReferenceVolumeForSegmentation } from './getReferenceVolumeForSegmentation';
9
+ async function computeMetabolicStats({ segmentationIds, segmentIndex, }) {
10
+ registerComputeWorker();
11
+ triggerWorkerProgress(WorkerTypes.COMPUTE_STATISTICS, 0);
12
+ const segmentation = getSegmentation(segmentationIds[0]);
13
+ const { imageIds: segImageIds } = segmentation.representationData
14
+ .Labelmap;
15
+ const isValidVolume = utilities.isValidVolume(segImageIds);
16
+ if (!isValidVolume) {
17
+ throw new Error('Invalid volume - TMTV cannot be calculated');
18
+ }
19
+ const stats = await calculateForVolume({
20
+ segmentationIds,
21
+ segmentIndex,
22
+ });
23
+ return stats;
24
+ }
25
+ async function calculateForVolume({ segmentationIds, segmentIndex }) {
26
+ const labelmapVolumes = segmentationIds.map((id) => {
27
+ return getOrCreateSegmentationVolume(id);
28
+ });
29
+ const mergedLabelmap = createMergedLabelmapForIndex(labelmapVolumes, segmentIndex);
30
+ if (!mergedLabelmap) {
31
+ throw new Error('Invalid volume - TMTV cannot be calculated');
32
+ }
33
+ const { imageData, dimensions, direction, origin, voxelManager } = mergedLabelmap;
34
+ const spacing = imageData.getSpacing();
35
+ const segmentationScalarData = voxelManager.getCompleteScalarDataArray();
36
+ const segmentationInfo = {
37
+ scalarData: segmentationScalarData,
38
+ dimensions,
39
+ spacing,
40
+ origin,
41
+ direction,
42
+ };
43
+ const referenceVolume = getReferenceVolumeForSegmentation(segmentationIds[0]);
44
+ const imageInfo = {
45
+ dimensions: referenceVolume.dimensions,
46
+ spacing: referenceVolume.spacing,
47
+ origin: referenceVolume.origin,
48
+ direction: referenceVolume.direction,
49
+ scalarData: referenceVolume.voxelManager.getCompleteScalarDataArray(),
50
+ };
51
+ const stats = await getWebWorkerManager().executeTask('compute', 'computeMetabolicStats', {
52
+ segmentationInfo,
53
+ imageInfo,
54
+ });
55
+ triggerWorkerProgress(WorkerTypes.COMPUTE_STATISTICS, 100);
56
+ return stats;
57
+ }
58
+ export { computeMetabolicStats };
@@ -1,7 +1,6 @@
1
1
  import { generateContourSetsFromLabelmap } from '../contours';
2
- import SegmentationRepresentations from '../../enums/SegmentationRepresentations';
3
2
  import findLargestBidirectional from './findLargestBidirectional';
4
- const { Labelmap } = SegmentationRepresentations;
3
+ import getOrCreateSegmentationVolume from './getOrCreateSegmentationVolume';
5
4
  export default function contourAndFindLargestBidirectional(segmentation) {
6
5
  const contours = generateContourSetsFromLabelmap({
7
6
  segmentations: segmentation,
@@ -9,15 +8,18 @@ export default function contourAndFindLargestBidirectional(segmentation) {
9
8
  if (!contours?.length || !contours[0].sliceContours.length) {
10
9
  return;
11
10
  }
12
- const { representationData, segments = [
11
+ const { segments = [
13
12
  null,
14
13
  { label: 'Unspecified', color: null, containedSegmentIndices: null },
15
14
  ], } = segmentation;
16
- const { volumeId: segVolumeId } = representationData[Labelmap];
15
+ const vol = getOrCreateSegmentationVolume(segmentation.segmentationId);
16
+ if (!vol) {
17
+ return;
18
+ }
17
19
  const segmentIndex = segments.findIndex((it) => !!it);
18
20
  if (segmentIndex === -1) {
19
21
  return;
20
22
  }
21
23
  segments[segmentIndex].segmentIndex = segmentIndex;
22
- return findLargestBidirectional(contours[0], segVolumeId, segments[segmentIndex]);
24
+ return findLargestBidirectional(contours[0], vol.volumeId, segments[segmentIndex]);
23
25
  }
@@ -1,4 +1,4 @@
1
- import { volumeLoader, utilities as csUtils } from '@cornerstonejs/core';
1
+ import { volumeLoader, utilities as csUtils, cache } from '@cornerstonejs/core';
2
2
  function createMergedLabelmapForIndex(labelmaps, segmentIndex = 1, volumeId = 'mergedLabelmap') {
3
3
  labelmaps.forEach(({ direction, dimensions, origin, spacing }) => {
4
4
  if (!csUtils.isEqual(dimensions, labelmaps[0].dimensions) ||
@@ -28,7 +28,15 @@ function createMergedLabelmapForIndex(labelmaps, segmentIndex = 1, volumeId = 'm
28
28
  direction: labelmap.direction,
29
29
  dimensions: labelmap.dimensions,
30
30
  };
31
- const mergedVolume = volumeLoader.createLocalVolume(volumeId, options);
31
+ const cachedVolume = cache.getVolume(volumeId);
32
+ let mergedVolume;
33
+ if (cachedVolume) {
34
+ mergedVolume = cachedVolume;
35
+ mergedVolume.voxelManager.setCompleteScalarDataArray(outputData);
36
+ }
37
+ else {
38
+ mergedVolume = volumeLoader.createLocalVolume(volumeId, options);
39
+ }
32
40
  return mergedVolume;
33
41
  }
34
42
  export default createMergedLabelmapForIndex;
@@ -1 +1,6 @@
1
+ import type { BidirectionalData } from './createBidirectionalToolData';
1
2
  export default function findLargestBidirectional(contours: any, segVolumeId: string, segment: any): any;
3
+ export declare function createBidirectionalForSlice(sliceContour: any, isInSegment: any, currentMax?: {
4
+ maxMajor: number;
5
+ maxMinor: number;
6
+ }): BidirectionalData;
@@ -18,7 +18,7 @@ export default function findLargestBidirectional(contours, segVolumeId, segment)
18
18
  }
19
19
  return maxBidirectional;
20
20
  }
21
- function createBidirectionalForSlice(sliceContour, isInSegment, currentMax = { maxMajor: 0, maxMinor: 0 }) {
21
+ export function createBidirectionalForSlice(sliceContour, isInSegment, currentMax = { maxMajor: 0, maxMinor: 0 }) {
22
22
  const { points } = sliceContour.polyData;
23
23
  const { maxMinor: currentMaxMinor, maxMajor: currentMaxMajor } = currentMax;
24
24
  let maxMajor = currentMaxMajor * currentMaxMajor;
@@ -0,0 +1,3 @@
1
+ import { type Types } from '@cornerstonejs/core';
2
+ declare function getOrCreateImageVolume(referencedImageIds: string[]): Types.IImageVolume | undefined;
3
+ export default getOrCreateImageVolume;
@@ -0,0 +1,18 @@
1
+ import { cache, volumeLoader, utilities as csUtils, } from '@cornerstonejs/core';
2
+ function getOrCreateImageVolume(referencedImageIds) {
3
+ if (!referencedImageIds || referencedImageIds.length <= 1) {
4
+ return;
5
+ }
6
+ const isValidVolume = csUtils.isValidVolume(referencedImageIds);
7
+ if (!isValidVolume) {
8
+ return;
9
+ }
10
+ const volumeId = cache.generateVolumeId(referencedImageIds);
11
+ let imageVolume = cache.getVolume(volumeId);
12
+ if (imageVolume) {
13
+ return imageVolume;
14
+ }
15
+ imageVolume = volumeLoader.createAndCacheVolumeFromImagesSync(volumeId, referencedImageIds);
16
+ return imageVolume;
17
+ }
18
+ export default getOrCreateImageVolume;
@@ -1,2 +1,3 @@
1
- declare function getOrCreateSegmentationVolume(segmentationId: any): any;
1
+ import { type Types } from '@cornerstonejs/core';
2
+ declare function getOrCreateSegmentationVolume(segmentationId: any): Types.IImageVolume | undefined;
2
3
  export default getOrCreateSegmentationVolume;
@@ -1,4 +1,4 @@
1
- import { cache, volumeLoader } from '@cornerstonejs/core';
1
+ import { cache, volumeLoader, utilities, } from '@cornerstonejs/core';
2
2
  import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
3
3
  function getOrCreateSegmentationVolume(segmentationId) {
4
4
  const { representationData } = getSegmentation(segmentationId);
@@ -15,6 +15,10 @@ function getOrCreateSegmentationVolume(segmentationId) {
15
15
  if (!labelmapImageIds || labelmapImageIds.length === 1) {
16
16
  return;
17
17
  }
18
+ const isValidVolume = utilities.isValidVolume(labelmapImageIds);
19
+ if (!isValidVolume) {
20
+ return;
21
+ }
18
22
  segVolume = volumeLoader.createAndCacheVolumeFromImagesSync(volumeId, labelmapImageIds);
19
23
  return segVolume;
20
24
  }
@@ -0,0 +1 @@
1
+ export declare function getReferenceVolumeForSegmentation(segmentationId: string): import("@cornerstonejs/core").ImageVolume;
@@ -0,0 +1,34 @@
1
+ import { cache } from '@cornerstonejs/core';
2
+ import { getSegmentation } from '../../stateManagement/segmentation/getSegmentation';
3
+ import getOrCreateImageVolume from './getOrCreateImageVolume';
4
+ export function getReferenceVolumeForSegmentation(segmentationId) {
5
+ const segmentation = getSegmentation(segmentationId);
6
+ if (!segmentation) {
7
+ return null;
8
+ }
9
+ let referenceImageIds;
10
+ const labelmap = segmentation.representationData.Labelmap;
11
+ if ('imageIds' in labelmap) {
12
+ const { imageIds } = labelmap;
13
+ const firstImage = cache.getImage(imageIds[0]);
14
+ const volumeInfo = cache.getVolumeContainingImageId(firstImage.referencedImageId);
15
+ if (volumeInfo?.volume) {
16
+ return volumeInfo.volume;
17
+ }
18
+ referenceImageIds = imageIds.map((imageId) => cache.getImage(imageId).referencedImageId);
19
+ }
20
+ else if ('volumeId' in labelmap) {
21
+ const { volumeId, referencedVolumeId } = labelmap;
22
+ if (referencedVolumeId) {
23
+ const refVolume = cache.getVolume(referencedVolumeId);
24
+ if (refVolume) {
25
+ return refVolume;
26
+ }
27
+ }
28
+ const segVolume = cache.getVolume(volumeId);
29
+ if (segVolume) {
30
+ referenceImageIds = segVolume.imageIds.map((imageId) => cache.getImage(imageId).referencedImageId);
31
+ }
32
+ }
33
+ return getOrCreateImageVolume(referenceImageIds);
34
+ }
@@ -0,0 +1 @@
1
+ export declare function getReferenceVolumeForSegmentationVolume(segmentationVolumeId: string): any;
@@ -0,0 +1,20 @@
1
+ import { cache } from '@cornerstonejs/core';
2
+ export function getReferenceVolumeForSegmentationVolume(segmentationVolumeId) {
3
+ const segmentationVolume = cache.getVolume(segmentationVolumeId);
4
+ if (!segmentationVolume) {
5
+ return null;
6
+ }
7
+ const referencedVolumeId = segmentationVolume.referencedVolumeId;
8
+ let imageVolume;
9
+ if (referencedVolumeId) {
10
+ imageVolume = cache.getVolume(referencedVolumeId);
11
+ }
12
+ else {
13
+ const imageIds = segmentationVolume.imageIds;
14
+ const image = cache.getImage(imageIds[0]);
15
+ const referencedImageId = image.referencedImageId;
16
+ const volumeInfo = cache.getVolumeContainingImageId(referencedImageId);
17
+ imageVolume = volumeInfo?.volume;
18
+ }
19
+ return imageVolume;
20
+ }
@@ -0,0 +1,5 @@
1
+ export declare function getSegmentLargestBidirectional({ segmentationId, segmentIndices, mode, }: {
2
+ segmentationId: any;
3
+ segmentIndices: any;
4
+ mode?: string;
5
+ }): Promise<any>;
@@ -0,0 +1,54 @@
1
+ import { getWebWorkerManager } from '@cornerstonejs/core';
2
+ import { WorkerTypes } from '../../enums';
3
+ import { registerComputeWorker } from '../registerComputeWorker';
4
+ import { triggerWorkerProgress, getSegmentationDataForWorker, prepareVolumeStrategyDataForWorker, prepareStackDataForWorker, } from './utilsForWorker';
5
+ export async function getSegmentLargestBidirectional({ segmentationId, segmentIndices, mode = 'individual', }) {
6
+ registerComputeWorker();
7
+ triggerWorkerProgress(WorkerTypes.COMPUTE_LARGEST_BIDIRECTIONAL, 0);
8
+ const segData = getSegmentationDataForWorker(segmentationId, segmentIndices);
9
+ if (!segData) {
10
+ return;
11
+ }
12
+ const { operationData, segImageIds, reconstructableVolume, indices } = segData;
13
+ const bidirectionalData = reconstructableVolume
14
+ ? await calculateVolumeBidirectional({
15
+ operationData,
16
+ indices,
17
+ mode,
18
+ })
19
+ : await calculateStackBidirectional({
20
+ segImageIds,
21
+ indices,
22
+ mode,
23
+ });
24
+ triggerWorkerProgress(WorkerTypes.COMPUTE_LARGEST_BIDIRECTIONAL, 100);
25
+ return bidirectionalData;
26
+ }
27
+ async function calculateVolumeBidirectional({ operationData, indices, mode }) {
28
+ const strategyData = prepareVolumeStrategyDataForWorker(operationData);
29
+ const { segmentationVoxelManager, segmentationImageData } = strategyData;
30
+ const segmentationScalarData = segmentationVoxelManager.getCompleteScalarDataArray();
31
+ const segmentationInfo = {
32
+ scalarData: segmentationScalarData,
33
+ dimensions: segmentationImageData.getDimensions(),
34
+ spacing: segmentationImageData.getSpacing(),
35
+ origin: segmentationImageData.getOrigin(),
36
+ direction: segmentationImageData.getDirection(),
37
+ };
38
+ const bidirectionalData = await getWebWorkerManager().executeTask('compute', 'getSegmentLargestBidirectionalInternal', {
39
+ segmentationInfo,
40
+ indices,
41
+ mode,
42
+ });
43
+ return bidirectionalData;
44
+ }
45
+ async function calculateStackBidirectional({ segImageIds, indices, mode }) {
46
+ const { segmentationInfo } = prepareStackDataForWorker(segImageIds);
47
+ const bidirectionalData = await getWebWorkerManager().executeTask('compute', 'getSegmentLargestBidirectionalInternal', {
48
+ segmentationInfo,
49
+ indices,
50
+ mode,
51
+ isStack: true,
52
+ });
53
+ return bidirectionalData;
54
+ }