@cornerstonejs/tools 1.31.0 → 1.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/stateManagement/annotation/AnnotationGroup.d.ts +18 -0
- package/dist/cjs/stateManagement/annotation/AnnotationGroup.js +73 -0
- package/dist/cjs/stateManagement/annotation/AnnotationGroup.js.map +1 -0
- package/dist/cjs/stateManagement/annotation/annotationState.js +1 -1
- package/dist/cjs/stateManagement/annotation/annotationState.js.map +1 -1
- package/dist/cjs/stateManagement/annotation/index.d.ts +2 -1
- package/dist/cjs/stateManagement/annotation/index.js +3 -1
- package/dist/cjs/stateManagement/annotation/index.js.map +1 -1
- package/dist/cjs/store/ToolGroupManager/ToolGroup.d.ts +1 -0
- package/dist/cjs/store/ToolGroupManager/ToolGroup.js +3 -1
- package/dist/cjs/store/ToolGroupManager/ToolGroup.js.map +1 -1
- package/dist/cjs/synchronizers/callbacks/stackImageSyncCallback.js +1 -1
- package/dist/cjs/synchronizers/callbacks/stackImageSyncCallback.js.map +1 -1
- package/dist/cjs/tools/annotation/KeyImageTool.d.ts +62 -0
- package/dist/cjs/tools/annotation/KeyImageTool.js +212 -0
- package/dist/cjs/tools/annotation/KeyImageTool.js.map +1 -0
- package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +2 -5
- package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
- package/dist/cjs/tools/annotation/ProbeTool.js +15 -4
- package/dist/cjs/tools/annotation/ProbeTool.js.map +1 -1
- package/dist/cjs/tools/annotation/VideoRedactionTool.js +4 -1
- package/dist/cjs/tools/annotation/VideoRedactionTool.js.map +1 -1
- package/dist/cjs/tools/base/AnnotationDisplayTool.js +4 -2
- package/dist/cjs/tools/base/AnnotationDisplayTool.js.map +1 -1
- package/dist/cjs/tools/base/AnnotationTool.js +2 -7
- package/dist/cjs/tools/base/AnnotationTool.js.map +1 -1
- package/dist/cjs/tools/base/BaseTool.js +9 -1
- package/dist/cjs/tools/base/BaseTool.js.map +1 -1
- package/dist/cjs/tools/index.d.ts +5 -4
- package/dist/cjs/tools/index.js +9 -7
- package/dist/cjs/tools/index.js.map +1 -1
- package/dist/cjs/types/CalculatorTypes.d.ts +1 -1
- package/dist/cjs/utilities/annotationFrameRange.d.ts +13 -0
- package/dist/cjs/utilities/annotationFrameRange.js +46 -0
- package/dist/cjs/utilities/annotationFrameRange.js.map +1 -0
- package/dist/cjs/utilities/index.d.ts +2 -1
- package/dist/cjs/utilities/index.js +3 -1
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/math/basic/BasicStatsCalculator.d.ts +0 -1
- package/dist/cjs/utilities/math/basic/BasicStatsCalculator.js +35 -22
- package/dist/cjs/utilities/math/basic/BasicStatsCalculator.js.map +1 -1
- package/dist/cjs/utilities/math/ellipse/pointInEllipse.d.ts +2 -1
- package/dist/cjs/utilities/math/ellipse/pointInEllipse.js.map +1 -1
- package/dist/cjs/utilities/math/sphere/pointInSphere.d.ts +1 -1
- package/dist/cjs/utilities/math/sphere/pointInSphere.js +3 -3
- package/dist/cjs/utilities/math/sphere/pointInSphere.js.map +1 -1
- package/dist/cjs/utilities/planar/filterAnnotationsForDisplay.js +19 -2
- package/dist/cjs/utilities/planar/filterAnnotationsForDisplay.js.map +1 -1
- package/dist/cjs/utilities/pointInShapeCallback.d.ts +6 -5
- package/dist/cjs/utilities/pointInShapeCallback.js +27 -26
- package/dist/cjs/utilities/pointInShapeCallback.js.map +1 -1
- package/dist/cjs/utilities/roundNumber.d.ts +1 -1
- package/dist/cjs/utilities/roundNumber.js +3 -0
- package/dist/cjs/utilities/roundNumber.js.map +1 -1
- package/dist/cjs/utilities/viewport/isViewportPreScaled.js +1 -1
- package/dist/cjs/utilities/viewport/isViewportPreScaled.js.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/stateManagement/annotation/AnnotationGroup.js +70 -0
- package/dist/esm/stateManagement/annotation/AnnotationGroup.js.map +1 -0
- package/dist/esm/stateManagement/annotation/annotationState.js +1 -1
- package/dist/esm/stateManagement/annotation/annotationState.js.map +1 -1
- package/dist/esm/stateManagement/annotation/index.js +2 -1
- package/dist/esm/stateManagement/annotation/index.js.map +1 -1
- package/dist/esm/store/ToolGroupManager/ToolGroup.js +3 -1
- package/dist/esm/store/ToolGroupManager/ToolGroup.js.map +1 -1
- package/dist/esm/synchronizers/callbacks/stackImageSyncCallback.js +1 -1
- package/dist/esm/synchronizers/callbacks/stackImageSyncCallback.js.map +1 -1
- package/dist/esm/tools/annotation/KeyImageTool.js +207 -0
- package/dist/esm/tools/annotation/KeyImageTool.js.map +1 -0
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js +3 -6
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
- package/dist/esm/tools/annotation/ProbeTool.js +17 -6
- package/dist/esm/tools/annotation/ProbeTool.js.map +1 -1
- package/dist/esm/tools/annotation/VideoRedactionTool.js +4 -1
- package/dist/esm/tools/annotation/VideoRedactionTool.js.map +1 -1
- package/dist/esm/tools/base/AnnotationDisplayTool.js +4 -2
- package/dist/esm/tools/base/AnnotationDisplayTool.js.map +1 -1
- package/dist/esm/tools/base/AnnotationTool.js +3 -8
- package/dist/esm/tools/base/AnnotationTool.js.map +1 -1
- package/dist/esm/tools/base/BaseTool.js +9 -1
- package/dist/esm/tools/base/BaseTool.js.map +1 -1
- package/dist/esm/tools/index.js +5 -4
- package/dist/esm/tools/index.js.map +1 -1
- package/dist/esm/utilities/annotationFrameRange.js +43 -0
- package/dist/esm/utilities/annotationFrameRange.js.map +1 -0
- package/dist/esm/utilities/index.js +2 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/math/basic/BasicStatsCalculator.js +35 -22
- package/dist/esm/utilities/math/basic/BasicStatsCalculator.js.map +1 -1
- package/dist/esm/utilities/math/ellipse/pointInEllipse.js.map +1 -1
- package/dist/esm/utilities/math/sphere/pointInSphere.js +3 -3
- package/dist/esm/utilities/math/sphere/pointInSphere.js.map +1 -1
- package/dist/esm/utilities/planar/filterAnnotationsForDisplay.js +19 -2
- package/dist/esm/utilities/planar/filterAnnotationsForDisplay.js.map +1 -1
- package/dist/esm/utilities/pointInShapeCallback.js +27 -26
- package/dist/esm/utilities/pointInShapeCallback.js.map +1 -1
- package/dist/esm/utilities/roundNumber.js +3 -0
- package/dist/esm/utilities/roundNumber.js.map +1 -1
- package/dist/esm/utilities/viewport/isViewportPreScaled.js +1 -1
- package/dist/esm/utilities/viewport/isViewportPreScaled.js.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/stateManagement/annotation/AnnotationGroup.d.ts +19 -0
- package/dist/types/stateManagement/annotation/AnnotationGroup.d.ts.map +1 -0
- package/dist/types/stateManagement/annotation/index.d.ts +2 -1
- package/dist/types/stateManagement/annotation/index.d.ts.map +1 -1
- package/dist/types/store/ToolGroupManager/ToolGroup.d.ts +1 -0
- package/dist/types/store/ToolGroupManager/ToolGroup.d.ts.map +1 -1
- package/dist/types/synchronizers/callbacks/stackImageSyncCallback.d.ts.map +1 -1
- package/dist/types/tools/annotation/KeyImageTool.d.ts +63 -0
- package/dist/types/tools/annotation/KeyImageTool.d.ts.map +1 -0
- package/dist/types/tools/annotation/PlanarFreehandROITool.d.ts.map +1 -1
- package/dist/types/tools/annotation/ProbeTool.d.ts.map +1 -1
- package/dist/types/tools/annotation/VideoRedactionTool.d.ts.map +1 -1
- package/dist/types/tools/base/AnnotationDisplayTool.d.ts.map +1 -1
- package/dist/types/tools/base/AnnotationTool.d.ts.map +1 -1
- package/dist/types/tools/base/BaseTool.d.ts.map +1 -1
- package/dist/types/tools/index.d.ts +5 -4
- package/dist/types/tools/index.d.ts.map +1 -1
- package/dist/types/types/CalculatorTypes.d.ts +1 -1
- package/dist/types/types/CalculatorTypes.d.ts.map +1 -1
- package/dist/types/utilities/annotationFrameRange.d.ts +14 -0
- package/dist/types/utilities/annotationFrameRange.d.ts.map +1 -0
- package/dist/types/utilities/index.d.ts +2 -1
- package/dist/types/utilities/index.d.ts.map +1 -1
- package/dist/types/utilities/math/basic/BasicStatsCalculator.d.ts +0 -1
- package/dist/types/utilities/math/basic/BasicStatsCalculator.d.ts.map +1 -1
- package/dist/types/utilities/math/ellipse/pointInEllipse.d.ts +2 -1
- package/dist/types/utilities/math/ellipse/pointInEllipse.d.ts.map +1 -1
- package/dist/types/utilities/math/sphere/pointInSphere.d.ts +1 -1
- package/dist/types/utilities/math/sphere/pointInSphere.d.ts.map +1 -1
- package/dist/types/utilities/planar/filterAnnotationsForDisplay.d.ts.map +1 -1
- package/dist/types/utilities/pointInShapeCallback.d.ts +6 -5
- package/dist/types/utilities/pointInShapeCallback.d.ts.map +1 -1
- package/dist/types/utilities/roundNumber.d.ts +1 -1
- package/dist/types/utilities/roundNumber.d.ts.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +2 -0
- package/src/stateManagement/annotation/AnnotationGroup.ts +120 -0
- package/src/stateManagement/annotation/annotationState.ts +1 -1
- package/src/stateManagement/annotation/index.ts +2 -0
- package/src/store/ToolGroupManager/ToolGroup.ts +10 -1
- package/src/synchronizers/callbacks/stackImageSyncCallback.ts +2 -1
- package/src/tools/annotation/KeyImageTool.ts +435 -0
- package/src/tools/annotation/PlanarFreehandROITool.ts +4 -6
- package/src/tools/annotation/ProbeTool.ts +19 -6
- package/src/tools/annotation/VideoRedactionTool.ts +10 -1
- package/src/tools/base/AnnotationDisplayTool.ts +3 -4
- package/src/tools/base/AnnotationTool.ts +3 -6
- package/src/tools/base/BaseTool.ts +18 -2
- package/src/tools/index.ts +8 -5
- package/src/types/CalculatorTypes.ts +1 -1
- package/src/utilities/annotationFrameRange.ts +78 -0
- package/src/utilities/index.ts +2 -0
- package/src/utilities/math/basic/BasicStatsCalculator.ts +51 -23
- package/src/utilities/math/ellipse/pointInEllipse.ts +2 -1
- package/src/utilities/math/sphere/pointInSphere.ts +4 -7
- package/src/utilities/planar/filterAnnotationsForDisplay.ts +29 -3
- package/src/utilities/pointInShapeCallback.ts +46 -38
- package/src/utilities/roundNumber.ts +7 -1
- package/src/utilities/viewport/isViewportPreScaled.ts +1 -1
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
triggerEvent,
|
|
8
8
|
eventTarget,
|
|
9
9
|
utilities as csUtils,
|
|
10
|
-
utilities,
|
|
11
10
|
} from '@cornerstonejs/core';
|
|
12
11
|
import type { Types } from '@cornerstonejs/core';
|
|
13
12
|
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
import { state } from '../../store';
|
|
25
24
|
import { Events } from '../../enums';
|
|
26
25
|
import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
|
|
26
|
+
import roundNumber from '../../utilities/roundNumber';
|
|
27
27
|
import {
|
|
28
28
|
resetElementCursor,
|
|
29
29
|
hideElementCursor,
|
|
@@ -578,20 +578,33 @@ class ProbeTool extends AnnotationTool {
|
|
|
578
578
|
index[1] = Math.round(index[1]);
|
|
579
579
|
index[2] = Math.round(index[2]);
|
|
580
580
|
|
|
581
|
+
const samplesPerPixel =
|
|
582
|
+
scalarData.length / dimensions[2] / dimensions[1] / dimensions[0];
|
|
583
|
+
|
|
581
584
|
if (csUtils.indexWithinDimensions(index, dimensions)) {
|
|
582
585
|
this.isHandleOutsideImage = false;
|
|
583
|
-
const yMultiple = dimensions[0];
|
|
584
|
-
const zMultiple = dimensions[0] * dimensions[1];
|
|
586
|
+
const yMultiple = dimensions[0] * samplesPerPixel;
|
|
587
|
+
const zMultiple = dimensions[0] * dimensions[1] * samplesPerPixel;
|
|
585
588
|
|
|
589
|
+
const baseIndex =
|
|
590
|
+
index[2] * zMultiple +
|
|
591
|
+
index[1] * yMultiple +
|
|
592
|
+
index[0] * samplesPerPixel;
|
|
586
593
|
const value =
|
|
587
|
-
|
|
594
|
+
samplesPerPixel > 2
|
|
595
|
+
? [
|
|
596
|
+
scalarData[baseIndex],
|
|
597
|
+
scalarData[baseIndex + 1],
|
|
598
|
+
scalarData[baseIndex + 2],
|
|
599
|
+
]
|
|
600
|
+
: scalarData[baseIndex];
|
|
588
601
|
|
|
589
602
|
// Index[2] for stackViewport is always 0, but for visualization
|
|
590
603
|
// we reset it to be imageId index
|
|
591
604
|
if (targetId.startsWith('imageId:')) {
|
|
592
605
|
const imageId = targetId.split('imageId:')[1];
|
|
593
606
|
const imageURI = csUtils.imageIdToURI(imageId);
|
|
594
|
-
const viewports =
|
|
607
|
+
const viewports = csUtils.getViewportsWithImageURI(
|
|
595
608
|
imageURI,
|
|
596
609
|
renderingEngineId
|
|
597
610
|
);
|
|
@@ -651,7 +664,7 @@ function defaultGetTextLines(data, targetId): string[] {
|
|
|
651
664
|
|
|
652
665
|
textLines.push(`(${index[0]}, ${index[1]}, ${index[2]})`);
|
|
653
666
|
|
|
654
|
-
textLines.push(`${value
|
|
667
|
+
textLines.push(`${roundNumber(value)} ${modalityUnit}`);
|
|
655
668
|
|
|
656
669
|
return textLines;
|
|
657
670
|
}
|
|
@@ -73,6 +73,15 @@ class VideoRedactionTool extends AnnotationTool {
|
|
|
73
73
|
|
|
74
74
|
this.isDrawing = true;
|
|
75
75
|
|
|
76
|
+
const camera = viewport.getCamera();
|
|
77
|
+
const { viewPlaneNormal, viewUp } = camera;
|
|
78
|
+
const referencedImageId = this.getReferencedImageId(
|
|
79
|
+
viewport,
|
|
80
|
+
worldPos,
|
|
81
|
+
viewPlaneNormal,
|
|
82
|
+
viewUp
|
|
83
|
+
);
|
|
84
|
+
|
|
76
85
|
const annotation = {
|
|
77
86
|
metadata: {
|
|
78
87
|
// We probably just want a different type of data here, hacking this
|
|
@@ -80,7 +89,7 @@ class VideoRedactionTool extends AnnotationTool {
|
|
|
80
89
|
viewPlaneNormal: <Types.Point3>[0, 0, 1],
|
|
81
90
|
viewUp: <Types.Point3>[0, 1, 0],
|
|
82
91
|
FrameOfReferenceUID: viewport.getFrameOfReferenceUID(),
|
|
83
|
-
referencedImageId
|
|
92
|
+
referencedImageId,
|
|
84
93
|
toolName: this.getToolName(),
|
|
85
94
|
},
|
|
86
95
|
data: {
|
|
@@ -132,11 +132,10 @@ abstract class AnnotationDisplayTool extends BaseTool {
|
|
|
132
132
|
|
|
133
133
|
let referencedImageId;
|
|
134
134
|
|
|
135
|
-
if (
|
|
136
|
-
viewport instanceof StackViewport ||
|
|
137
|
-
viewport instanceof VideoViewport
|
|
138
|
-
) {
|
|
135
|
+
if (viewport instanceof StackViewport) {
|
|
139
136
|
referencedImageId = targetId.split('imageId:')[1];
|
|
137
|
+
} else if (viewport instanceof VideoViewport) {
|
|
138
|
+
referencedImageId = targetId.split('videoId:')[1];
|
|
140
139
|
} else {
|
|
141
140
|
const volumeId = targetId.split('volumeId:')[1];
|
|
142
141
|
const imageVolume = cache.getVolume(volumeId);
|
|
@@ -305,13 +305,10 @@ abstract class AnnotationTool extends AnnotationDisplayTool {
|
|
|
305
305
|
const volumeId = targetId.split('volumeId:')[1];
|
|
306
306
|
const volume = cache.getVolume(volumeId);
|
|
307
307
|
return volume.scaling?.PT !== undefined;
|
|
308
|
-
} else if (viewport instanceof StackViewport) {
|
|
309
|
-
const scalingModule: Types.ScalingParameters | undefined =
|
|
310
|
-
imageId && metaData.get('scalingModule', imageId);
|
|
311
|
-
return typeof scalingModule?.suvbw === 'number';
|
|
312
|
-
} else {
|
|
313
|
-
throw new Error('Viewport is not a valid type');
|
|
314
308
|
}
|
|
309
|
+
const scalingModule: Types.ScalingParameters | undefined =
|
|
310
|
+
imageId && metaData.get('scalingModule', imageId);
|
|
311
|
+
return typeof scalingModule?.suvbw === 'number';
|
|
315
312
|
}
|
|
316
313
|
|
|
317
314
|
/**
|
|
@@ -141,7 +141,10 @@ abstract class BaseTool implements IBaseTool {
|
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* Get the image that is displayed for the targetId in the cachedStats
|
|
144
|
-
* which can be
|
|
144
|
+
* which can be
|
|
145
|
+
* * imageId:<imageId>
|
|
146
|
+
* * volumeId:<volumeId>
|
|
147
|
+
* * videoId:<basePathForVideo>/frames/<frameSpecifier>
|
|
145
148
|
*
|
|
146
149
|
* @param targetId - annotation targetId stored in the cached stats
|
|
147
150
|
* @param renderingEngine - The rendering engine
|
|
@@ -183,6 +186,19 @@ abstract class BaseTool implements IBaseTool {
|
|
|
183
186
|
return;
|
|
184
187
|
}
|
|
185
188
|
|
|
189
|
+
return viewports[0].getImageData();
|
|
190
|
+
} else if (targetId.startsWith('videoId:')) {
|
|
191
|
+
// Video id can be multi-valued for the frame information
|
|
192
|
+
const imageURI = utilities.imageIdToURI(targetId);
|
|
193
|
+
const viewports = utilities.getViewportsWithImageURI(
|
|
194
|
+
imageURI,
|
|
195
|
+
renderingEngine.id
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
if (!viewports || !viewports.length) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
186
202
|
return viewports[0].getImageData();
|
|
187
203
|
} else {
|
|
188
204
|
throw new Error(
|
|
@@ -207,7 +223,7 @@ abstract class BaseTool implements IBaseTool {
|
|
|
207
223
|
} else if (viewport instanceof BaseVolumeViewport) {
|
|
208
224
|
return `volumeId:${this.getTargetVolumeId(viewport)}`;
|
|
209
225
|
} else if (viewport instanceof VideoViewport) {
|
|
210
|
-
return
|
|
226
|
+
return `videoId:${viewport.getCurrentImageId()}`;
|
|
211
227
|
} else {
|
|
212
228
|
throw new Error(
|
|
213
229
|
'getTargetId: viewport must be a StackViewport or VolumeViewport'
|
package/src/tools/index.ts
CHANGED
|
@@ -14,7 +14,11 @@ import AdvancedMagnifyTool from './AdvancedMagnifyTool';
|
|
|
14
14
|
import ReferenceLinesTool from './ReferenceLinesTool';
|
|
15
15
|
import OverlayGridTool from './OverlayGridTool';
|
|
16
16
|
import SegmentationIntersectionTool from './SegmentationIntersectionTool';
|
|
17
|
-
|
|
17
|
+
import ReferenceCursors from './ReferenceCursors';
|
|
18
|
+
import ReferenceLines from './ReferenceLinesTool';
|
|
19
|
+
import ScaleOverlayTool from './ScaleOverlayTool';
|
|
20
|
+
|
|
21
|
+
// Annotation tools
|
|
18
22
|
import BidirectionalTool from './annotation/BidirectionalTool';
|
|
19
23
|
import LengthTool from './annotation/LengthTool';
|
|
20
24
|
import ProbeTool from './annotation/ProbeTool';
|
|
@@ -26,9 +30,7 @@ import PlanarFreehandROITool from './annotation/PlanarFreehandROITool';
|
|
|
26
30
|
import ArrowAnnotateTool from './annotation/ArrowAnnotateTool';
|
|
27
31
|
import AngleTool from './annotation/AngleTool';
|
|
28
32
|
import CobbAngleTool from './annotation/CobbAngleTool';
|
|
29
|
-
import
|
|
30
|
-
import ReferenceLines from './ReferenceLinesTool';
|
|
31
|
-
import ScaleOverlayTool from './ScaleOverlayTool';
|
|
33
|
+
import KeyImageTool from './annotation/KeyImageTool';
|
|
32
34
|
|
|
33
35
|
// Segmentation DisplayTool
|
|
34
36
|
import SegmentationDisplayTool from './displayTools/SegmentationDisplayTool';
|
|
@@ -59,6 +61,7 @@ export {
|
|
|
59
61
|
ZoomTool,
|
|
60
62
|
VolumeRotateMouseWheelTool,
|
|
61
63
|
MIPJumpToClickTool,
|
|
64
|
+
ReferenceCursors,
|
|
62
65
|
// Annotation Tools
|
|
63
66
|
CrosshairsTool,
|
|
64
67
|
ReferenceLinesTool,
|
|
@@ -74,7 +77,7 @@ export {
|
|
|
74
77
|
ArrowAnnotateTool,
|
|
75
78
|
AngleTool,
|
|
76
79
|
CobbAngleTool,
|
|
77
|
-
|
|
80
|
+
KeyImageTool,
|
|
78
81
|
// Segmentations Display
|
|
79
82
|
SegmentationDisplayTool,
|
|
80
83
|
// Segmentations Tools
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { triggerEvent, eventTarget } from '@cornerstonejs/core';
|
|
2
|
+
import Events from '../enums/Events';
|
|
3
|
+
import { Annotation } from '../types';
|
|
4
|
+
|
|
5
|
+
export type FramesRange = [number, number] | number;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This class handles the annotation frame range values for multiframes.
|
|
9
|
+
* Mostly used for the Video viewport, it allows references to
|
|
10
|
+
* a range of frame values.
|
|
11
|
+
*/
|
|
12
|
+
export default class AnnotationFrameRange {
|
|
13
|
+
protected static frameRangeExtractor =
|
|
14
|
+
/(\/frames\/|[&?]frameNumber=)([^/&?]*)/i;
|
|
15
|
+
|
|
16
|
+
protected static imageIdToFrames(imageId: string): FramesRange {
|
|
17
|
+
const match = imageId.match(this.frameRangeExtractor);
|
|
18
|
+
if (!match || !match[2]) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const range = match[2].split('-').map((it) => Number(it));
|
|
22
|
+
if (range.length === 1) {
|
|
23
|
+
return range[0];
|
|
24
|
+
}
|
|
25
|
+
return range as FramesRange;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static framesToString(range) {
|
|
29
|
+
if (Array.isArray(range)) {
|
|
30
|
+
return `${range[0]}-${range[1]}`;
|
|
31
|
+
}
|
|
32
|
+
return String(range);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected static framesToImageId(
|
|
36
|
+
imageId: string,
|
|
37
|
+
range: FramesRange | string
|
|
38
|
+
): string {
|
|
39
|
+
const match = imageId.match(this.frameRangeExtractor);
|
|
40
|
+
if (!match || !match[2]) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const newRangeString = this.framesToString(range);
|
|
44
|
+
return imageId.replace(
|
|
45
|
+
this.frameRangeExtractor,
|
|
46
|
+
`${match[1]}${newRangeString}`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sets the range of frames to associate with the given annotation.
|
|
52
|
+
* The range can be a single frame number (1 based according to DICOM),
|
|
53
|
+
* or a range of values in the format `min-max` where min, max are inclusive
|
|
54
|
+
* Modifies the referencedImageID to specify the updated URL.
|
|
55
|
+
*/
|
|
56
|
+
public static setFrameRange(
|
|
57
|
+
annotation: Annotation,
|
|
58
|
+
range: FramesRange | string,
|
|
59
|
+
eventBase?: { viewportId; renderingEngineId }
|
|
60
|
+
) {
|
|
61
|
+
const { referencedImageId } = annotation.metadata;
|
|
62
|
+
annotation.metadata.referencedImageId = this.framesToImageId(
|
|
63
|
+
referencedImageId,
|
|
64
|
+
range
|
|
65
|
+
);
|
|
66
|
+
const eventDetail = {
|
|
67
|
+
...eventBase,
|
|
68
|
+
annotation,
|
|
69
|
+
};
|
|
70
|
+
triggerEvent(eventTarget, Events.ANNOTATION_MODIFIED, eventDetail);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public static getFrameRange(
|
|
74
|
+
annotation: Annotation
|
|
75
|
+
): number | [number, number] {
|
|
76
|
+
return this.imageIdToFrames(annotation.metadata.referencedImageId);
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/utilities/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ import pointInSurroundingSphereCallback from './pointInSurroundingSphereCallback
|
|
|
20
20
|
import scroll from './scroll';
|
|
21
21
|
import roundNumber from './roundNumber';
|
|
22
22
|
import { pointToString } from './pointToString';
|
|
23
|
+
import annotationFrameRange from './annotationFrameRange';
|
|
23
24
|
|
|
24
25
|
// name spaces
|
|
25
26
|
import * as segmentation from './segmentation';
|
|
@@ -78,4 +79,5 @@ export {
|
|
|
78
79
|
pointToString,
|
|
79
80
|
polyDataUtils,
|
|
80
81
|
voi,
|
|
82
|
+
annotationFrameRange,
|
|
81
83
|
};
|
|
@@ -2,11 +2,10 @@ import { Statistics } from '../../../types';
|
|
|
2
2
|
import Calculator from './Calculator';
|
|
3
3
|
|
|
4
4
|
export default class BasicStatsCalculator extends Calculator {
|
|
5
|
-
private static max = -Infinity;
|
|
6
|
-
private static
|
|
7
|
-
private static
|
|
8
|
-
private static
|
|
9
|
-
private static squaredDiffSum = 0;
|
|
5
|
+
private static max = [-Infinity];
|
|
6
|
+
private static sum = [0];
|
|
7
|
+
private static sumSquares = [0];
|
|
8
|
+
private static squaredDiffSum = [0];
|
|
10
9
|
private static count = 0;
|
|
11
10
|
|
|
12
11
|
/**
|
|
@@ -15,16 +14,34 @@ export default class BasicStatsCalculator extends Calculator {
|
|
|
15
14
|
* @param value of the point in the shape of the annotation
|
|
16
15
|
*/
|
|
17
16
|
static statsCallback = ({ value: newValue }): void => {
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
if (
|
|
18
|
+
Array.isArray(newValue) &&
|
|
19
|
+
newValue.length > 1 &&
|
|
20
|
+
this.max.length === 1
|
|
21
|
+
) {
|
|
22
|
+
this.max.push(this.max[0], this.max[0]);
|
|
23
|
+
this.sum.push(this.sum[0], this.sum[0]);
|
|
24
|
+
this.sumSquares.push(this.sumSquares[0], this.sumSquares[0]);
|
|
25
|
+
this.squaredDiffSum.push(this.squaredDiffSum[0], this.squaredDiffSum[0]);
|
|
21
26
|
}
|
|
22
27
|
|
|
28
|
+
const newArray = Array.isArray(newValue) ? newValue : [newValue];
|
|
23
29
|
this.count += 1;
|
|
24
30
|
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
this.max.forEach(
|
|
32
|
+
(it, idx) => (this.max[idx] = Math.max(it, newArray[idx]))
|
|
33
|
+
);
|
|
34
|
+
this.sum.map((it, idx) => (this.sum[idx] += newArray[idx]));
|
|
35
|
+
this.sumSquares.map(
|
|
36
|
+
(it, idx) => (this.sumSquares[idx] += newArray[idx] ** 2)
|
|
37
|
+
);
|
|
38
|
+
this.squaredDiffSum.map(
|
|
39
|
+
(it, idx) =>
|
|
40
|
+
(this.squaredDiffSum[idx] += Math.pow(
|
|
41
|
+
newArray[idx] - this.sum[idx] / this.count,
|
|
42
|
+
2
|
|
43
|
+
))
|
|
44
|
+
);
|
|
28
45
|
};
|
|
29
46
|
|
|
30
47
|
/**
|
|
@@ -38,23 +55,34 @@ export default class BasicStatsCalculator extends Calculator {
|
|
|
38
55
|
*/
|
|
39
56
|
|
|
40
57
|
static getStatistics = (): Statistics[] => {
|
|
41
|
-
const mean = this.sum / this.count;
|
|
42
|
-
const stdDev =
|
|
43
|
-
|
|
44
|
-
this.sumSquares / this.count - mean ** 2
|
|
58
|
+
const mean = this.sum.map((sum) => sum / this.count);
|
|
59
|
+
const stdDev = this.squaredDiffSum.map((squaredDiffSum) =>
|
|
60
|
+
Math.sqrt(squaredDiffSum / this.count)
|
|
45
61
|
);
|
|
62
|
+
const stdDevWithSumSquare = this.sumSquares.map((it, idx) =>
|
|
63
|
+
Math.sqrt(this.sumSquares[idx] / this.count - mean[idx] ** 2)
|
|
64
|
+
);
|
|
65
|
+
const currentMax = this.max;
|
|
46
66
|
|
|
47
|
-
this.max = -Infinity;
|
|
48
|
-
this.sum = 0;
|
|
49
|
-
this.sumSquares = 0;
|
|
50
|
-
this.squaredDiffSum = 0;
|
|
67
|
+
this.max = [-Infinity];
|
|
68
|
+
this.sum = [0];
|
|
69
|
+
this.sumSquares = [0];
|
|
70
|
+
this.squaredDiffSum = [0];
|
|
51
71
|
this.count = 0;
|
|
52
72
|
|
|
53
73
|
return [
|
|
54
|
-
{ name: 'max', value:
|
|
55
|
-
{ name: 'mean', value: mean, unit: null },
|
|
56
|
-
{ name: 'stdDev', value: stdDev, unit: null },
|
|
57
|
-
{
|
|
74
|
+
{ name: 'max', value: singleArrayAsNumber(currentMax), unit: null },
|
|
75
|
+
{ name: 'mean', value: singleArrayAsNumber(mean), unit: null },
|
|
76
|
+
{ name: 'stdDev', value: singleArrayAsNumber(stdDev), unit: null },
|
|
77
|
+
{
|
|
78
|
+
name: 'stdDevWithSumSquare',
|
|
79
|
+
value: singleArrayAsNumber(stdDevWithSumSquare),
|
|
80
|
+
unit: null,
|
|
81
|
+
},
|
|
58
82
|
];
|
|
59
83
|
};
|
|
60
84
|
}
|
|
85
|
+
|
|
86
|
+
function singleArrayAsNumber(val: number[]) {
|
|
87
|
+
return val.length === 1 ? val[0] : val;
|
|
88
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Types } from '@cornerstonejs/core';
|
|
2
|
+
import { vec3 } from 'gl-matrix';
|
|
2
3
|
|
|
3
4
|
type Ellipse = {
|
|
4
5
|
center: Types.Point3;
|
|
@@ -15,7 +16,7 @@ type Ellipse = {
|
|
|
15
16
|
*/
|
|
16
17
|
export default function pointInEllipse(
|
|
17
18
|
ellipse: Ellipse,
|
|
18
|
-
pointLPS:
|
|
19
|
+
pointLPS: vec3
|
|
19
20
|
): boolean {
|
|
20
21
|
const { center: circleCenterWorld, xRadius, yRadius, zRadius } = ellipse;
|
|
21
22
|
const [x, y, z] = pointLPS;
|
|
@@ -16,16 +16,13 @@ type Sphere = {
|
|
|
16
16
|
* @param pointLPS - the point to check in world coordinates
|
|
17
17
|
* @returns boolean
|
|
18
18
|
*/
|
|
19
|
-
export default function pointInSphere(
|
|
20
|
-
sphere: Sphere,
|
|
21
|
-
pointLPS: Types.Point3
|
|
22
|
-
): boolean {
|
|
19
|
+
export default function pointInSphere(sphere: Sphere, pointLPS: vec3): boolean {
|
|
23
20
|
const { center, radius } = sphere;
|
|
24
21
|
|
|
25
22
|
return (
|
|
26
|
-
(pointLPS[0] - center[0])
|
|
27
|
-
(pointLPS[1] - center[1])
|
|
28
|
-
(pointLPS[2] - center[2])
|
|
23
|
+
(pointLPS[0] - center[0]) * (pointLPS[0] - center[0]) +
|
|
24
|
+
(pointLPS[1] - center[1]) * (pointLPS[1] - center[1]) +
|
|
25
|
+
(pointLPS[2] - center[2]) * (pointLPS[2] - center[2]) <=
|
|
29
26
|
radius ** 2
|
|
30
27
|
);
|
|
31
28
|
}
|
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import filterAnnotationsWithinSlice from './filterAnnotationsWithinSlice';
|
|
10
10
|
import { Annotations } from '../../types';
|
|
11
|
+
import { annotationFrameRange } from '..';
|
|
12
|
+
|
|
13
|
+
const baseUrlExtractor = /(videoId:|imageId:|volumeId:)([a-zA-Z]*:)/;
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* Given the viewport and the annotations, it filters the annotations array and only
|
|
@@ -43,7 +46,7 @@ export default function filterAnnotationsForDisplay(
|
|
|
43
46
|
|
|
44
47
|
if (imageId === undefined) {
|
|
45
48
|
// This annotation was not drawn on a non-coplanar reformat, and such does
|
|
46
|
-
//
|
|
49
|
+
// not have a referenced imageId.
|
|
47
50
|
return false;
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -54,8 +57,31 @@ export default function filterAnnotationsForDisplay(
|
|
|
54
57
|
} else if (viewport instanceof VideoViewport) {
|
|
55
58
|
const frameOfReferenceUID: string = viewport.getFrameOfReferenceUID();
|
|
56
59
|
|
|
57
|
-
return annotations.filter((
|
|
58
|
-
|
|
60
|
+
return annotations.filter((annotation) => {
|
|
61
|
+
if (!annotation.isVisible) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (annotation.metadata.FrameOfReferenceUID !== frameOfReferenceUID) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const testURI = annotation.metadata.referencedImageId.replace(
|
|
68
|
+
baseUrlExtractor,
|
|
69
|
+
''
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (!viewport.hasImageURI(testURI)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const range = annotationFrameRange.getFrameRange(annotation);
|
|
76
|
+
const frameNumber = viewport.getFrameNumber();
|
|
77
|
+
if (Array.isArray(range)) {
|
|
78
|
+
return frameNumber >= range[0] && frameNumber <= range[1];
|
|
79
|
+
}
|
|
80
|
+
// Arbitrary 5 frames of slop on the video for matching single frame
|
|
81
|
+
// number to position - this allows the annotation to display when
|
|
82
|
+
// the video element is not exactly the same timing as expected or when
|
|
83
|
+
// playing video back.
|
|
84
|
+
return Math.abs(frameNumber - range) <= 5;
|
|
59
85
|
});
|
|
60
86
|
} else if (viewport instanceof VolumeViewport) {
|
|
61
87
|
const camera = viewport.getCamera();
|
|
@@ -6,8 +6,8 @@ import BoundsIJK from '../types/BoundsIJK';
|
|
|
6
6
|
export type PointInShape = {
|
|
7
7
|
value: number;
|
|
8
8
|
index: number;
|
|
9
|
-
pointIJK:
|
|
10
|
-
pointLPS:
|
|
9
|
+
pointIJK: vec3;
|
|
10
|
+
pointLPS: vec3;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export type PointInShapeCallback = ({
|
|
@@ -18,14 +18,11 @@ export type PointInShapeCallback = ({
|
|
|
18
18
|
}: {
|
|
19
19
|
value: number;
|
|
20
20
|
index: number;
|
|
21
|
-
pointIJK:
|
|
22
|
-
pointLPS:
|
|
21
|
+
pointIJK: vec3;
|
|
22
|
+
pointLPS: vec3;
|
|
23
23
|
}) => void;
|
|
24
24
|
|
|
25
|
-
export type ShapeFnCriteria = (
|
|
26
|
-
pointIJK: Types.Point3,
|
|
27
|
-
pointLPS: Types.Point3
|
|
28
|
-
) => boolean;
|
|
25
|
+
export type ShapeFnCriteria = (pointIJK: vec3, pointLPS: vec3) => boolean;
|
|
29
26
|
|
|
30
27
|
/**
|
|
31
28
|
* For each point in the image (If boundsIJK is not provided, otherwise, for each
|
|
@@ -105,46 +102,57 @@ export default function pointInShapeCallback(
|
|
|
105
102
|
scanAxisNormal[2] * scanAxisSpacing
|
|
106
103
|
);
|
|
107
104
|
|
|
108
|
-
const
|
|
109
|
-
|
|
105
|
+
const xMultiple =
|
|
106
|
+
scalarData.length / dimensions[2] / dimensions[1] / dimensions[0];
|
|
107
|
+
const yMultiple = dimensions[0] * xMultiple;
|
|
108
|
+
const zMultiple = dimensions[1] * yMultiple;
|
|
110
109
|
|
|
111
110
|
const pointsInShape: Array<PointInShape> = [];
|
|
111
|
+
|
|
112
|
+
const currentPos = vec3.clone(worldPosStart);
|
|
113
|
+
|
|
112
114
|
for (let k = kMin; k <= kMax; k++) {
|
|
115
|
+
const startPosJ = vec3.clone(currentPos);
|
|
116
|
+
|
|
113
117
|
for (let j = jMin; j <= jMax; j++) {
|
|
118
|
+
const startPosI = vec3.clone(currentPos);
|
|
119
|
+
|
|
114
120
|
for (let i = iMin; i <= iMax; i++) {
|
|
115
121
|
const pointIJK: Types.Point3 = [i, j, k];
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
dK * scanAxisStep[2],
|
|
135
|
-
];
|
|
136
|
-
|
|
137
|
-
if (pointInShapeFn(pointLPS, pointIJK)) {
|
|
138
|
-
const index = k * zMultiple + j * yMultiple + i;
|
|
139
|
-
const value = scalarData[index];
|
|
140
|
-
|
|
141
|
-
pointsInShape.push({ value, index, pointIJK, pointLPS });
|
|
142
|
-
if (callback !== null) {
|
|
143
|
-
callback({ value, index, pointIJK, pointLPS });
|
|
122
|
+
|
|
123
|
+
// The current world position (pointLPS) is now in currentPos
|
|
124
|
+
if (pointInShapeFn(currentPos as Types.Point3, currentPos)) {
|
|
125
|
+
const index = k * zMultiple + j * yMultiple + i * xMultiple;
|
|
126
|
+
let value;
|
|
127
|
+
if (xMultiple > 2) {
|
|
128
|
+
value = [
|
|
129
|
+
scalarData[index],
|
|
130
|
+
scalarData[index + 1],
|
|
131
|
+
scalarData[index + 2],
|
|
132
|
+
];
|
|
133
|
+
} else {
|
|
134
|
+
value = scalarData[index];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
pointsInShape.push({ value, index, pointIJK, pointLPS: currentPos });
|
|
138
|
+
if (callback) {
|
|
139
|
+
callback({ value, index, pointIJK, pointLPS: currentPos });
|
|
144
140
|
}
|
|
145
141
|
}
|
|
142
|
+
|
|
143
|
+
// Increment currentPos by rowStep for the next iteration
|
|
144
|
+
vec3.add(currentPos, currentPos, rowStep);
|
|
146
145
|
}
|
|
146
|
+
|
|
147
|
+
// Reset currentPos to the start of the next J line and increment by columnStep
|
|
148
|
+
vec3.copy(currentPos, startPosI);
|
|
149
|
+
vec3.add(currentPos, currentPos, columnStep);
|
|
147
150
|
}
|
|
151
|
+
|
|
152
|
+
// Reset currentPos to the start of the next K slice and increment by scanAxisStep
|
|
153
|
+
vec3.copy(currentPos, startPosJ);
|
|
154
|
+
vec3.add(currentPos, currentPos, scanAxisStep);
|
|
148
155
|
}
|
|
156
|
+
|
|
149
157
|
return pointsInShape;
|
|
150
158
|
}
|
|
@@ -10,7 +10,13 @@
|
|
|
10
10
|
* @param value - to return a fixed measurement value from
|
|
11
11
|
* @param precision - defining how many digits after 1..9 are desired
|
|
12
12
|
*/
|
|
13
|
-
function roundNumber(
|
|
13
|
+
function roundNumber(
|
|
14
|
+
value: string | number | (string | number)[],
|
|
15
|
+
precision = 2
|
|
16
|
+
): string {
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
return value.map((v) => roundNumber(v, precision)).join(', ');
|
|
19
|
+
}
|
|
14
20
|
if (value === undefined || value === null || value === '') {
|
|
15
21
|
return 'NaN';
|
|
16
22
|
}
|