@cornerstonejs/tools 5.4.11 → 5.4.13

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 (46) hide show
  1. package/dist/esm/index.d.ts +2 -2
  2. package/dist/esm/index.js +2 -2
  3. package/dist/esm/stateManagement/segmentation/helpers/isSegmentationOverlayCompatible.d.ts +3 -0
  4. package/dist/esm/stateManagement/segmentation/helpers/isSegmentationOverlayCompatible.js +87 -0
  5. package/dist/esm/stateManagement/segmentation/internalAddSegmentationRepresentation.js +5 -0
  6. package/dist/esm/stateManagement/segmentation/labelmapModel/labelmapEditTransaction.d.ts +12 -2
  7. package/dist/esm/stateManagement/segmentation/labelmapModel/labelmapEditTransaction.js +21 -1
  8. package/dist/esm/stateManagement/segmentation/labelmapModel/labelmapImageReferenceResolver.d.ts +5 -0
  9. package/dist/esm/stateManagement/segmentation/labelmapModel/labelmapImageReferenceResolver.js +47 -1
  10. package/dist/esm/stateManagement/segmentation/labelmapModel/privateLabelmap.d.ts +5 -1
  11. package/dist/esm/stateManagement/segmentation/labelmapModel/privateLabelmap.js +49 -4
  12. package/dist/esm/tools/annotation/ClickSegmentTool.d.ts +51 -0
  13. package/dist/esm/tools/annotation/ClickSegmentTool.js +748 -0
  14. package/dist/esm/tools/annotation/regionSegmentHoverCursors.d.ts +4 -0
  15. package/dist/esm/tools/annotation/regionSegmentHoverCursors.js +4 -0
  16. package/dist/esm/tools/displayTools/Labelmap/syncStackLabelmapActors.js +5 -0
  17. package/dist/esm/tools/index.d.ts +2 -1
  18. package/dist/esm/tools/index.js +2 -1
  19. package/dist/esm/tools/segmentation/BrushTool.js +7 -3
  20. package/dist/esm/tools/segmentation/strategies/BrushStrategy.js +21 -0
  21. package/dist/esm/tools/segmentation/strategies/utils/crossLayerErase.js +20 -0
  22. package/dist/esm/utilities/segmentation/commitSliceMasksToLabelmap.d.ts +12 -0
  23. package/dist/esm/utilities/segmentation/commitSliceMasksToLabelmap.js +128 -0
  24. package/dist/esm/utilities/segmentation/createEnsureSliceLoadedForVolume.d.ts +2 -0
  25. package/dist/esm/utilities/segmentation/createEnsureSliceLoadedForVolume.js +36 -0
  26. package/dist/esm/utilities/segmentation/createLabelmapMemo.d.ts +7 -0
  27. package/dist/esm/utilities/segmentation/createLabelmapMemo.js +41 -8
  28. package/dist/esm/utilities/segmentation/floodFillIslandRemoval.d.ts +20 -0
  29. package/dist/esm/utilities/segmentation/floodFillIslandRemoval.js +118 -0
  30. package/dist/esm/utilities/segmentation/floodFillSliceLazy.d.ts +33 -0
  31. package/dist/esm/utilities/segmentation/floodFillSliceLazy.js +167 -0
  32. package/dist/esm/utilities/segmentation/growCut/floodFillIntensityRangeTypes.d.ts +51 -0
  33. package/dist/esm/utilities/segmentation/growCut/floodFillIntensityRangeTypes.js +0 -0
  34. package/dist/esm/utilities/segmentation/growCut/getViewportVoiMappingForVolume.d.ts +10 -0
  35. package/dist/esm/utilities/segmentation/growCut/getViewportVoiMappingForVolume.js +21 -0
  36. package/dist/esm/utilities/segmentation/growCut/intensityRange/adaptiveRegionIntensityRange.d.ts +57 -0
  37. package/dist/esm/utilities/segmentation/growCut/intensityRange/adaptiveRegionIntensityRange.js +559 -0
  38. package/dist/esm/utilities/segmentation/growCut/neighborhoodStats.d.ts +10 -0
  39. package/dist/esm/utilities/segmentation/growCut/neighborhoodStats.js +44 -0
  40. package/dist/esm/utilities/segmentation/growCut/runFloodFillSegmentation.d.ts +61 -0
  41. package/dist/esm/utilities/segmentation/growCut/runFloodFillSegmentation.js +524 -0
  42. package/dist/esm/utilities/segmentation/islandRemoval.d.ts +3 -2
  43. package/dist/esm/utilities/segmentation/islandRemoval.js +3 -0
  44. package/dist/esm/version.d.ts +1 -1
  45. package/dist/esm/version.js +1 -1
  46. package/package.json +5 -5
@@ -0,0 +1,167 @@
1
+ export const FLOOD_SLICE_FLAG_VISITED = 1;
2
+ export async function floodFill3dSliceLazy(getter, seed, options) {
3
+ const { width: w, height: h, depth: d, equals, ensureSliceLoaded, yieldEvery = 500, planar = false, maxDeltaK, maxDeltaIJ, isCancelled, maxVoxels, shouldContinue, validateEvery = 2048, } = options;
4
+ const [sx, sy, sz] = seed;
5
+ if (sx < 0 || sx >= w || sy < 0 || sy >= h || sz < 0 || sz >= d) {
6
+ return {
7
+ sliceMasks: new Map(),
8
+ voxelCount: 0,
9
+ truncated: false,
10
+ bbox: null,
11
+ };
12
+ }
13
+ if (ensureSliceLoaded) {
14
+ await ensureSliceLoaded(sz);
15
+ }
16
+ const startNode = getter(sx, sy, sz);
17
+ if (!equals(startNode, startNode)) {
18
+ return {
19
+ sliceMasks: new Map(),
20
+ voxelCount: 0,
21
+ truncated: false,
22
+ bbox: null,
23
+ };
24
+ }
25
+ const frameSize = w * h;
26
+ const sliceMasks = new Map();
27
+ function sliceFlags(z) {
28
+ let a = sliceMasks.get(z);
29
+ if (!a) {
30
+ a = new Uint8Array(frameSize);
31
+ sliceMasks.set(z, a);
32
+ }
33
+ return a;
34
+ }
35
+ function isVisited(z, x, y) {
36
+ const a = sliceMasks.get(z);
37
+ if (!a) {
38
+ return false;
39
+ }
40
+ return (a[y * w + x] & FLOOD_SLICE_FLAG_VISITED) !== 0;
41
+ }
42
+ function setVisited(z, x, y) {
43
+ sliceFlags(z)[y * w + x] |= FLOOD_SLICE_FLAG_VISITED;
44
+ }
45
+ function pack(x, y, z) {
46
+ return z * frameSize + y * w + x;
47
+ }
48
+ function unpack(p) {
49
+ const x = p % w;
50
+ const t1 = Math.floor(p / w);
51
+ const y = t1 % h;
52
+ const z = Math.floor(t1 / h);
53
+ return [x, y, z];
54
+ }
55
+ const dirs = planar
56
+ ? [
57
+ [1, 0, 0],
58
+ [-1, 0, 0],
59
+ [0, 1, 0],
60
+ [0, -1, 0],
61
+ ]
62
+ : [
63
+ [1, 0, 0],
64
+ [-1, 0, 0],
65
+ [0, 1, 0],
66
+ [0, -1, 0],
67
+ [0, 0, 1],
68
+ [0, 0, -1],
69
+ ];
70
+ const queue = [];
71
+ let qh = 0;
72
+ queue.push(pack(sx, sy, sz));
73
+ setVisited(sz, sx, sy);
74
+ let minX = sx;
75
+ let maxX = sx;
76
+ let minY = sy;
77
+ let maxY = sy;
78
+ let minZ = sz;
79
+ let maxZ = sz;
80
+ const currentBBox = () => ({
81
+ min: [minX, minY, minZ],
82
+ max: [maxX, maxY, maxZ],
83
+ });
84
+ let steps = 0;
85
+ let truncated = false;
86
+ let nextValidateAt = validateEvery;
87
+ while (qh < queue.length) {
88
+ if (isCancelled?.()) {
89
+ break;
90
+ }
91
+ if (maxVoxels !== undefined && queue.length > maxVoxels) {
92
+ truncated = true;
93
+ break;
94
+ }
95
+ if (shouldContinue && queue.length >= nextValidateAt) {
96
+ nextValidateAt += validateEvery;
97
+ if (!shouldContinue({ voxelCount: queue.length, bbox: currentBBox() })) {
98
+ truncated = true;
99
+ break;
100
+ }
101
+ }
102
+ steps++;
103
+ if (yieldEvery > 0 && steps % yieldEvery === 0) {
104
+ await new Promise((r) => setTimeout(r, 0));
105
+ }
106
+ const p = queue[qh++];
107
+ const [x, y, z] = unpack(p);
108
+ for (let di = 0; di < dirs.length; di++) {
109
+ const nx = x + dirs[di][0];
110
+ const ny = y + dirs[di][1];
111
+ const nz = z + dirs[di][2];
112
+ if (nx < 0 || nx >= w || ny < 0 || ny >= h || nz < 0 || nz >= d) {
113
+ continue;
114
+ }
115
+ if (maxDeltaK >= 0 && Math.abs(nz - sz) > maxDeltaK) {
116
+ continue;
117
+ }
118
+ if (maxDeltaIJ >= 0 &&
119
+ (Math.abs(nx - sx) > maxDeltaIJ || Math.abs(ny - sy) > maxDeltaIJ)) {
120
+ continue;
121
+ }
122
+ if (planar && nz !== sz) {
123
+ continue;
124
+ }
125
+ if (isVisited(nz, nx, ny)) {
126
+ continue;
127
+ }
128
+ if (ensureSliceLoaded) {
129
+ await ensureSliceLoaded(nz);
130
+ if (isCancelled?.()) {
131
+ break;
132
+ }
133
+ }
134
+ const nv = getter(nx, ny, nz);
135
+ if (!equals(nv, startNode)) {
136
+ continue;
137
+ }
138
+ setVisited(nz, nx, ny);
139
+ queue.push(pack(nx, ny, nz));
140
+ if (nx < minX) {
141
+ minX = nx;
142
+ }
143
+ else if (nx > maxX) {
144
+ maxX = nx;
145
+ }
146
+ if (ny < minY) {
147
+ minY = ny;
148
+ }
149
+ else if (ny > maxY) {
150
+ maxY = ny;
151
+ }
152
+ if (nz < minZ) {
153
+ minZ = nz;
154
+ }
155
+ else if (nz > maxZ) {
156
+ maxZ = nz;
157
+ }
158
+ }
159
+ }
160
+ const voxelCount = queue.length;
161
+ return {
162
+ sliceMasks,
163
+ voxelCount,
164
+ truncated,
165
+ bbox: voxelCount > 0 ? currentBBox() : null,
166
+ };
167
+ }
@@ -0,0 +1,51 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ import type { ViewportVoiMappingForTool } from './getViewportVoiMappingForVolume.js';
3
+ export type FloodFillIntensityRangeDiagnostics = {
4
+ neighborhoodMean: number;
5
+ neighborhoodStdDev: number;
6
+ clickedVoxelValue: number;
7
+ positiveStdDevMultiplier: number;
8
+ neighborhoodRadius: number;
9
+ strategy?: string;
10
+ strategyMode?: 'triClass' | 'exactRange';
11
+ mappedBand?: {
12
+ min: number;
13
+ max: number;
14
+ };
15
+ canvasSampleCount?: number;
16
+ canvasDiskBandCenteredOnSeed?: boolean;
17
+ canvasDiskLetterboxTrimmed?: boolean;
18
+ adaptive?: {
19
+ toleranceBytes: number;
20
+ regionSizePx: number;
21
+ regionAreaMm2?: number;
22
+ backgroundByte: number;
23
+ seedByte: number;
24
+ polarity?: number;
25
+ seedSnapped: boolean;
26
+ windowSize: [number, number];
27
+ growthCurve?: Array<[number, number]>;
28
+ explosionLevel?: number;
29
+ borderTouchLevel?: number;
30
+ };
31
+ };
32
+ export type FloodFillIntensityRangeResult = {
33
+ min: number;
34
+ max: number;
35
+ ijkStart: Types.Point3;
36
+ diagnostics: FloodFillIntensityRangeDiagnostics;
37
+ };
38
+ export type FloodFillIntensityRangeOptions = {
39
+ positiveStdDevMultiplier?: number;
40
+ initialNeighborhoodRadius?: number;
41
+ voiMapping?: ViewportVoiMappingForTool | null;
42
+ viewport?: Types.IViewport;
43
+ element?: HTMLDivElement;
44
+ referencedVolumeId?: string;
45
+ canvasPoint?: {
46
+ x: number;
47
+ y: number;
48
+ };
49
+ canvasDiskRadiusPx?: number;
50
+ };
51
+ export type GetFloodFillIntensityRange = (referencedVolume: Types.IImageVolume, worldPosition: Types.Point3, options?: FloodFillIntensityRangeOptions) => FloodFillIntensityRangeResult | null;
@@ -0,0 +1,10 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ export type ViewportVoiMappingForTool = {
3
+ voiRange: {
4
+ lower: number;
5
+ upper: number;
6
+ };
7
+ VOILUTFunction?: string;
8
+ invert?: boolean;
9
+ };
10
+ export declare function getViewportVoiMappingForVolume(viewport: Types.IViewport, volumeId?: string): ViewportVoiMappingForTool | null;
@@ -0,0 +1,21 @@
1
+ export function getViewportVoiMappingForVolume(viewport, volumeId) {
2
+ const getProps = viewport.getProperties;
3
+ if (typeof getProps !== 'function') {
4
+ return null;
5
+ }
6
+ const props = volumeId
7
+ ? getProps.call(viewport, volumeId)
8
+ : getProps.call(viewport);
9
+ if (!props?.voiRange) {
10
+ return null;
11
+ }
12
+ const { lower, upper } = props.voiRange;
13
+ if (typeof lower !== 'number' || typeof upper !== 'number') {
14
+ return null;
15
+ }
16
+ return {
17
+ voiRange: { lower, upper },
18
+ VOILUTFunction: props.VOILUTFunction,
19
+ invert: props.invert === true,
20
+ };
21
+ }
@@ -0,0 +1,57 @@
1
+ import type { Types } from '@cornerstonejs/core';
2
+ import type { FloodFillIntensityRangeOptions, FloodFillIntensityRangeResult } from '../floodFillIntensityRangeTypes.js';
3
+ import type { GetFloodFillIntensityRange } from '../floodFillIntensityRangeTypes.js';
4
+ import type { ViewportVoiMappingForTool } from '../getViewportVoiMappingForVolume.js';
5
+ export type AdaptiveRegionProbeReason = 'ok' | 'outside-volume' | 'flat-region' | 'too-small' | 'unbounded' | 'off-target';
6
+ export type AdaptiveRegionExpandContext = {
7
+ seedByte: number;
8
+ polarity: 1 | -1;
9
+ voiMapping: ViewportVoiMappingForTool | null;
10
+ rawWindow: {
11
+ min: number;
12
+ span: number;
13
+ };
14
+ seedIjk: Types.Point3;
15
+ seedScalar: number;
16
+ growthCurve: Array<[number, number]>;
17
+ chosenToleranceBytes: number;
18
+ growthEndLevel: number;
19
+ pxAreaMm2: number;
20
+ };
21
+ export type AdaptiveRegionProbeResult = {
22
+ viable: boolean;
23
+ reason: AdaptiveRegionProbeReason;
24
+ range: FloodFillIntensityRangeResult | null;
25
+ regionSizePx?: number;
26
+ regionAreaMm2?: number;
27
+ toleranceBytes?: number;
28
+ clickNeighborJoinLevels?: Array<number | null>;
29
+ expandContext?: AdaptiveRegionExpandContext;
30
+ debug?: {
31
+ growthCurve: Array<[number, number]>;
32
+ quietRuns: Array<[number, number, number]>;
33
+ seedByte: number;
34
+ backgroundByte: number;
35
+ polarity: number;
36
+ borderTouchLevel: number;
37
+ explosionLevel: number;
38
+ clickJoinLevel: number;
39
+ minRegionPx: number;
40
+ maxRegionPx: number;
41
+ pxAreaMm2: number;
42
+ };
43
+ };
44
+ export type AdaptiveRegionCoreInput = {
45
+ dimensions: Types.Point3;
46
+ getScalar: (i: number, j: number, k: number) => number;
47
+ ijkClick: Types.Point3;
48
+ inPlaneAxes?: [number, number];
49
+ inPlaneSpacing?: [number, number];
50
+ voiMapping?: ViewportVoiMappingForTool | null;
51
+ toleranceScale?: number;
52
+ };
53
+ export declare function resolveAdaptiveBandAtTolerance(context: AdaptiveRegionExpandContext, toleranceBytes: number): FloodFillIntensityRangeResult;
54
+ export declare function probeAdaptiveRegionCore(input: AdaptiveRegionCoreInput): AdaptiveRegionProbeResult;
55
+ export declare function resolveInPlaneAxes(referencedVolume: Types.IImageVolume, viewport: Types.IViewport | undefined): [number, number];
56
+ export declare function probeAdaptiveRegion(referencedVolume: Types.IImageVolume, worldPosition: Types.Point3, options?: FloodFillIntensityRangeOptions): AdaptiveRegionProbeResult;
57
+ export declare const getAdaptiveRegionIntensityRange: GetFloodFillIntensityRange;