@cornerstonejs/adapters 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/@cornerstonejs/adapters.es.js +3472 -28871
- package/dist/@cornerstonejs/adapters.es.js.map +1 -1
- package/dist/@cornerstonejs/dts/adapters/Cornerstone3D/Angle.d.ts +44 -0
- package/dist/@cornerstonejs/dts/adapters/Cornerstone3D/Bidirectional.d.ts +49 -0
- package/dist/@cornerstonejs/dts/adapters/Cornerstone3D/CobbAngle.d.ts +44 -0
- package/dist/@cornerstonejs/dts/adapters/Cornerstone3D/MeasurementReport.d.ts +53 -0
- package/dist/@cornerstonejs/dts/adapters/Cornerstone3D/PlanarFreehandROI.d.ts +30 -0
- package/dist/@cornerstonejs/dts/adapters/Cornerstone3D/RectangleROI.d.ts +30 -0
- package/dist/@cornerstonejs/dts/adapters/Cornerstone3D/index.d.ts +21 -0
- package/dist/@cornerstonejs/dts/index.d.ts +3 -0
- package/package.json +10 -35
- package/src/{index.js → index.ts} +0 -0
- package/dist/@cornerstonejs/adapters.js +0 -30195
- 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/CobbAngle.js +0 -125
- 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 -26
- 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,632 +0,0 @@
|
|
|
1
|
-
import { log, utilities, normalizers, derivations } from "dcmjs";
|
|
2
|
-
import ndarray from "ndarray";
|
|
3
|
-
|
|
4
|
-
const {
|
|
5
|
-
rotateDirectionCosinesInPlane,
|
|
6
|
-
flipImageOrientationPatient: flipIOP,
|
|
7
|
-
flipMatrix2D,
|
|
8
|
-
rotateMatrix902D
|
|
9
|
-
} = utilities.orientation;
|
|
10
|
-
|
|
11
|
-
const { datasetToBlob, BitArray, DicomMessage, DicomMetaDictionary } =
|
|
12
|
-
utilities;
|
|
13
|
-
|
|
14
|
-
const { Normalizer } = normalizers;
|
|
15
|
-
const { Segmentation: SegmentationDerivation } = derivations;
|
|
16
|
-
|
|
17
|
-
const Segmentation = {
|
|
18
|
-
generateSegmentation,
|
|
19
|
-
generateToolState
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export default Segmentation;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
* @typedef {Object} BrushData
|
|
27
|
-
* @property {Object} toolState - The cornerstoneTools global toolState.
|
|
28
|
-
* @property {Object[]} segments - The cornerstoneTools segment metadata that corresponds to the
|
|
29
|
-
* seriesInstanceUid.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* generateSegmentation - Generates cornerstoneTools brush data, given a stack of
|
|
34
|
-
* imageIds, images and the cornerstoneTools brushData.
|
|
35
|
-
*
|
|
36
|
-
* @param {object[]} images An array of the cornerstone image objects.
|
|
37
|
-
* @param {BrushData} brushData and object containing the brushData.
|
|
38
|
-
* @returns {type} description
|
|
39
|
-
*/
|
|
40
|
-
function generateSegmentation(
|
|
41
|
-
images,
|
|
42
|
-
brushData,
|
|
43
|
-
options = { includeSliceSpacing: true }
|
|
44
|
-
) {
|
|
45
|
-
const { toolState, segments } = brushData;
|
|
46
|
-
|
|
47
|
-
// Calculate the dimensions of the data cube.
|
|
48
|
-
const image0 = images[0];
|
|
49
|
-
|
|
50
|
-
const dims = {
|
|
51
|
-
x: image0.columns,
|
|
52
|
-
y: image0.rows,
|
|
53
|
-
z: images.length
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
dims.xy = dims.x * dims.y;
|
|
57
|
-
|
|
58
|
-
const numSegments = _getSegCount(seg, segments);
|
|
59
|
-
|
|
60
|
-
if (!numSegments) {
|
|
61
|
-
throw new Error("No segments to export!");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const isMultiframe = image0.imageId.includes("?frame");
|
|
65
|
-
const seg = _createSegFromImages(images, isMultiframe, options);
|
|
66
|
-
|
|
67
|
-
const { referencedFramesPerSegment, segmentIndicies } =
|
|
68
|
-
_getNumberOfFramesPerSegment(toolState, images, segments);
|
|
69
|
-
|
|
70
|
-
let NumberOfFrames = 0;
|
|
71
|
-
|
|
72
|
-
for (let i = 0; i < referencedFramesPerSegment.length; i++) {
|
|
73
|
-
NumberOfFrames += referencedFramesPerSegment[i].length;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
seg.setNumberOfFrames(NumberOfFrames);
|
|
77
|
-
|
|
78
|
-
for (let i = 0; i < segmentIndicies.length; i++) {
|
|
79
|
-
const segmentIndex = segmentIndicies[i];
|
|
80
|
-
const referencedFrameIndicies = referencedFramesPerSegment[i];
|
|
81
|
-
|
|
82
|
-
// Frame numbers start from 1.
|
|
83
|
-
const referencedFrameNumbers = referencedFrameIndicies.map(element => {
|
|
84
|
-
return element + 1;
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const segment = segments[segmentIndex];
|
|
88
|
-
|
|
89
|
-
seg.addSegment(
|
|
90
|
-
segment,
|
|
91
|
-
_extractCornerstoneToolsPixelData(
|
|
92
|
-
segmentIndex,
|
|
93
|
-
referencedFrameIndicies,
|
|
94
|
-
toolState,
|
|
95
|
-
images,
|
|
96
|
-
dims
|
|
97
|
-
),
|
|
98
|
-
referencedFrameNumbers
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
seg.bitPackPixelData();
|
|
103
|
-
|
|
104
|
-
const segBlob = datasetToBlob(seg.dataset);
|
|
105
|
-
|
|
106
|
-
return segBlob;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function _extractCornerstoneToolsPixelData(
|
|
110
|
-
segmentIndex,
|
|
111
|
-
referencedFrames,
|
|
112
|
-
toolState,
|
|
113
|
-
images,
|
|
114
|
-
dims
|
|
115
|
-
) {
|
|
116
|
-
const pixelData = new Uint8Array(dims.xy * referencedFrames.length);
|
|
117
|
-
|
|
118
|
-
let pixelDataIndex = 0;
|
|
119
|
-
|
|
120
|
-
for (let i = 0; i < referencedFrames.length; i++) {
|
|
121
|
-
const frame = referencedFrames[i];
|
|
122
|
-
|
|
123
|
-
const imageId = images[frame].imageId;
|
|
124
|
-
const imageIdSpecificToolState = toolState[imageId];
|
|
125
|
-
|
|
126
|
-
const brushPixelData =
|
|
127
|
-
imageIdSpecificToolState.brush.data[segmentIndex].pixelData;
|
|
128
|
-
|
|
129
|
-
for (let p = 0; p < brushPixelData.length; p++) {
|
|
130
|
-
pixelData[pixelDataIndex] = brushPixelData[p];
|
|
131
|
-
pixelDataIndex++;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return pixelData;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function _getNumberOfFramesPerSegment(toolState, images, segments) {
|
|
139
|
-
const segmentIndicies = [];
|
|
140
|
-
const referencedFramesPerSegment = [];
|
|
141
|
-
|
|
142
|
-
for (let i = 0; i < segments.length; i++) {
|
|
143
|
-
if (segments[i]) {
|
|
144
|
-
segmentIndicies.push(i);
|
|
145
|
-
referencedFramesPerSegment.push([]);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
for (let z = 0; z < images.length; z++) {
|
|
150
|
-
const imageId = images[z].imageId;
|
|
151
|
-
const imageIdSpecificToolState = toolState[imageId];
|
|
152
|
-
|
|
153
|
-
for (let i = 0; i < segmentIndicies.length; i++) {
|
|
154
|
-
const segIdx = segmentIndicies[i];
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
imageIdSpecificToolState &&
|
|
158
|
-
imageIdSpecificToolState.brush &&
|
|
159
|
-
imageIdSpecificToolState.brush.data &&
|
|
160
|
-
imageIdSpecificToolState.brush.data[segIdx] &&
|
|
161
|
-
imageIdSpecificToolState.brush.data[segIdx].pixelData
|
|
162
|
-
) {
|
|
163
|
-
referencedFramesPerSegment[i].push(z);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
referencedFramesPerSegment,
|
|
170
|
-
segmentIndicies
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function _getSegCount(seg, segments) {
|
|
175
|
-
let numSegments = 0;
|
|
176
|
-
|
|
177
|
-
for (let i = 0; i < segments.length; i++) {
|
|
178
|
-
if (segments[i]) {
|
|
179
|
-
numSegments++;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return numSegments;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* _createSegFromImages - description
|
|
188
|
-
*
|
|
189
|
-
* @param {Object[]} images An array of the cornerstone image objects.
|
|
190
|
-
* @param {Boolean} isMultiframe Whether the images are multiframe.
|
|
191
|
-
* @returns {Object} The Seg derived dataSet.
|
|
192
|
-
*/
|
|
193
|
-
function _createSegFromImages(images, isMultiframe, options) {
|
|
194
|
-
const datasets = [];
|
|
195
|
-
|
|
196
|
-
if (isMultiframe) {
|
|
197
|
-
const image = images[0];
|
|
198
|
-
const arrayBuffer = image.data.byteArray.buffer;
|
|
199
|
-
|
|
200
|
-
const dicomData = DicomMessage.readFile(arrayBuffer);
|
|
201
|
-
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
|
202
|
-
|
|
203
|
-
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
|
204
|
-
|
|
205
|
-
datasets.push(dataset);
|
|
206
|
-
} else {
|
|
207
|
-
for (let i = 0; i < images.length; i++) {
|
|
208
|
-
const image = images[i];
|
|
209
|
-
const arrayBuffer = image.data.byteArray.buffer;
|
|
210
|
-
const dicomData = DicomMessage.readFile(arrayBuffer);
|
|
211
|
-
const dataset = DicomMetaDictionary.naturalizeDataset(
|
|
212
|
-
dicomData.dict
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
|
216
|
-
datasets.push(dataset);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const multiframe = Normalizer.normalizeToDataset(datasets);
|
|
221
|
-
|
|
222
|
-
return new SegmentationDerivation([multiframe], options);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* generateToolState - Given a set of cornrstoneTools imageIds and a Segmentation buffer,
|
|
227
|
-
* derive cornerstoneTools toolState and brush metadata.
|
|
228
|
-
*
|
|
229
|
-
* @param {string[]} imageIds An array of the imageIds.
|
|
230
|
-
* @param {ArrayBuffer} arrayBuffer The SEG arrayBuffer.
|
|
231
|
-
* @param {*} metadataProvider
|
|
232
|
-
* @returns {Object} The toolState and an object from which the
|
|
233
|
-
* segment metadata can be derived.
|
|
234
|
-
*/
|
|
235
|
-
function generateToolState(imageIds, arrayBuffer, metadataProvider) {
|
|
236
|
-
const dicomData = DicomMessage.readFile(arrayBuffer);
|
|
237
|
-
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
|
238
|
-
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
|
239
|
-
const multiframe = Normalizer.normalizeToDataset([dataset]);
|
|
240
|
-
|
|
241
|
-
const imagePlaneModule = metadataProvider.get(
|
|
242
|
-
"imagePlaneModule",
|
|
243
|
-
imageIds[0]
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
if (!imagePlaneModule) {
|
|
247
|
-
console.warn("Insufficient metadata, imagePlaneModule missing.");
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const ImageOrientationPatient = Array.isArray(imagePlaneModule.rowCosines)
|
|
251
|
-
? [...imagePlaneModule.rowCosines, ...imagePlaneModule.columnCosines]
|
|
252
|
-
: [
|
|
253
|
-
imagePlaneModule.rowCosines.x,
|
|
254
|
-
imagePlaneModule.rowCosines.y,
|
|
255
|
-
imagePlaneModule.rowCosines.z,
|
|
256
|
-
imagePlaneModule.columnCosines.x,
|
|
257
|
-
imagePlaneModule.columnCosines.y,
|
|
258
|
-
imagePlaneModule.columnCosines.z
|
|
259
|
-
];
|
|
260
|
-
|
|
261
|
-
// Get IOP from ref series, compute supported orientations:
|
|
262
|
-
const validOrientations = getValidOrientations(ImageOrientationPatient);
|
|
263
|
-
|
|
264
|
-
const SharedFunctionalGroupsSequence =
|
|
265
|
-
multiframe.SharedFunctionalGroupsSequence;
|
|
266
|
-
|
|
267
|
-
const sharedImageOrientationPatient =
|
|
268
|
-
SharedFunctionalGroupsSequence.PlaneOrientationSequence
|
|
269
|
-
? SharedFunctionalGroupsSequence.PlaneOrientationSequence
|
|
270
|
-
.ImageOrientationPatient
|
|
271
|
-
: undefined;
|
|
272
|
-
|
|
273
|
-
const sliceLength = multiframe.Columns * multiframe.Rows;
|
|
274
|
-
const segMetadata = getSegmentMetadata(multiframe);
|
|
275
|
-
const pixelData = unpackPixelData(multiframe);
|
|
276
|
-
|
|
277
|
-
const PerFrameFunctionalGroupsSequence =
|
|
278
|
-
multiframe.PerFrameFunctionalGroupsSequence;
|
|
279
|
-
|
|
280
|
-
const toolState = {};
|
|
281
|
-
|
|
282
|
-
let inPlane = true;
|
|
283
|
-
|
|
284
|
-
for (let i = 0; i < PerFrameFunctionalGroupsSequence.length; i++) {
|
|
285
|
-
const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[i];
|
|
286
|
-
|
|
287
|
-
const ImageOrientationPatientI =
|
|
288
|
-
sharedImageOrientationPatient ||
|
|
289
|
-
PerFrameFunctionalGroups.PlaneOrientationSequence
|
|
290
|
-
.ImageOrientationPatient;
|
|
291
|
-
|
|
292
|
-
const pixelDataI2D = ndarray(
|
|
293
|
-
new Uint8Array(pixelData.buffer, i * sliceLength, sliceLength),
|
|
294
|
-
[multiframe.Rows, multiframe.Columns]
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
const alignedPixelDataI = alignPixelDataWithSourceData(
|
|
298
|
-
pixelDataI2D,
|
|
299
|
-
ImageOrientationPatientI,
|
|
300
|
-
validOrientations
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
if (!alignedPixelDataI) {
|
|
304
|
-
console.warn(
|
|
305
|
-
"This segmentation object is not in-plane with the source data. Bailing out of IO. It'd be better to render this with vtkjs. "
|
|
306
|
-
);
|
|
307
|
-
inPlane = false;
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const segmentIndex =
|
|
312
|
-
PerFrameFunctionalGroups.SegmentIdentificationSequence
|
|
313
|
-
.ReferencedSegmentNumber - 1;
|
|
314
|
-
|
|
315
|
-
let SourceImageSequence;
|
|
316
|
-
if (
|
|
317
|
-
SharedFunctionalGroupsSequence.DerivationImageSequence &&
|
|
318
|
-
SharedFunctionalGroupsSequence.DerivationImageSequence
|
|
319
|
-
.SourceImageSequence
|
|
320
|
-
) {
|
|
321
|
-
SourceImageSequence =
|
|
322
|
-
SharedFunctionalGroupsSequence.DerivationImageSequence
|
|
323
|
-
.SourceImageSequence[i];
|
|
324
|
-
} else {
|
|
325
|
-
SourceImageSequence =
|
|
326
|
-
PerFrameFunctionalGroups.DerivationImageSequence
|
|
327
|
-
.SourceImageSequence;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
const imageId = getImageIdOfSourceImage(
|
|
331
|
-
SourceImageSequence,
|
|
332
|
-
imageIds,
|
|
333
|
-
metadataProvider
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
addImageIdSpecificBrushToolState(
|
|
337
|
-
toolState,
|
|
338
|
-
imageId,
|
|
339
|
-
segmentIndex,
|
|
340
|
-
alignedPixelDataI
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (!inPlane) {
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return { toolState, segMetadata };
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* unpackPixelData - Unpacks bitpacked pixelData if the Segmentation is BINARY.
|
|
353
|
-
*
|
|
354
|
-
* @param {Object} multiframe The multiframe dataset.
|
|
355
|
-
* @return {Uint8Array} The unpacked pixelData.
|
|
356
|
-
*/
|
|
357
|
-
function unpackPixelData(multiframe) {
|
|
358
|
-
const segType = multiframe.SegmentationType;
|
|
359
|
-
|
|
360
|
-
if (segType === "BINARY") {
|
|
361
|
-
return BitArray.unpack(multiframe.PixelData);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const pixelData = new Uint8Array(multiframe.PixelData);
|
|
365
|
-
|
|
366
|
-
const max = multiframe.MaximumFractionalValue;
|
|
367
|
-
const onlyMaxAndZero =
|
|
368
|
-
pixelData.find(element => element !== 0 && element !== max) ===
|
|
369
|
-
undefined;
|
|
370
|
-
|
|
371
|
-
if (!onlyMaxAndZero) {
|
|
372
|
-
log.warn(
|
|
373
|
-
"This is a fractional segmentation, which is not currently supported."
|
|
374
|
-
);
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
log.warn(
|
|
379
|
-
"This segmentation object is actually binary... processing as such."
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
return pixelData;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* addImageIdSpecificBrushToolState - Adds brush pixel data to cornerstoneTools
|
|
387
|
-
* formatted toolState object.
|
|
388
|
-
*
|
|
389
|
-
* @param {Object} toolState The toolState object to modify
|
|
390
|
-
* @param {String} imageId The imageId of the toolState to add the data.
|
|
391
|
-
* @param {Number} segmentIndex The index of the segment data being added.
|
|
392
|
-
* @param {Ndarray} pixelData2D The pixelData in Ndarry 2D format.
|
|
393
|
-
*/
|
|
394
|
-
function addImageIdSpecificBrushToolState(
|
|
395
|
-
toolState,
|
|
396
|
-
imageId,
|
|
397
|
-
segmentIndex,
|
|
398
|
-
pixelData2D
|
|
399
|
-
) {
|
|
400
|
-
if (!toolState[imageId]) {
|
|
401
|
-
toolState[imageId] = {};
|
|
402
|
-
toolState[imageId].brush = {};
|
|
403
|
-
toolState[imageId].brush.data = [];
|
|
404
|
-
} else if (!toolState[imageId].brush) {
|
|
405
|
-
toolState[imageId].brush = {};
|
|
406
|
-
toolState[imageId].brush.data = [];
|
|
407
|
-
} else if (!toolState[imageId].brush.data) {
|
|
408
|
-
toolState[imageId].brush.data = [];
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
toolState[imageId].brush.data[segmentIndex] = {};
|
|
412
|
-
|
|
413
|
-
const brushDataI = toolState[imageId].brush.data[segmentIndex];
|
|
414
|
-
|
|
415
|
-
brushDataI.pixelData = new Uint8Array(pixelData2D.data.length);
|
|
416
|
-
|
|
417
|
-
const cToolsPixelData = brushDataI.pixelData;
|
|
418
|
-
|
|
419
|
-
for (let p = 0; p < cToolsPixelData.length; p++) {
|
|
420
|
-
if (pixelData2D.data[p]) {
|
|
421
|
-
cToolsPixelData[p] = 1;
|
|
422
|
-
} else {
|
|
423
|
-
cToolsPixelData[p] = 0;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* getImageIdOfSourceImage - Returns the Cornerstone imageId of the source image.
|
|
430
|
-
*
|
|
431
|
-
* @param {Object} SourceImageSequence Sequence describing the source image.
|
|
432
|
-
* @param {String[]} imageIds A list of imageIds.
|
|
433
|
-
* @param {Object} metadataProvider A Cornerstone metadataProvider to query
|
|
434
|
-
* metadata from imageIds.
|
|
435
|
-
* @return {String} The corresponding imageId.
|
|
436
|
-
*/
|
|
437
|
-
function getImageIdOfSourceImage(
|
|
438
|
-
SourceImageSequence,
|
|
439
|
-
imageIds,
|
|
440
|
-
metadataProvider
|
|
441
|
-
) {
|
|
442
|
-
const { ReferencedSOPInstanceUID, ReferencedFrameNumber } =
|
|
443
|
-
SourceImageSequence;
|
|
444
|
-
|
|
445
|
-
return ReferencedFrameNumber
|
|
446
|
-
? getImageIdOfReferencedFrame(
|
|
447
|
-
ReferencedSOPInstanceUID,
|
|
448
|
-
ReferencedFrameNumber,
|
|
449
|
-
imageIds,
|
|
450
|
-
metadataProvider
|
|
451
|
-
)
|
|
452
|
-
: getImageIdOfReferencedSingleFramedSOPInstance(
|
|
453
|
-
ReferencedSOPInstanceUID,
|
|
454
|
-
imageIds,
|
|
455
|
-
metadataProvider
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* getImageIdOfReferencedSingleFramedSOPInstance - Returns the imageId
|
|
461
|
-
* corresponding to the specified sopInstanceUid for single-frame images.
|
|
462
|
-
*
|
|
463
|
-
* @param {String} sopInstanceUid The sopInstanceUid of the desired image.
|
|
464
|
-
* @param {String[]} imageIds The list of imageIds.
|
|
465
|
-
* @param {Object} metadataProvider The metadataProvider to obtain sopInstanceUids
|
|
466
|
-
* from the cornerstone imageIds.
|
|
467
|
-
* @return {String} The imageId that corresponds to the sopInstanceUid.
|
|
468
|
-
*/
|
|
469
|
-
function getImageIdOfReferencedSingleFramedSOPInstance(
|
|
470
|
-
sopInstanceUid,
|
|
471
|
-
imageIds,
|
|
472
|
-
metadataProvider
|
|
473
|
-
) {
|
|
474
|
-
return imageIds.find(imageId => {
|
|
475
|
-
const sopCommonModule = metadataProvider.get(
|
|
476
|
-
"sopCommonModule",
|
|
477
|
-
imageId
|
|
478
|
-
);
|
|
479
|
-
if (!sopCommonModule) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
return sopCommonModule.sopInstanceUID === sopInstanceUid;
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* getImageIdOfReferencedFrame - Returns the imageId corresponding to the
|
|
489
|
-
* specified sopInstanceUid and frameNumber for multi-frame images.
|
|
490
|
-
*
|
|
491
|
-
* @param {String} sopInstanceUid The sopInstanceUid of the desired image.
|
|
492
|
-
* @param {Number} frameNumber The frame number.
|
|
493
|
-
* @param {String} imageIds The list of imageIds.
|
|
494
|
-
* @param {Object} metadataProvider The metadataProvider to obtain sopInstanceUids
|
|
495
|
-
* from the cornerstone imageIds.
|
|
496
|
-
* @return {String} The imageId that corresponds to the sopInstanceUid.
|
|
497
|
-
*/
|
|
498
|
-
function getImageIdOfReferencedFrame(
|
|
499
|
-
sopInstanceUid,
|
|
500
|
-
frameNumber,
|
|
501
|
-
imageIds,
|
|
502
|
-
metadataProvider
|
|
503
|
-
) {
|
|
504
|
-
const imageId = imageIds.find(imageId => {
|
|
505
|
-
const sopCommonModule = metadataProvider.get(
|
|
506
|
-
"sopCommonModule",
|
|
507
|
-
imageId
|
|
508
|
-
);
|
|
509
|
-
if (!sopCommonModule) {
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
const imageIdFrameNumber = Number(imageId.split("frame=")[1]);
|
|
514
|
-
|
|
515
|
-
return (
|
|
516
|
-
//frameNumber is zero indexed for cornerstoneWADOImageLoader image Ids.
|
|
517
|
-
sopCommonModule.sopInstanceUID === sopInstanceUid &&
|
|
518
|
-
imageIdFrameNumber === frameNumber - 1
|
|
519
|
-
);
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
return imageId;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* getValidOrientations - returns an array of valid orientations.
|
|
527
|
-
*
|
|
528
|
-
* @param iop - The row (0..2) an column (3..5) direction cosines.
|
|
529
|
-
* @return An array of valid orientations.
|
|
530
|
-
*/
|
|
531
|
-
function getValidOrientations(iop) {
|
|
532
|
-
const orientations = [];
|
|
533
|
-
|
|
534
|
-
// [0, 1, 2]: 0, 0hf, 0vf
|
|
535
|
-
// [3, 4, 5]: 90, 90hf, 90vf
|
|
536
|
-
// [6, 7]: 180, 270
|
|
537
|
-
|
|
538
|
-
orientations[0] = iop;
|
|
539
|
-
orientations[1] = flipIOP.h(iop);
|
|
540
|
-
orientations[2] = flipIOP.v(iop);
|
|
541
|
-
|
|
542
|
-
const iop90 = rotateDirectionCosinesInPlane(iop, Math.PI / 2);
|
|
543
|
-
|
|
544
|
-
orientations[3] = iop90;
|
|
545
|
-
orientations[4] = flipIOP.h(iop90);
|
|
546
|
-
orientations[5] = flipIOP.v(iop90);
|
|
547
|
-
|
|
548
|
-
orientations[6] = rotateDirectionCosinesInPlane(iop, Math.PI);
|
|
549
|
-
orientations[7] = rotateDirectionCosinesInPlane(iop, 1.5 * Math.PI);
|
|
550
|
-
|
|
551
|
-
return orientations;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* alignPixelDataWithSourceData -
|
|
556
|
-
*
|
|
557
|
-
* @param pixelData2D - The data to align.
|
|
558
|
-
* @param iop - The orientation of the image slice.
|
|
559
|
-
* @param orientations - An array of valid imageOrientationPatient values.
|
|
560
|
-
* @return The aligned pixelData.
|
|
561
|
-
*/
|
|
562
|
-
function alignPixelDataWithSourceData(pixelData2D, iop, orientations) {
|
|
563
|
-
if (compareIOP(iop, orientations[0])) {
|
|
564
|
-
//Same orientation.
|
|
565
|
-
return pixelData2D;
|
|
566
|
-
} else if (compareIOP(iop, orientations[1])) {
|
|
567
|
-
//Flipped vertically.
|
|
568
|
-
return flipMatrix2D.v(pixelData2D);
|
|
569
|
-
} else if (compareIOP(iop, orientations[2])) {
|
|
570
|
-
//Flipped horizontally.
|
|
571
|
-
return flipMatrix2D.h(pixelData2D);
|
|
572
|
-
} else if (compareIOP(iop, orientations[3])) {
|
|
573
|
-
//Rotated 90 degrees.
|
|
574
|
-
return rotateMatrix902D(pixelData2D);
|
|
575
|
-
} else if (compareIOP(iop, orientations[4])) {
|
|
576
|
-
//Rotated 90 degrees and fliped horizontally.
|
|
577
|
-
return flipMatrix2D.h(rotateMatrix902D(pixelData2D));
|
|
578
|
-
} else if (compareIOP(iop, orientations[5])) {
|
|
579
|
-
//Rotated 90 degrees and fliped vertically.
|
|
580
|
-
return flipMatrix2D.v(rotateMatrix902D(pixelData2D));
|
|
581
|
-
} else if (compareIOP(iop, orientations[6])) {
|
|
582
|
-
//Rotated 180 degrees. // TODO -> Do this more effeciently, there is a 1:1 mapping like 90 degree rotation.
|
|
583
|
-
return rotateMatrix902D(rotateMatrix902D(pixelData2D));
|
|
584
|
-
} else if (compareIOP(iop, orientations[7])) {
|
|
585
|
-
//Rotated 270 degrees. // TODO -> Do this more effeciently, there is a 1:1 mapping like 90 degree rotation.
|
|
586
|
-
return rotateMatrix902D(
|
|
587
|
-
rotateMatrix902D(rotateMatrix902D(pixelData2D))
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
const dx = 1e-5;
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* compareIOP - Returns true if iop1 and iop2 are equal
|
|
596
|
-
* within a tollerance, dx.
|
|
597
|
-
*
|
|
598
|
-
* @param iop1 - An ImageOrientationPatient array.
|
|
599
|
-
* @param iop2 - An ImageOrientationPatient array.
|
|
600
|
-
* @return True if iop1 and iop2 are equal.
|
|
601
|
-
*/
|
|
602
|
-
function compareIOP(iop1, iop2) {
|
|
603
|
-
return (
|
|
604
|
-
Math.abs(iop1[0] - iop2[0]) < dx &&
|
|
605
|
-
Math.abs(iop1[1] - iop2[1]) < dx &&
|
|
606
|
-
Math.abs(iop1[2] - iop2[2]) < dx &&
|
|
607
|
-
Math.abs(iop1[3] - iop2[3]) < dx &&
|
|
608
|
-
Math.abs(iop1[4] - iop2[4]) < dx &&
|
|
609
|
-
Math.abs(iop1[5] - iop2[5]) < dx
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
function getSegmentMetadata(multiframe) {
|
|
614
|
-
const data = [];
|
|
615
|
-
|
|
616
|
-
const segmentSequence = multiframe.SegmentSequence;
|
|
617
|
-
|
|
618
|
-
if (Array.isArray(segmentSequence)) {
|
|
619
|
-
for (let segIdx = 0; segIdx < segmentSequence.length; segIdx++) {
|
|
620
|
-
data.push(segmentSequence[segIdx]);
|
|
621
|
-
}
|
|
622
|
-
} else {
|
|
623
|
-
// Only one segment, will be stored as an object.
|
|
624
|
-
data.push(segmentSequence);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
return {
|
|
628
|
-
seriesInstanceUid:
|
|
629
|
-
multiframe.ReferencedSeriesSequence.SeriesInstanceUID,
|
|
630
|
-
data
|
|
631
|
-
};
|
|
632
|
-
}
|