@cornerstonejs/adapters 3.31.0 → 3.31.2
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/adapters/Cornerstone/Bidirectional.js +1 -0
- package/dist/esm/adapters/Cornerstone/MeasurementReport.js +1 -0
- package/dist/esm/adapters/Cornerstone3D/Angle.d.ts +23 -62
- package/dist/esm/adapters/Cornerstone3D/Angle.js +29 -141
- package/dist/esm/adapters/Cornerstone3D/ArrowAnnotate.d.ts +13 -27
- package/dist/esm/adapters/Cornerstone3D/ArrowAnnotate.js +26 -144
- package/dist/esm/adapters/Cornerstone3D/BaseAdapter3D.d.ts +2 -12
- package/dist/esm/adapters/Cornerstone3D/BaseAdapter3D.js +13 -42
- package/dist/esm/adapters/Cornerstone3D/Bidirectional.d.ts +23 -80
- package/dist/esm/adapters/Cornerstone3D/Bidirectional.js +33 -167
- package/dist/esm/adapters/Cornerstone3D/CircleROI.d.ts +16 -33
- package/dist/esm/adapters/Cornerstone3D/CircleROI.js +28 -129
- package/dist/esm/adapters/Cornerstone3D/CobbAngle.d.ts +23 -62
- package/dist/esm/adapters/Cornerstone3D/CobbAngle.js +22 -144
- package/dist/esm/adapters/Cornerstone3D/EllipticalROI.d.ts +7 -29
- package/dist/esm/adapters/Cornerstone3D/EllipticalROI.js +25 -176
- package/dist/esm/adapters/Cornerstone3D/KeyImage.d.ts +8 -10
- package/dist/esm/adapters/Cornerstone3D/KeyImage.js +2 -2
- package/dist/esm/adapters/Cornerstone3D/Length.d.ts +15 -44
- package/dist/esm/adapters/Cornerstone3D/Length.js +28 -121
- package/dist/esm/adapters/Cornerstone3D/MeasurementReport.d.ts +57 -38
- package/dist/esm/adapters/Cornerstone3D/MeasurementReport.js +66 -30
- package/dist/esm/adapters/Cornerstone3D/PlanarFreehandROI.d.ts +16 -41
- package/dist/esm/adapters/Cornerstone3D/PlanarFreehandROI.js +25 -132
- package/dist/esm/adapters/Cornerstone3D/Probe.d.ts +7 -21
- package/dist/esm/adapters/Cornerstone3D/Probe.js +12 -39
- package/dist/esm/adapters/Cornerstone3D/RectangleROI.d.ts +1 -1
- package/dist/esm/adapters/Cornerstone3D/RectangleROI.js +7 -2
- package/dist/esm/adapters/Cornerstone3D/UltrasoundDirectional.d.ts +6 -15
- package/dist/esm/adapters/Cornerstone3D/UltrasoundDirectional.js +6 -2
- package/dist/esm/adapters/helpers/index.d.ts +3 -0
- package/dist/esm/adapters/helpers/index.js +3 -0
- package/dist/esm/adapters/helpers/scoordToWorld.d.ts +5 -0
- package/dist/esm/adapters/helpers/scoordToWorld.js +32 -0
- package/dist/esm/adapters/helpers/toPoint3.d.ts +3 -0
- package/dist/esm/adapters/helpers/toPoint3.js +18 -0
- package/dist/esm/adapters/helpers/toScoordType.d.ts +23 -0
- package/dist/esm/adapters/helpers/toScoordType.js +33 -0
- package/dist/esm/adapters/index.js +1 -0
- package/dist/esm/version.d.ts +1 -1
- package/package.json +5 -5
|
@@ -1,27 +1,37 @@
|
|
|
1
|
+
import { type Types as CSTypes } from "@cornerstonejs/core";
|
|
2
|
+
import type { Types } from "@cornerstonejs/tools";
|
|
3
|
+
type Annotation = Types.Annotation;
|
|
1
4
|
type SpatialCoordinatesState = {
|
|
2
5
|
description?: string;
|
|
3
6
|
sopInstanceUid?: string;
|
|
4
|
-
annotation:
|
|
5
|
-
annotationUID: string;
|
|
6
|
-
metadata: {
|
|
7
|
-
toolName: string;
|
|
8
|
-
referencedImageId?: string;
|
|
9
|
-
FrameOfReferenceUID: string;
|
|
10
|
-
label: string;
|
|
11
|
-
};
|
|
12
|
-
data?: unknown;
|
|
13
|
-
};
|
|
7
|
+
annotation: Annotation;
|
|
14
8
|
finding?: unknown;
|
|
15
9
|
findingSites?: unknown;
|
|
16
10
|
};
|
|
11
|
+
type ScoordType = {
|
|
12
|
+
GraphicData: number[];
|
|
13
|
+
};
|
|
17
14
|
type SetupMeasurementData = {
|
|
18
15
|
defaultState: SpatialCoordinatesState;
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
state?: SpatialCoordinatesState;
|
|
17
|
+
is3DMeasurement?: boolean;
|
|
18
|
+
scoord?: ScoordType;
|
|
19
|
+
worldCoords?: CSTypes.Point3[];
|
|
20
|
+
scoordArgs?: {
|
|
21
|
+
referencedImageId: string;
|
|
22
|
+
is3DMeasurement: boolean;
|
|
23
|
+
};
|
|
24
|
+
NUMGroup: {
|
|
25
|
+
MeasuredValueSequence: {
|
|
26
|
+
NumericValue: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
SCOORDGroup?: ScoordType;
|
|
21
30
|
ReferencedSOPSequence?: Record<string, unknown>;
|
|
22
31
|
ReferencedSOPInstanceUID?: string;
|
|
32
|
+
referencedImageId?: string;
|
|
23
33
|
ReferencedFrameNumber?: string;
|
|
24
|
-
SCOORD3DGroup?:
|
|
34
|
+
SCOORD3DGroup?: ScoordType;
|
|
25
35
|
FrameOfReferenceUID?: string;
|
|
26
36
|
};
|
|
27
37
|
type SpatialCoordinatesData = Omit<SetupMeasurementData, "defaultState" | "NUMGroup"> & {
|
|
@@ -38,17 +48,17 @@ export interface MeasurementAdapter {
|
|
|
38
48
|
trackingIdentifiers: Set<string>;
|
|
39
49
|
parentType: string;
|
|
40
50
|
init(toolType: string, representation: any, options?: AdapterOptions): any;
|
|
41
|
-
getMeasurementData(measurementGroup: any, sopInstanceUIDToImageIdMap: any,
|
|
51
|
+
getMeasurementData(measurementGroup: any, sopInstanceUIDToImageIdMap: any, metadata: any, trackingIdentifier: string): any;
|
|
42
52
|
isValidCornerstoneTrackingIdentifier(trackingIdentifier: string): boolean;
|
|
43
|
-
getTID300RepresentationArguments(tool: any,
|
|
53
|
+
getTID300RepresentationArguments(tool: any, is3DMeasurement: any): Record<string, unknown>;
|
|
44
54
|
}
|
|
45
55
|
export default class MeasurementReport {
|
|
46
56
|
static CORNERSTONE_3D_TAG: string;
|
|
47
57
|
static measurementAdapterByToolType: Map<string, MeasurementAdapter>;
|
|
48
58
|
static measurementAdapterByTrackingIdentifier: Map<string, MeasurementAdapter>;
|
|
49
|
-
static getTID300ContentItem(tool: any, ReferencedSOPSequence: any, toolClass: any,
|
|
59
|
+
static getTID300ContentItem(tool: any, ReferencedSOPSequence: any, toolClass: any, is3DMeasurement: any): any;
|
|
50
60
|
static codeValueMatch: (group: any, code: any, oldCode?: any) => boolean;
|
|
51
|
-
static getMeasurementGroup(toolType: any, toolData: any, ReferencedSOPSequence: any,
|
|
61
|
+
static getMeasurementGroup(toolType: any, toolData: any, ReferencedSOPSequence: any, is3DMeasurement: any): any;
|
|
52
62
|
static getCornerstoneLabelFromDefaultState(defaultState: any): any;
|
|
53
63
|
static generateDatasetMeta(): {
|
|
54
64
|
FileMetaInformationVersion: {
|
|
@@ -77,7 +87,28 @@ export default class MeasurementReport {
|
|
|
77
87
|
toolType: any;
|
|
78
88
|
sopInstanceUIDToImageIdMap: any;
|
|
79
89
|
metadata: any;
|
|
80
|
-
}):
|
|
90
|
+
}): {
|
|
91
|
+
SCOORDGroup: any;
|
|
92
|
+
ReferencedSOPSequence: any;
|
|
93
|
+
ReferencedSOPInstanceUID: any;
|
|
94
|
+
ReferencedFrameNumber: any;
|
|
95
|
+
referencedImageId: any;
|
|
96
|
+
state: {
|
|
97
|
+
description: any;
|
|
98
|
+
sopInstanceUid: any;
|
|
99
|
+
annotation: {
|
|
100
|
+
data: {
|
|
101
|
+
annotationUID: any;
|
|
102
|
+
};
|
|
103
|
+
annotationUID: any;
|
|
104
|
+
metadata: {
|
|
105
|
+
toolName: any;
|
|
106
|
+
referencedImageId: any;
|
|
107
|
+
FrameOfReferenceUID: any;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
};
|
|
81
112
|
static processSCOORD3DGroup({ SCOORD3DGroup, toolType }: {
|
|
82
113
|
SCOORD3DGroup: any;
|
|
83
114
|
toolType: any;
|
|
@@ -96,28 +127,16 @@ export default class MeasurementReport {
|
|
|
96
127
|
findingSiteGroups: any;
|
|
97
128
|
toolType: any;
|
|
98
129
|
}): {
|
|
99
|
-
defaultState:
|
|
100
|
-
|
|
101
|
-
findingSites: any;
|
|
102
|
-
description?: string;
|
|
103
|
-
sopInstanceUid?: string;
|
|
104
|
-
annotation: {
|
|
105
|
-
annotationUID: string;
|
|
106
|
-
metadata: {
|
|
107
|
-
toolName: string;
|
|
108
|
-
referencedImageId?: string;
|
|
109
|
-
FrameOfReferenceUID: string;
|
|
110
|
-
label: string;
|
|
111
|
-
};
|
|
112
|
-
data?: unknown;
|
|
113
|
-
};
|
|
114
|
-
};
|
|
130
|
+
defaultState: SpatialCoordinatesState;
|
|
131
|
+
state: SpatialCoordinatesState;
|
|
115
132
|
NUMGroup: any;
|
|
116
|
-
|
|
133
|
+
scoord: ScoordType;
|
|
134
|
+
SCOORDGroup: ScoordType;
|
|
117
135
|
ReferencedSOPSequence: Record<string, unknown>;
|
|
118
136
|
ReferencedSOPInstanceUID: string;
|
|
137
|
+
referencedImageId: string;
|
|
119
138
|
ReferencedFrameNumber: string;
|
|
120
|
-
SCOORD3DGroup:
|
|
139
|
+
SCOORD3DGroup: ScoordType;
|
|
121
140
|
FrameOfReferenceUID: string;
|
|
122
141
|
};
|
|
123
142
|
static getSetupMeasurementData(MeasurementGroup: any, sopInstanceUIDToImageIdMap: any, metadata: any, toolType: any): SetupMeasurementData;
|
|
@@ -137,8 +156,8 @@ export default class MeasurementReport {
|
|
|
137
156
|
toolData: any;
|
|
138
157
|
toolTypes: any;
|
|
139
158
|
}): string;
|
|
140
|
-
static generateReport(toolState: any, metadataProvider: any,
|
|
141
|
-
static generateToolState(dataset: any, sopInstanceUIDToImageIdMap: any,
|
|
159
|
+
static generateReport(toolState: any, metadataProvider: any, options: any): any;
|
|
160
|
+
static generateToolState(dataset: any, sopInstanceUIDToImageIdMap: any, metadata: any, hooks: any): {};
|
|
142
161
|
static registerTool(toolAdapter: MeasurementAdapter, replace?: boolean | ((original: any) => void)): void;
|
|
143
162
|
static registerTrackingIdentifier(toolClass: any, ...trackingIdentifiers: string[]): void;
|
|
144
163
|
static getAdapterForTrackingIdentifier(trackingIdentifier: string): MeasurementAdapter;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { utilities, derivations, normalizers, data } from 'dcmjs';
|
|
2
|
-
import { cache } from '@cornerstonejs/core';
|
|
1
|
+
import { utilities as utilities$1, derivations, normalizers, data } from 'dcmjs';
|
|
2
|
+
import { utilities, cache } from '@cornerstonejs/core';
|
|
3
3
|
import CORNERSTONE_3D_TAG from './cornerstone3DTag.js';
|
|
4
4
|
import { toArray } from '../helpers/toArray.js';
|
|
5
5
|
import { codeMeaningEquals } from '../helpers/codeMeaningEquals.js';
|
|
6
6
|
import 'buffer';
|
|
7
7
|
import { copyStudyTags } from '../helpers/copyStudyTags.js';
|
|
8
8
|
import { copySeriesTags } from '../helpers/copySeriesTags.js';
|
|
9
|
+
import { scoordToWorld } from '../helpers/scoordToWorld.js';
|
|
10
|
+
import { toPoint3 } from '../helpers/toPoint3.js';
|
|
9
11
|
import CodingScheme from './CodingScheme.js';
|
|
10
12
|
import { NO_IMAGE_ID } from './constants/index.js';
|
|
11
13
|
|
|
@@ -13,7 +15,7 @@ var _MeasurementReport;
|
|
|
13
15
|
const {
|
|
14
16
|
TID1500,
|
|
15
17
|
addAccessors
|
|
16
|
-
} = utilities;
|
|
18
|
+
} = utilities$1;
|
|
17
19
|
const {
|
|
18
20
|
StructuredReport
|
|
19
21
|
} = derivations;
|
|
@@ -40,20 +42,20 @@ const FINDING_SITE_OLD = {
|
|
|
40
42
|
CodeValue: "G-C0E3"
|
|
41
43
|
};
|
|
42
44
|
class MeasurementReport {
|
|
43
|
-
static getTID300ContentItem(tool, ReferencedSOPSequence, toolClass,
|
|
44
|
-
const args = toolClass.getTID300RepresentationArguments(tool,
|
|
45
|
+
static getTID300ContentItem(tool, ReferencedSOPSequence, toolClass, is3DMeasurement) {
|
|
46
|
+
const args = toolClass.getTID300RepresentationArguments(tool, is3DMeasurement);
|
|
45
47
|
args.ReferencedSOPSequence = ReferencedSOPSequence;
|
|
46
48
|
const TID300Measurement = new toolClass.TID300Representation(args);
|
|
47
49
|
return TID300Measurement;
|
|
48
50
|
}
|
|
49
|
-
static getMeasurementGroup(toolType, toolData, ReferencedSOPSequence,
|
|
51
|
+
static getMeasurementGroup(toolType, toolData, ReferencedSOPSequence, is3DMeasurement) {
|
|
50
52
|
const toolTypeData = toolData[toolType];
|
|
51
53
|
const toolClass = this.measurementAdapterByToolType.get(toolType);
|
|
52
54
|
if (!toolTypeData || !toolTypeData.data || !toolTypeData.data.length || !toolClass) {
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
const Measurements = toolTypeData.data.map(tool => {
|
|
56
|
-
return this.getTID300ContentItem(tool, ReferencedSOPSequence, toolClass,
|
|
58
|
+
return this.getTID300ContentItem(tool, ReferencedSOPSequence, toolClass, is3DMeasurement);
|
|
57
59
|
});
|
|
58
60
|
return new TID1501MeasurementGroup(Measurements);
|
|
59
61
|
}
|
|
@@ -110,21 +112,25 @@ class MeasurementReport {
|
|
|
110
112
|
} = ReferencedSOPSequence;
|
|
111
113
|
const referencedImageId = sopInstanceUIDToImageIdMap[ReferencedSOPInstanceUID];
|
|
112
114
|
const imagePlaneModule = metadata.get("imagePlaneModule", referencedImageId);
|
|
115
|
+
const annotationUID = DicomMetaDictionary.uid();
|
|
113
116
|
return {
|
|
114
117
|
SCOORDGroup,
|
|
115
118
|
ReferencedSOPSequence,
|
|
116
119
|
ReferencedSOPInstanceUID,
|
|
117
120
|
ReferencedFrameNumber,
|
|
121
|
+
referencedImageId,
|
|
118
122
|
state: {
|
|
119
123
|
description: undefined,
|
|
120
124
|
sopInstanceUid: ReferencedSOPInstanceUID,
|
|
121
125
|
annotation: {
|
|
122
|
-
|
|
126
|
+
data: {
|
|
127
|
+
annotationUID
|
|
128
|
+
},
|
|
129
|
+
annotationUID,
|
|
123
130
|
metadata: {
|
|
124
131
|
toolName: toolType,
|
|
125
132
|
referencedImageId,
|
|
126
|
-
FrameOfReferenceUID: imagePlaneModule.frameOfReferenceUID
|
|
127
|
-
label: ""
|
|
133
|
+
FrameOfReferenceUID: imagePlaneModule.frameOfReferenceUID
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
136
|
}
|
|
@@ -135,21 +141,26 @@ class MeasurementReport {
|
|
|
135
141
|
SCOORD3DGroup,
|
|
136
142
|
toolType
|
|
137
143
|
} = _ref2;
|
|
138
|
-
|
|
144
|
+
const annotationUID = DicomMetaDictionary.uid();
|
|
145
|
+
const toolData = {
|
|
139
146
|
SCOORD3DGroup,
|
|
140
147
|
FrameOfReferenceUID: SCOORD3DGroup.ReferencedFrameOfReferenceUID,
|
|
141
148
|
state: {
|
|
142
149
|
description: undefined,
|
|
143
150
|
annotation: {
|
|
144
|
-
annotationUID
|
|
151
|
+
annotationUID,
|
|
152
|
+
data: {
|
|
153
|
+
annotationUID
|
|
154
|
+
},
|
|
145
155
|
metadata: {
|
|
146
156
|
toolName: toolType,
|
|
147
|
-
FrameOfReferenceUID: SCOORD3DGroup.ReferencedFrameOfReferenceUID
|
|
148
|
-
label: ""
|
|
157
|
+
FrameOfReferenceUID: SCOORD3DGroup.ReferencedFrameOfReferenceUID
|
|
149
158
|
}
|
|
150
159
|
}
|
|
151
160
|
}
|
|
152
161
|
};
|
|
162
|
+
utilities.updatePlaneRestriction(toPoint3(SCOORD3DGroup.GraphicData), toolData.state.annotation.metadata);
|
|
163
|
+
return toolData;
|
|
153
164
|
}
|
|
154
165
|
static getSpatialCoordinatesState(_ref3) {
|
|
155
166
|
let {
|
|
@@ -192,7 +203,8 @@ class MeasurementReport {
|
|
|
192
203
|
ReferencedSOPInstanceUID,
|
|
193
204
|
ReferencedFrameNumber,
|
|
194
205
|
SCOORD3DGroup,
|
|
195
|
-
FrameOfReferenceUID
|
|
206
|
+
FrameOfReferenceUID,
|
|
207
|
+
referencedImageId
|
|
196
208
|
} = this.getSpatialCoordinatesState({
|
|
197
209
|
NUMGroup,
|
|
198
210
|
sopInstanceUIDToImageIdMap,
|
|
@@ -203,21 +215,21 @@ class MeasurementReport {
|
|
|
203
215
|
const findingSites = findingSiteGroups.map(fsg => {
|
|
204
216
|
return addAccessors(fsg.ConceptCodeSequence);
|
|
205
217
|
});
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
};
|
|
211
|
-
if (defaultState.finding) {
|
|
212
|
-
defaultState.description = defaultState.finding.CodeMeaning;
|
|
218
|
+
state.finding = finding;
|
|
219
|
+
state.findingSites = findingSites;
|
|
220
|
+
if (finding) {
|
|
221
|
+
state.description = finding.CodeMeaning;
|
|
213
222
|
}
|
|
214
|
-
|
|
223
|
+
state.annotation.data.label = MeasurementReport.getCornerstoneLabelFromDefaultState(state);
|
|
215
224
|
return {
|
|
216
|
-
defaultState,
|
|
225
|
+
defaultState: state,
|
|
226
|
+
state,
|
|
217
227
|
NUMGroup,
|
|
228
|
+
scoord: SCOORD3DGroup || SCOORDGroup,
|
|
218
229
|
SCOORDGroup,
|
|
219
230
|
ReferencedSOPSequence,
|
|
220
231
|
ReferencedSOPInstanceUID,
|
|
232
|
+
referencedImageId,
|
|
221
233
|
ReferencedFrameNumber,
|
|
222
234
|
SCOORD3DGroup,
|
|
223
235
|
FrameOfReferenceUID
|
|
@@ -231,7 +243,7 @@ class MeasurementReport {
|
|
|
231
243
|
const findingGroup = contentSequenceArr.find(group => this.codeValueMatch(group, FINDING));
|
|
232
244
|
const findingSiteGroups = contentSequenceArr.filter(group => this.codeValueMatch(group, FINDING_SITE, FINDING_SITE_OLD)) || [];
|
|
233
245
|
const NUMGroup = contentSequenceArr.find(group => group.ValueType === "NUM");
|
|
234
|
-
|
|
246
|
+
const spatialGroup = this.processSpatialCoordinatesGroup({
|
|
235
247
|
NUMGroup,
|
|
236
248
|
sopInstanceUIDToImageIdMap,
|
|
237
249
|
metadata,
|
|
@@ -239,6 +251,23 @@ class MeasurementReport {
|
|
|
239
251
|
findingSiteGroups,
|
|
240
252
|
toolType
|
|
241
253
|
});
|
|
254
|
+
const {
|
|
255
|
+
referencedImageId
|
|
256
|
+
} = spatialGroup.state.annotation.metadata;
|
|
257
|
+
const is3DMeasurement = !!spatialGroup.SCOORD3DGroup;
|
|
258
|
+
const scoordArgs = {
|
|
259
|
+
referencedImageId,
|
|
260
|
+
is3DMeasurement
|
|
261
|
+
};
|
|
262
|
+
const scoord = spatialGroup.SCOORD3DGroup || spatialGroup.SCOORDGroup;
|
|
263
|
+
const worldCoords = scoordToWorld(scoordArgs, scoord);
|
|
264
|
+
return {
|
|
265
|
+
...spatialGroup,
|
|
266
|
+
is3DMeasurement,
|
|
267
|
+
scoordArgs,
|
|
268
|
+
scoord,
|
|
269
|
+
worldCoords
|
|
270
|
+
};
|
|
242
271
|
}
|
|
243
272
|
static generateReferencedSOPSequence(_ref5) {
|
|
244
273
|
let {
|
|
@@ -286,10 +315,13 @@ class MeasurementReport {
|
|
|
286
315
|
const referenceToolData = toolData?.[toolTypes?.[0]]?.data?.[0];
|
|
287
316
|
const volumeId = referenceToolData?.metadata?.volumeId;
|
|
288
317
|
const volume = cache.getVolume(volumeId);
|
|
318
|
+
if (!volume) {
|
|
319
|
+
throw new Error(`No volume found for ${volumeId}`);
|
|
320
|
+
}
|
|
289
321
|
const imageId = volume.imageIds[0];
|
|
290
322
|
return imageId;
|
|
291
323
|
}
|
|
292
|
-
static generateReport(toolState, metadataProvider,
|
|
324
|
+
static generateReport(toolState, metadataProvider, options) {
|
|
293
325
|
let allMeasurementGroups = [];
|
|
294
326
|
const sopInstanceUIDsToSeriesInstanceUIDMap = {};
|
|
295
327
|
const derivationSourceDatasets = [];
|
|
@@ -298,6 +330,7 @@ class MeasurementReport {
|
|
|
298
330
|
Object.keys(toolState).forEach(imageId => {
|
|
299
331
|
const toolData = toolState[imageId];
|
|
300
332
|
const toolTypes = Object.keys(toolData);
|
|
333
|
+
const is3DMeasurement = imageId === NO_IMAGE_ID;
|
|
301
334
|
const ReferencedSOPSequence = this.generateReferencedSOPSequence({
|
|
302
335
|
toolData,
|
|
303
336
|
toolTypes,
|
|
@@ -306,12 +339,12 @@ class MeasurementReport {
|
|
|
306
339
|
sopInstanceUIDsToSeriesInstanceUIDMap,
|
|
307
340
|
derivationSourceDatasets
|
|
308
341
|
});
|
|
309
|
-
if (
|
|
342
|
+
if (is3DMeasurement) {
|
|
310
343
|
is3DSR = true;
|
|
311
344
|
}
|
|
312
345
|
const measurementGroups = [];
|
|
313
346
|
toolTypes.forEach(toolType => {
|
|
314
|
-
const group = this.getMeasurementGroup(toolType, toolData, ReferencedSOPSequence,
|
|
347
|
+
const group = this.getMeasurementGroup(toolType, toolData, ReferencedSOPSequence, is3DMeasurement);
|
|
315
348
|
if (group) {
|
|
316
349
|
measurementGroups.push(group);
|
|
317
350
|
}
|
|
@@ -331,10 +364,13 @@ class MeasurementReport {
|
|
|
331
364
|
report.SpecificCharacterSet = "ISO_IR 192";
|
|
332
365
|
if (is3DSR) {
|
|
333
366
|
report.dataset.SOPClassUID = DicomMetaDictionary.sopClassUIDsByName.Comprehensive3DSR;
|
|
367
|
+
if (!report.dataset.SOPClassUID) {
|
|
368
|
+
throw new Error(`NO sop class defined for Comprehensive3DSR in ${JSON.stringify(DicomMetaDictionary.sopClassUIDsByName)}`);
|
|
369
|
+
}
|
|
334
370
|
}
|
|
335
371
|
return report;
|
|
336
372
|
}
|
|
337
|
-
static generateToolState(dataset, sopInstanceUIDToImageIdMap,
|
|
373
|
+
static generateToolState(dataset, sopInstanceUIDToImageIdMap, metadata, hooks) {
|
|
338
374
|
if (dataset.ContentTemplateSequence.TemplateIdentifier !== "1500") {
|
|
339
375
|
throw new Error("This package can currently only interpret DICOM SR TID 1500");
|
|
340
376
|
}
|
|
@@ -356,7 +392,7 @@ class MeasurementReport {
|
|
|
356
392
|
const trackingUniqueIdentifierValue = trackingUniqueIdentifierGroup?.UID;
|
|
357
393
|
const toolAdapter = hooks?.getToolClass?.(measurementGroup, dataset, this.measurementAdapterByToolType) || this.getAdapterForTrackingIdentifier(trackingIdentifierValue);
|
|
358
394
|
if (toolAdapter) {
|
|
359
|
-
const measurement = toolAdapter.getMeasurementData(measurementGroup, sopInstanceUIDToImageIdMap,
|
|
395
|
+
const measurement = toolAdapter.getMeasurementData(measurementGroup, sopInstanceUIDToImageIdMap, metadata, trackingIdentifierValue);
|
|
360
396
|
measurement.TrackingUniqueIdentifier = trackingUniqueIdentifierValue;
|
|
361
397
|
console.log(`=== ${toolAdapter.toolType} ===`);
|
|
362
398
|
console.log(measurement);
|
|
@@ -1,48 +1,23 @@
|
|
|
1
1
|
import BaseAdapter3D from "./BaseAdapter3D";
|
|
2
2
|
declare class PlanarFreehandROI extends BaseAdapter3D {
|
|
3
3
|
static closedContourThreshold: number;
|
|
4
|
-
static getMeasurementData(MeasurementGroup: any, sopInstanceUIDToImageIdMap: any,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ReferencedFrameNumber: any;
|
|
11
|
-
}): any;
|
|
12
|
-
static getMeasurementDataFromScoord3D({ defaultState, SCOORD3DGroup }: {
|
|
13
|
-
defaultState: any;
|
|
14
|
-
SCOORD3DGroup: any;
|
|
15
|
-
}): any;
|
|
16
|
-
static getTID300RepresentationArguments(tool: any, worldToImageCoords: any): {
|
|
17
|
-
points: any;
|
|
18
|
-
area: any;
|
|
19
|
-
areaUnit: any;
|
|
20
|
-
perimeter: any;
|
|
21
|
-
modalityUnit: any;
|
|
22
|
-
mean: any;
|
|
23
|
-
max: any;
|
|
24
|
-
stdDev: any;
|
|
25
|
-
trackingIdentifierTextValue: string;
|
|
26
|
-
finding: any;
|
|
27
|
-
findingSites: any;
|
|
28
|
-
ReferencedFrameOfReferenceUID: any;
|
|
29
|
-
use3DSpatialCoordinates: boolean;
|
|
30
|
-
} | {
|
|
31
|
-
points: any;
|
|
32
|
-
area: any;
|
|
33
|
-
areaUnit: any;
|
|
34
|
-
perimeter: any;
|
|
35
|
-
modalityUnit: any;
|
|
36
|
-
mean: any;
|
|
37
|
-
max: any;
|
|
38
|
-
stdDev: any;
|
|
39
|
-
trackingIdentifierTextValue: string;
|
|
40
|
-
finding: any;
|
|
41
|
-
findingSites: any;
|
|
42
|
-
use3DSpatialCoordinates: boolean;
|
|
4
|
+
static getMeasurementData(MeasurementGroup: any, sopInstanceUIDToImageIdMap: any, metadata: any): {
|
|
5
|
+
description?: string;
|
|
6
|
+
sopInstanceUid?: string;
|
|
7
|
+
annotation: import("packages/tools/dist/esm/types").Annotation;
|
|
8
|
+
finding?: unknown;
|
|
9
|
+
findingSites?: unknown;
|
|
43
10
|
};
|
|
44
|
-
static
|
|
45
|
-
points:
|
|
11
|
+
static getTID300RepresentationArguments(tool: any, is3DMeasurement?: boolean): {
|
|
12
|
+
points: ({
|
|
13
|
+
x: any;
|
|
14
|
+
y: any;
|
|
15
|
+
z: any;
|
|
16
|
+
} | {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
z?: undefined;
|
|
20
|
+
})[];
|
|
46
21
|
area: any;
|
|
47
22
|
areaUnit: any;
|
|
48
23
|
perimeter: any;
|
|
@@ -2,54 +2,25 @@ import MeasurementReport from './MeasurementReport.js';
|
|
|
2
2
|
import { utilities } from 'dcmjs';
|
|
3
3
|
import { vec3 } from 'gl-matrix';
|
|
4
4
|
import BaseAdapter3D from './BaseAdapter3D.js';
|
|
5
|
+
import 'buffer';
|
|
6
|
+
import { toScoords } from '../helpers/toScoordType.js';
|
|
7
|
+
import '@cornerstonejs/core';
|
|
5
8
|
|
|
6
9
|
var _PlanarFreehandROI;
|
|
7
10
|
const {
|
|
8
11
|
Polyline: TID300Polyline
|
|
9
12
|
} = utilities.TID300;
|
|
10
13
|
class PlanarFreehandROI extends BaseAdapter3D {
|
|
11
|
-
static getMeasurementData(MeasurementGroup, sopInstanceUIDToImageIdMap,
|
|
14
|
+
static getMeasurementData(MeasurementGroup, sopInstanceUIDToImageIdMap, metadata) {
|
|
12
15
|
const {
|
|
13
|
-
|
|
16
|
+
state,
|
|
14
17
|
NUMGroup,
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
scoord,
|
|
19
|
+
scoordArgs,
|
|
20
|
+
worldCoords,
|
|
21
|
+
referencedImageId,
|
|
17
22
|
ReferencedFrameNumber
|
|
18
|
-
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup, sopInstanceUIDToImageIdMap, metadata,
|
|
19
|
-
if (SCOORDGroup) {
|
|
20
|
-
return this.getMeasurementDataFromScoord({
|
|
21
|
-
defaultState,
|
|
22
|
-
SCOORDGroup,
|
|
23
|
-
imageToWorldCoords,
|
|
24
|
-
NUMGroup,
|
|
25
|
-
ReferencedFrameNumber
|
|
26
|
-
});
|
|
27
|
-
} else if (SCOORD3DGroup) {
|
|
28
|
-
return this.getMeasurementDataFromScoord3D({
|
|
29
|
-
defaultState,
|
|
30
|
-
SCOORD3DGroup
|
|
31
|
-
});
|
|
32
|
-
} else {
|
|
33
|
-
throw new Error("Can't get measurement data with missing SCOORD and SCOORD3D groups.");
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
static getMeasurementDataFromScoord(_ref) {
|
|
37
|
-
let {
|
|
38
|
-
defaultState,
|
|
39
|
-
SCOORDGroup,
|
|
40
|
-
imageToWorldCoords,
|
|
41
|
-
NUMGroup,
|
|
42
|
-
ReferencedFrameNumber
|
|
43
|
-
} = _ref;
|
|
44
|
-
const referencedImageId = defaultState.annotation.metadata.referencedImageId;
|
|
45
|
-
const {
|
|
46
|
-
GraphicData
|
|
47
|
-
} = SCOORDGroup;
|
|
48
|
-
const worldCoords = [];
|
|
49
|
-
for (let i = 0; i < GraphicData.length; i += 2) {
|
|
50
|
-
const point = imageToWorldCoords(referencedImageId, [GraphicData[i], GraphicData[i + 1]]);
|
|
51
|
-
worldCoords.push(point);
|
|
52
|
-
}
|
|
23
|
+
} = MeasurementReport.getSetupMeasurementData(MeasurementGroup, sopInstanceUIDToImageIdMap, metadata, this.toolType);
|
|
53
24
|
const distanceBetweenFirstAndLastPoint = vec3.distance(worldCoords[worldCoords.length - 1], worldCoords[0]);
|
|
54
25
|
let isOpenContour = true;
|
|
55
26
|
if (distanceBetweenFirstAndLastPoint < this.closedContourThreshold) {
|
|
@@ -60,8 +31,8 @@ class PlanarFreehandROI extends BaseAdapter3D {
|
|
|
60
31
|
if (isOpenContour) {
|
|
61
32
|
points.push(worldCoords[0], worldCoords[worldCoords.length - 1]);
|
|
62
33
|
}
|
|
63
|
-
const state = defaultState;
|
|
64
34
|
state.annotation.data = {
|
|
35
|
+
...state.annotation.data,
|
|
65
36
|
contour: {
|
|
66
37
|
polyline: worldCoords,
|
|
67
38
|
closed: !isOpenContour
|
|
@@ -73,56 +44,19 @@ class PlanarFreehandROI extends BaseAdapter3D {
|
|
|
73
44
|
hasMoved: false
|
|
74
45
|
}
|
|
75
46
|
},
|
|
76
|
-
|
|
47
|
+
frameNumber: ReferencedFrameNumber
|
|
48
|
+
};
|
|
49
|
+
if (referencedImageId) {
|
|
50
|
+
state.annotation.data.cachedStats = {
|
|
77
51
|
[`imageId:${referencedImageId}`]: {
|
|
78
52
|
area: NUMGroup ? NUMGroup.MeasuredValueSequence.NumericValue : null
|
|
79
53
|
}
|
|
80
|
-
}
|
|
81
|
-
frameNumber: ReferencedFrameNumber
|
|
82
|
-
};
|
|
83
|
-
return state;
|
|
84
|
-
}
|
|
85
|
-
static getMeasurementDataFromScoord3D(_ref2) {
|
|
86
|
-
let {
|
|
87
|
-
defaultState,
|
|
88
|
-
SCOORD3DGroup
|
|
89
|
-
} = _ref2;
|
|
90
|
-
const {
|
|
91
|
-
GraphicData
|
|
92
|
-
} = SCOORD3DGroup;
|
|
93
|
-
const worldCoords = [];
|
|
94
|
-
for (let i = 0; i < GraphicData.length; i += 3) {
|
|
95
|
-
const point = [GraphicData[i], GraphicData[i + 1], GraphicData[i + 2]];
|
|
96
|
-
worldCoords.push(point);
|
|
54
|
+
};
|
|
97
55
|
}
|
|
98
|
-
const distanceBetweenFirstAndLastPoint = vec3.distance(worldCoords[worldCoords.length - 1], worldCoords[0]);
|
|
99
|
-
let isOpenContour = true;
|
|
100
|
-
if (distanceBetweenFirstAndLastPoint < this.closedContourThreshold) {
|
|
101
|
-
worldCoords.pop();
|
|
102
|
-
isOpenContour = false;
|
|
103
|
-
}
|
|
104
|
-
const points = [];
|
|
105
|
-
if (isOpenContour) {
|
|
106
|
-
points.push(worldCoords[0], worldCoords[worldCoords.length - 1]);
|
|
107
|
-
}
|
|
108
|
-
const state = defaultState;
|
|
109
|
-
state.annotation.data = {
|
|
110
|
-
contour: {
|
|
111
|
-
polyline: worldCoords,
|
|
112
|
-
closed: !isOpenContour
|
|
113
|
-
},
|
|
114
|
-
handles: {
|
|
115
|
-
points,
|
|
116
|
-
activeHandleIndex: null,
|
|
117
|
-
textBox: {
|
|
118
|
-
hasMoved: false
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
cachedStats: {}
|
|
122
|
-
};
|
|
123
56
|
return state;
|
|
124
57
|
}
|
|
125
|
-
static getTID300RepresentationArguments(tool
|
|
58
|
+
static getTID300RepresentationArguments(tool) {
|
|
59
|
+
let is3DMeasurement = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
126
60
|
const {
|
|
127
61
|
data,
|
|
128
62
|
finding,
|
|
@@ -137,13 +71,14 @@ class PlanarFreehandROI extends BaseAdapter3D {
|
|
|
137
71
|
const {
|
|
138
72
|
referencedImageId
|
|
139
73
|
} = metadata;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
74
|
+
const scoordProps = {
|
|
75
|
+
is3DMeasurement,
|
|
76
|
+
referencedImageId
|
|
77
|
+
};
|
|
78
|
+
const points = toScoords(scoordProps, polyline);
|
|
144
79
|
if (!isOpenContour) {
|
|
145
80
|
const firstPoint = points[0];
|
|
146
|
-
points.push(
|
|
81
|
+
points.push(firstPoint);
|
|
147
82
|
}
|
|
148
83
|
const {
|
|
149
84
|
area,
|
|
@@ -166,52 +101,10 @@ class PlanarFreehandROI extends BaseAdapter3D {
|
|
|
166
101
|
trackingIdentifierTextValue: this.trackingIdentifierTextValue,
|
|
167
102
|
finding,
|
|
168
103
|
findingSites: findingSites || [],
|
|
104
|
+
ReferencedFrameOfReferenceUID: is3DMeasurement ? metadata.FrameOfReferenceUID : null,
|
|
169
105
|
use3DSpatialCoordinates: false
|
|
170
106
|
};
|
|
171
107
|
}
|
|
172
|
-
static getTID300RepresentationArgumentsSCOORD3D(tool) {
|
|
173
|
-
const {
|
|
174
|
-
data,
|
|
175
|
-
finding,
|
|
176
|
-
findingSites,
|
|
177
|
-
metadata
|
|
178
|
-
} = tool;
|
|
179
|
-
const {
|
|
180
|
-
polyline,
|
|
181
|
-
closed
|
|
182
|
-
} = data.contour;
|
|
183
|
-
const isOpenContour = closed !== true;
|
|
184
|
-
const points = polyline;
|
|
185
|
-
if (!isOpenContour) {
|
|
186
|
-
const firstPoint = points[0];
|
|
187
|
-
points.push([firstPoint[0], firstPoint[1], firstPoint[2]]);
|
|
188
|
-
}
|
|
189
|
-
const cachedStatsKeys = Object.keys(data.cachedStats)[0];
|
|
190
|
-
const {
|
|
191
|
-
area = undefined,
|
|
192
|
-
areaUnit = undefined,
|
|
193
|
-
modalityUnit = undefined,
|
|
194
|
-
perimeter = undefined,
|
|
195
|
-
mean = undefined,
|
|
196
|
-
max = undefined,
|
|
197
|
-
stdDev = undefined
|
|
198
|
-
} = cachedStatsKeys ? data.cachedStats[cachedStatsKeys] : {};
|
|
199
|
-
return {
|
|
200
|
-
points,
|
|
201
|
-
area,
|
|
202
|
-
areaUnit,
|
|
203
|
-
perimeter,
|
|
204
|
-
modalityUnit,
|
|
205
|
-
mean,
|
|
206
|
-
max,
|
|
207
|
-
stdDev,
|
|
208
|
-
trackingIdentifierTextValue: this.trackingIdentifierTextValue,
|
|
209
|
-
finding,
|
|
210
|
-
findingSites: findingSites || [],
|
|
211
|
-
ReferencedFrameOfReferenceUID: metadata.FrameOfReferenceUID,
|
|
212
|
-
use3DSpatialCoordinates: true
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
108
|
}
|
|
216
109
|
_PlanarFreehandROI = PlanarFreehandROI;
|
|
217
110
|
_PlanarFreehandROI.closedContourThreshold = 1e-5;
|