@cornerstonejs/tools 2.0.0-beta.22 → 2.0.0-beta.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/eventListeners/segmentation/labelmap/onLabelmapSegmentationDataModified.js +27 -23
- package/dist/esm/eventListeners/segmentation/segmentationDataModifiedEventListener.js +2 -2
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/stateManagement/index.d.ts +2 -0
- package/dist/esm/stateManagement/index.js +2 -2
- package/dist/esm/stateManagement/segmentation/SegmentationRenderingEngine.d.ts +1 -2
- package/dist/esm/stateManagement/segmentation/SegmentationStateManager.d.ts +3 -0
- package/dist/esm/stateManagement/segmentation/SegmentationStateManager.js +59 -11
- package/dist/esm/stateManagement/segmentation/getStackSegmentationImageIdsForViewport.d.ts +1 -0
- package/dist/esm/stateManagement/segmentation/getStackSegmentationImageIdsForViewport.js +5 -0
- package/dist/esm/stateManagement/segmentation/segmentationState.d.ts +2 -1
- package/dist/esm/stateManagement/segmentation/segmentationState.js +2 -1
- package/dist/esm/tools/annotation/BidirectionalTool.js +6 -6
- package/dist/esm/tools/annotation/CircleROITool.js +12 -12
- package/dist/esm/tools/annotation/DragProbeTool.js +2 -2
- package/dist/esm/tools/annotation/EllipticalROITool.js +11 -11
- package/dist/esm/tools/annotation/HeightTool.js +4 -4
- package/dist/esm/tools/annotation/LengthTool.js +4 -4
- package/dist/esm/tools/annotation/PlanarFreehandROITool.d.ts +4 -4
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js +19 -19
- package/dist/esm/tools/annotation/ProbeTool.js +8 -8
- package/dist/esm/tools/annotation/RectangleROITool.js +11 -11
- package/dist/esm/tools/annotation/SplineROITool.js +6 -6
- package/dist/esm/tools/index.d.ts +1 -3
- package/dist/esm/tools/index.js +1 -3
- package/dist/esm/tools/segmentation/BrushTool.d.ts +38 -0
- package/dist/esm/tools/segmentation/BrushTool.js +40 -7
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.js +8 -8
- package/dist/esm/tools/segmentation/RectangleROIStartEndThresholdTool.js +8 -8
- package/dist/esm/tools/segmentation/strategies/BrushStrategy.js +10 -5
- package/dist/esm/tools/segmentation/strategies/compositions/islandRemoval.js +1 -1
- package/dist/esm/tools/segmentation/strategies/compositions/preview.js +2 -2
- package/dist/esm/tools/segmentation/strategies/compositions/setValue.js +6 -4
- package/dist/esm/tools/segmentation/strategies/fillSphere.js +5 -14
- package/dist/esm/types/LabelmapToolOperationData.d.ts +5 -0
- package/dist/esm/types/ToolSpecificAnnotationTypes.d.ts +2 -2
- package/dist/esm/utilities/contours/generateContourSetsFromLabelmap.js +7 -7
- package/dist/esm/utilities/getCalibratedUnits.d.ts +2 -2
- package/dist/esm/utilities/getCalibratedUnits.js +13 -13
- package/dist/esm/utilities/segmentation/getSegmentIndexAtLabelmapBorder.js +7 -3
- package/dist/esm/utilities/segmentation/isLineInSegment.js +3 -3
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/esm/tools/StackScrollToolMouseWheelTool.d.ts +0 -16
- package/dist/esm/tools/StackScrollToolMouseWheelTool.js +0 -33
|
@@ -148,7 +148,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
148
148
|
isPreScaled: isViewportPreScaled(viewport, targetId),
|
|
149
149
|
isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
|
|
150
150
|
};
|
|
151
|
-
const
|
|
151
|
+
const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
|
|
152
152
|
const calibratedScale = getCalibratedLengthUnitsAndScale(image, () => {
|
|
153
153
|
const polyline = data.contour.polyline;
|
|
154
154
|
const numPoints = polyline.length;
|
|
@@ -175,7 +175,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
175
175
|
imageData,
|
|
176
176
|
metadata,
|
|
177
177
|
cachedStats,
|
|
178
|
-
|
|
178
|
+
modalityUnit,
|
|
179
179
|
calibratedScale,
|
|
180
180
|
});
|
|
181
181
|
}
|
|
@@ -185,7 +185,7 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
185
185
|
canvasCoordinates,
|
|
186
186
|
targetId,
|
|
187
187
|
cachedStats,
|
|
188
|
-
|
|
188
|
+
modalityUnit,
|
|
189
189
|
calibratedScale,
|
|
190
190
|
});
|
|
191
191
|
}
|
|
@@ -365,14 +365,14 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
365
365
|
if (!this.commonData?.movingTextBox) {
|
|
366
366
|
const { data } = annotation;
|
|
367
367
|
if (!data.cachedStats[targetId] ||
|
|
368
|
-
data.cachedStats[targetId].
|
|
368
|
+
data.cachedStats[targetId].areaUnit == null) {
|
|
369
369
|
data.cachedStats[targetId] = {
|
|
370
370
|
Modality: null,
|
|
371
371
|
area: null,
|
|
372
372
|
max: null,
|
|
373
373
|
mean: null,
|
|
374
374
|
stdDev: null,
|
|
375
|
-
|
|
375
|
+
areaUnit: null,
|
|
376
376
|
};
|
|
377
377
|
this._calculateCachedStats(annotation, viewport, renderingEngine, enabledElement);
|
|
378
378
|
}
|
|
@@ -381,8 +381,8 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
|
-
updateClosedCachedStats({ viewport, points, imageData, metadata, cachedStats, targetId,
|
|
385
|
-
const { scale,
|
|
384
|
+
updateClosedCachedStats({ viewport, points, imageData, metadata, cachedStats, targetId, modalityUnit, canvasCoordinates, calibratedScale, }) {
|
|
385
|
+
const { scale, areaUnit, units } = calibratedScale;
|
|
386
386
|
const { voxelManager } = viewport.getImageData();
|
|
387
387
|
const canvasPoint = canvasCoordinates[0];
|
|
388
388
|
const originalWorldPoint = viewport.canvasToWorld(canvasPoint);
|
|
@@ -484,45 +484,45 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
484
484
|
stdDev: stats.stdDev?.value,
|
|
485
485
|
statsArray: stats.array,
|
|
486
486
|
pointsInShape: pointsInShape,
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
487
|
+
areaUnit,
|
|
488
|
+
modalityUnit,
|
|
489
|
+
unit: units,
|
|
490
490
|
};
|
|
491
491
|
}
|
|
492
|
-
updateOpenCachedStats({ targetId, metadata, canvasCoordinates, cachedStats,
|
|
492
|
+
updateOpenCachedStats({ targetId, metadata, canvasCoordinates, cachedStats, modalityUnit, calibratedScale, }) {
|
|
493
493
|
const { scale, units } = calibratedScale;
|
|
494
494
|
cachedStats[targetId] = {
|
|
495
495
|
Modality: metadata.Modality,
|
|
496
496
|
length: calculatePerimeter(canvasCoordinates, false) / scale,
|
|
497
|
-
|
|
497
|
+
modalityUnit,
|
|
498
498
|
getPixelValueUnitunit: units,
|
|
499
499
|
};
|
|
500
500
|
}
|
|
501
501
|
}
|
|
502
502
|
function defaultGetTextLines(data, targetId) {
|
|
503
503
|
const cachedVolumeStats = data.cachedStats[targetId];
|
|
504
|
-
const { area, mean, stdDev, length, perimeter, max, isEmptyArea,
|
|
504
|
+
const { area, mean, stdDev, length, perimeter, max, isEmptyArea, unit, areaUnit, modalityUnit, } = cachedVolumeStats || {};
|
|
505
505
|
const textLines = [];
|
|
506
506
|
if (area) {
|
|
507
507
|
const areaLine = isEmptyArea
|
|
508
508
|
? `Area: Oblique not supported`
|
|
509
|
-
: `Area: ${csUtils.roundNumber(area)} ${
|
|
509
|
+
: `Area: ${csUtils.roundNumber(area)} ${areaUnit}`;
|
|
510
510
|
textLines.push(areaLine);
|
|
511
511
|
}
|
|
512
512
|
if (mean) {
|
|
513
|
-
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${
|
|
513
|
+
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
|
|
514
514
|
}
|
|
515
515
|
if (Number.isFinite(max)) {
|
|
516
|
-
textLines.push(`Max: ${csUtils.roundNumber(max)} ${
|
|
516
|
+
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
|
|
517
517
|
}
|
|
518
518
|
if (stdDev) {
|
|
519
|
-
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${
|
|
519
|
+
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
|
|
520
520
|
}
|
|
521
521
|
if (perimeter) {
|
|
522
|
-
textLines.push(`Perimeter: ${csUtils.roundNumber(perimeter)} ${
|
|
522
|
+
textLines.push(`Perimeter: ${csUtils.roundNumber(perimeter)} ${unit}`);
|
|
523
523
|
}
|
|
524
524
|
if (length) {
|
|
525
|
-
textLines.push(`${csUtils.roundNumber(length)} ${
|
|
525
|
+
textLines.push(`${csUtils.roundNumber(length)} ${unit}`);
|
|
526
526
|
}
|
|
527
527
|
return textLines;
|
|
528
528
|
}
|
|
@@ -278,25 +278,25 @@ class ProbeTool extends AnnotationTool {
|
|
|
278
278
|
const viewport = viewports[0];
|
|
279
279
|
ijk[2] = viewport.getCurrentImageIdIndex();
|
|
280
280
|
}
|
|
281
|
-
let
|
|
281
|
+
let modalityUnit;
|
|
282
282
|
if (modality === 'US') {
|
|
283
283
|
const calibratedResults = getCalibratedProbeUnitsAndValue(image, [
|
|
284
284
|
ijk,
|
|
285
285
|
]);
|
|
286
286
|
const hasEnhancedRegionValues = calibratedResults.values.every((value) => value !== null);
|
|
287
287
|
value = (hasEnhancedRegionValues ? calibratedResults.values : value);
|
|
288
|
-
|
|
288
|
+
modalityUnit = hasEnhancedRegionValues
|
|
289
289
|
? calibratedResults.units
|
|
290
290
|
: 'raw';
|
|
291
291
|
}
|
|
292
292
|
else {
|
|
293
|
-
|
|
293
|
+
modalityUnit = getPixelValueUnits(modality, annotation.metadata.referencedImageId, pixelUnitsOptions);
|
|
294
294
|
}
|
|
295
295
|
cachedStats[targetId] = {
|
|
296
296
|
index: ijk,
|
|
297
297
|
value,
|
|
298
298
|
Modality: modality,
|
|
299
|
-
|
|
299
|
+
modalityUnit,
|
|
300
300
|
};
|
|
301
301
|
}
|
|
302
302
|
else {
|
|
@@ -314,19 +314,19 @@ class ProbeTool extends AnnotationTool {
|
|
|
314
314
|
}
|
|
315
315
|
function defaultGetTextLines(data, targetId) {
|
|
316
316
|
const cachedVolumeStats = data.cachedStats[targetId];
|
|
317
|
-
const { index, value,
|
|
317
|
+
const { index, value, modalityUnit } = cachedVolumeStats;
|
|
318
318
|
if (value === undefined) {
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
321
321
|
const textLines = [];
|
|
322
322
|
textLines.push(`(${index[0]}, ${index[1]}, ${index[2]})`);
|
|
323
|
-
if (value instanceof Array &&
|
|
323
|
+
if (value instanceof Array && modalityUnit instanceof Array) {
|
|
324
324
|
for (let i = 0; i < value.length; i++) {
|
|
325
|
-
textLines.push(`${csUtils.roundNumber(value[i])} ${
|
|
325
|
+
textLines.push(`${csUtils.roundNumber(value[i])} ${modalityUnit[i]}`);
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
else {
|
|
329
|
-
textLines.push(`${csUtils.roundNumber(value)} ${
|
|
329
|
+
textLines.push(`${csUtils.roundNumber(value)} ${modalityUnit}`);
|
|
330
330
|
}
|
|
331
331
|
return textLines;
|
|
332
332
|
}
|
|
@@ -346,14 +346,14 @@ class RectangleROITool extends AnnotationTool {
|
|
|
346
346
|
});
|
|
347
347
|
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
348
348
|
if (!data.cachedStats[targetId] ||
|
|
349
|
-
data.cachedStats[targetId].
|
|
349
|
+
data.cachedStats[targetId].areaUnit == null) {
|
|
350
350
|
data.cachedStats[targetId] = {
|
|
351
351
|
Modality: null,
|
|
352
352
|
area: null,
|
|
353
353
|
max: null,
|
|
354
354
|
mean: null,
|
|
355
355
|
stdDev: null,
|
|
356
|
-
|
|
356
|
+
areaUnit: null,
|
|
357
357
|
};
|
|
358
358
|
this._calculateCachedStats(annotation, viewPlaneNormal, viewUp, renderingEngine, enabledElement);
|
|
359
359
|
}
|
|
@@ -487,13 +487,13 @@ class RectangleROITool extends AnnotationTool {
|
|
|
487
487
|
];
|
|
488
488
|
const { worldWidth, worldHeight } = getWorldWidthAndHeightFromCorners(viewPlaneNormal, viewUp, worldPos1, worldPos2);
|
|
489
489
|
const handles = [pos1Index, pos2Index];
|
|
490
|
-
const { scale,
|
|
490
|
+
const { scale, areaUnit } = getCalibratedLengthUnitsAndScale(image, handles);
|
|
491
491
|
const area = Math.abs(worldWidth * worldHeight) / (scale * scale);
|
|
492
492
|
const pixelUnitsOptions = {
|
|
493
493
|
isPreScaled: isViewportPreScaled(viewport, targetId),
|
|
494
494
|
isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
|
|
495
495
|
};
|
|
496
|
-
const
|
|
496
|
+
const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, pixelUnitsOptions);
|
|
497
497
|
const pointsInShape = voxelManager.forEach(this.configuration.statsCalculator.statsCallback, {
|
|
498
498
|
boundsIJK,
|
|
499
499
|
imageData,
|
|
@@ -508,8 +508,8 @@ class RectangleROITool extends AnnotationTool {
|
|
|
508
508
|
max: stats.max?.value,
|
|
509
509
|
statsArray: stats.array,
|
|
510
510
|
pointsInShape: pointsInShape,
|
|
511
|
-
|
|
512
|
-
|
|
511
|
+
areaUnit,
|
|
512
|
+
modalityUnit,
|
|
513
513
|
};
|
|
514
514
|
}
|
|
515
515
|
else {
|
|
@@ -532,15 +532,15 @@ class RectangleROITool extends AnnotationTool {
|
|
|
532
532
|
}
|
|
533
533
|
function defaultGetTextLines(data, targetId) {
|
|
534
534
|
const cachedVolumeStats = data.cachedStats[targetId];
|
|
535
|
-
const { area, mean, max, stdDev,
|
|
535
|
+
const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats;
|
|
536
536
|
if (mean === undefined) {
|
|
537
537
|
return;
|
|
538
538
|
}
|
|
539
539
|
const textLines = [];
|
|
540
|
-
textLines.push(`Area: ${csUtils.roundNumber(area)} ${
|
|
541
|
-
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${
|
|
542
|
-
textLines.push(`Max: ${csUtils.roundNumber(max)} ${
|
|
543
|
-
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${
|
|
540
|
+
textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnit}`);
|
|
541
|
+
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
|
|
542
|
+
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
|
|
543
|
+
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
|
|
544
544
|
return textLines;
|
|
545
545
|
}
|
|
546
546
|
RectangleROITool.toolName = 'RectangleROI';
|
|
@@ -452,7 +452,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
452
452
|
const deltaInX = vec3.distance(originalWorldPoint, deltaXPoint);
|
|
453
453
|
const deltaInY = vec3.distance(originalWorldPoint, deltaYPoint);
|
|
454
454
|
const { imageData } = image;
|
|
455
|
-
const { scale,
|
|
455
|
+
const { scale, areaUnit } = getCalibratedLengthUnitsAndScale(image, () => {
|
|
456
456
|
const { maxX: canvasMaxX, maxY: canvasMaxY, minX: canvasMinX, minY: canvasMinY, } = math.polyline.getAABB(canvasCoordinates);
|
|
457
457
|
const topLeftBBWorld = viewport.canvasToWorld([
|
|
458
458
|
canvasMinX,
|
|
@@ -471,7 +471,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
471
471
|
cachedStats[targetId] = {
|
|
472
472
|
Modality: metadata.Modality,
|
|
473
473
|
area,
|
|
474
|
-
|
|
474
|
+
areaUnit,
|
|
475
475
|
};
|
|
476
476
|
}
|
|
477
477
|
this.triggerAnnotationModified(annotation, enabledElement, ChangeTypes.StatsUpdated);
|
|
@@ -560,11 +560,11 @@ class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
560
560
|
});
|
|
561
561
|
super.renderAnnotationInstance(renderContext);
|
|
562
562
|
if (!data.cachedStats[targetId] ||
|
|
563
|
-
data.cachedStats[targetId].
|
|
563
|
+
data.cachedStats[targetId].areaUnit == null) {
|
|
564
564
|
data.cachedStats[targetId] = {
|
|
565
565
|
Modality: null,
|
|
566
566
|
area: null,
|
|
567
|
-
|
|
567
|
+
areaUnit: null,
|
|
568
568
|
};
|
|
569
569
|
this._calculateCachedStats(annotation, element);
|
|
570
570
|
}
|
|
@@ -715,12 +715,12 @@ class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
715
715
|
}
|
|
716
716
|
function defaultGetTextLines(data, targetId) {
|
|
717
717
|
const cachedVolumeStats = data.cachedStats[targetId];
|
|
718
|
-
const { area, isEmptyArea,
|
|
718
|
+
const { area, isEmptyArea, areaUnit } = cachedVolumeStats;
|
|
719
719
|
const textLines = [];
|
|
720
720
|
if (area) {
|
|
721
721
|
const areaLine = isEmptyArea
|
|
722
722
|
? `Area: Oblique not supported`
|
|
723
|
-
: `Area: ${utilities.roundNumber(area)} ${
|
|
723
|
+
: `Area: ${utilities.roundNumber(area)} ${areaUnit}`;
|
|
724
724
|
textLines.push(areaLine);
|
|
725
725
|
}
|
|
726
726
|
return textLines;
|
|
@@ -5,7 +5,6 @@ import WindowLevelTool from './WindowLevelTool';
|
|
|
5
5
|
import WindowLevelRegionTool from './WindowLevelRegionTool';
|
|
6
6
|
import StackScrollTool from './StackScrollTool';
|
|
7
7
|
import PlanarRotateTool from './PlanarRotateTool';
|
|
8
|
-
import StackScrollMouseWheelTool from './StackScrollToolMouseWheelTool';
|
|
9
8
|
import ZoomTool from './ZoomTool';
|
|
10
9
|
import VolumeRotateMouseWheelTool from './VolumeRotateMouseWheelTool';
|
|
11
10
|
import MIPJumpToClickTool from './MIPJumpToClickTool';
|
|
@@ -16,7 +15,6 @@ import ReferenceLinesTool from './ReferenceLinesTool';
|
|
|
16
15
|
import OverlayGridTool from './OverlayGridTool';
|
|
17
16
|
import SegmentationIntersectionTool from './SegmentationIntersectionTool';
|
|
18
17
|
import ReferenceCursors from './ReferenceCursors';
|
|
19
|
-
import ReferenceLines from './ReferenceLinesTool';
|
|
20
18
|
import ScaleOverlayTool from './ScaleOverlayTool';
|
|
21
19
|
import SculptorTool from './SculptorTool';
|
|
22
20
|
import BidirectionalTool from './annotation/BidirectionalTool';
|
|
@@ -49,4 +47,4 @@ import BrushTool from './segmentation/BrushTool';
|
|
|
49
47
|
import PaintFillTool from './segmentation/PaintFillTool';
|
|
50
48
|
import OrientationMarkerTool from './OrientationMarkerTool';
|
|
51
49
|
import SegmentSelectTool from './segmentation/SegmentSelectTool';
|
|
52
|
-
export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool,
|
|
50
|
+
export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, ZoomTool, VolumeRotateMouseWheelTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, SculptorTool, SegmentSelectTool, };
|
package/dist/esm/tools/index.js
CHANGED
|
@@ -5,7 +5,6 @@ import WindowLevelTool from './WindowLevelTool';
|
|
|
5
5
|
import WindowLevelRegionTool from './WindowLevelRegionTool';
|
|
6
6
|
import StackScrollTool from './StackScrollTool';
|
|
7
7
|
import PlanarRotateTool from './PlanarRotateTool';
|
|
8
|
-
import StackScrollMouseWheelTool from './StackScrollToolMouseWheelTool';
|
|
9
8
|
import ZoomTool from './ZoomTool';
|
|
10
9
|
import VolumeRotateMouseWheelTool from './VolumeRotateMouseWheelTool';
|
|
11
10
|
import MIPJumpToClickTool from './MIPJumpToClickTool';
|
|
@@ -16,7 +15,6 @@ import ReferenceLinesTool from './ReferenceLinesTool';
|
|
|
16
15
|
import OverlayGridTool from './OverlayGridTool';
|
|
17
16
|
import SegmentationIntersectionTool from './SegmentationIntersectionTool';
|
|
18
17
|
import ReferenceCursors from './ReferenceCursors';
|
|
19
|
-
import ReferenceLines from './ReferenceLinesTool';
|
|
20
18
|
import ScaleOverlayTool from './ScaleOverlayTool';
|
|
21
19
|
import SculptorTool from './SculptorTool';
|
|
22
20
|
import BidirectionalTool from './annotation/BidirectionalTool';
|
|
@@ -49,4 +47,4 @@ import BrushTool from './segmentation/BrushTool';
|
|
|
49
47
|
import PaintFillTool from './segmentation/PaintFillTool';
|
|
50
48
|
import OrientationMarkerTool from './OrientationMarkerTool';
|
|
51
49
|
import SegmentSelectTool from './segmentation/SegmentSelectTool';
|
|
52
|
-
export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool,
|
|
50
|
+
export { BaseTool, AnnotationTool, AnnotationDisplayTool, PanTool, TrackballRotateTool, DragProbeTool, WindowLevelTool, WindowLevelRegionTool, StackScrollTool, PlanarRotateTool, ZoomTool, VolumeRotateMouseWheelTool, MIPJumpToClickTool, ReferenceCursors, CrosshairsTool, ReferenceLinesTool, OverlayGridTool, SegmentationIntersectionTool, BidirectionalTool, LengthTool, HeightTool, ProbeTool, RectangleROITool, EllipticalROITool, CircleROITool, SplineROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, LivewireContourTool, LivewireContourSegmentationTool, ArrowAnnotateTool, AngleTool, CobbAngleTool, UltrasoundDirectionalTool, KeyImageTool, AnnotationEraserTool as EraserTool, RectangleScissorsTool, CircleScissorsTool, SphereScissorsTool, RectangleROIThresholdTool, RectangleROIStartEndThresholdTool, CircleROIStartEndThresholdTool, SplineContourSegmentationTool, BrushTool, MagnifyTool, AdvancedMagnifyTool, PaintFillTool, ScaleOverlayTool, OrientationMarkerTool, SculptorTool, SegmentSelectTool, };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Types } from '@cornerstonejs/core';
|
|
2
2
|
import type { PublicToolProps, ToolProps, EventTypes, SVGDrawingHelper } from '../../types';
|
|
3
3
|
import { BaseTool } from '../base';
|
|
4
|
+
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
4
5
|
export type PreviewData = {
|
|
5
6
|
preview: unknown;
|
|
6
7
|
timer?: number;
|
|
@@ -24,11 +25,22 @@ declare class BrushTool extends BaseTool {
|
|
|
24
25
|
referencedVolumeId: any;
|
|
25
26
|
segmentsLocked: number[] | [];
|
|
26
27
|
imageId?: undefined;
|
|
28
|
+
override?: undefined;
|
|
27
29
|
} | {
|
|
28
30
|
imageId: string;
|
|
29
31
|
segmentsLocked: number[] | [];
|
|
30
32
|
volumeId?: undefined;
|
|
31
33
|
referencedVolumeId?: undefined;
|
|
34
|
+
override?: undefined;
|
|
35
|
+
} | {
|
|
36
|
+
imageId: string;
|
|
37
|
+
segmentsLocked: number[] | [];
|
|
38
|
+
override: {
|
|
39
|
+
voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
|
|
40
|
+
imageData: vtkImageData;
|
|
41
|
+
};
|
|
42
|
+
volumeId?: undefined;
|
|
43
|
+
referencedVolumeId?: undefined;
|
|
32
44
|
};
|
|
33
45
|
preMouseDownCallback: (evt: EventTypes.MouseDownActivateEventType) => boolean;
|
|
34
46
|
mouseMoveCallback: (evt: EventTypes.InteractionEventType) => void;
|
|
@@ -48,8 +60,13 @@ declare class BrushTool extends BaseTool {
|
|
|
48
60
|
viewUp: any;
|
|
49
61
|
strategySpecificConfiguration: any;
|
|
50
62
|
preview: unknown;
|
|
63
|
+
override: {
|
|
64
|
+
voxelManager: Types.IVoxelManager<number>;
|
|
65
|
+
imageData: vtkImageData;
|
|
66
|
+
};
|
|
51
67
|
segmentsLocked: number[];
|
|
52
68
|
imageId?: string;
|
|
69
|
+
imageIds?: string[];
|
|
53
70
|
volumeId?: string;
|
|
54
71
|
referencedVolumeId?: string;
|
|
55
72
|
} | {
|
|
@@ -67,6 +84,23 @@ declare class BrushTool extends BaseTool {
|
|
|
67
84
|
referencedVolumeId: any;
|
|
68
85
|
segmentsLocked: number[] | [];
|
|
69
86
|
imageId?: undefined;
|
|
87
|
+
override?: undefined;
|
|
88
|
+
} | {
|
|
89
|
+
points: any;
|
|
90
|
+
segmentIndex: number;
|
|
91
|
+
previewColors: any;
|
|
92
|
+
viewPlaneNormal: any;
|
|
93
|
+
toolGroupId: string;
|
|
94
|
+
segmentationId: string;
|
|
95
|
+
segmentationRepresentationUID: string;
|
|
96
|
+
viewUp: any;
|
|
97
|
+
strategySpecificConfiguration: any;
|
|
98
|
+
preview: unknown;
|
|
99
|
+
imageId: string;
|
|
100
|
+
segmentsLocked: number[] | [];
|
|
101
|
+
volumeId?: undefined;
|
|
102
|
+
referencedVolumeId?: undefined;
|
|
103
|
+
override?: undefined;
|
|
70
104
|
} | {
|
|
71
105
|
points: any;
|
|
72
106
|
segmentIndex: number;
|
|
@@ -80,6 +114,10 @@ declare class BrushTool extends BaseTool {
|
|
|
80
114
|
preview: unknown;
|
|
81
115
|
imageId: string;
|
|
82
116
|
segmentsLocked: number[] | [];
|
|
117
|
+
override: {
|
|
118
|
+
voxelManager: Types.IVoxelManager<number> | Types.IVoxelManager<Types.RGB>;
|
|
119
|
+
imageData: vtkImageData;
|
|
120
|
+
};
|
|
83
121
|
volumeId?: undefined;
|
|
84
122
|
referencedVolumeId?: undefined;
|
|
85
123
|
};
|
|
@@ -10,10 +10,11 @@ import { drawCircle as drawCircleSvg } from '../../drawingSvg';
|
|
|
10
10
|
import { resetElementCursor, hideElementCursor, } from '../../cursors/elementCursor';
|
|
11
11
|
import triggerAnnotationRenderForViewportUIDs from '../../utilities/triggerAnnotationRenderForViewportIds';
|
|
12
12
|
import { isVolumeSegmentation } from './strategies/utils/stackVolumeCheck';
|
|
13
|
-
import { getActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, getSegmentation, } from '../../stateManagement/segmentation/segmentationState';
|
|
13
|
+
import { getActiveSegmentationRepresentation, getCurrentLabelmapImageIdForViewport, getSegmentation, getStackSegmentationImageIdsForViewport, } from '../../stateManagement/segmentation/segmentationState';
|
|
14
14
|
import { getLockedSegmentIndices } from '../../stateManagement/segmentation/segmentLocking';
|
|
15
15
|
import { getActiveSegmentIndex } from '../../stateManagement/segmentation/getActiveSegmentIndex';
|
|
16
16
|
import { getSegmentIndexColor } from '../../stateManagement/segmentation/config/segmentationColor';
|
|
17
|
+
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
17
18
|
class BrushTool extends BaseTool {
|
|
18
19
|
constructor(toolProps = {}, defaultToolProps = {
|
|
19
20
|
supportedInteractionTypes: ['Mouse', 'Touch'],
|
|
@@ -141,7 +142,6 @@ class BrushTool extends BaseTool {
|
|
|
141
142
|
const eventData = evt.detail;
|
|
142
143
|
const { element, currentPoints } = eventData;
|
|
143
144
|
const enabledElement = getEnabledElement(element);
|
|
144
|
-
const { renderingEngine } = enabledElement;
|
|
145
145
|
this.updateCursor(evt);
|
|
146
146
|
const { viewportIdsToRender } = this._hoverData;
|
|
147
147
|
triggerAnnotationRenderForViewportUIDs(viewportIdsToRender);
|
|
@@ -235,12 +235,45 @@ class BrushTool extends BaseTool {
|
|
|
235
235
|
return;
|
|
236
236
|
}
|
|
237
237
|
if (this.configuration.activeStrategy.includes('SPHERE')) {
|
|
238
|
-
|
|
238
|
+
const referencedImageIds = viewport.getImageIds();
|
|
239
|
+
const isValidVolumeForSphere = csUtils.isValidVolume(referencedImageIds);
|
|
240
|
+
if (!isValidVolumeForSphere) {
|
|
241
|
+
throw new Error('Volume is not reconstructable for sphere manipulation');
|
|
242
|
+
}
|
|
243
|
+
const labelmapImageIds = getStackSegmentationImageIdsForViewport(viewport.id, segmentationId);
|
|
244
|
+
if (!labelmapImageIds || labelmapImageIds.length === 1) {
|
|
245
|
+
return {
|
|
246
|
+
imageId: segmentationImageId,
|
|
247
|
+
segmentsLocked,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const tempVolumeId = 'tempVolumeId';
|
|
251
|
+
const { dimensions, direction, origin, spacing, numberOfComponents, imageIds: sortedLabelmapImageIds, } = csUtils.generateVolumePropsFromImageIds(labelmapImageIds, tempVolumeId);
|
|
252
|
+
const newVoxelManager = csUtils.VoxelManager.createImageVolumeVoxelManager({
|
|
253
|
+
dimensions,
|
|
254
|
+
imageIds: sortedLabelmapImageIds,
|
|
255
|
+
numberOfComponents,
|
|
256
|
+
});
|
|
257
|
+
const newImageData = vtkImageData.newInstance();
|
|
258
|
+
newImageData.setDimensions(dimensions);
|
|
259
|
+
newImageData.setSpacing(spacing);
|
|
260
|
+
newImageData.setDirection(direction);
|
|
261
|
+
newImageData.setOrigin(origin);
|
|
262
|
+
return {
|
|
263
|
+
imageId: segmentationImageId,
|
|
264
|
+
segmentsLocked,
|
|
265
|
+
override: {
|
|
266
|
+
voxelManager: newVoxelManager,
|
|
267
|
+
imageData: newImageData,
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
return {
|
|
273
|
+
imageId: segmentationImageId,
|
|
274
|
+
segmentsLocked,
|
|
275
|
+
};
|
|
239
276
|
}
|
|
240
|
-
return {
|
|
241
|
-
imageId: segmentationImageId,
|
|
242
|
-
segmentsLocked,
|
|
243
|
-
};
|
|
244
277
|
}
|
|
245
278
|
}
|
|
246
279
|
createHoverData(element, centerCanvas) {
|
|
@@ -345,7 +345,7 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
|
|
|
345
345
|
isPreScaled: isViewportPreScaled(viewport, targetId),
|
|
346
346
|
isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
|
|
347
347
|
};
|
|
348
|
-
const
|
|
348
|
+
const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
|
|
349
349
|
for (let i = 0; i < projectionPoints.length; i++) {
|
|
350
350
|
if (!imageVolume) {
|
|
351
351
|
continue;
|
|
@@ -409,8 +409,8 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
|
|
|
409
409
|
stdDev: stats.stdDev?.value,
|
|
410
410
|
max: stats.max?.value,
|
|
411
411
|
statsArray: stats.array,
|
|
412
|
-
|
|
413
|
-
|
|
412
|
+
areaUnit: measureInfo.areaUnit,
|
|
413
|
+
modalityUnit,
|
|
414
414
|
};
|
|
415
415
|
}
|
|
416
416
|
_calculateCachedStatsTool(annotation, enabledElement) {
|
|
@@ -456,15 +456,15 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
|
|
|
456
456
|
}
|
|
457
457
|
function defaultGetTextLines(data) {
|
|
458
458
|
const cachedVolumeStats = data.cachedStats.statistics;
|
|
459
|
-
const { area, mean, max, stdDev,
|
|
459
|
+
const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats;
|
|
460
460
|
if (mean === undefined) {
|
|
461
461
|
return;
|
|
462
462
|
}
|
|
463
463
|
const textLines = [];
|
|
464
|
-
textLines.push(`Area: ${csUtils.roundNumber(area)} ${
|
|
465
|
-
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${
|
|
466
|
-
textLines.push(`Max: ${csUtils.roundNumber(max)} ${
|
|
467
|
-
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${
|
|
464
|
+
textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnit}`);
|
|
465
|
+
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
|
|
466
|
+
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
|
|
467
|
+
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
|
|
468
468
|
return textLines;
|
|
469
469
|
}
|
|
470
470
|
CircleROIStartEndThresholdTool.toolName = 'CircleROIStartEndThreshold';
|
|
@@ -316,7 +316,7 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
316
316
|
isPreScaled: isViewportPreScaled(viewport, targetId),
|
|
317
317
|
isSuvScaled: this.isSuvScaled(viewport, targetId, annotation.metadata.referencedImageId),
|
|
318
318
|
};
|
|
319
|
-
const
|
|
319
|
+
const modalityUnit = getPixelValueUnits(metadata.Modality, annotation.metadata.referencedImageId, modalityUnitOptions);
|
|
320
320
|
for (let i = 0; i < projectionPoints.length; i++) {
|
|
321
321
|
if (!imageVolume) {
|
|
322
322
|
continue;
|
|
@@ -367,8 +367,8 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
367
367
|
stdDev: stats.stdDev?.value,
|
|
368
368
|
max: stats.max?.value,
|
|
369
369
|
statsArray: stats.array,
|
|
370
|
-
|
|
371
|
-
|
|
370
|
+
areaUnit: measureInfo.areaUnit,
|
|
371
|
+
modalityUnit,
|
|
372
372
|
};
|
|
373
373
|
}
|
|
374
374
|
_calculateCachedStatsTool(annotation, enabledElement) {
|
|
@@ -410,15 +410,15 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
410
410
|
}
|
|
411
411
|
function defaultGetTextLines(data) {
|
|
412
412
|
const cachedVolumeStats = data.cachedStats.statistics;
|
|
413
|
-
const { area, mean, max, stdDev,
|
|
413
|
+
const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats;
|
|
414
414
|
if (mean === undefined) {
|
|
415
415
|
return;
|
|
416
416
|
}
|
|
417
417
|
const textLines = [];
|
|
418
|
-
textLines.push(`Area: ${csUtils.roundNumber(area)} ${
|
|
419
|
-
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${
|
|
420
|
-
textLines.push(`Max: ${csUtils.roundNumber(max)} ${
|
|
421
|
-
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${
|
|
418
|
+
textLines.push(`Area: ${csUtils.roundNumber(area)} ${areaUnit}`);
|
|
419
|
+
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
|
|
420
|
+
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
|
|
421
|
+
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
|
|
422
422
|
return textLines;
|
|
423
423
|
}
|
|
424
424
|
RectangleROIStartEndThresholdTool.toolName = 'RectangleROIStartEndThreshold';
|
|
@@ -37,7 +37,8 @@ export default class BrushStrategy {
|
|
|
37
37
|
}
|
|
38
38
|
this._fill.forEach((func) => func(initializedData));
|
|
39
39
|
const { segmentationVoxelManager, previewVoxelManager, previewSegmentIndex, } = initializedData;
|
|
40
|
-
triggerSegmentationDataModified(initializedData.segmentationId, segmentationVoxelManager.
|
|
40
|
+
triggerSegmentationDataModified(initializedData.segmentationId, segmentationVoxelManager.getArrayOfModifiedSlices());
|
|
41
|
+
segmentationVoxelManager.resetModifiedSlices();
|
|
41
42
|
if (!previewSegmentIndex || !previewVoxelManager.modifiedSlices.size) {
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
@@ -69,7 +70,9 @@ export default class BrushStrategy {
|
|
|
69
70
|
BrushStrategy.childFunctions[key](this, result[key]);
|
|
70
71
|
}
|
|
71
72
|
});
|
|
72
|
-
this.strategyFunction = (enabledElement, operationData) =>
|
|
73
|
+
this.strategyFunction = (enabledElement, operationData) => {
|
|
74
|
+
return this.fill(enabledElement, operationData);
|
|
75
|
+
};
|
|
73
76
|
for (const key of Object.keys(BrushStrategy.childFunctions)) {
|
|
74
77
|
this.strategyFunction[key] = this[key];
|
|
75
78
|
}
|
|
@@ -82,9 +85,11 @@ export default class BrushStrategy {
|
|
|
82
85
|
return operationData.preview;
|
|
83
86
|
}
|
|
84
87
|
const { imageVoxelManager, segmentationVoxelManager, segmentationImageData, } = data;
|
|
88
|
+
const segmentationVoxelManagerToUse = operationData.override?.voxelManager || segmentationVoxelManager;
|
|
89
|
+
const segmentationImageDataToUse = operationData.override?.imageData || segmentationImageData;
|
|
85
90
|
const previewVoxelManager = operationData.preview?.previewVoxelManager ||
|
|
86
91
|
VoxelManager.createHistoryVoxelManager({
|
|
87
|
-
sourceVoxelManager:
|
|
92
|
+
sourceVoxelManager: segmentationVoxelManagerToUse,
|
|
88
93
|
});
|
|
89
94
|
const previewEnabled = !!operationData.previewColors;
|
|
90
95
|
const previewSegmentIndex = previewEnabled ? 255 : undefined;
|
|
@@ -94,8 +99,8 @@ export default class BrushStrategy {
|
|
|
94
99
|
...operationData,
|
|
95
100
|
enabledElement,
|
|
96
101
|
imageVoxelManager,
|
|
97
|
-
segmentationVoxelManager,
|
|
98
|
-
segmentationImageData,
|
|
102
|
+
segmentationVoxelManager: segmentationVoxelManagerToUse,
|
|
103
|
+
segmentationImageData: segmentationImageDataToUse,
|
|
99
104
|
previewVoxelManager,
|
|
100
105
|
viewport,
|
|
101
106
|
centerWorld: null,
|
|
@@ -118,6 +118,6 @@ export default {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.
|
|
121
|
+
triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfModifiedSlices());
|
|
122
122
|
},
|
|
123
123
|
};
|
|
@@ -66,7 +66,7 @@ export default {
|
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
68
|
tracking.forEach(callback, {});
|
|
69
|
-
triggerSegmentationDataModified(operationData.segmentationId, tracking.
|
|
69
|
+
triggerSegmentationDataModified(operationData.segmentationId, tracking.getArrayOfModifiedSlices());
|
|
70
70
|
tracking.clear();
|
|
71
71
|
},
|
|
72
72
|
[StrategyCallbacks.RejectPreview]: (operationData) => {
|
|
@@ -78,7 +78,7 @@ export default {
|
|
|
78
78
|
segmentationVoxelManager.setAtIndex(index, value);
|
|
79
79
|
};
|
|
80
80
|
previewVoxelManager.forEach(callback);
|
|
81
|
-
triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.
|
|
81
|
+
triggerSegmentationDataModified(operationData.segmentationId, previewVoxelManager.getArrayOfModifiedSlices());
|
|
82
82
|
previewVoxelManager.clear();
|
|
83
83
|
},
|
|
84
84
|
};
|