@cornerstonejs/adapters 0.1.5 → 0.2.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/@cornerstonejs/adapters.es.js +1673 -2037
- package/dist/@cornerstonejs/adapters.es.js.map +1 -1
- package/dist/@cornerstonejs/dts/index.d.ts +3 -0
- package/package.json +9 -32
- package/src/{index.js → index.ts} +0 -0
- package/dist/@cornerstonejs/adapters.js +0 -30070
- package/dist/@cornerstonejs/adapters.js.map +0 -1
- package/src/adapters/Cornerstone/Angle.js +0 -92
- package/src/adapters/Cornerstone/ArrowAnnotate.js +0 -106
- package/src/adapters/Cornerstone/Bidirectional.js +0 -187
- package/src/adapters/Cornerstone/CircleRoi.js +0 -109
- package/src/adapters/Cornerstone/CobbAngle.js +0 -96
- package/src/adapters/Cornerstone/EllipticalRoi.js +0 -149
- package/src/adapters/Cornerstone/FreehandRoi.js +0 -82
- package/src/adapters/Cornerstone/Length.js +0 -80
- package/src/adapters/Cornerstone/MeasurementReport.js +0 -352
- package/src/adapters/Cornerstone/RectangleRoi.js +0 -92
- package/src/adapters/Cornerstone/Segmentation.js +0 -118
- package/src/adapters/Cornerstone/Segmentation_3X.js +0 -632
- package/src/adapters/Cornerstone/Segmentation_4X.js +0 -1543
- package/src/adapters/Cornerstone/cornerstone4Tag.js +0 -1
- package/src/adapters/Cornerstone/index.js +0 -27
- package/src/adapters/Cornerstone3D/ArrowAnnotate.js +0 -155
- package/src/adapters/Cornerstone3D/Bidirectional.js +0 -196
- package/src/adapters/Cornerstone3D/CodingScheme.js +0 -16
- package/src/adapters/Cornerstone3D/EllipticalROI.js +0 -204
- package/src/adapters/Cornerstone3D/Length.js +0 -113
- package/src/adapters/Cornerstone3D/MeasurementReport.js +0 -445
- package/src/adapters/Cornerstone3D/PlanarFreehandROI.js +0 -137
- package/src/adapters/Cornerstone3D/Probe.js +0 -106
- package/src/adapters/Cornerstone3D/cornerstone3DTag.js +0 -1
- package/src/adapters/Cornerstone3D/index.js +0 -24
- package/src/adapters/VTKjs/Segmentation.js +0 -223
- package/src/adapters/VTKjs/index.js +0 -7
- package/src/adapters/helpers.js +0 -19
- package/src/adapters/index.js +0 -11
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { utilities } from "dcmjs";
|
|
2
|
-
import CORNERSTONE_3D_TAG from "./cornerstone3DTag";
|
|
3
|
-
import MeasurementReport from "./MeasurementReport";
|
|
4
|
-
|
|
5
|
-
const { Point: TID300Point } = utilities.TID300;
|
|
6
|
-
|
|
7
|
-
const PROBE = "Probe";
|
|
8
|
-
const trackingIdentifierTextValue = `${CORNERSTONE_3D_TAG}:${PROBE}`;
|
|
9
|
-
|
|
10
|
-
class Probe {
|
|
11
|
-
static getMeasurementData(
|
|
12
|
-
MeasurementGroup,
|
|
13
|
-
sopInstanceUIDToImageIdMap,
|
|
14
|
-
imageToWorldCoords,
|
|
15
|
-
metadata
|
|
16
|
-
) {
|
|
17
|
-
const { defaultState, SCOORDGroup, ReferencedFrameNumber } =
|
|
18
|
-
MeasurementReport.getSetupMeasurementData(
|
|
19
|
-
MeasurementGroup,
|
|
20
|
-
sopInstanceUIDToImageIdMap,
|
|
21
|
-
metadata,
|
|
22
|
-
Probe.toolType
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
const referencedImageId =
|
|
26
|
-
defaultState.annotation.metadata.referencedImageId;
|
|
27
|
-
|
|
28
|
-
const { GraphicData } = SCOORDGroup;
|
|
29
|
-
|
|
30
|
-
const worldCoords = [];
|
|
31
|
-
for (let i = 0; i < GraphicData.length; i += 2) {
|
|
32
|
-
const point = imageToWorldCoords(referencedImageId, [
|
|
33
|
-
GraphicData[i],
|
|
34
|
-
GraphicData[i + 1]
|
|
35
|
-
]);
|
|
36
|
-
worldCoords.push(point);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const state = defaultState;
|
|
40
|
-
|
|
41
|
-
state.annotation.data = {
|
|
42
|
-
handles: {
|
|
43
|
-
points: worldCoords,
|
|
44
|
-
activeHandleIndex: null,
|
|
45
|
-
textBox: {
|
|
46
|
-
hasMoved: false
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
frameNumber: ReferencedFrameNumber
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
return state;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
static getTID300RepresentationArguments(tool, worldToImageCoords) {
|
|
56
|
-
const { data, metadata } = tool;
|
|
57
|
-
let { finding, findingSites } = tool;
|
|
58
|
-
const { referencedImageId } = metadata;
|
|
59
|
-
|
|
60
|
-
if (!referencedImageId) {
|
|
61
|
-
throw new Error(
|
|
62
|
-
"Probe.getTID300RepresentationArguments: referencedImageId is not defined"
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const { points } = data.handles;
|
|
67
|
-
|
|
68
|
-
const pointsImage = points.map(point => {
|
|
69
|
-
const pointImage = worldToImageCoords(referencedImageId, point);
|
|
70
|
-
return {
|
|
71
|
-
x: pointImage[0],
|
|
72
|
-
y: pointImage[1]
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const TID300RepresentationArguments = {
|
|
77
|
-
points: pointsImage,
|
|
78
|
-
trackingIdentifierTextValue,
|
|
79
|
-
findingSites: findingSites || [],
|
|
80
|
-
finding
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return TID300RepresentationArguments;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
Probe.toolType = PROBE;
|
|
88
|
-
Probe.utilityToolType = PROBE;
|
|
89
|
-
Probe.TID300Representation = TID300Point;
|
|
90
|
-
Probe.isValidCornerstoneTrackingIdentifier = TrackingIdentifier => {
|
|
91
|
-
if (!TrackingIdentifier.includes(":")) {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const [cornerstone3DTag, toolType] = TrackingIdentifier.split(":");
|
|
96
|
-
|
|
97
|
-
if (cornerstone3DTag !== CORNERSTONE_3D_TAG) {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return toolType === PROBE;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
MeasurementReport.registerTool(Probe);
|
|
105
|
-
|
|
106
|
-
export default Probe;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default "Cornerstone3DTools@^0.1.0";
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import MeasurementReport from "./MeasurementReport";
|
|
2
|
-
import CodeScheme from "./CodingScheme";
|
|
3
|
-
import CORNERSTONE_3D_TAG from "./cornerstone3DTag";
|
|
4
|
-
|
|
5
|
-
import ArrowAnnotate from "./ArrowAnnotate";
|
|
6
|
-
import Bidirectional from "./Bidirectional";
|
|
7
|
-
import EllipticalROI from "./EllipticalROI";
|
|
8
|
-
import Length from "./Length";
|
|
9
|
-
import PlanarFreehandROI from "./PlanarFreehandROI";
|
|
10
|
-
import Probe from "./Probe";
|
|
11
|
-
|
|
12
|
-
const Cornerstone3D = {
|
|
13
|
-
Bidirectional,
|
|
14
|
-
Length,
|
|
15
|
-
EllipticalROI,
|
|
16
|
-
ArrowAnnotate,
|
|
17
|
-
Probe,
|
|
18
|
-
PlanarFreehandROI,
|
|
19
|
-
MeasurementReport,
|
|
20
|
-
CodeScheme,
|
|
21
|
-
CORNERSTONE_3D_TAG
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export default Cornerstone3D;
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import {data} from 'dcmjs';
|
|
2
|
-
|
|
3
|
-
const {Colors, BitArray} = data;
|
|
4
|
-
|
|
5
|
-
// TODO: Is there a better name for this? RGBAInt?
|
|
6
|
-
// Should we move it to Colors.js
|
|
7
|
-
function dicomlab2RGBA(cielab) {
|
|
8
|
-
const rgba = Colors.dicomlab2RGB(cielab).map(x => Math.round(x * 255));
|
|
9
|
-
rgba.push(255);
|
|
10
|
-
|
|
11
|
-
return rgba;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// TODO: Copied these functions in from VTK Math so we don't need a dependency.
|
|
15
|
-
// I guess we should put them somewhere
|
|
16
|
-
// https://github.com/Kitware/vtk-js/blob/master/Sources/Common/Core/Math/index.js
|
|
17
|
-
function cross(x, y, out) {
|
|
18
|
-
const Zx = x[1] * y[2] - x[2] * y[1];
|
|
19
|
-
const Zy = x[2] * y[0] - x[0] * y[2];
|
|
20
|
-
const Zz = x[0] * y[1] - x[1] * y[0];
|
|
21
|
-
out[0] = Zx;
|
|
22
|
-
out[1] = Zy;
|
|
23
|
-
out[2] = Zz;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function norm(x, n = 3) {
|
|
27
|
-
switch (n) {
|
|
28
|
-
case 1:
|
|
29
|
-
return Math.abs(x);
|
|
30
|
-
case 2:
|
|
31
|
-
return Math.sqrt(x[0] * x[0] + x[1] * x[1]);
|
|
32
|
-
case 3:
|
|
33
|
-
return Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
|
|
34
|
-
default: {
|
|
35
|
-
let sum = 0;
|
|
36
|
-
for (let i = 0; i < n; i++) {
|
|
37
|
-
sum += x[i] * x[i];
|
|
38
|
-
}
|
|
39
|
-
return Math.sqrt(sum);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function normalize(x) {
|
|
45
|
-
const den = norm(x);
|
|
46
|
-
if (den !== 0.0) {
|
|
47
|
-
x[0] /= den;
|
|
48
|
-
x[1] /= den;
|
|
49
|
-
x[2] /= den;
|
|
50
|
-
}
|
|
51
|
-
return den;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function subtract(a, b, out) {
|
|
55
|
-
out[0] = a[0] - b[0];
|
|
56
|
-
out[1] = a[1] - b[1];
|
|
57
|
-
out[2] = a[2] - b[2];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// TODO: This is a useful utility on its own. We should move it somewhere?
|
|
61
|
-
// dcmjs.adapters.vtk.Multiframe? dcmjs.utils?
|
|
62
|
-
function geometryFromFunctionalGroups(dataset, PerFrameFunctionalGroups) {
|
|
63
|
-
const geometry = {};
|
|
64
|
-
const pixelMeasures =
|
|
65
|
-
dataset.SharedFunctionalGroupsSequence.PixelMeasuresSequence;
|
|
66
|
-
const planeOrientation =
|
|
67
|
-
dataset.SharedFunctionalGroupsSequence.PlaneOrientationSequence;
|
|
68
|
-
|
|
69
|
-
// Find the origin of the volume from the PerFrameFunctionalGroups' ImagePositionPatient values
|
|
70
|
-
//
|
|
71
|
-
// TODO: assumes sorted frames. This should read the ImagePositionPatient from each frame and
|
|
72
|
-
// sort them to obtain the first and last position along the acquisition axis.
|
|
73
|
-
const firstFunctionalGroup = PerFrameFunctionalGroups[0];
|
|
74
|
-
const lastFunctionalGroup =
|
|
75
|
-
PerFrameFunctionalGroups[PerFrameFunctionalGroups.length - 1];
|
|
76
|
-
const firstPosition =
|
|
77
|
-
firstFunctionalGroup.PlanePositionSequence.ImagePositionPatient.map(
|
|
78
|
-
Number
|
|
79
|
-
);
|
|
80
|
-
const lastPosition =
|
|
81
|
-
lastFunctionalGroup.PlanePositionSequence.ImagePositionPatient.map(
|
|
82
|
-
Number
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
geometry.origin = firstPosition;
|
|
86
|
-
|
|
87
|
-
// NB: DICOM PixelSpacing is defined as Row then Column,
|
|
88
|
-
// unlike ImageOrientationPatient
|
|
89
|
-
geometry.spacing = [
|
|
90
|
-
pixelMeasures.PixelSpacing[1],
|
|
91
|
-
pixelMeasures.PixelSpacing[0],
|
|
92
|
-
pixelMeasures.SpacingBetweenSlices
|
|
93
|
-
].map(Number);
|
|
94
|
-
|
|
95
|
-
geometry.dimensions = [
|
|
96
|
-
dataset.Columns,
|
|
97
|
-
dataset.Rows,
|
|
98
|
-
PerFrameFunctionalGroups.length
|
|
99
|
-
].map(Number);
|
|
100
|
-
|
|
101
|
-
const orientation = planeOrientation.ImageOrientationPatient.map(Number);
|
|
102
|
-
const columnStepToPatient = orientation.slice(0, 3);
|
|
103
|
-
const rowStepToPatient = orientation.slice(3, 6);
|
|
104
|
-
|
|
105
|
-
geometry.planeNormal = [];
|
|
106
|
-
|
|
107
|
-
cross(columnStepToPatient, rowStepToPatient, geometry.planeNormal);
|
|
108
|
-
|
|
109
|
-
geometry.sliceStep = [];
|
|
110
|
-
subtract(lastPosition, firstPosition, geometry.sliceStep);
|
|
111
|
-
normalize(geometry.sliceStep);
|
|
112
|
-
geometry.direction = columnStepToPatient
|
|
113
|
-
.concat(rowStepToPatient)
|
|
114
|
-
.concat(geometry.sliceStep);
|
|
115
|
-
|
|
116
|
-
return geometry;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export default class Segmentation {
|
|
120
|
-
constructor() {}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Produces an array of Segments from an input DICOM Segmentation dataset
|
|
124
|
-
*
|
|
125
|
-
* Segments are returned with Geometry values that can be used to create
|
|
126
|
-
* VTK Image Data objects.
|
|
127
|
-
*
|
|
128
|
-
* @example Example usage to create VTK Volume actors from each segment:
|
|
129
|
-
*
|
|
130
|
-
* const actors = [];
|
|
131
|
-
* const segments = generateToolState(dataset);
|
|
132
|
-
* segments.forEach(segment => {
|
|
133
|
-
* // now make actors using the segment information
|
|
134
|
-
* const scalarArray = vtk.Common.Core.vtkDataArray.newInstance({
|
|
135
|
-
* name: "Scalars",
|
|
136
|
-
* numberOfComponents: 1,
|
|
137
|
-
* values: segment.pixelData,
|
|
138
|
-
* });
|
|
139
|
-
*
|
|
140
|
-
* const imageData = vtk.Common.DataModel.vtkImageData.newInstance();
|
|
141
|
-
* imageData.getPointData().setScalars(scalarArray);
|
|
142
|
-
* imageData.setDimensions(geometry.dimensions);
|
|
143
|
-
* imageData.setSpacing(geometry.spacing);
|
|
144
|
-
* imageData.setOrigin(geometry.origin);
|
|
145
|
-
* imageData.setDirection(geometry.direction);
|
|
146
|
-
*
|
|
147
|
-
* const mapper = vtk.Rendering.Core.vtkVolumeMapper.newInstance();
|
|
148
|
-
* mapper.setInputData(imageData);
|
|
149
|
-
* mapper.setSampleDistance(2.);
|
|
150
|
-
*
|
|
151
|
-
* const actor = vtk.Rendering.Core.vtkVolume.newInstance();
|
|
152
|
-
* actor.setMapper(mapper);
|
|
153
|
-
*
|
|
154
|
-
* actors.push(actor);
|
|
155
|
-
* });
|
|
156
|
-
*
|
|
157
|
-
* @param dataset
|
|
158
|
-
* @return {{}}
|
|
159
|
-
*/
|
|
160
|
-
static generateSegments(dataset) {
|
|
161
|
-
if (dataset.SegmentSequence.constructor.name !== "Array") {
|
|
162
|
-
dataset.SegmentSequence = [dataset.SegmentSequence];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
dataset.SegmentSequence.forEach(segment => {
|
|
166
|
-
// TODO: other interesting fields could be extracted from the segment
|
|
167
|
-
// TODO: Read SegmentsOverlay field
|
|
168
|
-
// http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.8.20.2.html
|
|
169
|
-
|
|
170
|
-
// TODO: Looks like vtkColor only wants RGB in 0-1 values.
|
|
171
|
-
// Why was this example converting to RGBA with 0-255 values?
|
|
172
|
-
const color = dicomlab2RGBA(segment.RecommendedDisplayCIELabValue);
|
|
173
|
-
|
|
174
|
-
segments[segment.SegmentNumber] = {
|
|
175
|
-
color,
|
|
176
|
-
functionalGroups: [],
|
|
177
|
-
offset: null,
|
|
178
|
-
size: null,
|
|
179
|
-
pixelData: null
|
|
180
|
-
};
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// make a list of functional groups per segment
|
|
184
|
-
dataset.PerFrameFunctionalGroupsSequence.forEach(functionalGroup => {
|
|
185
|
-
const segmentNumber =
|
|
186
|
-
functionalGroup.SegmentIdentificationSequence
|
|
187
|
-
.ReferencedSegmentNumber;
|
|
188
|
-
|
|
189
|
-
segments[segmentNumber].functionalGroups.push(functionalGroup);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// determine per-segment index into the pixel data
|
|
193
|
-
// TODO: only handles one-bit-per pixel
|
|
194
|
-
const frameSize = Math.ceil((dataset.Rows * dataset.Columns) / 8);
|
|
195
|
-
let nextOffset = 0;
|
|
196
|
-
|
|
197
|
-
Object.keys(segments).forEach(segmentNumber => {
|
|
198
|
-
const segment = segments[segmentNumber];
|
|
199
|
-
|
|
200
|
-
segment.numberOfFrames = segment.functionalGroups.length;
|
|
201
|
-
segment.size = segment.numberOfFrames * frameSize;
|
|
202
|
-
segment.offset = nextOffset;
|
|
203
|
-
|
|
204
|
-
nextOffset = segment.offset + segment.size;
|
|
205
|
-
|
|
206
|
-
const packedSegment = dataset.PixelData.slice(
|
|
207
|
-
segment.offset,
|
|
208
|
-
nextOffset
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
segment.pixelData = BitArray.unpack(packedSegment);
|
|
212
|
-
|
|
213
|
-
const geometry = geometryFromFunctionalGroups(
|
|
214
|
-
dataset,
|
|
215
|
-
segment.functionalGroups
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
segment.geometry = geometry;
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
return segments;
|
|
222
|
-
}
|
|
223
|
-
}
|
package/src/adapters/helpers.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const toArray = function (x) {
|
|
2
|
-
return Array.isArray(x) ? x : [x];
|
|
3
|
-
};
|
|
4
|
-
|
|
5
|
-
const codeMeaningEquals = codeMeaningName => {
|
|
6
|
-
return contentItem => {
|
|
7
|
-
return (
|
|
8
|
-
contentItem.ConceptNameCodeSequence.CodeMeaning === codeMeaningName
|
|
9
|
-
);
|
|
10
|
-
};
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const graphicTypeEquals = graphicType => {
|
|
14
|
-
return contentItem => {
|
|
15
|
-
return contentItem && contentItem.GraphicType === graphicType;
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export { toArray, codeMeaningEquals, graphicTypeEquals };
|