@cornerstonejs/adapters 4.13.0 → 4.13.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/Angle.js +4 -4
- package/dist/esm/adapters/Cornerstone/ArrowAnnotate.js +5 -5
- package/dist/esm/adapters/Cornerstone/Bidirectional.js +11 -11
- package/dist/esm/adapters/Cornerstone/CircleRoi.js +4 -4
- package/dist/esm/adapters/Cornerstone/CobbAngle.js +4 -4
- package/dist/esm/adapters/Cornerstone/EllipticalRoi.js +4 -4
- package/dist/esm/adapters/Cornerstone/FreehandRoi.js +5 -5
- package/dist/esm/adapters/Cornerstone/Length.js +4 -4
- package/dist/esm/adapters/Cornerstone/MeasurementReport.js +30 -30
- package/dist/esm/adapters/Cornerstone/RectangleRoi.js +5 -5
- package/dist/esm/adapters/Cornerstone/Segmentation_3X.js +10 -10
- package/dist/esm/adapters/Cornerstone/Segmentation_4X.js +53 -53
- package/dist/esm/adapters/Cornerstone/cornerstone4Tag.js +1 -1
- package/dist/esm/adapters/Cornerstone3D/CodingScheme.js +2 -2
- package/dist/esm/adapters/Cornerstone3D/cornerstone3DTag.js +1 -1
- package/dist/esm/adapters/VTKjs/Segmentation.js +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/package.json +4 -4
|
@@ -5,7 +5,7 @@ import CORNERSTONE_4_TAG from './cornerstone4Tag.js';
|
|
|
5
5
|
const {
|
|
6
6
|
Angle: TID300Angle
|
|
7
7
|
} = utilities.TID300;
|
|
8
|
-
const ANGLE =
|
|
8
|
+
const ANGLE = 'Angle';
|
|
9
9
|
class Angle {
|
|
10
10
|
/**
|
|
11
11
|
* Generate TID300 measurement data for a plane angle measurement - use a Angle, but label it as Angle
|
|
@@ -47,7 +47,7 @@ class Angle {
|
|
|
47
47
|
const point3 = handles.middle;
|
|
48
48
|
const point4 = handles.end;
|
|
49
49
|
const rAngle = tool.rAngle;
|
|
50
|
-
const trackingIdentifierTextValue =
|
|
50
|
+
const trackingIdentifierTextValue = 'cornerstoneTools@^4.0.0:Angle';
|
|
51
51
|
return {
|
|
52
52
|
point1,
|
|
53
53
|
point2,
|
|
@@ -64,10 +64,10 @@ Angle.toolType = ANGLE;
|
|
|
64
64
|
Angle.utilityToolType = ANGLE;
|
|
65
65
|
Angle.TID300Representation = TID300Angle;
|
|
66
66
|
Angle.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
67
|
-
if (!TrackingIdentifier.includes(
|
|
67
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
68
68
|
return false;
|
|
69
69
|
}
|
|
70
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
70
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
71
71
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
@@ -5,8 +5,8 @@ import CORNERSTONE_4_TAG from './cornerstone4Tag.js';
|
|
|
5
5
|
const {
|
|
6
6
|
Point: TID300Point
|
|
7
7
|
} = utilities.TID300;
|
|
8
|
-
const ARROW_ANNOTATE =
|
|
9
|
-
const CORNERSTONEFREETEXT =
|
|
8
|
+
const ARROW_ANNOTATE = 'ArrowAnnotate';
|
|
9
|
+
const CORNERSTONEFREETEXT = 'CORNERSTONEFREETEXT';
|
|
10
10
|
class ArrowAnnotate {
|
|
11
11
|
static getMeasurementData(MeasurementGroup) {
|
|
12
12
|
const {
|
|
@@ -67,7 +67,7 @@ class ArrowAnnotate {
|
|
|
67
67
|
if (!finding || finding.CodeValue !== CORNERSTONEFREETEXT) {
|
|
68
68
|
finding = {
|
|
69
69
|
CodeValue: CORNERSTONEFREETEXT,
|
|
70
|
-
CodingSchemeDesignator:
|
|
70
|
+
CodingSchemeDesignator: 'CST4',
|
|
71
71
|
CodeMeaning: tool.label
|
|
72
72
|
};
|
|
73
73
|
}
|
|
@@ -79,10 +79,10 @@ ArrowAnnotate.toolType = ARROW_ANNOTATE;
|
|
|
79
79
|
ArrowAnnotate.utilityToolType = ARROW_ANNOTATE;
|
|
80
80
|
ArrowAnnotate.TID300Representation = TID300Point;
|
|
81
81
|
ArrowAnnotate.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
82
|
-
if (!TrackingIdentifier.includes(
|
|
82
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
83
83
|
return false;
|
|
84
84
|
}
|
|
85
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
85
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
86
86
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
87
87
|
return false;
|
|
88
88
|
}
|
|
@@ -7,11 +7,11 @@ import '@cornerstonejs/core';
|
|
|
7
7
|
const {
|
|
8
8
|
Bidirectional: TID300Bidirectional
|
|
9
9
|
} = utilities.TID300;
|
|
10
|
-
const BIDIRECTIONAL =
|
|
11
|
-
const LONG_AXIS =
|
|
12
|
-
const SHORT_AXIS =
|
|
13
|
-
const FINDING =
|
|
14
|
-
const FINDING_SITE =
|
|
10
|
+
const BIDIRECTIONAL = 'Bidirectional';
|
|
11
|
+
const LONG_AXIS = 'Long Axis';
|
|
12
|
+
const SHORT_AXIS = 'Short Axis';
|
|
13
|
+
const FINDING = '121071';
|
|
14
|
+
const FINDING_SITE = 'G-C0E3';
|
|
15
15
|
class Bidirectional {
|
|
16
16
|
// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
|
|
17
17
|
static getMeasurementData(MeasurementGroup) {
|
|
@@ -21,9 +21,9 @@ class Bidirectional {
|
|
|
21
21
|
const findingGroup = toArray(ContentSequence).find(group => group.ConceptNameCodeSequence.CodeValue === FINDING);
|
|
22
22
|
const findingSiteGroups = toArray(ContentSequence).filter(group => group.ConceptNameCodeSequence.CodeValue === FINDING_SITE);
|
|
23
23
|
const longAxisNUMGroup = toArray(ContentSequence).find(group => group.ConceptNameCodeSequence.CodeMeaning === LONG_AXIS);
|
|
24
|
-
const longAxisSCOORDGroup = toArray(longAxisNUMGroup.ContentSequence).find(group => group.ValueType ===
|
|
24
|
+
const longAxisSCOORDGroup = toArray(longAxisNUMGroup.ContentSequence).find(group => group.ValueType === 'SCOORD');
|
|
25
25
|
const shortAxisNUMGroup = toArray(ContentSequence).find(group => group.ConceptNameCodeSequence.CodeMeaning === SHORT_AXIS);
|
|
26
|
-
const shortAxisSCOORDGroup = toArray(shortAxisNUMGroup.ContentSequence).find(group => group.ValueType ===
|
|
26
|
+
const shortAxisSCOORDGroup = toArray(shortAxisNUMGroup.ContentSequence).find(group => group.ValueType === 'SCOORD');
|
|
27
27
|
const {
|
|
28
28
|
ReferencedSOPSequence
|
|
29
29
|
} = longAxisSCOORDGroup.ContentSequence;
|
|
@@ -98,7 +98,7 @@ class Bidirectional {
|
|
|
98
98
|
isCreating: false,
|
|
99
99
|
longestDiameter,
|
|
100
100
|
shortestDiameter,
|
|
101
|
-
toolName:
|
|
101
|
+
toolName: 'Bidirectional',
|
|
102
102
|
visible: true,
|
|
103
103
|
finding: findingGroup ? findingGroup.ConceptCodeSequence : undefined,
|
|
104
104
|
findingSites: findingSiteGroups.map(fsg => fsg.ConceptCodeSequence)
|
|
@@ -118,7 +118,7 @@ class Bidirectional {
|
|
|
118
118
|
finding,
|
|
119
119
|
findingSites
|
|
120
120
|
} = tool;
|
|
121
|
-
const trackingIdentifierTextValue =
|
|
121
|
+
const trackingIdentifierTextValue = 'cornerstoneTools@^4.0.0:Bidirectional';
|
|
122
122
|
return {
|
|
123
123
|
longAxis: {
|
|
124
124
|
point1: start,
|
|
@@ -140,10 +140,10 @@ Bidirectional.toolType = BIDIRECTIONAL;
|
|
|
140
140
|
Bidirectional.utilityToolType = BIDIRECTIONAL;
|
|
141
141
|
Bidirectional.TID300Representation = TID300Bidirectional;
|
|
142
142
|
Bidirectional.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
143
|
-
if (!TrackingIdentifier.includes(
|
|
143
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
144
144
|
return false;
|
|
145
145
|
}
|
|
146
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
146
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
147
147
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
148
148
|
return false;
|
|
149
149
|
}
|
|
@@ -5,7 +5,7 @@ import CORNERSTONE_4_TAG from './cornerstone4Tag.js';
|
|
|
5
5
|
const {
|
|
6
6
|
Circle: TID300Circle
|
|
7
7
|
} = utilities.TID300;
|
|
8
|
-
const CIRCLEROI =
|
|
8
|
+
const CIRCLEROI = 'CircleRoi';
|
|
9
9
|
class CircleRoi {
|
|
10
10
|
/** Gets the measurement data for cornerstone, given DICOM SR measurement data. */
|
|
11
11
|
static getMeasurementData(MeasurementGroup) {
|
|
@@ -86,7 +86,7 @@ class CircleRoi {
|
|
|
86
86
|
const points = [];
|
|
87
87
|
points.push(center);
|
|
88
88
|
points.push(end);
|
|
89
|
-
const trackingIdentifierTextValue =
|
|
89
|
+
const trackingIdentifierTextValue = 'cornerstoneTools@^4.0.0:CircleRoi';
|
|
90
90
|
return {
|
|
91
91
|
area,
|
|
92
92
|
perimeter,
|
|
@@ -102,10 +102,10 @@ CircleRoi.toolType = CIRCLEROI;
|
|
|
102
102
|
CircleRoi.utilityToolType = CIRCLEROI;
|
|
103
103
|
CircleRoi.TID300Representation = TID300Circle;
|
|
104
104
|
CircleRoi.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
105
|
-
if (!TrackingIdentifier.includes(
|
|
105
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
106
106
|
return false;
|
|
107
107
|
}
|
|
108
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
108
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
109
109
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
110
110
|
return false;
|
|
111
111
|
}
|
|
@@ -5,7 +5,7 @@ import CORNERSTONE_4_TAG from './cornerstone4Tag.js';
|
|
|
5
5
|
const {
|
|
6
6
|
CobbAngle: TID300CobbAngle
|
|
7
7
|
} = utilities.TID300;
|
|
8
|
-
const COBB_ANGLE =
|
|
8
|
+
const COBB_ANGLE = 'CobbAngle';
|
|
9
9
|
class CobbAngle {
|
|
10
10
|
// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
|
|
11
11
|
static getMeasurementData(MeasurementGroup) {
|
|
@@ -52,7 +52,7 @@ class CobbAngle {
|
|
|
52
52
|
const point3 = handles.start2;
|
|
53
53
|
const point4 = handles.end2;
|
|
54
54
|
const rAngle = tool.rAngle;
|
|
55
|
-
const trackingIdentifierTextValue =
|
|
55
|
+
const trackingIdentifierTextValue = 'cornerstoneTools@^4.0.0:CobbAngle';
|
|
56
56
|
return {
|
|
57
57
|
point1,
|
|
58
58
|
point2,
|
|
@@ -69,10 +69,10 @@ CobbAngle.toolType = COBB_ANGLE;
|
|
|
69
69
|
CobbAngle.utilityToolType = COBB_ANGLE;
|
|
70
70
|
CobbAngle.TID300Representation = TID300CobbAngle;
|
|
71
71
|
CobbAngle.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
72
|
-
if (!TrackingIdentifier.includes(
|
|
72
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
73
73
|
return false;
|
|
74
74
|
}
|
|
75
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
75
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
76
76
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
77
77
|
return false;
|
|
78
78
|
}
|
|
@@ -5,7 +5,7 @@ import CORNERSTONE_4_TAG from './cornerstone4Tag.js';
|
|
|
5
5
|
const {
|
|
6
6
|
Ellipse: TID300Ellipse
|
|
7
7
|
} = utilities.TID300;
|
|
8
|
-
const ELLIPTICALROI =
|
|
8
|
+
const ELLIPTICALROI = 'EllipticalRoi';
|
|
9
9
|
class EllipticalRoi {
|
|
10
10
|
// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
|
|
11
11
|
static getMeasurementData(MeasurementGroup) {
|
|
@@ -148,7 +148,7 @@ class EllipticalRoi {
|
|
|
148
148
|
y: center.y
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
|
-
const trackingIdentifierTextValue =
|
|
151
|
+
const trackingIdentifierTextValue = 'cornerstoneTools@^4.0.0:EllipticalRoi';
|
|
152
152
|
return {
|
|
153
153
|
area,
|
|
154
154
|
points,
|
|
@@ -162,10 +162,10 @@ EllipticalRoi.toolType = ELLIPTICALROI;
|
|
|
162
162
|
EllipticalRoi.utilityToolType = ELLIPTICALROI;
|
|
163
163
|
EllipticalRoi.TID300Representation = TID300Ellipse;
|
|
164
164
|
EllipticalRoi.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
165
|
-
if (!TrackingIdentifier.includes(
|
|
165
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
166
166
|
return false;
|
|
167
167
|
}
|
|
168
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
168
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
169
169
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
170
170
|
return false;
|
|
171
171
|
}
|
|
@@ -57,7 +57,7 @@ class FreehandRoi {
|
|
|
57
57
|
area = 0,
|
|
58
58
|
perimeter = 0
|
|
59
59
|
} = cachedStats;
|
|
60
|
-
const trackingIdentifierTextValue =
|
|
60
|
+
const trackingIdentifierTextValue = 'cornerstoneTools@^4.0.0:FreehandRoi';
|
|
61
61
|
return {
|
|
62
62
|
points,
|
|
63
63
|
area,
|
|
@@ -68,14 +68,14 @@ class FreehandRoi {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
FreehandRoi.toolType =
|
|
72
|
-
FreehandRoi.utilityToolType =
|
|
71
|
+
FreehandRoi.toolType = 'FreehandRoi';
|
|
72
|
+
FreehandRoi.utilityToolType = 'FreehandRoi';
|
|
73
73
|
FreehandRoi.TID300Representation = TID300Polyline;
|
|
74
74
|
FreehandRoi.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
75
|
-
if (!TrackingIdentifier.includes(
|
|
75
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
76
76
|
return false;
|
|
77
77
|
}
|
|
78
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
78
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
79
79
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
80
80
|
return false;
|
|
81
81
|
}
|
|
@@ -5,7 +5,7 @@ import CORNERSTONE_4_TAG from './cornerstone4Tag.js';
|
|
|
5
5
|
const {
|
|
6
6
|
Length: TID300Length
|
|
7
7
|
} = utilities.TID300;
|
|
8
|
-
const LENGTH =
|
|
8
|
+
const LENGTH = 'Length';
|
|
9
9
|
class Length {
|
|
10
10
|
// TODO: this function is required for all Cornerstone Tool Adapters, since it is called by MeasurementReport.
|
|
11
11
|
static getMeasurementData(MeasurementGroup) {
|
|
@@ -42,7 +42,7 @@ class Length {
|
|
|
42
42
|
const point1 = handles.start;
|
|
43
43
|
const point2 = handles.end;
|
|
44
44
|
const distance = tool.length;
|
|
45
|
-
const trackingIdentifierTextValue =
|
|
45
|
+
const trackingIdentifierTextValue = 'cornerstoneTools@^4.0.0:Length';
|
|
46
46
|
return {
|
|
47
47
|
point1,
|
|
48
48
|
point2,
|
|
@@ -57,10 +57,10 @@ Length.toolType = LENGTH;
|
|
|
57
57
|
Length.utilityToolType = LENGTH;
|
|
58
58
|
Length.TID300Representation = TID300Length;
|
|
59
59
|
Length.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
60
|
-
if (!TrackingIdentifier.includes(
|
|
60
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
63
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
64
64
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
65
65
|
return false;
|
|
66
66
|
}
|
|
@@ -21,16 +21,16 @@ const {
|
|
|
21
21
|
DicomMetaDictionary
|
|
22
22
|
} = data;
|
|
23
23
|
const FINDING = {
|
|
24
|
-
CodingSchemeDesignator:
|
|
25
|
-
CodeValue:
|
|
24
|
+
CodingSchemeDesignator: 'DCM',
|
|
25
|
+
CodeValue: '121071'
|
|
26
26
|
};
|
|
27
27
|
const FINDING_SITE = {
|
|
28
|
-
CodingSchemeDesignator:
|
|
29
|
-
CodeValue:
|
|
28
|
+
CodingSchemeDesignator: 'SCT',
|
|
29
|
+
CodeValue: '363698007'
|
|
30
30
|
};
|
|
31
31
|
const FINDING_SITE_OLD = {
|
|
32
|
-
CodingSchemeDesignator:
|
|
33
|
-
CodeValue:
|
|
32
|
+
CodingSchemeDesignator: 'SRT',
|
|
33
|
+
CodeValue: 'G-C0E3'
|
|
34
34
|
};
|
|
35
35
|
const codeValueMatch = (group, code, oldCode) => {
|
|
36
36
|
const {
|
|
@@ -74,8 +74,8 @@ class MeasurementReport {
|
|
|
74
74
|
const contentSequenceArr = toArray(ContentSequence);
|
|
75
75
|
const findingGroup = contentSequenceArr.find(group => codeValueMatch(group, FINDING));
|
|
76
76
|
const findingSiteGroups = contentSequenceArr.filter(group => codeValueMatch(group, FINDING_SITE, FINDING_SITE_OLD)) || [];
|
|
77
|
-
const NUMGroup = contentSequenceArr.find(group => group.ValueType ===
|
|
78
|
-
const SCOORDGroup = toArray(NUMGroup.ContentSequence).find(group => group.ValueType ===
|
|
77
|
+
const NUMGroup = contentSequenceArr.find(group => group.ValueType === 'NUM');
|
|
78
|
+
const SCOORDGroup = toArray(NUMGroup.ContentSequence).find(group => group.ValueType === 'SCOORD');
|
|
79
79
|
const {
|
|
80
80
|
ReferencedSOPSequence
|
|
81
81
|
} = SCOORDGroup.ContentSequence;
|
|
@@ -117,16 +117,16 @@ class MeasurementReport {
|
|
|
117
117
|
let allMeasurementGroups = [];
|
|
118
118
|
const firstImageId = Object.keys(toolState)[0];
|
|
119
119
|
if (!firstImageId) {
|
|
120
|
-
throw new Error(
|
|
120
|
+
throw new Error('No measurements provided.');
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/* Patient ID
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const generalSeriesModule = metadataProvider.get(
|
|
124
|
+
Warning - Missing attribute or value that would be needed to build DICOMDIR - Patient ID
|
|
125
|
+
Warning - Missing attribute or value that would be needed to build DICOMDIR - Study Date
|
|
126
|
+
Warning - Missing attribute or value that would be needed to build DICOMDIR - Study Time
|
|
127
|
+
Warning - Missing attribute or value that would be needed to build DICOMDIR - Study ID
|
|
128
|
+
*/
|
|
129
|
+
const generalSeriesModule = metadataProvider.get('generalSeriesModule', firstImageId);
|
|
130
130
|
|
|
131
131
|
//const sopCommonModule = metadataProvider.get('sopCommonModule', firstImageId);
|
|
132
132
|
|
|
@@ -140,8 +140,8 @@ class MeasurementReport {
|
|
|
140
140
|
|
|
141
141
|
// Loop through each image in the toolData
|
|
142
142
|
Object.keys(toolState).forEach(imageId => {
|
|
143
|
-
const sopCommonModule = metadataProvider.get(
|
|
144
|
-
const frameNumber = metadataProvider.get(
|
|
143
|
+
const sopCommonModule = metadataProvider.get('sopCommonModule', imageId);
|
|
144
|
+
const frameNumber = metadataProvider.get('frameNumber', imageId);
|
|
145
145
|
const toolData = toolState[imageId];
|
|
146
146
|
const toolTypes = Object.keys(toolData);
|
|
147
147
|
const ReferencedSOPSequence = {
|
|
@@ -180,26 +180,26 @@ class MeasurementReport {
|
|
|
180
180
|
const _meta = {
|
|
181
181
|
FileMetaInformationVersion: {
|
|
182
182
|
Value: [fileMetaInformationVersionArray.buffer],
|
|
183
|
-
vr:
|
|
183
|
+
vr: 'OB'
|
|
184
184
|
},
|
|
185
185
|
//MediaStorageSOPClassUID
|
|
186
186
|
//MediaStorageSOPInstanceUID: sopCommonModule.sopInstanceUID,
|
|
187
187
|
TransferSyntaxUID: {
|
|
188
|
-
Value: [
|
|
189
|
-
vr:
|
|
188
|
+
Value: ['1.2.840.10008.1.2.1'],
|
|
189
|
+
vr: 'UI'
|
|
190
190
|
},
|
|
191
191
|
ImplementationClassUID: {
|
|
192
192
|
Value: [DicomMetaDictionary.uid()],
|
|
193
193
|
// TODO: could be git hash or other valid id
|
|
194
|
-
vr:
|
|
194
|
+
vr: 'UI'
|
|
195
195
|
},
|
|
196
196
|
ImplementationVersionName: {
|
|
197
|
-
Value: [
|
|
198
|
-
vr:
|
|
197
|
+
Value: ['dcmjs'],
|
|
198
|
+
vr: 'SH'
|
|
199
199
|
}
|
|
200
200
|
};
|
|
201
201
|
const _vrMap = {
|
|
202
|
-
PixelData:
|
|
202
|
+
PixelData: 'OW'
|
|
203
203
|
};
|
|
204
204
|
derivationSourceDataset._meta = _meta;
|
|
205
205
|
derivationSourceDataset._vrMap = _vrMap;
|
|
@@ -209,7 +209,7 @@ class MeasurementReport {
|
|
|
209
209
|
// Merge the derived dataset with the content from the Measurement Report
|
|
210
210
|
report.dataset = Object.assign(report.dataset, contentItem);
|
|
211
211
|
report.dataset._meta = _meta;
|
|
212
|
-
report.dataset.SpecificCharacterSet =
|
|
212
|
+
report.dataset.SpecificCharacterSet = 'ISO_IR 192';
|
|
213
213
|
return report;
|
|
214
214
|
}
|
|
215
215
|
|
|
@@ -223,12 +223,12 @@ class MeasurementReport {
|
|
|
223
223
|
static generateToolState(dataset) {
|
|
224
224
|
let hooks = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
225
225
|
// For now, bail out if the dataset is not a TID1500 SR with length measurements
|
|
226
|
-
if (dataset.ContentTemplateSequence.TemplateIdentifier !==
|
|
227
|
-
throw new Error(
|
|
226
|
+
if (dataset.ContentTemplateSequence.TemplateIdentifier !== '1500') {
|
|
227
|
+
throw new Error('This package can currently only interpret DICOM SR TID 1500');
|
|
228
228
|
}
|
|
229
|
-
const REPORT =
|
|
230
|
-
const GROUP =
|
|
231
|
-
const TRACKING_IDENTIFIER =
|
|
229
|
+
const REPORT = 'Imaging Measurements';
|
|
230
|
+
const GROUP = 'Measurement Group';
|
|
231
|
+
const TRACKING_IDENTIFIER = 'Tracking Identifier';
|
|
232
232
|
|
|
233
233
|
// Identify the Imaging Measurements
|
|
234
234
|
const imagingMeasurementContent = toArray(dataset.ContentSequence).find(codeMeaningEquals(REPORT));
|
|
@@ -60,7 +60,7 @@ class RectangleRoi {
|
|
|
60
60
|
area,
|
|
61
61
|
perimeter
|
|
62
62
|
} = cachedStats;
|
|
63
|
-
const trackingIdentifierTextValue =
|
|
63
|
+
const trackingIdentifierTextValue = 'cornerstoneTools@^4.0.0:RectangleRoi';
|
|
64
64
|
return {
|
|
65
65
|
points,
|
|
66
66
|
area,
|
|
@@ -71,14 +71,14 @@ class RectangleRoi {
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
RectangleRoi.toolType =
|
|
75
|
-
RectangleRoi.utilityToolType =
|
|
74
|
+
RectangleRoi.toolType = 'RectangleRoi';
|
|
75
|
+
RectangleRoi.utilityToolType = 'RectangleRoi';
|
|
76
76
|
RectangleRoi.TID300Representation = TID300Polyline;
|
|
77
77
|
RectangleRoi.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
78
|
-
if (!TrackingIdentifier.includes(
|
|
78
|
+
if (!TrackingIdentifier.includes(':')) {
|
|
79
79
|
return false;
|
|
80
80
|
}
|
|
81
|
-
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(
|
|
81
|
+
const [cornerstone4Tag, toolType] = TrackingIdentifier.split(':');
|
|
82
82
|
if (cornerstone4Tag !== CORNERSTONE_4_TAG) {
|
|
83
83
|
return false;
|
|
84
84
|
}
|
|
@@ -60,9 +60,9 @@ function generateSegmentation(images, brushData) {
|
|
|
60
60
|
dims.xy = dims.x * dims.y;
|
|
61
61
|
const numSegments = _getSegCount(seg, segments);
|
|
62
62
|
if (!numSegments) {
|
|
63
|
-
throw new Error(
|
|
63
|
+
throw new Error('No segments to export!');
|
|
64
64
|
}
|
|
65
|
-
const isMultiframe = image0.imageId.includes(
|
|
65
|
+
const isMultiframe = image0.imageId.includes('?frame');
|
|
66
66
|
const seg = _createSegFromImages(images, isMultiframe, options);
|
|
67
67
|
const {
|
|
68
68
|
referencedFramesPerSegment,
|
|
@@ -164,9 +164,9 @@ function generateToolState(imageIds, arrayBuffer, metadataProvider) {
|
|
|
164
164
|
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
|
165
165
|
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
|
166
166
|
const multiframe = Normalizer.normalizeToDataset([dataset]);
|
|
167
|
-
const imagePlaneModule = metadataProvider.get(
|
|
167
|
+
const imagePlaneModule = metadataProvider.get('imagePlaneModule', imageIds[0]);
|
|
168
168
|
if (!imagePlaneModule) {
|
|
169
|
-
console.warn(
|
|
169
|
+
console.warn('Insufficient metadata, imagePlaneModule missing.');
|
|
170
170
|
}
|
|
171
171
|
const ImageOrientationPatient = Array.isArray(imagePlaneModule.rowCosines) ? [...imagePlaneModule.rowCosines, ...imagePlaneModule.columnCosines] : [imagePlaneModule.rowCosines.x, imagePlaneModule.rowCosines.y, imagePlaneModule.rowCosines.z, imagePlaneModule.columnCosines.x, imagePlaneModule.columnCosines.y, imagePlaneModule.columnCosines.z];
|
|
172
172
|
|
|
@@ -217,17 +217,17 @@ function generateToolState(imageIds, arrayBuffer, metadataProvider) {
|
|
|
217
217
|
*/
|
|
218
218
|
function unpackPixelData(multiframe) {
|
|
219
219
|
const segType = multiframe.SegmentationType;
|
|
220
|
-
if (segType ===
|
|
220
|
+
if (segType === 'BINARY') {
|
|
221
221
|
return BitArray.unpack(multiframe.PixelData);
|
|
222
222
|
}
|
|
223
223
|
const pixelData = new Uint8Array(multiframe.PixelData);
|
|
224
224
|
const max = multiframe.MaximumFractionalValue;
|
|
225
225
|
const onlyMaxAndZero = pixelData.find(element => element !== 0 && element !== max) === undefined;
|
|
226
226
|
if (!onlyMaxAndZero) {
|
|
227
|
-
log.warn(
|
|
227
|
+
log.warn('This is a fractional segmentation, which is not currently supported.');
|
|
228
228
|
return;
|
|
229
229
|
}
|
|
230
|
-
log.warn(
|
|
230
|
+
log.warn('This segmentation object is actually binary... processing as such.');
|
|
231
231
|
return pixelData;
|
|
232
232
|
}
|
|
233
233
|
|
|
@@ -293,7 +293,7 @@ function getImageIdOfSourceImage(SourceImageSequence, imageIds, metadataProvider
|
|
|
293
293
|
*/
|
|
294
294
|
function getImageIdOfReferencedSingleFramedSOPInstance(sopInstanceUid, imageIds, metadataProvider) {
|
|
295
295
|
return imageIds.find(imageId => {
|
|
296
|
-
const sopCommonModule = metadataProvider.get(
|
|
296
|
+
const sopCommonModule = metadataProvider.get('sopCommonModule', imageId);
|
|
297
297
|
if (!sopCommonModule) {
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
@@ -314,11 +314,11 @@ function getImageIdOfReferencedSingleFramedSOPInstance(sopInstanceUid, imageIds,
|
|
|
314
314
|
*/
|
|
315
315
|
function getImageIdOfReferencedFrame(sopInstanceUid, frameNumber, imageIds, metadataProvider) {
|
|
316
316
|
const imageId = imageIds.find(imageId => {
|
|
317
|
-
const sopCommonModule = metadataProvider.get(
|
|
317
|
+
const sopCommonModule = metadataProvider.get('sopCommonModule', imageId);
|
|
318
318
|
if (!sopCommonModule) {
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
321
|
-
const imageIdFrameNumber = Number(imageId.split(
|
|
321
|
+
const imageIdFrameNumber = Number(imageId.split('frame=')[1]);
|
|
322
322
|
return (
|
|
323
323
|
//frameNumber is zero indexed for cornerstoneDICOMImageLoader image Ids.
|
|
324
324
|
sopCommonModule.sopInstanceUID === sopInstanceUid && imageIdFrameNumber === frameNumber - 1
|
|
@@ -128,19 +128,19 @@ function fillSegmentation(segmentation, inputLabelmaps3D) {
|
|
|
128
128
|
// to be 1 for BINARY. This is not ideal and there should be a better format for compression in this manner
|
|
129
129
|
// added to the standard.
|
|
130
130
|
segmentation.assignToDataset({
|
|
131
|
-
BitsAllocated:
|
|
132
|
-
BitsStored:
|
|
133
|
-
HighBit:
|
|
134
|
-
SegmentationType:
|
|
135
|
-
SegmentationFractionalType:
|
|
136
|
-
MaximumFractionalValue:
|
|
131
|
+
BitsAllocated: '8',
|
|
132
|
+
BitsStored: '8',
|
|
133
|
+
HighBit: '7',
|
|
134
|
+
SegmentationType: 'FRACTIONAL',
|
|
135
|
+
SegmentationFractionalType: 'PROBABILITY',
|
|
136
|
+
MaximumFractionalValue: '255'
|
|
137
137
|
});
|
|
138
138
|
segmentation.dataset._meta.TransferSyntaxUID = {
|
|
139
|
-
Value: [
|
|
140
|
-
vr:
|
|
139
|
+
Value: ['1.2.840.10008.1.2.5'],
|
|
140
|
+
vr: 'UI'
|
|
141
141
|
};
|
|
142
|
-
segmentation.dataset.SpecificCharacterSet =
|
|
143
|
-
segmentation.dataset._vrMap.PixelData =
|
|
142
|
+
segmentation.dataset.SpecificCharacterSet = 'ISO_IR 192';
|
|
143
|
+
segmentation.dataset._vrMap.PixelData = 'OB';
|
|
144
144
|
segmentation.dataset.PixelData = rleEncodedFrames;
|
|
145
145
|
} else {
|
|
146
146
|
// If no rleEncoding, at least bitpack the data.
|
|
@@ -200,11 +200,11 @@ async function generateToolState(referencedImageIds, arrayBuffer, metadataProvid
|
|
|
200
200
|
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
|
201
201
|
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
|
202
202
|
const multiframe = Normalizer.normalizeToDataset([dataset]);
|
|
203
|
-
const imagePlaneModule = metadataProvider.get(
|
|
204
|
-
const generalSeriesModule = metadataProvider.get(
|
|
203
|
+
const imagePlaneModule = metadataProvider.get('imagePlaneModule', referencedImageIds[0]);
|
|
204
|
+
const generalSeriesModule = metadataProvider.get('generalSeriesModule', referencedImageIds[0]);
|
|
205
205
|
const SeriesInstanceUID = generalSeriesModule.seriesInstanceUID;
|
|
206
206
|
if (!imagePlaneModule) {
|
|
207
|
-
console.warn(
|
|
207
|
+
console.warn('Insufficient metadata, imagePlaneModule missing.');
|
|
208
208
|
}
|
|
209
209
|
const ImageOrientationPatient = Array.isArray(imagePlaneModule.rowCosines) ? [...imagePlaneModule.rowCosines, ...imagePlaneModule.columnCosines] : [imagePlaneModule.rowCosines.x, imagePlaneModule.rowCosines.y, imagePlaneModule.rowCosines.z, imagePlaneModule.columnCosines.x, imagePlaneModule.columnCosines.y, imagePlaneModule.columnCosines.z];
|
|
210
210
|
|
|
@@ -215,11 +215,11 @@ async function generateToolState(referencedImageIds, arrayBuffer, metadataProvid
|
|
|
215
215
|
const TransferSyntaxUID = multiframe._meta.TransferSyntaxUID.Value[0];
|
|
216
216
|
let pixelData;
|
|
217
217
|
let pixelDataChunks;
|
|
218
|
-
if (TransferSyntaxUID ===
|
|
218
|
+
if (TransferSyntaxUID === '1.2.840.10008.1.2.5') {
|
|
219
219
|
const rleEncodedFrames = Array.isArray(multiframe.PixelData) ? multiframe.PixelData : [multiframe.PixelData];
|
|
220
220
|
pixelData = decode(rleEncodedFrames, multiframe.Rows, multiframe.Columns);
|
|
221
221
|
if (multiframe.BitsStored === 1) {
|
|
222
|
-
console.warn(
|
|
222
|
+
console.warn('No implementation for rle + bitbacking.');
|
|
223
223
|
return;
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -230,7 +230,7 @@ async function generateToolState(referencedImageIds, arrayBuffer, metadataProvid
|
|
|
230
230
|
maxBytesPerChunk
|
|
231
231
|
});
|
|
232
232
|
if (!pixelDataChunks) {
|
|
233
|
-
throw new Error(
|
|
233
|
+
throw new Error('Fractional segmentations are not yet supported');
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
const orientation = checkOrientation(multiframe, validOrientations, [imagePlaneModule.rows, imagePlaneModule.columns, referencedImageIds.length], tolerance);
|
|
@@ -241,7 +241,7 @@ async function generateToolState(referencedImageIds, arrayBuffer, metadataProvid
|
|
|
241
241
|
const sopUIDImageIdIndexMap = referencedImageIds.reduce((acc, imageId) => {
|
|
242
242
|
const {
|
|
243
243
|
sopInstanceUID
|
|
244
|
-
} = metadataProvider.get(
|
|
244
|
+
} = metadataProvider.get('generalImageModule', imageId);
|
|
245
245
|
acc[sopInstanceUID] = imageId;
|
|
246
246
|
return acc;
|
|
247
247
|
}, {});
|
|
@@ -251,25 +251,25 @@ async function generateToolState(referencedImageIds, arrayBuffer, metadataProvid
|
|
|
251
251
|
}
|
|
252
252
|
let insertFunction;
|
|
253
253
|
switch (orientation) {
|
|
254
|
-
case
|
|
254
|
+
case 'Planar':
|
|
255
255
|
if (overlapping) {
|
|
256
256
|
insertFunction = insertOverlappingPixelDataPlanar;
|
|
257
257
|
} else {
|
|
258
258
|
insertFunction = insertPixelDataPlanar;
|
|
259
259
|
}
|
|
260
260
|
break;
|
|
261
|
-
case
|
|
261
|
+
case 'Perpendicular':
|
|
262
262
|
//insertFunction = insertPixelDataPerpendicular;
|
|
263
|
-
throw new Error(
|
|
264
|
-
case
|
|
265
|
-
throw new Error(
|
|
263
|
+
throw new Error('Segmentations orthogonal to the acquisition plane of the source data are not yet supported.');
|
|
264
|
+
case 'Oblique':
|
|
265
|
+
throw new Error('Segmentations oblique to the acquisition plane of the source data are not yet supported.');
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
/* if SEGs are overlapping:
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
1) the labelmapBuffer will contain M volumes which have non-overlapping segments;
|
|
270
|
+
2) segmentsOnFrame will have M * numberOfFrames values to track in which labelMap are the segments;
|
|
271
|
+
3) insertFunction will return the number of LabelMaps
|
|
272
|
+
4) generateToolState return is an array*/
|
|
273
273
|
|
|
274
274
|
const segmentsOnFrameArray = [];
|
|
275
275
|
segmentsOnFrameArray[0] = [];
|
|
@@ -282,7 +282,7 @@ async function generateToolState(referencedImageIds, arrayBuffer, metadataProvid
|
|
|
282
282
|
// a function for each imageId in the for loop.
|
|
283
283
|
const imageIdMaps = referencedImageIds.reduce((acc, curr, index) => {
|
|
284
284
|
acc.indices[curr] = index;
|
|
285
|
-
acc.metadata[curr] = metadataProvider.get(
|
|
285
|
+
acc.metadata[curr] = metadataProvider.get('instance', curr);
|
|
286
286
|
return acc;
|
|
287
287
|
}, {
|
|
288
288
|
indices: {},
|
|
@@ -533,7 +533,7 @@ function findReferenceSourceImageId(multiframe, frameSegment, imageIds, metadata
|
|
|
533
533
|
}
|
|
534
534
|
}
|
|
535
535
|
} else if (SourceImageSequence && SourceImageSequence.length !== 0) {
|
|
536
|
-
console.warn(
|
|
536
|
+
console.warn('DerivationImageSequence not present, using SourceImageSequence assuming SEG has the same geometry as the source image.');
|
|
537
537
|
frameSourceImageSequence = SourceImageSequence[frameSegment];
|
|
538
538
|
}
|
|
539
539
|
if (frameSourceImageSequence) {
|
|
@@ -578,12 +578,12 @@ function checkSEGsOverlapping(pixelData, multiframe, imageIds, validOrientations
|
|
|
578
578
|
for (let frameSegment = 0; frameSegment < groupsLen; ++frameSegment) {
|
|
579
579
|
const segmentIndex = getSegmentIndex(multiframe, frameSegment);
|
|
580
580
|
if (segmentIndex === undefined) {
|
|
581
|
-
console.warn(
|
|
581
|
+
console.warn('Could not retrieve the segment index for frame segment ' + frameSegment + ', skipping this frame.');
|
|
582
582
|
continue;
|
|
583
583
|
}
|
|
584
584
|
const imageId = findReferenceSourceImageId(multiframe, frameSegment, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
|
|
585
585
|
if (!imageId) {
|
|
586
|
-
console.warn("Image not present in stack, can't import frame : " + frameSegment +
|
|
586
|
+
console.warn("Image not present in stack, can't import frame : " + frameSegment + '.');
|
|
587
587
|
continue;
|
|
588
588
|
}
|
|
589
589
|
const imageIdIndex = imageIds.findIndex(element => element === imageId);
|
|
@@ -607,7 +607,7 @@ function checkSEGsOverlapping(pixelData, multiframe, imageIds, validOrientations
|
|
|
607
607
|
const pixelDataI2D = ndarray(view, [Rows, Columns]);
|
|
608
608
|
const alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
|
|
609
609
|
if (!alignedPixelDataI) {
|
|
610
|
-
console.warn(
|
|
610
|
+
console.warn('Individual SEG frames are out of plane with respect to the first SEG frame, this is not yet supported, skipping this frame.');
|
|
611
611
|
continue;
|
|
612
612
|
}
|
|
613
613
|
const data = alignedPixelDataI.data;
|
|
@@ -658,7 +658,7 @@ function insertOverlappingPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray,
|
|
|
658
658
|
const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[i];
|
|
659
659
|
const segmentIndex = getSegmentIndex(multiframe, i);
|
|
660
660
|
if (segmentIndex === undefined) {
|
|
661
|
-
throw new Error(
|
|
661
|
+
throw new Error('Could not retrieve the segment index. Aborting segmentation loading.');
|
|
662
662
|
}
|
|
663
663
|
if (segmentIndex !== segmentIndexToProcess) {
|
|
664
664
|
continue;
|
|
@@ -672,16 +672,16 @@ function insertOverlappingPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray,
|
|
|
672
672
|
const pixelDataI2D = ndarray(view, [Rows, Columns]);
|
|
673
673
|
const alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
|
|
674
674
|
if (!alignedPixelDataI) {
|
|
675
|
-
throw new Error(
|
|
675
|
+
throw new Error('Individual SEG frames are out of plane with respect to the first SEG frame. ' + 'This is not yet supported. Aborting segmentation loading.');
|
|
676
676
|
}
|
|
677
677
|
const imageId = findReferenceSourceImageId(multiframe, i, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
|
|
678
678
|
if (!imageId) {
|
|
679
|
-
console.warn("Image not present in stack, can't import frame : " + i +
|
|
679
|
+
console.warn("Image not present in stack, can't import frame : " + i + '.');
|
|
680
680
|
continue;
|
|
681
681
|
}
|
|
682
|
-
const sourceImageMetadata = metadataProvider.get(
|
|
682
|
+
const sourceImageMetadata = metadataProvider.get('instance', imageId);
|
|
683
683
|
if (Rows !== sourceImageMetadata.Rows || Columns !== sourceImageMetadata.Columns) {
|
|
684
|
-
throw new Error(
|
|
684
|
+
throw new Error('Individual SEG frames have different geometry dimensions (Rows and Columns) ' + 'respect to the source image reference frame. This is not yet supported. ' + 'Aborting segmentation loading. ');
|
|
685
685
|
}
|
|
686
686
|
const imageIdIndex = imageIds.findIndex(element => element === imageId);
|
|
687
687
|
const byteOffset = sliceLength * imageIdIndex * TypedArrayConstructor.BYTES_PER_ELEMENT;
|
|
@@ -763,23 +763,23 @@ function insertPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray, labelmapBu
|
|
|
763
763
|
const pixelDataI2D = ndarray(view, [Rows, Columns]);
|
|
764
764
|
const alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
|
|
765
765
|
if (!alignedPixelDataI) {
|
|
766
|
-
throw new Error(
|
|
766
|
+
throw new Error('Individual SEG frames are out of plane with respect to the first SEG frame. ' + 'This is not yet supported. Aborting segmentation loading.');
|
|
767
767
|
}
|
|
768
768
|
const segmentIndex = getSegmentIndex(multiframe, i);
|
|
769
769
|
if (segmentIndex === undefined) {
|
|
770
|
-
throw new Error(
|
|
770
|
+
throw new Error('Could not retrieve the segment index. Aborting segmentation loading.');
|
|
771
771
|
}
|
|
772
772
|
if (!segmentsPixelIndices.has(segmentIndex)) {
|
|
773
773
|
segmentsPixelIndices.set(segmentIndex, {});
|
|
774
774
|
}
|
|
775
775
|
const imageId = findReferenceSourceImageId(multiframe, i, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
|
|
776
776
|
if (!imageId) {
|
|
777
|
-
console.warn("Image not present in stack, can't import frame : " + i +
|
|
777
|
+
console.warn("Image not present in stack, can't import frame : " + i + '.');
|
|
778
778
|
continue;
|
|
779
779
|
}
|
|
780
780
|
const sourceImageMetadata = imageIdMaps.metadata[imageId];
|
|
781
781
|
if (Rows !== sourceImageMetadata.Rows || Columns !== sourceImageMetadata.Columns) {
|
|
782
|
-
throw new Error(
|
|
782
|
+
throw new Error('Individual SEG frames have different geometry dimensions (Rows and Columns) ' + 'respect to the source image reference frame. This is not yet supported. ' + 'Aborting segmentation loading. ');
|
|
783
783
|
}
|
|
784
784
|
const imageIdIndex = imageIdMaps.indices[imageId];
|
|
785
785
|
const byteOffset = sliceLength * imageIdIndex * TypedArrayConstructor.BYTES_PER_ELEMENT;
|
|
@@ -845,15 +845,15 @@ function unpackPixelData(multiframe, options) {
|
|
|
845
845
|
data = multiframe.PixelData;
|
|
846
846
|
}
|
|
847
847
|
if (data === undefined) {
|
|
848
|
-
log.error(
|
|
848
|
+
log.error('This segmentation pixelData is undefined.');
|
|
849
849
|
}
|
|
850
|
-
if (segType ===
|
|
850
|
+
if (segType === 'BINARY') {
|
|
851
851
|
// For extreme big data, we can't unpack the data at once and we need to
|
|
852
852
|
// chunk it and unpack each chunk separately.
|
|
853
853
|
// MAX 2GB is the limit right now to allocate a buffer
|
|
854
854
|
return getUnpackedChunks(data, options.maxBytesPerChunk);
|
|
855
855
|
}
|
|
856
|
-
if (segType ===
|
|
856
|
+
if (segType === 'LABELMAP') {
|
|
857
857
|
// For LABELMAP, we can return the data as is, since it is already in a
|
|
858
858
|
// format that Cornerstone can handle. Also here we are returning the
|
|
859
859
|
// whole data at once, since the storage is more efficent than BINARY mode
|
|
@@ -872,7 +872,7 @@ function unpackPixelData(multiframe, options) {
|
|
|
872
872
|
// This is a fractional segmentation, which is not currently supported.
|
|
873
873
|
return;
|
|
874
874
|
}
|
|
875
|
-
log.warn(
|
|
875
|
+
log.warn('This segmentation object is actually binary... processing as such.');
|
|
876
876
|
return pixelData;
|
|
877
877
|
}
|
|
878
878
|
function getUnpackedChunks(data, maxBytesPerChunk) {
|
|
@@ -911,15 +911,15 @@ function getImageIdOfSourceImageBySourceImageSequence(SourceImageSequence, sopUI
|
|
|
911
911
|
return undefined;
|
|
912
912
|
}
|
|
913
913
|
if (ReferencedFrameNumber !== undefined) {
|
|
914
|
-
if (baseImageId.includes(
|
|
914
|
+
if (baseImageId.includes('frames/')) {
|
|
915
915
|
return baseImageId.replace(/frames\/\d+/, `frames/${ReferencedFrameNumber}`);
|
|
916
|
-
} else if (baseImageId.includes(
|
|
916
|
+
} else if (baseImageId.includes('dicomfile:')) {
|
|
917
917
|
// dicomfile base 1, despite having frame=
|
|
918
918
|
return baseImageId.replace(/frame=\d+/, `frame=${ReferencedFrameNumber}`);
|
|
919
|
-
} else if (baseImageId.includes(
|
|
919
|
+
} else if (baseImageId.includes('frame=')) {
|
|
920
920
|
return baseImageId.replace(/frame=\d+/, `frame=${ReferencedFrameNumber - 1}`);
|
|
921
921
|
} else {
|
|
922
|
-
if (baseImageId.includes(
|
|
922
|
+
if (baseImageId.includes('wadors:')) {
|
|
923
923
|
return `${baseImageId}/frames/${ReferencedFrameNumber}`;
|
|
924
924
|
} else {
|
|
925
925
|
return `${baseImageId}?frame=${ReferencedFrameNumber - 1}`;
|
|
@@ -958,7 +958,7 @@ function getImageIdOfSourceImagebyGeometry(ReferencedSeriesInstanceUID, FrameOfR
|
|
|
958
958
|
}
|
|
959
959
|
const segFramePosition = PerFrameFunctionalGroup.PlanePositionSequence[0].ImagePositionPatient;
|
|
960
960
|
for (let imageId of imageIds) {
|
|
961
|
-
const sourceImageMetadata = metadataProvider.get(
|
|
961
|
+
const sourceImageMetadata = metadataProvider.get('instance', imageId);
|
|
962
962
|
if (!sourceImageMetadata) {
|
|
963
963
|
continue;
|
|
964
964
|
}
|
|
@@ -969,7 +969,7 @@ function getImageIdOfSourceImagebyGeometry(ReferencedSeriesInstanceUID, FrameOfR
|
|
|
969
969
|
|
|
970
970
|
// For multiframe images, check each frame's position
|
|
971
971
|
if (isMultiframe) {
|
|
972
|
-
const framePosition = metadataProvider.get(
|
|
972
|
+
const framePosition = metadataProvider.get('imagePlaneModule', imageId)?.imagePositionPatient;
|
|
973
973
|
if (framePosition && utilities$1.isEqual(segFramePosition, framePosition, tolerance)) {
|
|
974
974
|
return imageId;
|
|
975
975
|
}
|
|
@@ -1099,7 +1099,7 @@ function readFromUnpackedChunks(chunks, offset, length) {
|
|
|
1099
1099
|
function getUnpackedOffsetAndLength(chunks, offset, length) {
|
|
1100
1100
|
var totalBytes = chunks.reduce((total, chunk) => total + chunk.length, 0);
|
|
1101
1101
|
if (offset < 0 || offset + length > totalBytes) {
|
|
1102
|
-
throw new Error(
|
|
1102
|
+
throw new Error('Offset and length out of bounds');
|
|
1103
1103
|
}
|
|
1104
1104
|
var startChunkIndex = 0;
|
|
1105
1105
|
var startOffsetInChunk = offset;
|
|
@@ -1140,9 +1140,9 @@ function calculateCentroid(imageIdIndexBufferIndex, multiframe, metadataProvider
|
|
|
1140
1140
|
|
|
1141
1141
|
// Get metadata for this slice
|
|
1142
1142
|
const imageId = imageIds[z];
|
|
1143
|
-
const imagePlaneModule = metadataProvider.get(
|
|
1143
|
+
const imagePlaneModule = metadataProvider.get('imagePlaneModule', imageId);
|
|
1144
1144
|
if (!imagePlaneModule) {
|
|
1145
|
-
console.debug(
|
|
1145
|
+
console.debug('Missing imagePlaneModule metadata for centroid calculation');
|
|
1146
1146
|
continue;
|
|
1147
1147
|
}
|
|
1148
1148
|
const {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// This is a custom coding scheme defined to store some annotations from Cornerstone.
|
|
2
2
|
// Note: CodeMeaning is VR type LO, which means we only actually support 64 characters
|
|
3
3
|
// here this is fine for most labels, but may be problematic at some point.
|
|
4
|
-
const CORNERSTONEFREETEXT =
|
|
4
|
+
const CORNERSTONEFREETEXT = 'CORNERSTONEFREETEXT';
|
|
5
5
|
|
|
6
6
|
// Cornerstone specified coding scheme for storing findings
|
|
7
|
-
const CodingSchemeDesignator =
|
|
7
|
+
const CodingSchemeDesignator = 'CORNERSTONEJS';
|
|
8
8
|
const CodingScheme = {
|
|
9
9
|
CodingSchemeDesignator,
|
|
10
10
|
codeValues: {
|
|
@@ -132,7 +132,7 @@ class Segmentation {
|
|
|
132
132
|
* @return {{}}
|
|
133
133
|
*/
|
|
134
134
|
static generateSegments(dataset) {
|
|
135
|
-
if (dataset.SegmentSequence.constructor.name !==
|
|
135
|
+
if (dataset.SegmentSequence.constructor.name !== 'Array') {
|
|
136
136
|
dataset.SegmentSequence = [dataset.SegmentSequence];
|
|
137
137
|
}
|
|
138
138
|
dataset.SegmentSequence.forEach(segment => {
|
package/dist/esm/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "4.13.
|
|
1
|
+
export declare const version = "4.13.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/adapters",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.2",
|
|
4
4
|
"description": "Adapters for Cornerstone3D to/from formats including DICOM SR and others",
|
|
5
5
|
"module": "./dist/esm/index.js",
|
|
6
6
|
"types": "./dist/esm/index.d.ts",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"ndarray": "1.0.19"
|
|
90
90
|
},
|
|
91
91
|
"peerDependencies": {
|
|
92
|
-
"@cornerstonejs/core": "4.13.
|
|
93
|
-
"@cornerstonejs/tools": "4.13.
|
|
92
|
+
"@cornerstonejs/core": "4.13.2",
|
|
93
|
+
"@cornerstonejs/tools": "4.13.2"
|
|
94
94
|
},
|
|
95
|
-
"gitHead": "
|
|
95
|
+
"gitHead": "d44816f5bfb4e383bf9a2a15e35515e397ead45c"
|
|
96
96
|
}
|