@cornerstonejs/tools 1.51.4 → 1.52.0
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/cjs/drawingSvg/drawPolyline.js +1 -1
- package/dist/cjs/drawingSvg/drawPolyline.js.map +1 -1
- package/dist/cjs/enums/ChangeTypes.d.ts +2 -1
- package/dist/cjs/enums/ChangeTypes.js +1 -0
- package/dist/cjs/enums/ChangeTypes.js.map +1 -1
- package/dist/cjs/eventDispatchers/keyboardEventHandlers/keyDown.js +1 -1
- package/dist/cjs/eventDispatchers/keyboardEventHandlers/keyDown.js.map +1 -1
- package/dist/cjs/stateManagement/annotation/helpers/state.d.ts +2 -1
- package/dist/cjs/stateManagement/annotation/helpers/state.js +2 -1
- package/dist/cjs/stateManagement/annotation/helpers/state.js.map +1 -1
- package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.d.ts +4 -0
- package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.js +82 -0
- package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.js.map +1 -1
- package/dist/cjs/tools/annotation/LivewireContourTool.d.ts +10 -9
- package/dist/cjs/tools/annotation/LivewireContourTool.js +56 -43
- package/dist/cjs/tools/annotation/LivewireContourTool.js.map +1 -1
- package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +2 -1
- package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
- package/dist/cjs/tools/annotation/SplineROITool.js +3 -3
- package/dist/cjs/tools/annotation/SplineROITool.js.map +1 -1
- package/dist/cjs/types/ContourSegmentationAnnotation.d.ts +8 -0
- package/dist/cjs/types/InterpolationTypes.d.ts +2 -0
- package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +2 -5
- package/dist/cjs/utilities/contours/interpolation/acceptAutogeneratedInterpolations.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/createPolylineToolData.js +2 -1
- package/dist/cjs/utilities/contours/interpolation/createPolylineToolData.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/findAnnotationForInterpolation.js +2 -1
- package/dist/cjs/utilities/contours/interpolation/findAnnotationForInterpolation.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/getInterpolationData.js +3 -1
- package/dist/cjs/utilities/contours/interpolation/getInterpolationData.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/interpolate.d.ts +8 -0
- package/dist/cjs/utilities/contours/interpolation/interpolate.js +56 -42
- package/dist/cjs/utilities/contours/interpolation/interpolate.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/selectHandles.d.ts +4 -0
- package/dist/cjs/utilities/contours/interpolation/selectHandles.js +170 -0
- package/dist/cjs/utilities/contours/interpolation/selectHandles.js.map +1 -0
- package/dist/cjs/utilities/livewire/LivewireScissors.d.ts +2 -1
- package/dist/cjs/utilities/livewire/LivewireScissors.js +46 -24
- package/dist/cjs/utilities/livewire/LivewireScissors.js.map +1 -1
- package/dist/cjs/utilities/segmentation/InterpolationManager/InterpolationManager.js +13 -3
- package/dist/cjs/utilities/segmentation/InterpolationManager/InterpolationManager.js.map +1 -1
- package/dist/esm/drawingSvg/drawPolyline.js +1 -1
- package/dist/esm/drawingSvg/drawPolyline.js.map +1 -1
- package/dist/esm/enums/ChangeTypes.js +1 -0
- package/dist/esm/enums/ChangeTypes.js.map +1 -1
- package/dist/esm/eventDispatchers/keyboardEventHandlers/keyDown.js +1 -1
- package/dist/esm/eventDispatchers/keyboardEventHandlers/keyDown.js.map +1 -1
- package/dist/esm/stateManagement/annotation/helpers/state.js +3 -2
- package/dist/esm/stateManagement/annotation/helpers/state.js.map +1 -1
- package/dist/esm/tools/annotation/LivewireContourSegmentationTool.js +81 -0
- package/dist/esm/tools/annotation/LivewireContourSegmentationTool.js.map +1 -1
- package/dist/esm/tools/annotation/LivewireContourTool.js +57 -44
- package/dist/esm/tools/annotation/LivewireContourTool.js.map +1 -1
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js +2 -1
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
- package/dist/esm/tools/annotation/SplineROITool.js +3 -3
- package/dist/esm/tools/annotation/SplineROITool.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/acceptAutogeneratedInterpolations.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/createPolylineToolData.js +2 -1
- package/dist/esm/utilities/contours/interpolation/createPolylineToolData.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/findAnnotationForInterpolation.js +2 -1
- package/dist/esm/utilities/contours/interpolation/findAnnotationForInterpolation.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/getInterpolationData.js +3 -1
- package/dist/esm/utilities/contours/interpolation/getInterpolationData.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/interpolate.js +57 -43
- package/dist/esm/utilities/contours/interpolation/interpolate.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/selectHandles.js +164 -0
- package/dist/esm/utilities/contours/interpolation/selectHandles.js.map +1 -0
- package/dist/esm/utilities/livewire/LivewireScissors.js +46 -24
- package/dist/esm/utilities/livewire/LivewireScissors.js.map +1 -1
- package/dist/esm/utilities/segmentation/InterpolationManager/InterpolationManager.js +13 -3
- package/dist/esm/utilities/segmentation/InterpolationManager/InterpolationManager.js.map +1 -1
- package/dist/types/enums/ChangeTypes.d.ts +2 -1
- package/dist/types/enums/ChangeTypes.d.ts.map +1 -1
- package/dist/types/stateManagement/annotation/helpers/state.d.ts +2 -1
- package/dist/types/stateManagement/annotation/helpers/state.d.ts.map +1 -1
- package/dist/types/tools/annotation/LivewireContourSegmentationTool.d.ts +4 -0
- package/dist/types/tools/annotation/LivewireContourSegmentationTool.d.ts.map +1 -1
- package/dist/types/tools/annotation/LivewireContourTool.d.ts +10 -9
- package/dist/types/tools/annotation/LivewireContourTool.d.ts.map +1 -1
- package/dist/types/tools/annotation/PlanarFreehandROITool.d.ts.map +1 -1
- package/dist/types/types/ContourSegmentationAnnotation.d.ts +8 -0
- package/dist/types/types/ContourSegmentationAnnotation.d.ts.map +1 -1
- package/dist/types/types/InterpolationTypes.d.ts +2 -0
- package/dist/types/types/InterpolationTypes.d.ts.map +1 -1
- package/dist/types/types/ToolSpecificAnnotationTypes.d.ts +2 -5
- package/dist/types/types/ToolSpecificAnnotationTypes.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/acceptAutogeneratedInterpolations.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/createPolylineToolData.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/findAnnotationForInterpolation.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/getInterpolationData.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/interpolate.d.ts +8 -0
- package/dist/types/utilities/contours/interpolation/interpolate.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/selectHandles.d.ts +5 -0
- package/dist/types/utilities/contours/interpolation/selectHandles.d.ts.map +1 -0
- package/dist/types/utilities/livewire/LivewireScissors.d.ts +2 -1
- package/dist/types/utilities/livewire/LivewireScissors.d.ts.map +1 -1
- package/dist/types/utilities/segmentation/InterpolationManager/InterpolationManager.d.ts.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/src/drawingSvg/drawPolyline.ts +1 -1
- package/src/enums/ChangeTypes.ts +4 -0
- package/src/eventDispatchers/keyboardEventHandlers/keyDown.ts +1 -1
- package/src/stateManagement/annotation/helpers/state.ts +4 -2
- package/src/tools/annotation/LivewireContourSegmentationTool.ts +151 -0
- package/src/tools/annotation/LivewireContourTool.ts +113 -82
- package/src/tools/annotation/PlanarFreehandROITool.ts +8 -3
- package/src/tools/annotation/SplineROITool.ts +3 -3
- package/src/types/ContourSegmentationAnnotation.ts +38 -0
- package/src/types/InterpolationTypes.ts +6 -0
- package/src/types/ToolSpecificAnnotationTypes.ts +7 -5
- package/src/utilities/contours/interpolation/acceptAutogeneratedInterpolations.ts +3 -1
- package/src/utilities/contours/interpolation/createPolylineToolData.ts +7 -1
- package/src/utilities/contours/interpolation/findAnnotationForInterpolation.ts +2 -1
- package/src/utilities/contours/interpolation/getInterpolationData.ts +3 -1
- package/src/utilities/contours/interpolation/interpolate.ts +94 -75
- package/src/utilities/contours/interpolation/selectHandles.ts +240 -0
- package/src/utilities/livewire/LivewireScissors.ts +65 -53
- package/src/utilities/segmentation/InterpolationManager/InterpolationManager.ts +39 -4
- package/dist/cjs/utilities/contours/PointsArray.d.ts +0 -29
- package/dist/cjs/utilities/contours/PointsArray.js +0 -104
- package/dist/cjs/utilities/contours/PointsArray.js.map +0 -1
- package/dist/esm/utilities/contours/PointsArray.js +0 -98
- package/dist/esm/utilities/contours/PointsArray.js.map +0 -1
- package/dist/types/utilities/contours/PointsArray.d.ts +0 -30
- package/dist/types/utilities/contours/PointsArray.d.ts.map +0 -1
- package/src/utilities/contours/PointsArray.ts +0 -165
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { vec3 } from 'gl-matrix';
|
|
2
|
+
import { utilities } from '@cornerstonejs/core';
|
|
3
|
+
import type { PointsArray3 } from './interpolate';
|
|
4
|
+
import { add } from '@kitware/vtk.js/Common/Core/Math';
|
|
5
|
+
|
|
6
|
+
const { PointsManager } = utilities;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Selects handles by looking for local maximums in the angle that the local
|
|
10
|
+
* vector makes
|
|
11
|
+
*
|
|
12
|
+
* @param polyline - an array of points, usually the polyline for the contour to
|
|
13
|
+
* select handles from.
|
|
14
|
+
* @param handleCount - a guideline for how many handles to create
|
|
15
|
+
*/
|
|
16
|
+
export default function selectHandles(
|
|
17
|
+
polyline: PointsArray3,
|
|
18
|
+
handleCount = 8
|
|
19
|
+
): PointsArray3 {
|
|
20
|
+
const handles = PointsManager.create3(handleCount) as PointsArray3;
|
|
21
|
+
handles.sources = [];
|
|
22
|
+
const { sources: destPoints } = handles;
|
|
23
|
+
const { length, sources: sourcePoints = [] } = polyline;
|
|
24
|
+
const distance = 6;
|
|
25
|
+
if (length < distance * 3) {
|
|
26
|
+
console.log('Adding subselect handles', handleCount, length);
|
|
27
|
+
return polyline.subselect(handleCount);
|
|
28
|
+
}
|
|
29
|
+
const interval = Math.min(30, Math.floor(length / 3));
|
|
30
|
+
sourcePoints.forEach(() =>
|
|
31
|
+
destPoints.push(PointsManager.create3(handleCount))
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const dotValues = createDotValues(polyline, distance);
|
|
35
|
+
|
|
36
|
+
const minimumRegions = findMinimumRegions(dotValues, handleCount);
|
|
37
|
+
const indices = [];
|
|
38
|
+
if (minimumRegions?.length > 2) {
|
|
39
|
+
let lastHandle = -1;
|
|
40
|
+
const thirdInterval = interval / 3;
|
|
41
|
+
minimumRegions.forEach((region) => {
|
|
42
|
+
const [start, , end] = region;
|
|
43
|
+
const midIndex = Math.ceil((start + end) / 2);
|
|
44
|
+
if (end - lastHandle < thirdInterval) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (midIndex - start > 2 * thirdInterval) {
|
|
48
|
+
addInterval(indices, lastHandle, start, interval, length);
|
|
49
|
+
lastHandle = addInterval(indices, start, midIndex, interval, length);
|
|
50
|
+
} else {
|
|
51
|
+
lastHandle = addInterval(
|
|
52
|
+
indices,
|
|
53
|
+
lastHandle,
|
|
54
|
+
midIndex,
|
|
55
|
+
interval,
|
|
56
|
+
length
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (end - lastHandle > thirdInterval) {
|
|
60
|
+
lastHandle = addInterval(indices, lastHandle, end, interval, length);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
const firstHandle = indices[0];
|
|
64
|
+
const lastDistance = indexValue(firstHandle + length - lastHandle, length);
|
|
65
|
+
// Check that there is enough space between the last and first handle to
|
|
66
|
+
// need an extra handle.
|
|
67
|
+
if (lastDistance > 2 * thirdInterval) {
|
|
68
|
+
addInterval(
|
|
69
|
+
indices,
|
|
70
|
+
lastHandle,
|
|
71
|
+
// Don't add a point too close to the first handle
|
|
72
|
+
firstHandle - thirdInterval,
|
|
73
|
+
interval,
|
|
74
|
+
length
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
const interval = Math.floor(length / handleCount);
|
|
79
|
+
addInterval(indices, -1, length - interval, interval, length);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
indices.forEach((index) => {
|
|
83
|
+
const point = polyline.getPointArray(index);
|
|
84
|
+
handles.push(point);
|
|
85
|
+
sourcePoints.forEach((source, destSourceIndex) =>
|
|
86
|
+
destPoints[destSourceIndex].push(source.getPoint(index))
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
return handles;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates an array of the dot products between each point in the points array
|
|
94
|
+
* and a point +/- distance from that point, unitized to vector length 1.
|
|
95
|
+
* That is, this is a measure of the angle at the given point, where 1 is a
|
|
96
|
+
* straight line, and -1 is a 180 degree angle change.
|
|
97
|
+
*
|
|
98
|
+
* @param polyline - the array of Point3 values
|
|
99
|
+
* @param distance - previous/next distance to use for the vectors for the dot product
|
|
100
|
+
* @returns - Float32Array of dot products, one per point in the source array.
|
|
101
|
+
*/
|
|
102
|
+
export function createDotValues(
|
|
103
|
+
polyline: PointsArray3,
|
|
104
|
+
distance = 6
|
|
105
|
+
): Float32Array {
|
|
106
|
+
const { length } = polyline;
|
|
107
|
+
const prevVec3 = vec3.create();
|
|
108
|
+
const nextVec3 = vec3.create();
|
|
109
|
+
const dotValues = new Float32Array(length);
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < length; i++) {
|
|
112
|
+
const point = polyline.getPoint(i);
|
|
113
|
+
const prevPoint = polyline.getPoint(i - distance);
|
|
114
|
+
const nextPoint = polyline.getPoint((i + distance) % length);
|
|
115
|
+
vec3.sub(prevVec3, point, prevPoint);
|
|
116
|
+
vec3.sub(nextVec3, nextPoint, point);
|
|
117
|
+
const dot =
|
|
118
|
+
vec3.dot(prevVec3, nextVec3) / (vec3.len(prevVec3) * vec3.len(nextVec3));
|
|
119
|
+
dotValues[i] = dot;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return dotValues;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Finds minimum regions in the dot products. These are detected as
|
|
127
|
+
* center points of the dot values regions having a minimum value - that is,
|
|
128
|
+
* where the direction of the line is changing fastest.
|
|
129
|
+
*/
|
|
130
|
+
function findMinimumRegions(dotValues, handleCount) {
|
|
131
|
+
const { max, deviation } = getStats(dotValues);
|
|
132
|
+
const { length } = dotValues;
|
|
133
|
+
// Fallback for very uniform ojects.
|
|
134
|
+
if (deviation < 0.01 || length < handleCount * 3) {
|
|
135
|
+
// TODO - create handleCount evenly spaced handles
|
|
136
|
+
return [0, Math.floor(length / 3), Math.floor((length * 2) / 3)];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const inflection = [];
|
|
140
|
+
let pair = null;
|
|
141
|
+
let minValue;
|
|
142
|
+
let minIndex = 0;
|
|
143
|
+
|
|
144
|
+
for (let i = 0; i < length; i++) {
|
|
145
|
+
const dot = dotValues[i];
|
|
146
|
+
if (dot < max - deviation) {
|
|
147
|
+
if (pair) {
|
|
148
|
+
pair[2] = i;
|
|
149
|
+
if (dot < minValue) {
|
|
150
|
+
minValue = dot;
|
|
151
|
+
minIndex = i;
|
|
152
|
+
}
|
|
153
|
+
pair[1] = minIndex;
|
|
154
|
+
} else {
|
|
155
|
+
minValue = dot;
|
|
156
|
+
minIndex = i;
|
|
157
|
+
pair = [i, i, i];
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
if (pair) {
|
|
161
|
+
inflection.push(pair);
|
|
162
|
+
pair = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (pair) {
|
|
167
|
+
if (inflection[0][0] === 0) {
|
|
168
|
+
inflection[0][0] = pair[0];
|
|
169
|
+
} else {
|
|
170
|
+
pair[1] = minIndex;
|
|
171
|
+
pair[2] = length - 1;
|
|
172
|
+
inflection.push(pair);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return inflection;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Adds points in between the start and finish.
|
|
181
|
+
* This is currently just the center point for short values and the start/center/end
|
|
182
|
+
* for ranges where the distance between these is at least the increment.
|
|
183
|
+
*/
|
|
184
|
+
export function addInterval(indices, start, finish, interval, length) {
|
|
185
|
+
if (finish < start) {
|
|
186
|
+
// Always want a positive distance even if the long way round
|
|
187
|
+
finish += length;
|
|
188
|
+
}
|
|
189
|
+
const distance = finish - start;
|
|
190
|
+
const count = Math.ceil(distance / interval);
|
|
191
|
+
if (count <= 0) {
|
|
192
|
+
if (indices[indices.length - 1] !== finish) {
|
|
193
|
+
indices.push(indexValue(finish, length));
|
|
194
|
+
}
|
|
195
|
+
return finish;
|
|
196
|
+
}
|
|
197
|
+
// Don't add the start index, and always add the end index
|
|
198
|
+
for (let i = 1; i <= count; i++) {
|
|
199
|
+
const index = indexValue(start + (i * distance) / count, length);
|
|
200
|
+
indices.push(index);
|
|
201
|
+
}
|
|
202
|
+
return indices[indices.length - 1];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Gets the index value of a closed polyline, rounding the value and
|
|
207
|
+
* doing the module operation as required.
|
|
208
|
+
*/
|
|
209
|
+
function indexValue(v, length) {
|
|
210
|
+
return (Math.round(v) + length) % length;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Gets statistics on the provided array numbers.
|
|
215
|
+
*/
|
|
216
|
+
function getStats(dotValues) {
|
|
217
|
+
const { length } = dotValues;
|
|
218
|
+
let sum = 0;
|
|
219
|
+
let min = Infinity;
|
|
220
|
+
let max = -Infinity;
|
|
221
|
+
let sumSq = 0;
|
|
222
|
+
for (let i = 0; i < length; i++) {
|
|
223
|
+
const dot = dotValues[i];
|
|
224
|
+
sum += dot;
|
|
225
|
+
min = Math.min(min, dot);
|
|
226
|
+
max = Math.max(max, dot);
|
|
227
|
+
}
|
|
228
|
+
const mean = sum / length;
|
|
229
|
+
for (let i = 0; i < length; i++) {
|
|
230
|
+
const valueDiff = dotValues[i] - mean;
|
|
231
|
+
sumSq += valueDiff * valueDiff;
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
mean,
|
|
235
|
+
max,
|
|
236
|
+
min,
|
|
237
|
+
sumSq,
|
|
238
|
+
deviation: Math.sqrt(sumSq / length),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
@@ -105,45 +105,47 @@ export class LivewireScissors {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
109
|
-
* This will remove points, up to count which have a high gradient, up to
|
|
110
|
-
* count of them where the clip value is larger than that provided.
|
|
108
|
+
* Finds a nearby point with a minimum cost nearby
|
|
111
109
|
*
|
|
112
|
-
* @
|
|
110
|
+
* @param testPoint - to look nearby
|
|
111
|
+
* @param delta - how long a distance to look
|
|
112
|
+
* @returns A point having the minimum weighted distance from the testPoint
|
|
113
113
|
*/
|
|
114
|
-
public
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
let
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
114
|
+
public findMinNearby(testPoint: Types.Point2, delta = 2) {
|
|
115
|
+
const [x, y] = testPoint;
|
|
116
|
+
const { costs } = this;
|
|
117
|
+
|
|
118
|
+
const xRange = [
|
|
119
|
+
Math.max(0, x - delta),
|
|
120
|
+
Math.min(x + delta + 1, this.width),
|
|
121
|
+
];
|
|
122
|
+
const yRange = [
|
|
123
|
+
Math.max(0, y - delta),
|
|
124
|
+
Math.min(y + delta + 1, this.height),
|
|
125
|
+
];
|
|
126
|
+
let minValue = costs[this._getPointIndex(y, x)] * 0.8;
|
|
127
|
+
|
|
128
|
+
let minPoint = testPoint;
|
|
129
|
+
for (let xTest = xRange[0]; xTest < xRange[1]; xTest++) {
|
|
130
|
+
for (let yTest = yRange[0]; yTest < yRange[1]; yTest++) {
|
|
131
|
+
// Cost values are 0...1, with 1 being a poor choice for the
|
|
132
|
+
// livewire fitting - thus, we want to minimize our value, so the
|
|
133
|
+
// distance cost should be low for the center point.
|
|
134
|
+
const distanceCost =
|
|
135
|
+
1 -
|
|
136
|
+
(Math.abs(xTest - testPoint[0]) + Math.abs(yTest - testPoint[1])) /
|
|
137
|
+
delta /
|
|
138
|
+
2;
|
|
139
|
+
const weightCost = costs[this._getPointIndex(yTest, xTest)];
|
|
140
|
+
|
|
141
|
+
const weight = weightCost * 0.8 + distanceCost * 0.2;
|
|
142
|
+
if (weight < minValue) {
|
|
143
|
+
minPoint = [xTest, yTest];
|
|
144
|
+
minValue = weight;
|
|
145
|
+
}
|
|
141
146
|
}
|
|
142
|
-
removeCount++;
|
|
143
147
|
}
|
|
144
|
-
|
|
145
|
-
// along a high gradient.
|
|
146
|
-
return 0;
|
|
148
|
+
return minPoint;
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
/**
|
|
@@ -281,7 +283,7 @@ export class LivewireScissors {
|
|
|
281
283
|
|
|
282
284
|
// If it is at the end, back up one
|
|
283
285
|
if (y + 1 === height) {
|
|
284
|
-
index -=
|
|
286
|
+
index -= width;
|
|
285
287
|
}
|
|
286
288
|
|
|
287
289
|
return data[index] - data[index + width];
|
|
@@ -420,14 +422,9 @@ export class LivewireScissors {
|
|
|
420
422
|
let pixelIndex = 0;
|
|
421
423
|
|
|
422
424
|
for (let y = 0; y < height; y++) {
|
|
423
|
-
for (let x = 0; x < width
|
|
425
|
+
for (let x = 0; x < width; x++) {
|
|
424
426
|
gradX[pixelIndex++] = this._getDeltaX(x, y);
|
|
425
427
|
}
|
|
426
|
-
|
|
427
|
-
// Make the last column the same as the previous one because there is
|
|
428
|
-
// no way to calculate `dx` since x+1 gets out of bounds
|
|
429
|
-
gradX[pixelIndex] = gradX[pixelIndex - 1];
|
|
430
|
-
pixelIndex++;
|
|
431
428
|
}
|
|
432
429
|
|
|
433
430
|
return gradX;
|
|
@@ -444,18 +441,12 @@ export class LivewireScissors {
|
|
|
444
441
|
const gradY = new Float32Array(width * height);
|
|
445
442
|
let pixelIndex = 0;
|
|
446
443
|
|
|
447
|
-
for (let y = 0; y < height
|
|
444
|
+
for (let y = 0; y < height; y++) {
|
|
448
445
|
for (let x = 0; x < width; x++) {
|
|
449
446
|
gradY[pixelIndex++] = this._getDeltaY(x, y);
|
|
450
447
|
}
|
|
451
448
|
}
|
|
452
449
|
|
|
453
|
-
// Make the last row the same as the previous one because there is
|
|
454
|
-
// no way to calculate `dy` since y+1 gets out of bounds
|
|
455
|
-
for (let len = gradY.length; pixelIndex < len; pixelIndex++) {
|
|
456
|
-
gradY[pixelIndex] = gradY[pixelIndex - width];
|
|
457
|
-
}
|
|
458
|
-
|
|
459
450
|
return gradY;
|
|
460
451
|
}
|
|
461
452
|
|
|
@@ -481,7 +472,7 @@ export class LivewireScissors {
|
|
|
481
472
|
}
|
|
482
473
|
|
|
483
474
|
/**
|
|
484
|
-
* Compute the
|
|
475
|
+
* Compute the gradient direction, in radians, between two points
|
|
485
476
|
*
|
|
486
477
|
* @param px - Point `p` x-coordinate of point p.
|
|
487
478
|
* @param py - Point `p` y-coordinate of point p.
|
|
@@ -506,23 +497,44 @@ export class LivewireScissors {
|
|
|
506
497
|
dp = -dp;
|
|
507
498
|
dq = -dq;
|
|
508
499
|
}
|
|
509
|
-
|
|
510
500
|
if (px !== qx && py !== qy) {
|
|
511
501
|
// It's going diagonally between pixels
|
|
512
502
|
dp *= Math.SQRT1_2;
|
|
513
503
|
dq *= Math.SQRT1_2;
|
|
514
504
|
}
|
|
505
|
+
dq = Math.min(Math.max(dq, -1), 1);
|
|
506
|
+
|
|
507
|
+
const direction =
|
|
508
|
+
TWO_THIRD_PI * (Math.acos(Math.min(dp, 1)) + Math.acos(dq));
|
|
509
|
+
if (isNaN(direction) || !isFinite(direction)) {
|
|
510
|
+
console.warn('Found non-direction:', px, py, qx, qy, dp, dq, direction);
|
|
511
|
+
return 1;
|
|
512
|
+
}
|
|
513
|
+
return direction;
|
|
514
|
+
}
|
|
515
515
|
|
|
516
|
-
|
|
516
|
+
/** Gets the cost to go from A to B */
|
|
517
|
+
public getCost(pointA, pointB): number {
|
|
518
|
+
return this._getWeightedDistance(pointA, pointB);
|
|
517
519
|
}
|
|
518
520
|
|
|
519
521
|
/**
|
|
520
522
|
* Return a weighted distance between two points
|
|
521
523
|
*/
|
|
522
524
|
private _getWeightedDistance(pointA: Types.Point2, pointB: Types.Point2) {
|
|
523
|
-
const { _getPointIndex: index } = this;
|
|
525
|
+
const { _getPointIndex: index, width, height } = this;
|
|
524
526
|
const [aX, aY] = pointA;
|
|
525
527
|
const [bX, bY] = pointB;
|
|
528
|
+
// Assign a cost of 1 to any points outside the image, prevents using invalid
|
|
529
|
+
// points
|
|
530
|
+
if (bX < 0 || bX >= width || bY < 0 || bY >= height) {
|
|
531
|
+
return 1;
|
|
532
|
+
}
|
|
533
|
+
// Use a cost of 0 if the point was outside and is now going inside
|
|
534
|
+
if (aX < 0 || aY < 0 || aX >= width || aY >= height) {
|
|
535
|
+
return 0;
|
|
536
|
+
}
|
|
537
|
+
|
|
526
538
|
const bIndex = index(bY, bX);
|
|
527
539
|
|
|
528
540
|
// Weighted distance function
|
|
@@ -20,6 +20,11 @@ import getViewportForAnnotation from '../../getViewportForAnnotation';
|
|
|
20
20
|
|
|
21
21
|
const { uuidv4 } = csUtils;
|
|
22
22
|
|
|
23
|
+
const ChangeTypesForInterpolation = [
|
|
24
|
+
ChangeTypes.HandlesUpdated,
|
|
25
|
+
ChangeTypes.InterpolationUpdated,
|
|
26
|
+
];
|
|
27
|
+
|
|
23
28
|
export default class InterpolationManager {
|
|
24
29
|
static toolNames = [];
|
|
25
30
|
|
|
@@ -31,13 +36,35 @@ export default class InterpolationManager {
|
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Accepts the autogenerated interpolations, marking them as non-autogenerated.
|
|
34
|
-
* Can provide a selector to choose which ones to accept
|
|
39
|
+
* Can provide a selector to choose which ones to accept.
|
|
40
|
+
*
|
|
41
|
+
* Rules for which items to select:
|
|
42
|
+
* 1. Only choose annotations having the same segment index and segmentationID
|
|
43
|
+
* 2. Exclude all contours having the same interpolation UID as any other contours
|
|
44
|
+
* on the same slice.
|
|
45
|
+
* 3. Exclude autogenerated annotations
|
|
46
|
+
* 4. Exclude any reset interpolationUIDs (this is a manual operation to allow
|
|
47
|
+
* creating a new interpolation)
|
|
48
|
+
* 5. Find the set of interpolationUID's remaining
|
|
49
|
+
* a. If the set is of size 0, assign a new interpolationUID
|
|
50
|
+
* b. If the set is of size 1, assign that interpolationUID
|
|
51
|
+
* c. Otherwise (optional, otherwise do b for size>1 randomly),
|
|
52
|
+
* for every remaining annotation, find the one whose center
|
|
53
|
+
* point is closest to the center point of the new annotation.
|
|
54
|
+
* Choose that interpolationUID
|
|
55
|
+
*
|
|
56
|
+
* To allow creating new interpolated groups, the idea is to just use a new
|
|
57
|
+
* segment index, then have an operation to update the segment index of an
|
|
58
|
+
* interpolation set. That way the user can easily draw/see the difference,
|
|
59
|
+
* and then merge them as required.
|
|
60
|
+
* However, the base rules allow creating two contours on a single image to
|
|
61
|
+
* create a separate set.
|
|
35
62
|
*/
|
|
36
63
|
static acceptAutoGenerated(
|
|
37
64
|
annotationGroupSelector: AnnotationGroupSelector,
|
|
38
65
|
selector: AcceptInterpolationSelector = {}
|
|
39
66
|
) {
|
|
40
|
-
const { toolNames, segmentationId, segmentIndex } = selector;
|
|
67
|
+
const { toolNames, segmentationId, segmentIndex, sliceIndex } = selector;
|
|
41
68
|
for (const toolName of toolNames || InterpolationManager.toolNames) {
|
|
42
69
|
const annotations = annotationState.getAnnotations(
|
|
43
70
|
toolName,
|
|
@@ -47,13 +74,20 @@ export default class InterpolationManager {
|
|
|
47
74
|
continue;
|
|
48
75
|
}
|
|
49
76
|
for (const annotation of annotations) {
|
|
50
|
-
const { data, autoGenerated } = annotation;
|
|
77
|
+
const { data, autoGenerated, metadata } = annotation;
|
|
51
78
|
if (!autoGenerated) {
|
|
52
79
|
continue;
|
|
53
80
|
}
|
|
54
81
|
if (segmentIndex && segmentIndex !== data.segmentation.segmentIndex) {
|
|
55
82
|
continue;
|
|
56
83
|
}
|
|
84
|
+
if (
|
|
85
|
+
sliceIndex !== undefined &&
|
|
86
|
+
metadata &&
|
|
87
|
+
sliceIndex !== (metadata as any).referencedSliceIndex
|
|
88
|
+
) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
57
91
|
if (
|
|
58
92
|
segmentationId &&
|
|
59
93
|
segmentationId !== data.segmentation.segmentationId
|
|
@@ -141,7 +175,7 @@ export default class InterpolationManager {
|
|
|
141
175
|
|
|
142
176
|
if (
|
|
143
177
|
!this.toolNames.includes(toolName) ||
|
|
144
|
-
changeType
|
|
178
|
+
!ChangeTypesForInterpolation.includes(changeType)
|
|
145
179
|
) {
|
|
146
180
|
return;
|
|
147
181
|
}
|
|
@@ -162,6 +196,7 @@ export default class InterpolationManager {
|
|
|
162
196
|
sliceData,
|
|
163
197
|
annotation,
|
|
164
198
|
interpolationUID: annotation.interpolationUID,
|
|
199
|
+
isInterpolationUpdate: changeType === ChangeTypes.InterpolationUpdated,
|
|
165
200
|
};
|
|
166
201
|
interpolate(viewportData);
|
|
167
202
|
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { Types } from '@cornerstonejs/core';
|
|
2
|
-
export declare type PolyDataPointConfiguration = {
|
|
3
|
-
dimensions?: 2 | 3;
|
|
4
|
-
initialSize?: number;
|
|
5
|
-
};
|
|
6
|
-
export declare abstract class PointsArray<T> {
|
|
7
|
-
data: Float32Array;
|
|
8
|
-
dimensions: number;
|
|
9
|
-
length: number;
|
|
10
|
-
constructor(configuration?: PolyDataPointConfiguration);
|
|
11
|
-
protected forEach(func: (value: T, index: number) => void, point: T): void;
|
|
12
|
-
abstract getPoint(index: number, point: T): T;
|
|
13
|
-
reverse(): void;
|
|
14
|
-
protected map(f: (value: any, index: number) => T, factory: (index: number) => T): any[];
|
|
15
|
-
static fromXYZ({ x, y, z }: Types.PointsXYZ): PointsArray3;
|
|
16
|
-
}
|
|
17
|
-
export declare class PointsArray2 extends PointsArray<Types.Point2> {
|
|
18
|
-
constructor(configuration?: {});
|
|
19
|
-
forEach(func: (value: Types.Point2, index: number) => void, point?: Types.Point2): void;
|
|
20
|
-
getPoint(index: number, point?: Types.Point2): Types.Point2;
|
|
21
|
-
get points(): any[];
|
|
22
|
-
}
|
|
23
|
-
export declare class PointsArray3 extends PointsArray<Types.Point3> {
|
|
24
|
-
constructor(configuration?: {});
|
|
25
|
-
forEach(func: (value: Types.Point3, index: number) => void, point?: Types.Point3): void;
|
|
26
|
-
getPoint(index: number, point?: Types.Point3): Types.Point3;
|
|
27
|
-
get points(): any[];
|
|
28
|
-
getXYZ(): Types.PointsXYZ;
|
|
29
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PointsArray3 = exports.PointsArray2 = exports.PointsArray = void 0;
|
|
4
|
-
const gl_matrix_1 = require("gl-matrix");
|
|
5
|
-
class PointsArray {
|
|
6
|
-
constructor(configuration = {}) {
|
|
7
|
-
this.dimensions = 3;
|
|
8
|
-
this.length = 0;
|
|
9
|
-
const { initialSize = 1024, dimensions = 3 } = configuration;
|
|
10
|
-
this.data = new Float32Array(initialSize * dimensions);
|
|
11
|
-
this.dimensions = dimensions;
|
|
12
|
-
}
|
|
13
|
-
forEach(func, point) {
|
|
14
|
-
for (let i = 0; i < this.length; i++) {
|
|
15
|
-
func(this.getPoint(i, point), i);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
reverse() {
|
|
19
|
-
const midLength = Math.floor(this.length / 2);
|
|
20
|
-
for (let i = 0; i < midLength; i++) {
|
|
21
|
-
const indexStart = i * this.dimensions;
|
|
22
|
-
const indexEnd = (this.length - 1 - i) * this.dimensions;
|
|
23
|
-
for (let dimension = 0; dimension < this.dimensions; dimension++) {
|
|
24
|
-
const valueStart = this.data[indexStart + dimension];
|
|
25
|
-
this.data[indexStart + dimension] = this.data[indexEnd + dimension];
|
|
26
|
-
this.data[indexEnd + dimension] = valueStart;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
map(f, factory) {
|
|
31
|
-
const mapData = [];
|
|
32
|
-
for (let i = 0; i < this.length; i++) {
|
|
33
|
-
mapData.push(f(this.getPoint(i, factory(i)), i));
|
|
34
|
-
}
|
|
35
|
-
return mapData;
|
|
36
|
-
}
|
|
37
|
-
static fromXYZ({ x, y, z }) {
|
|
38
|
-
const array = new PointsArray3({ initialSize: x.length });
|
|
39
|
-
let offset = 0;
|
|
40
|
-
for (let i = 0; i < x.length; i++) {
|
|
41
|
-
array.data[offset++] = x[i];
|
|
42
|
-
array.data[offset++] = y[i];
|
|
43
|
-
array.data[offset++] = z[i];
|
|
44
|
-
}
|
|
45
|
-
array.length = x.length;
|
|
46
|
-
return array;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
exports.PointsArray = PointsArray;
|
|
50
|
-
class PointsArray2 extends PointsArray {
|
|
51
|
-
constructor(configuration = {}) {
|
|
52
|
-
super(Object.assign(Object.assign({}, configuration), { dimensions: 2 }));
|
|
53
|
-
}
|
|
54
|
-
forEach(func, point = gl_matrix_1.vec2.create()) {
|
|
55
|
-
super.forEach(func, point);
|
|
56
|
-
}
|
|
57
|
-
getPoint(index, point = gl_matrix_1.vec2.create()) {
|
|
58
|
-
if (index >= this.length) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
const index2 = index * 2;
|
|
62
|
-
point[0] = this.data[index2];
|
|
63
|
-
point[1] = this.data[index2 + 1];
|
|
64
|
-
return point;
|
|
65
|
-
}
|
|
66
|
-
get points() {
|
|
67
|
-
return this.map((point) => point, () => gl_matrix_1.vec2.create());
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
exports.PointsArray2 = PointsArray2;
|
|
71
|
-
class PointsArray3 extends PointsArray {
|
|
72
|
-
constructor(configuration = {}) {
|
|
73
|
-
super(Object.assign(Object.assign({}, configuration), { dimensions: 3 }));
|
|
74
|
-
}
|
|
75
|
-
forEach(func, point = gl_matrix_1.vec3.create()) {
|
|
76
|
-
super.forEach(func, point);
|
|
77
|
-
}
|
|
78
|
-
getPoint(index, point = gl_matrix_1.vec3.create()) {
|
|
79
|
-
if (index >= this.length) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const index2 = index * 3;
|
|
83
|
-
point[0] = this.data[index2];
|
|
84
|
-
point[1] = this.data[index2 + 1];
|
|
85
|
-
point[2] = this.data[index2 + 2];
|
|
86
|
-
return point;
|
|
87
|
-
}
|
|
88
|
-
get points() {
|
|
89
|
-
return this.map((point) => point, () => gl_matrix_1.vec3.create());
|
|
90
|
-
}
|
|
91
|
-
getXYZ() {
|
|
92
|
-
const x = [];
|
|
93
|
-
const y = [];
|
|
94
|
-
const z = [];
|
|
95
|
-
this.forEach((point) => {
|
|
96
|
-
x.push(point[0]);
|
|
97
|
-
y.push(point[1]);
|
|
98
|
-
z.push(point[2]);
|
|
99
|
-
});
|
|
100
|
-
return { x, y, z };
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
exports.PointsArray3 = PointsArray3;
|
|
104
|
-
//# sourceMappingURL=PointsArray.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PointsArray.js","sourceRoot":"","sources":["../../../../src/utilities/contours/PointsArray.ts"],"names":[],"mappings":"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"}
|