@cornerstonejs/tools 3.6.0 → 3.7.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/esm/tools/annotation/AngleTool.d.ts +4 -0
- package/dist/esm/tools/annotation/AngleTool.js +1 -5
- package/dist/esm/tools/annotation/ArrowAnnotateTool.d.ts +4 -0
- package/dist/esm/tools/annotation/ArrowAnnotateTool.js +1 -5
- package/dist/esm/tools/annotation/BidirectionalTool.d.ts +4 -0
- package/dist/esm/tools/annotation/BidirectionalTool.js +13 -5
- package/dist/esm/tools/annotation/CircleROITool.d.ts +4 -0
- package/dist/esm/tools/annotation/CircleROITool.js +1 -5
- package/dist/esm/tools/annotation/EllipticalROITool.d.ts +4 -0
- package/dist/esm/tools/annotation/EllipticalROITool.js +1 -5
- package/dist/esm/tools/annotation/LengthTool.d.ts +4 -0
- package/dist/esm/tools/annotation/LengthTool.js +1 -5
- package/dist/esm/tools/annotation/ProbeTool.d.ts +4 -0
- package/dist/esm/tools/annotation/ProbeTool.js +1 -5
- package/dist/esm/tools/annotation/RectangleROITool.d.ts +4 -0
- package/dist/esm/tools/annotation/RectangleROITool.js +1 -5
- package/dist/esm/tools/annotation/SplineROITool.d.ts +4 -0
- package/dist/esm/tools/annotation/SplineROITool.js +1 -10
- package/dist/esm/tools/base/AnnotationTool.d.ts +15 -0
- package/dist/esm/tools/base/AnnotationTool.js +42 -1
- package/dist/esm/tools/segmentation/SegmentBidirectionalTool.d.ts +4 -0
- package/dist/esm/tools/segmentation/SegmentBidirectionalTool.js +12 -4
- package/dist/esm/tools/segmentation/strategies/compositions/ensureImageVolume.js +5 -8
- package/dist/esm/types/CalculatorTypes.d.ts +6 -0
- package/dist/esm/utilities/math/basic/BasicStatsCalculator.js +21 -9
- package/dist/esm/utilities/segmentation/VolumetricCalculator.js +11 -2
- package/dist/esm/utilities/segmentation/computeMetabolicStats.d.ts +8 -0
- package/dist/esm/utilities/segmentation/computeMetabolicStats.js +58 -0
- package/dist/esm/utilities/segmentation/createMergedLabelmapForIndex.js +10 -2
- package/dist/esm/utilities/segmentation/getOrCreateImageVolume.d.ts +3 -0
- package/dist/esm/utilities/segmentation/getOrCreateImageVolume.js +18 -0
- package/dist/esm/utilities/segmentation/getOrCreateSegmentationVolume.d.ts +2 -1
- package/dist/esm/utilities/segmentation/getOrCreateSegmentationVolume.js +1 -1
- package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentation.d.ts +1 -0
- package/dist/esm/utilities/segmentation/getReferenceVolumeForSegmentation.js +34 -0
- package/dist/esm/utilities/segmentation/getStatistics.js +8 -1
- package/dist/esm/utilities/segmentation/index.d.ts +3 -1
- package/dist/esm/utilities/segmentation/index.js +3 -1
- package/dist/esm/workers/computeWorker.js +148 -94
- package/package.json +4 -4
|
@@ -19,6 +19,10 @@ declare class AngleTool extends AnnotationTool {
|
|
|
19
19
|
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
|
|
20
20
|
static hydrate: (viewportId: string, points: Types.Point3[], options?: {
|
|
21
21
|
annotationUID?: string;
|
|
22
|
+
toolInstance?: AngleTool;
|
|
23
|
+
referencedImageId?: string;
|
|
24
|
+
viewplaneNormal?: Types.Point3;
|
|
25
|
+
viewUp?: Types.Point3;
|
|
22
26
|
}) => AngleAnnotation;
|
|
23
27
|
addNewAnnotation: (evt: EventTypes.InteractionEventType) => AngleAnnotation;
|
|
24
28
|
isPointNearTool: (element: HTMLDivElement, annotation: AngleAnnotation, canvasCoords: Types.Point2, proximity: number) => boolean;
|
|
@@ -390,11 +390,7 @@ class AngleTool extends AnnotationTool {
|
|
|
390
390
|
if (!enabledElement) {
|
|
391
391
|
return;
|
|
392
392
|
}
|
|
393
|
-
const { viewport } = enabledElement;
|
|
394
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
395
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
396
|
-
const instance = new this();
|
|
397
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
393
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, viewport, } = this.hydrateBase(AngleTool, enabledElement, points, options);
|
|
398
394
|
const annotation = {
|
|
399
395
|
annotationUID: options?.annotationUID || csUtils.uuidv4(),
|
|
400
396
|
data: {
|
|
@@ -18,6 +18,10 @@ declare class ArrowAnnotateTool extends AnnotationTool {
|
|
|
18
18
|
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
|
|
19
19
|
static hydrate: (viewportId: string, points: Types.Point3[], text?: string, options?: {
|
|
20
20
|
annotationUID?: string;
|
|
21
|
+
toolInstance?: ArrowAnnotateTool;
|
|
22
|
+
referencedImageId?: string;
|
|
23
|
+
viewplaneNormal?: Types.Point3;
|
|
24
|
+
viewUp?: Types.Point3;
|
|
21
25
|
}) => ArrowAnnotation;
|
|
22
26
|
addNewAnnotation: (evt: EventTypes.InteractionEventType) => ArrowAnnotation;
|
|
23
27
|
isPointNearTool: (element: HTMLDivElement, annotation: ArrowAnnotation, canvasCoords: Types.Point2, proximity: number) => boolean;
|
|
@@ -392,11 +392,7 @@ class ArrowAnnotateTool extends AnnotationTool {
|
|
|
392
392
|
if (!enabledElement) {
|
|
393
393
|
return;
|
|
394
394
|
}
|
|
395
|
-
const { viewport } = enabledElement;
|
|
396
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
397
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
398
|
-
const instance = new this();
|
|
399
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
395
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, viewport, } = this.hydrateBase(ArrowAnnotateTool, enabledElement, points, options);
|
|
400
396
|
const annotation = {
|
|
401
397
|
annotationUID: options?.annotationUID || csUtils.uuidv4(),
|
|
402
398
|
data: {
|
|
@@ -20,6 +20,10 @@ declare class BidirectionalTool extends AnnotationTool {
|
|
|
20
20
|
addNewAnnotation(evt: EventTypes.InteractionEventType): BidirectionalAnnotation;
|
|
21
21
|
static hydrate: (viewportId: string, axis: [[Types.Point3, Types.Point3], [Types.Point3, Types.Point3]], options?: {
|
|
22
22
|
annotationUID?: string;
|
|
23
|
+
toolInstance?: BidirectionalTool;
|
|
24
|
+
referencedImageId?: string;
|
|
25
|
+
viewplaneNormal?: Types.Point3;
|
|
26
|
+
viewUp?: Types.Point3;
|
|
23
27
|
}) => BidirectionalAnnotation;
|
|
24
28
|
isPointNearTool: (element: HTMLDivElement, annotation: BidirectionalAnnotation, canvasCoords: Types.Point2, proximity: number) => boolean;
|
|
25
29
|
toolSelectedCallback: (evt: EventTypes.InteractionEventType, annotation: BidirectionalAnnotation) => void;
|
|
@@ -714,22 +714,29 @@ class BidirectionalTool extends AnnotationTool {
|
|
|
714
714
|
if (!enabledElement) {
|
|
715
715
|
return;
|
|
716
716
|
}
|
|
717
|
-
const { viewport } = enabledElement;
|
|
718
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
719
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
720
|
-
const instance = new this();
|
|
717
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, viewport, } = this.hydrateBase(BidirectionalTool, enabledElement, [], options);
|
|
721
718
|
const [majorAxis, minorAxis] = axis;
|
|
722
719
|
const [major0, major1] = majorAxis;
|
|
723
720
|
const [minor0, minor1] = minorAxis;
|
|
724
721
|
const points = [major0, major1, minor0, minor1];
|
|
725
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
726
722
|
const annotation = {
|
|
727
723
|
annotationUID: options?.annotationUID || utilities.uuidv4(),
|
|
728
724
|
data: {
|
|
729
725
|
handles: {
|
|
730
726
|
points,
|
|
731
727
|
activeHandleIndex: null,
|
|
728
|
+
textBox: {
|
|
729
|
+
hasMoved: false,
|
|
730
|
+
worldPosition: [0, 0, 0],
|
|
731
|
+
worldBoundingBox: {
|
|
732
|
+
topLeft: [0, 0, 0],
|
|
733
|
+
topRight: [0, 0, 0],
|
|
734
|
+
bottomLeft: [0, 0, 0],
|
|
735
|
+
bottomRight: [0, 0, 0],
|
|
736
|
+
},
|
|
737
|
+
},
|
|
732
738
|
},
|
|
739
|
+
cachedStats: {},
|
|
733
740
|
},
|
|
734
741
|
highlighted: false,
|
|
735
742
|
autoGenerated: false,
|
|
@@ -746,6 +753,7 @@ class BidirectionalTool extends AnnotationTool {
|
|
|
746
753
|
};
|
|
747
754
|
addAnnotation(annotation, viewport.element);
|
|
748
755
|
triggerAnnotationRenderForViewportIds([viewport.id]);
|
|
756
|
+
return annotation;
|
|
749
757
|
}; }
|
|
750
758
|
_calculateLength(pos1, pos2) {
|
|
751
759
|
const dx = pos1[0] - pos2[0];
|
|
@@ -34,6 +34,10 @@ declare class CircleROITool extends AnnotationTool {
|
|
|
34
34
|
_isInsideVolume: (index1: any, index2: any, dimensions: any) => boolean;
|
|
35
35
|
static hydrate: (viewportId: string, points: Types.Point3[], options?: {
|
|
36
36
|
annotationUID?: string;
|
|
37
|
+
toolInstance?: CircleROITool;
|
|
38
|
+
referencedImageId?: string;
|
|
39
|
+
viewplaneNormal?: Types.Point3;
|
|
40
|
+
viewUp?: Types.Point3;
|
|
37
41
|
}) => CircleROIAnnotation;
|
|
38
42
|
}
|
|
39
43
|
export default CircleROITool;
|
|
@@ -570,11 +570,7 @@ class CircleROITool extends AnnotationTool {
|
|
|
570
570
|
if (!enabledElement) {
|
|
571
571
|
return;
|
|
572
572
|
}
|
|
573
|
-
const { viewport } = enabledElement;
|
|
574
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
575
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
576
|
-
const instance = new this();
|
|
577
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
573
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, viewport, } = this.hydrateBase(CircleROITool, enabledElement, points, options);
|
|
578
574
|
const annotation = {
|
|
579
575
|
annotationUID: options?.annotationUID || csUtils.uuidv4(),
|
|
580
576
|
data: {
|
|
@@ -22,6 +22,10 @@ declare class EllipticalROITool extends AnnotationTool {
|
|
|
22
22
|
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
|
|
23
23
|
static hydrate: (viewportId: string, points: Types.Point3[], options?: {
|
|
24
24
|
annotationUID?: string;
|
|
25
|
+
toolInstance?: EllipticalROITool;
|
|
26
|
+
referencedImageId?: string;
|
|
27
|
+
viewplaneNormal?: Types.Point3;
|
|
28
|
+
viewUp?: Types.Point3;
|
|
25
29
|
}) => EllipticalROIAnnotation;
|
|
26
30
|
addNewAnnotation: (evt: EventTypes.InteractionEventType) => EllipticalROIAnnotation;
|
|
27
31
|
isPointNearTool: (element: HTMLDivElement, annotation: EllipticalROIAnnotation, canvasCoords: Types.Point2, proximity: number) => boolean;
|
|
@@ -636,11 +636,7 @@ class EllipticalROITool extends AnnotationTool {
|
|
|
636
636
|
if (!enabledElement) {
|
|
637
637
|
return;
|
|
638
638
|
}
|
|
639
|
-
const { viewport } = enabledElement;
|
|
640
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
641
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
642
|
-
const instance = new this();
|
|
643
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
639
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, viewport, } = this.hydrateBase(EllipticalROITool, enabledElement, points, options);
|
|
644
640
|
const annotation = {
|
|
645
641
|
annotationUID: options?.annotationUID || csUtils.uuidv4(),
|
|
646
642
|
data: {
|
|
@@ -18,6 +18,10 @@ declare class LengthTool extends AnnotationTool {
|
|
|
18
18
|
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
|
|
19
19
|
static hydrate: (viewportId: string, points: Types.Point3[], options?: {
|
|
20
20
|
annotationUID?: string;
|
|
21
|
+
toolInstance?: LengthTool;
|
|
22
|
+
referencedImageId?: string;
|
|
23
|
+
viewplaneNormal?: Types.Point3;
|
|
24
|
+
viewUp?: Types.Point3;
|
|
21
25
|
}) => LengthAnnotation;
|
|
22
26
|
addNewAnnotation: (evt: EventTypes.InteractionEventType) => LengthAnnotation;
|
|
23
27
|
isPointNearTool: (element: HTMLDivElement, annotation: LengthAnnotation, canvasCoords: Types.Point2, proximity: number) => boolean;
|
|
@@ -361,11 +361,7 @@ class LengthTool extends AnnotationTool {
|
|
|
361
361
|
if (!enabledElement) {
|
|
362
362
|
return;
|
|
363
363
|
}
|
|
364
|
-
const { viewport } = enabledElement;
|
|
365
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
366
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
367
|
-
const instance = new this();
|
|
368
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
364
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, viewport, } = this.hydrateBase(LengthTool, enabledElement, points, options);
|
|
369
365
|
const annotation = {
|
|
370
366
|
annotationUID: options?.annotationUID || utilities.uuidv4(),
|
|
371
367
|
data: {
|
|
@@ -19,6 +19,10 @@ declare class ProbeTool extends AnnotationTool {
|
|
|
19
19
|
toolSelectedCallback(): void;
|
|
20
20
|
static hydrate: (viewportId: string, points: Types.Point3[], options?: {
|
|
21
21
|
annotationUID?: string;
|
|
22
|
+
toolInstance?: ProbeTool;
|
|
23
|
+
referencedImageId?: string;
|
|
24
|
+
viewplaneNormal?: Types.Point3;
|
|
25
|
+
viewUp?: Types.Point3;
|
|
22
26
|
}) => ProbeAnnotation;
|
|
23
27
|
addNewAnnotation: (evt: EventTypes.InteractionEventType) => ProbeAnnotation;
|
|
24
28
|
getHandleNearImagePoint(element: HTMLDivElement, annotation: ProbeAnnotation, canvasCoords: Types.Point2, proximity: number): ToolHandle | undefined;
|
|
@@ -224,11 +224,7 @@ class ProbeTool extends AnnotationTool {
|
|
|
224
224
|
if (!enabledElement) {
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
|
-
const { viewport } = enabledElement;
|
|
228
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
229
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
230
|
-
const instance = new this();
|
|
231
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
227
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, viewUp, instance, viewport, } = this.hydrateBase(ProbeTool, enabledElement, points, options);
|
|
232
228
|
const annotation = {
|
|
233
229
|
annotationUID: options?.annotationUID || csUtils.uuidv4(),
|
|
234
230
|
data: {
|
|
@@ -38,6 +38,10 @@ declare class RectangleROITool extends AnnotationTool {
|
|
|
38
38
|
_isInsideVolume: (index1: any, index2: any, dimensions: any) => boolean;
|
|
39
39
|
static hydrate: (viewportId: string, points: Types.Point3[], options?: {
|
|
40
40
|
annotationUID?: string;
|
|
41
|
+
toolInstance?: RectangleROITool;
|
|
42
|
+
referencedImageId?: string;
|
|
43
|
+
viewplaneNormal?: Types.Point3;
|
|
44
|
+
viewUp?: Types.Point3;
|
|
41
45
|
}) => RectangleROIAnnotation;
|
|
42
46
|
}
|
|
43
47
|
export default RectangleROITool;
|
|
@@ -521,11 +521,7 @@ class RectangleROITool extends AnnotationTool {
|
|
|
521
521
|
if (!enabledElement) {
|
|
522
522
|
return;
|
|
523
523
|
}
|
|
524
|
-
const { viewport } = enabledElement;
|
|
525
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
526
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
527
|
-
const instance = new this();
|
|
528
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
524
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, viewport, } = this.hydrateBase(RectangleROITool, enabledElement, points, options);
|
|
529
525
|
const annotation = {
|
|
530
526
|
annotationUID: options?.annotationUID || csUtils.uuidv4(),
|
|
531
527
|
data: {
|
|
@@ -68,6 +68,10 @@ declare class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
68
68
|
static hydrate: (viewportId: string, points: Types.Point3[], options?: {
|
|
69
69
|
annotationUID?: string;
|
|
70
70
|
splineType?: SplineTypesEnum;
|
|
71
|
+
toolInstance?: SplineROITool;
|
|
72
|
+
referencedImageId?: string;
|
|
73
|
+
viewplaneNormal?: Types.Point3;
|
|
74
|
+
viewUp?: Types.Point3;
|
|
71
75
|
}) => SplineROIAnnotation;
|
|
72
76
|
}
|
|
73
77
|
export default SplineROITool;
|
|
@@ -726,19 +726,11 @@ class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
726
726
|
console.warn('Spline requires at least 3 control points');
|
|
727
727
|
return;
|
|
728
728
|
}
|
|
729
|
-
const { viewport } = enabledElement;
|
|
730
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
731
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
732
|
-
const instance = new this();
|
|
733
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
729
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, viewUp, instance, viewport, } = this.hydrateBase(SplineROITool, enabledElement, points, options);
|
|
734
730
|
const splineType = options?.splineType || SplineTypesEnum.CatmullRom;
|
|
735
731
|
const splineConfig = instance._getSplineConfig(splineType);
|
|
736
732
|
const SplineClass = splineConfig.Class;
|
|
737
733
|
const splineInstance = new SplineClass();
|
|
738
|
-
const canvasPoints = points.map((point) => viewport.worldToCanvas(point));
|
|
739
|
-
splineInstance.setControlPoints(canvasPoints);
|
|
740
|
-
const splinePolylineCanvas = splineInstance.getPolylinePoints();
|
|
741
|
-
const splinePolylineWorld = splinePolylineCanvas.map((point) => viewport.canvasToWorld(point));
|
|
742
734
|
const annotation = {
|
|
743
735
|
annotationUID: options?.annotationUID || utilities.uuidv4(),
|
|
744
736
|
data: {
|
|
@@ -753,7 +745,6 @@ class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
753
745
|
},
|
|
754
746
|
contour: {
|
|
755
747
|
closed: true,
|
|
756
|
-
polyline: splinePolylineWorld,
|
|
757
748
|
},
|
|
758
749
|
},
|
|
759
750
|
highlighted: false,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { StackViewport } from '@cornerstonejs/core';
|
|
1
2
|
import type { Types } from '@cornerstonejs/core';
|
|
2
3
|
import AnnotationDisplayTool from './AnnotationDisplayTool';
|
|
3
4
|
import type { Annotation, Annotations, EventTypes, ToolHandle, InteractionTypes, ToolProps, PublicToolProps } from '../../types';
|
|
@@ -54,5 +55,19 @@ declare abstract class AnnotationTool extends AnnotationDisplayTool {
|
|
|
54
55
|
restoreMemo: () => void;
|
|
55
56
|
};
|
|
56
57
|
protected createMemo(element: any, annotation: any, options?: any): void;
|
|
58
|
+
protected static hydrateBase<T extends AnnotationTool>(ToolClass: new () => T, enabledElement: Types.IEnabledElement, points: Types.Point3[], options?: {
|
|
59
|
+
annotationUID?: string;
|
|
60
|
+
toolInstance?: T;
|
|
61
|
+
referencedImageId?: string;
|
|
62
|
+
viewplaneNormal?: Types.Point3;
|
|
63
|
+
viewUp?: Types.Point3;
|
|
64
|
+
}): {
|
|
65
|
+
FrameOfReferenceUID: string;
|
|
66
|
+
referencedImageId: any;
|
|
67
|
+
viewPlaneNormal: Types.Point3;
|
|
68
|
+
viewUp: Types.Point3;
|
|
69
|
+
instance: T;
|
|
70
|
+
viewport: StackViewport | import("@cornerstonejs/core").VolumeViewport;
|
|
71
|
+
};
|
|
57
72
|
}
|
|
58
73
|
export default AnnotationTool;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseVolumeViewport, cache, getEnabledElement, metaData, utilities as csUtils, } from '@cornerstonejs/core';
|
|
1
|
+
import { BaseVolumeViewport, cache, getEnabledElement, metaData, utilities as csUtils, StackViewport, } from '@cornerstonejs/core';
|
|
2
2
|
import { vec2 } from 'gl-matrix';
|
|
3
3
|
import AnnotationDisplayTool from './AnnotationDisplayTool';
|
|
4
4
|
import { isAnnotationLocked } from '../../stateManagement/annotation/annotationLocking';
|
|
@@ -266,6 +266,47 @@ class AnnotationTool extends AnnotationDisplayTool {
|
|
|
266
266
|
createMemo(element, annotation, options) {
|
|
267
267
|
this.memo ||= AnnotationTool.createAnnotationMemo(element, annotation, options);
|
|
268
268
|
}
|
|
269
|
+
static hydrateBase(ToolClass, enabledElement, points, options = {}) {
|
|
270
|
+
if (!enabledElement) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const { viewport } = enabledElement;
|
|
274
|
+
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
275
|
+
const camera = viewport.getCamera();
|
|
276
|
+
const viewPlaneNormal = options.viewplaneNormal ?? camera.viewPlaneNormal;
|
|
277
|
+
const viewUp = options.viewUp ?? camera.viewUp;
|
|
278
|
+
const instance = options.toolInstance || new ToolClass();
|
|
279
|
+
let referencedImageId;
|
|
280
|
+
let finalViewPlaneNormal = viewPlaneNormal;
|
|
281
|
+
let finalViewUp = viewUp;
|
|
282
|
+
if (options.referencedImageId) {
|
|
283
|
+
referencedImageId = options.referencedImageId;
|
|
284
|
+
finalViewPlaneNormal = undefined;
|
|
285
|
+
finalViewUp = undefined;
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
if (viewport instanceof StackViewport) {
|
|
289
|
+
const closestImageIndex = csUtils.getClosestStackImageIndexForPoint(points[0], viewport);
|
|
290
|
+
if (closestImageIndex) {
|
|
291
|
+
referencedImageId = viewport.getImageIds()[closestImageIndex];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
else if (viewport instanceof BaseVolumeViewport) {
|
|
295
|
+
referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
throw new Error('Unsupported viewport type');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
FrameOfReferenceUID,
|
|
303
|
+
referencedImageId,
|
|
304
|
+
viewPlaneNormal: finalViewPlaneNormal,
|
|
305
|
+
viewUp: finalViewUp,
|
|
306
|
+
instance,
|
|
307
|
+
viewport,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
269
310
|
}
|
|
270
311
|
AnnotationTool.toolName = 'AnnotationTool';
|
|
271
312
|
export default AnnotationTool;
|
|
@@ -21,6 +21,10 @@ declare class SegmentBidirectionalTool extends BidirectionalTool {
|
|
|
21
21
|
segmentIndex?: number;
|
|
22
22
|
segmentationId?: string;
|
|
23
23
|
annotationUID?: string;
|
|
24
|
+
toolInstance?: SegmentBidirectionalTool;
|
|
25
|
+
referencedImageId?: string;
|
|
26
|
+
viewplaneNormal?: Types.Point3;
|
|
27
|
+
viewUp?: Types.Point3;
|
|
24
28
|
}) => SegmentBidirectionalAnnotation;
|
|
25
29
|
renderAnnotation: (enabledElement: Types.IEnabledElement, svgDrawingHelper: SVGDrawingHelper) => boolean;
|
|
26
30
|
}
|
|
@@ -201,8 +201,6 @@ class SegmentBidirectionalTool extends BidirectionalTool {
|
|
|
201
201
|
return;
|
|
202
202
|
}
|
|
203
203
|
const { viewport } = enabledElement;
|
|
204
|
-
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
205
|
-
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
|
206
204
|
const existingAnnotations = getAllAnnotations();
|
|
207
205
|
const toolAnnotations = existingAnnotations.filter((annotation) => annotation.metadata.toolName === 'SegmentBidirectional');
|
|
208
206
|
const existingAnnotation = toolAnnotations.find((annotation) => {
|
|
@@ -216,19 +214,29 @@ class SegmentBidirectionalTool extends BidirectionalTool {
|
|
|
216
214
|
if (existingAnnotation) {
|
|
217
215
|
removeAnnotation(existingAnnotation.annotationUID);
|
|
218
216
|
}
|
|
219
|
-
const instance =
|
|
217
|
+
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, } = this.hydrateBase(SegmentBidirectionalTool, enabledElement, axis[0], options);
|
|
220
218
|
const [majorAxis, minorAxis] = axis;
|
|
221
219
|
const [major0, major1] = majorAxis;
|
|
222
220
|
const [minor0, minor1] = minorAxis;
|
|
223
221
|
const points = [major0, major1, minor0, minor1];
|
|
224
|
-
const referencedImageId = instance.getReferencedImageId(viewport, points[0], viewPlaneNormal, viewUp);
|
|
225
222
|
const annotation = {
|
|
226
223
|
annotationUID: options?.annotationUID || utilities.uuidv4(),
|
|
227
224
|
data: {
|
|
228
225
|
handles: {
|
|
229
226
|
points,
|
|
230
227
|
activeHandleIndex: null,
|
|
228
|
+
textBox: {
|
|
229
|
+
hasMoved: false,
|
|
230
|
+
worldPosition: [0, 0, 0],
|
|
231
|
+
worldBoundingBox: {
|
|
232
|
+
topLeft: [0, 0, 0],
|
|
233
|
+
topRight: [0, 0, 0],
|
|
234
|
+
bottomLeft: [0, 0, 0],
|
|
235
|
+
bottomRight: [0, 0, 0],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
231
238
|
},
|
|
239
|
+
cachedStats: {},
|
|
232
240
|
},
|
|
233
241
|
highlighted: false,
|
|
234
242
|
autoGenerated: false,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { cache, utilities as csUtils
|
|
1
|
+
import { cache, utilities as csUtils } from '@cornerstonejs/core';
|
|
2
2
|
import StrategyCallbacks from '../../../../enums/StrategyCallbacks';
|
|
3
3
|
import { getSegmentation } from '../../../../stateManagement/segmentation/getSegmentation';
|
|
4
|
+
import getOrCreateImageVolume from '../../../../utilities/segmentation/getOrCreateImageVolume';
|
|
4
5
|
export default {
|
|
5
6
|
[StrategyCallbacks.EnsureImageVolumeFor3DManipulation]: (data) => {
|
|
6
7
|
const { operationData, viewport } = data;
|
|
@@ -21,14 +22,10 @@ export default {
|
|
|
21
22
|
return image.referencedImageId;
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
operationData.imageVoxelManager = imageVolume.voxelManager;
|
|
28
|
-
operationData.imageData = imageVolume.imageData;
|
|
29
|
-
return;
|
|
25
|
+
const imageVolume = getOrCreateImageVolume(referencedImageIds);
|
|
26
|
+
if (!imageVolume) {
|
|
27
|
+
throw new Error('Failed to create or get image volume');
|
|
30
28
|
}
|
|
31
|
-
imageVolume = volumeLoader.createAndCacheVolumeFromImagesSync(volumeId, referencedImageIds);
|
|
32
29
|
operationData.imageVoxelManager = imageVolume.voxelManager;
|
|
33
30
|
operationData.imageData = imageVolume.imageData;
|
|
34
31
|
},
|
|
@@ -45,6 +45,12 @@ type NamedStatistics = {
|
|
|
45
45
|
lesionGlycolysis?: Statistics & {
|
|
46
46
|
name: 'lesionGlycolysis';
|
|
47
47
|
};
|
|
48
|
+
maxLPS?: Statistics & {
|
|
49
|
+
name: 'maxLPS';
|
|
50
|
+
};
|
|
51
|
+
minLPS?: Statistics & {
|
|
52
|
+
name: 'minLPS';
|
|
53
|
+
};
|
|
48
54
|
maxIJKs?: Array<{
|
|
49
55
|
value: number;
|
|
50
56
|
pointIJK: Types.Point3;
|
|
@@ -55,15 +55,15 @@ function basicStatsCallback(state, newValue, pointLPS = null, pointIJK = null) {
|
|
|
55
55
|
if (value < state.min[idx]) {
|
|
56
56
|
state.min[idx] = value;
|
|
57
57
|
if (idx === 0) {
|
|
58
|
-
state.minIJK = pointIJK;
|
|
59
|
-
state.minLPS = pointLPS;
|
|
58
|
+
state.minIJK = pointIJK ? [...pointIJK] : null;
|
|
59
|
+
state.minLPS = pointLPS ? [...pointLPS] : null;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
if (value > state.max[idx]) {
|
|
63
63
|
state.max[idx] = value;
|
|
64
64
|
if (idx === 0) {
|
|
65
|
-
state.maxIJK = pointIJK;
|
|
66
|
-
state.maxLPS = pointLPS;
|
|
65
|
+
state.maxIJK = pointIJK ? [...pointIJK] : null;
|
|
66
|
+
state.maxLPS = pointLPS ? [...pointLPS] : null;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
});
|
|
@@ -105,16 +105,16 @@ function basicGetStatistics(state, unit) {
|
|
|
105
105
|
label: 'Max Pixel',
|
|
106
106
|
value: state.max.length === 1 ? state.max[0] : state.max,
|
|
107
107
|
unit,
|
|
108
|
-
pointIJK: state.maxIJK,
|
|
109
|
-
pointLPS: state.maxLPS,
|
|
108
|
+
pointIJK: state.maxIJK ? [...state.maxIJK] : null,
|
|
109
|
+
pointLPS: state.maxLPS ? [...state.maxLPS] : null,
|
|
110
110
|
},
|
|
111
111
|
min: {
|
|
112
112
|
name: 'min',
|
|
113
113
|
label: 'Min Pixel',
|
|
114
114
|
value: state.min.length === 1 ? state.min[0] : state.min,
|
|
115
115
|
unit,
|
|
116
|
-
pointIJK: state.minIJK,
|
|
117
|
-
pointLPS: state.minLPS,
|
|
116
|
+
pointIJK: state.minIJK ? [...state.minIJK] : null,
|
|
117
|
+
pointLPS: state.minLPS ? [...state.minLPS] : null,
|
|
118
118
|
},
|
|
119
119
|
mean: {
|
|
120
120
|
name: 'mean',
|
|
@@ -152,10 +152,22 @@ function basicGetStatistics(state, unit) {
|
|
|
152
152
|
value: kurtosis.length === 1 ? kurtosis[0] : kurtosis,
|
|
153
153
|
unit: null,
|
|
154
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
|
+
},
|
|
155
167
|
pointsInShape: state.pointsInShape,
|
|
156
168
|
array: [],
|
|
157
169
|
};
|
|
158
|
-
named.array.push(named.max, named.mean, named.stdDev, named.median, named.skewness, named.kurtosis, 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);
|
|
159
171
|
const store = state.pointsInShape !== null;
|
|
160
172
|
const freshState = createBasicStatsState(store);
|
|
161
173
|
state.max = freshState.max;
|
|
@@ -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(
|
|
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,
|
|
32
|
+
maxIJKs.splice(i, 0, dataCopy);
|
|
24
33
|
break;
|
|
25
34
|
}
|
|
26
35
|
}
|
|
@@ -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,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
|
|
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;
|
|
@@ -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
|
-
|
|
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, utilities } 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);
|
|
@@ -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
|
+
}
|
|
@@ -116,7 +116,14 @@ const processSegmentationStatistics = ({ stats, unit, spacing, segmentationImage
|
|
|
116
116
|
value: mean.value,
|
|
117
117
|
unit,
|
|
118
118
|
};
|
|
119
|
+
stats.peakPoint = {
|
|
120
|
+
name: 'peakLPS',
|
|
121
|
+
label: 'Peak SUV Point',
|
|
122
|
+
value: testMax.pointLPS ? [...testMax.pointLPS] : null,
|
|
123
|
+
unit: null,
|
|
124
|
+
};
|
|
119
125
|
updateStatsArray(stats, stats.peakValue);
|
|
126
|
+
updateStatsArray(stats, stats.peakPoint);
|
|
120
127
|
}
|
|
121
128
|
}
|
|
122
129
|
if (stats.volume && stats.mean) {
|
|
@@ -169,7 +176,7 @@ async function calculateStackStatistics({ segImageIds, indices, unit, mode }) {
|
|
|
169
176
|
}
|
|
170
177
|
}
|
|
171
178
|
function getSphereStats(testMax, radiusIJK, segData, imageVoxels, spacing) {
|
|
172
|
-
const { pointIJK: centerIJK } = testMax;
|
|
179
|
+
const { pointIJK: centerIJK, pointLPS: centerLPS } = testMax;
|
|
173
180
|
if (!centerIJK) {
|
|
174
181
|
return;
|
|
175
182
|
}
|
|
@@ -22,10 +22,12 @@ import * as growCut from './growCut';
|
|
|
22
22
|
import * as LabelmapMemo from './createLabelmapMemo';
|
|
23
23
|
import IslandRemoval from './islandRemoval';
|
|
24
24
|
import getOrCreateSegmentationVolume from './getOrCreateSegmentationVolume';
|
|
25
|
+
import getOrCreateImageVolume from './getOrCreateImageVolume';
|
|
25
26
|
import getStatistics from './getStatistics';
|
|
26
27
|
import * as validateLabelmap from './validateLabelmap';
|
|
27
28
|
import { computeStackLabelmapFromVolume } from '../../stateManagement/segmentation/helpers/computeStackLabelmapFromVolume';
|
|
28
29
|
import { computeVolumeLabelmapFromStack } from '../../stateManagement/segmentation/helpers/computeVolumeLabelmapFromStack';
|
|
29
30
|
import { getReferenceVolumeForSegmentationVolume } from './getReferenceVolumeForSegmentationVolume';
|
|
30
31
|
import { getSegmentLargestBidirectional } from './getSegmentLargestBidirectional';
|
|
31
|
-
|
|
32
|
+
import { computeMetabolicStats } from './computeMetabolicStats';
|
|
33
|
+
export { thresholdVolumeByRange, createMergedLabelmapForIndex, createLabelmapVolumeForViewport, rectangleROIThresholdVolumeByRange, triggerSegmentationRender, triggerSegmentationRenderBySegmentationId, floodFill, getBrushSizeForToolGroup, setBrushSizeForToolGroup, getBrushThresholdForToolGroup, setBrushThresholdForToolGroup, VolumetricCalculator, SegmentStatsCalculator, thresholdSegmentationByRange, contourAndFindLargestBidirectional, createBidirectionalToolData, segmentContourAction, invalidateBrushCursor, getUniqueSegmentIndices, getSegmentIndexAtWorldPoint, getSegmentIndexAtLabelmapBorder, getHoveredContourSegmentationAnnotation, getBrushToolInstances, growCut, LabelmapMemo, IslandRemoval, getOrCreateSegmentationVolume, getOrCreateImageVolume, getStatistics, validateLabelmap, computeStackLabelmapFromVolume, computeVolumeLabelmapFromStack, getReferenceVolumeForSegmentationVolume, getSegmentLargestBidirectional, computeMetabolicStats, };
|
|
@@ -22,10 +22,12 @@ import * as growCut from './growCut';
|
|
|
22
22
|
import * as LabelmapMemo from './createLabelmapMemo';
|
|
23
23
|
import IslandRemoval from './islandRemoval';
|
|
24
24
|
import getOrCreateSegmentationVolume from './getOrCreateSegmentationVolume';
|
|
25
|
+
import getOrCreateImageVolume from './getOrCreateImageVolume';
|
|
25
26
|
import getStatistics from './getStatistics';
|
|
26
27
|
import * as validateLabelmap from './validateLabelmap';
|
|
27
28
|
import { computeStackLabelmapFromVolume } from '../../stateManagement/segmentation/helpers/computeStackLabelmapFromVolume';
|
|
28
29
|
import { computeVolumeLabelmapFromStack } from '../../stateManagement/segmentation/helpers/computeVolumeLabelmapFromStack';
|
|
29
30
|
import { getReferenceVolumeForSegmentationVolume } from './getReferenceVolumeForSegmentationVolume';
|
|
30
31
|
import { getSegmentLargestBidirectional } from './getSegmentLargestBidirectional';
|
|
31
|
-
|
|
32
|
+
import { computeMetabolicStats } from './computeMetabolicStats';
|
|
33
|
+
export { thresholdVolumeByRange, createMergedLabelmapForIndex, createLabelmapVolumeForViewport, rectangleROIThresholdVolumeByRange, triggerSegmentationRender, triggerSegmentationRenderBySegmentationId, floodFill, getBrushSizeForToolGroup, setBrushSizeForToolGroup, getBrushThresholdForToolGroup, setBrushThresholdForToolGroup, VolumetricCalculator, SegmentStatsCalculator, thresholdSegmentationByRange, contourAndFindLargestBidirectional, createBidirectionalToolData, segmentContourAction, invalidateBrushCursor, getUniqueSegmentIndices, getSegmentIndexAtWorldPoint, getSegmentIndexAtLabelmapBorder, getHoveredContourSegmentationAnnotation, getBrushToolInstances, growCut, LabelmapMemo, IslandRemoval, getOrCreateSegmentationVolume, getOrCreateImageVolume, getStatistics, validateLabelmap, computeStackLabelmapFromVolume, computeVolumeLabelmapFromStack, getReferenceVolumeForSegmentationVolume, getSegmentLargestBidirectional, computeMetabolicStats, };
|
|
@@ -29,15 +29,24 @@ const computeWorker = {
|
|
|
29
29
|
scalarData,
|
|
30
30
|
};
|
|
31
31
|
},
|
|
32
|
-
createVTKImageData: (dimensions, origin, direction, spacing) => {
|
|
32
|
+
createVTKImageData: (dimensions, origin, direction, spacing, scalarData) => {
|
|
33
33
|
const imageData = vtkImageData.newInstance();
|
|
34
34
|
imageData.setDimensions(dimensions);
|
|
35
35
|
imageData.setOrigin(origin);
|
|
36
36
|
imageData.setDirection(direction);
|
|
37
37
|
imageData.setSpacing(spacing);
|
|
38
|
+
if (!scalarData) {
|
|
39
|
+
return imageData;
|
|
40
|
+
}
|
|
41
|
+
const scalarArray = vtkDataArray.newInstance({
|
|
42
|
+
name: 'Scalars',
|
|
43
|
+
numberOfComponents: 1,
|
|
44
|
+
values: scalarData,
|
|
45
|
+
});
|
|
46
|
+
imageData.getPointData().setScalars(scalarArray);
|
|
38
47
|
return imageData;
|
|
39
48
|
},
|
|
40
|
-
processSegmentStatistics: (segVoxelManager, imageVoxelManager, indices, bounds) => {
|
|
49
|
+
processSegmentStatistics: ({ segVoxelManager, imageVoxelManager, indices, bounds, imageData, }) => {
|
|
41
50
|
segVoxelManager.forEach(({ value, pointIJK, pointLPS, index }) => {
|
|
42
51
|
if (indices.indexOf(value) === -1) {
|
|
43
52
|
return;
|
|
@@ -51,6 +60,7 @@ const computeWorker = {
|
|
|
51
60
|
});
|
|
52
61
|
}, {
|
|
53
62
|
boundsIJK: bounds || imageVoxelManager.getDefaultBounds(),
|
|
63
|
+
imageData,
|
|
54
64
|
});
|
|
55
65
|
},
|
|
56
66
|
performMarchingSquares: (imageData, sliceIndex = null, slicingMode = null) => {
|
|
@@ -99,8 +109,14 @@ const computeWorker = {
|
|
|
99
109
|
const { segmentation, image } = computeWorker.getArgsFromInfo(args);
|
|
100
110
|
const { voxelManager: segVoxelManager, spacing: segmentationSpacing } = segmentation;
|
|
101
111
|
const { voxelManager: imageVoxelManager } = image;
|
|
112
|
+
const imageData = computeWorker.createVTKImageData(segmentation.dimensions, segmentation.origin, segmentation.direction, segmentation.spacing);
|
|
102
113
|
SegmentStatsCalculator.statsInit({ storePointData: false, indices, mode });
|
|
103
|
-
computeWorker.processSegmentStatistics(
|
|
114
|
+
computeWorker.processSegmentStatistics({
|
|
115
|
+
segVoxelManager,
|
|
116
|
+
imageVoxelManager,
|
|
117
|
+
indices,
|
|
118
|
+
imageData,
|
|
119
|
+
});
|
|
104
120
|
const stats = SegmentStatsCalculator.getStatistics({
|
|
105
121
|
spacing: segmentationSpacing,
|
|
106
122
|
unit: 'mm',
|
|
@@ -108,6 +124,33 @@ const computeWorker = {
|
|
|
108
124
|
});
|
|
109
125
|
return stats;
|
|
110
126
|
},
|
|
127
|
+
computeMetabolicStats({ segmentationInfo, imageInfo }) {
|
|
128
|
+
const { scalarData, dimensions, spacing, origin, direction } = segmentationInfo;
|
|
129
|
+
const { spacing: imageSpacing, dimensions: imageDimensions, direction: imageDirection, origin: imageOrigin, scalarData: imageScalarData, } = imageInfo;
|
|
130
|
+
const segVoxelManager = computeWorker.createVoxelManager(segmentationInfo.dimensions, segmentationInfo.scalarData);
|
|
131
|
+
const refVoxelManager = computeWorker.createVoxelManager(imageDimensions, imageScalarData);
|
|
132
|
+
let suv = 0;
|
|
133
|
+
let numVoxels = 0;
|
|
134
|
+
const scalarDataLength = segVoxelManager.getScalarDataLength();
|
|
135
|
+
for (let i = 0; i < scalarDataLength; i++) {
|
|
136
|
+
if (segVoxelManager.getAtIndex(i) !== 0) {
|
|
137
|
+
suv += refVoxelManager.getAtIndex(i);
|
|
138
|
+
numVoxels++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const tmtv = 1e-3 * numVoxels * spacing[0] * spacing[1] * spacing[2];
|
|
142
|
+
const averageSuv = numVoxels > 0 ? suv / numVoxels : 0;
|
|
143
|
+
const tlg = averageSuv *
|
|
144
|
+
numVoxels *
|
|
145
|
+
imageSpacing[0] *
|
|
146
|
+
imageSpacing[1] *
|
|
147
|
+
imageSpacing[2] *
|
|
148
|
+
1e-3;
|
|
149
|
+
return {
|
|
150
|
+
tmtv,
|
|
151
|
+
tlg,
|
|
152
|
+
};
|
|
153
|
+
},
|
|
111
154
|
calculateSegmentsStatisticsStack: (args) => {
|
|
112
155
|
const { segmentationInfo, imageInfo, indices, mode } = args;
|
|
113
156
|
SegmentStatsCalculator.statsInit({ storePointData: true, indices, mode });
|
|
@@ -121,7 +164,13 @@ const computeWorker = {
|
|
|
121
164
|
];
|
|
122
165
|
const segVoxelManager = computeWorker.createVoxelManager(segDimensions, segInfo.scalarData);
|
|
123
166
|
const imageVoxelManager = computeWorker.createVoxelManager(segDimensions, imgInfo.scalarData);
|
|
124
|
-
computeWorker.
|
|
167
|
+
const imageData = computeWorker.createVTKImageData(segDimensions, segInfo.origin, segInfo.direction, segInfo.spacing);
|
|
168
|
+
computeWorker.processSegmentStatistics({
|
|
169
|
+
segVoxelManager,
|
|
170
|
+
imageVoxelManager,
|
|
171
|
+
indices,
|
|
172
|
+
imageData,
|
|
173
|
+
});
|
|
125
174
|
}
|
|
126
175
|
const spacing = segmentationInfo[0].spacing;
|
|
127
176
|
const stats = SegmentStatsCalculator.getStatistics({
|
|
@@ -141,58 +190,119 @@ const computeWorker = {
|
|
|
141
190
|
segmentationInfo: segmentationInfo[0],
|
|
142
191
|
}));
|
|
143
192
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
: dimensions;
|
|
148
|
-
const stackSpacing = isStack ? [spacing[0], spacing[1], 1] : spacing;
|
|
149
|
-
const imageData = computeWorker.createVTKImageData(stackDimensions, origin, direction, stackSpacing);
|
|
150
|
-
let contourSets;
|
|
151
|
-
if (!isStack) {
|
|
152
|
-
contourSets = computeWorker.generateContourSetsFromLabelmapVolume({
|
|
153
|
-
segmentation,
|
|
193
|
+
return isStack
|
|
194
|
+
? computeWorker.calculateBidirectionalStack({
|
|
195
|
+
segmentationInfo,
|
|
154
196
|
indices,
|
|
155
|
-
imageData,
|
|
156
197
|
mode,
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
contourSets = computeWorker.generateContourSetsFromLabelmapStack({
|
|
161
|
-
segmentationInfo,
|
|
198
|
+
})
|
|
199
|
+
: computeWorker.calculateVolumetricBidirectional({
|
|
200
|
+
segmentation,
|
|
162
201
|
indices,
|
|
163
202
|
mode,
|
|
164
203
|
});
|
|
204
|
+
},
|
|
205
|
+
findLargestBidirectionalFromContours: (contours, isInSegment, segmentIndex) => {
|
|
206
|
+
let maxBidirectional;
|
|
207
|
+
for (const sliceContour of contours) {
|
|
208
|
+
const bidirectional = createBidirectionalForSlice(sliceContour, isInSegment, maxBidirectional);
|
|
209
|
+
if (!bidirectional) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
maxBidirectional = bidirectional;
|
|
165
213
|
}
|
|
166
|
-
|
|
214
|
+
if (maxBidirectional) {
|
|
215
|
+
return {
|
|
216
|
+
segmentIndex,
|
|
217
|
+
majorAxis: maxBidirectional.majorAxis,
|
|
218
|
+
minorAxis: maxBidirectional.minorAxis,
|
|
219
|
+
maxMajor: maxBidirectional.maxMajor,
|
|
220
|
+
maxMinor: maxBidirectional.maxMinor,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
},
|
|
225
|
+
calculateBidirectionalStack: ({ segmentationInfo, indices, mode }) => {
|
|
226
|
+
const segments = computeWorker.createSegmentsFromIndices(indices);
|
|
227
|
+
let bidirectionalResults = [];
|
|
228
|
+
for (let i = 0; i < segmentationInfo.length; i++) {
|
|
229
|
+
const segInfo = segmentationInfo[i];
|
|
230
|
+
const dimensions = segInfo.dimensions;
|
|
231
|
+
const segScalarData = segInfo.scalarData;
|
|
232
|
+
const { spacing, direction, origin } = segInfo;
|
|
233
|
+
const voxelManager = computeWorker.createVoxelManager(dimensions, segScalarData);
|
|
234
|
+
const pixelsPerSlice = dimensions[0] * dimensions[1];
|
|
235
|
+
for (let segIndex = 1; segIndex < segments.length; segIndex++) {
|
|
236
|
+
const segment = segments[segIndex];
|
|
237
|
+
if (!segment) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const segmentIndex = segment.segmentIndex;
|
|
241
|
+
if (computeWorker.isSliceEmptyForSegmentVolume(0, segScalarData, pixelsPerSlice, segmentIndex)) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const sliceContours = [];
|
|
245
|
+
const filteredData = new Uint8Array(segScalarData.length);
|
|
246
|
+
for (let i = 0; i < segScalarData.length; i++) {
|
|
247
|
+
filteredData[i] = segScalarData[i] === segmentIndex ? 1 : 0;
|
|
248
|
+
}
|
|
249
|
+
const scalarArray = vtkDataArray.newInstance({
|
|
250
|
+
name: 'Pixels',
|
|
251
|
+
numberOfComponents: 1,
|
|
252
|
+
values: filteredData,
|
|
253
|
+
});
|
|
254
|
+
const imageData = computeWorker.createVTKImageData(dimensions, origin, direction, [spacing[0], spacing[1], 1]);
|
|
255
|
+
imageData.getPointData().setScalars(scalarArray);
|
|
256
|
+
try {
|
|
257
|
+
const msOutput = computeWorker.performMarchingSquares(imageData, null, 2);
|
|
258
|
+
const contourData = computeWorker.createContoursFromPolyData(msOutput);
|
|
259
|
+
if (contourData) {
|
|
260
|
+
sliceContours.push(contourData);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (e) {
|
|
264
|
+
console.warn(e);
|
|
265
|
+
}
|
|
266
|
+
const isInSegment = createIsInSegmentMetadata({
|
|
267
|
+
dimensions,
|
|
268
|
+
imageData,
|
|
269
|
+
voxelManager,
|
|
270
|
+
segmentIndex,
|
|
271
|
+
});
|
|
272
|
+
const bidirectionalResult = computeWorker.findLargestBidirectionalFromContours(sliceContours, isInSegment, segmentIndex);
|
|
273
|
+
if (bidirectionalResult) {
|
|
274
|
+
bidirectionalResults.push(bidirectionalResult);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return bidirectionalResults;
|
|
279
|
+
},
|
|
280
|
+
calculateVolumetricBidirectional: ({ segmentation, indices, mode }) => {
|
|
281
|
+
const { voxelManager, dimensions, origin, direction, spacing } = segmentation;
|
|
282
|
+
const imageData = computeWorker.createVTKImageData(dimensions, origin, direction, spacing);
|
|
283
|
+
const contourSets = computeWorker.generateContourSetsFromLabelmapVolume({
|
|
284
|
+
segmentation,
|
|
285
|
+
indices,
|
|
286
|
+
imageData,
|
|
287
|
+
mode,
|
|
288
|
+
});
|
|
289
|
+
const bidirectionalResults = [];
|
|
167
290
|
for (let i = 0; i < contourSets.length; i++) {
|
|
168
291
|
const contourSet = contourSets[i];
|
|
169
292
|
const { segmentIndex } = contourSet.segment;
|
|
170
293
|
const contours = contourSet.sliceContours;
|
|
171
|
-
let maxBidirectional;
|
|
172
294
|
const isInSegment = createIsInSegmentMetadata({
|
|
173
295
|
dimensions,
|
|
174
296
|
imageData,
|
|
175
297
|
voxelManager,
|
|
176
298
|
segmentIndex,
|
|
177
299
|
});
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
maxBidirectional = bidirectional;
|
|
184
|
-
}
|
|
185
|
-
if (maxBidirectional) {
|
|
186
|
-
bidirectionalData.push({
|
|
187
|
-
segmentIndex,
|
|
188
|
-
majorAxis: maxBidirectional.majorAxis,
|
|
189
|
-
minorAxis: maxBidirectional.minorAxis,
|
|
190
|
-
maxMajor: maxBidirectional.maxMajor,
|
|
191
|
-
maxMinor: maxBidirectional.maxMinor,
|
|
192
|
-
});
|
|
300
|
+
const bidirectionalResult = computeWorker.findLargestBidirectionalFromContours(contours, isInSegment, segmentIndex);
|
|
301
|
+
if (bidirectionalResult) {
|
|
302
|
+
bidirectionalResults.push(bidirectionalResult);
|
|
193
303
|
}
|
|
194
304
|
}
|
|
195
|
-
return
|
|
305
|
+
return bidirectionalResults;
|
|
196
306
|
},
|
|
197
307
|
generateContourSetsFromLabelmapVolume: (args) => {
|
|
198
308
|
const { segmentation, indices, imageData } = args;
|
|
@@ -259,62 +369,6 @@ const computeWorker = {
|
|
|
259
369
|
}
|
|
260
370
|
return ContourSets;
|
|
261
371
|
},
|
|
262
|
-
generateContourSetsFromLabelmapStack: (args) => {
|
|
263
|
-
const { segmentationInfo, indices } = args;
|
|
264
|
-
let ContourSets = [];
|
|
265
|
-
for (let i = 0; i < segmentationInfo.length; i++) {
|
|
266
|
-
const segInfo = segmentationInfo[i];
|
|
267
|
-
const dimensions = segInfo.dimensions;
|
|
268
|
-
const segScalarData = segInfo.scalarData;
|
|
269
|
-
const { spacing, direction, origin } = segInfo;
|
|
270
|
-
const segments = computeWorker.createSegmentsFromIndices(indices);
|
|
271
|
-
const pixelsPerSlice = dimensions[0] * dimensions[1];
|
|
272
|
-
const numSegments = segments.length;
|
|
273
|
-
for (let segIndex = 0; segIndex < numSegments; segIndex++) {
|
|
274
|
-
const segment = segments[segIndex];
|
|
275
|
-
if (!segment) {
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
const segmentIndex = segment.segmentIndex;
|
|
279
|
-
if (computeWorker.isSliceEmptyForSegmentVolume(0, segScalarData, pixelsPerSlice, segmentIndex)) {
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
const sliceContours = [];
|
|
283
|
-
const filteredData = new Uint8Array(segScalarData.length);
|
|
284
|
-
for (let i = 0; i < segScalarData.length; i++) {
|
|
285
|
-
if (segScalarData[i] === segmentIndex) {
|
|
286
|
-
filteredData[i] = 1;
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
filteredData[i] = 0;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
const scalarArray = vtkDataArray.newInstance({
|
|
293
|
-
name: 'Pixels',
|
|
294
|
-
numberOfComponents: 1,
|
|
295
|
-
values: filteredData,
|
|
296
|
-
});
|
|
297
|
-
const imageData = computeWorker.createVTKImageData(dimensions, origin, direction, [spacing[0], spacing[1], 1]);
|
|
298
|
-
imageData.getPointData().setScalars(scalarArray);
|
|
299
|
-
try {
|
|
300
|
-
const msOutput = computeWorker.performMarchingSquares(imageData, null, 2);
|
|
301
|
-
const contourData = computeWorker.createContoursFromPolyData(msOutput);
|
|
302
|
-
if (contourData) {
|
|
303
|
-
sliceContours.push(contourData);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
catch (e) {
|
|
307
|
-
console.warn(e);
|
|
308
|
-
}
|
|
309
|
-
const ContourSet = {
|
|
310
|
-
sliceContours,
|
|
311
|
-
segment,
|
|
312
|
-
};
|
|
313
|
-
ContourSets.push(ContourSet);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return ContourSets;
|
|
317
|
-
},
|
|
318
372
|
isSliceEmptyForSegmentVolume: (sliceIndex, segData, pixelsPerSlice, segIndex) => {
|
|
319
373
|
const startIdx = sliceIndex * pixelsPerSlice;
|
|
320
374
|
const endIdx = startIdx + pixelsPerSlice;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/tools",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "Cornerstone3D Tools",
|
|
5
5
|
"types": "./dist/esm/index.d.ts",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -103,8 +103,8 @@
|
|
|
103
103
|
"canvas": "^2.11.2"
|
|
104
104
|
},
|
|
105
105
|
"peerDependencies": {
|
|
106
|
-
"@cornerstonejs/core": "^3.
|
|
107
|
-
"@kitware/vtk.js": "32.12.
|
|
106
|
+
"@cornerstonejs/core": "^3.7.0",
|
|
107
|
+
"@kitware/vtk.js": "32.12.1",
|
|
108
108
|
"@types/d3-array": "^3.0.4",
|
|
109
109
|
"@types/d3-interpolate": "^3.0.1",
|
|
110
110
|
"d3-array": "^3.2.3",
|
|
@@ -122,5 +122,5 @@
|
|
|
122
122
|
"type": "individual",
|
|
123
123
|
"url": "https://ohif.org/donate"
|
|
124
124
|
},
|
|
125
|
-
"gitHead": "
|
|
125
|
+
"gitHead": "177fce2dd8c2278d3c8473f15df795e757d40b7e"
|
|
126
126
|
}
|