@cornerstonejs/adapters 3.0.0-beta.5 → 3.0.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.
Files changed (76) hide show
  1. package/dist/esm/adapters/Cornerstone/Angle.js +61 -74
  2. package/dist/esm/adapters/Cornerstone/ArrowAnnotate.js +76 -76
  3. package/dist/esm/adapters/Cornerstone/Bidirectional.js +130 -146
  4. package/dist/esm/adapters/Cornerstone/CircleRoi.js +96 -95
  5. package/dist/esm/adapters/Cornerstone/CobbAngle.js +66 -79
  6. package/dist/esm/adapters/Cornerstone/EllipticalRoi.js +152 -152
  7. package/dist/esm/adapters/Cornerstone/FreehandRoi.js +68 -69
  8. package/dist/esm/adapters/Cornerstone/Length.js +54 -63
  9. package/dist/esm/adapters/Cornerstone/MeasurementReport.js +221 -224
  10. package/dist/esm/adapters/Cornerstone/ParametricMap.js +88 -110
  11. package/dist/esm/adapters/Cornerstone/RectangleRoi.js +72 -78
  12. package/dist/esm/adapters/Cornerstone/Segmentation.js +7 -7
  13. package/dist/esm/adapters/Cornerstone/Segmentation_3X.js +113 -104
  14. package/dist/esm/adapters/Cornerstone/Segmentation_4X.js +434 -462
  15. package/dist/esm/adapters/Cornerstone/index.js +14 -14
  16. package/dist/esm/adapters/Cornerstone3D/Angle.d.ts +2 -5
  17. package/dist/esm/adapters/Cornerstone3D/Angle.js +89 -100
  18. package/dist/esm/adapters/Cornerstone3D/ArrowAnnotate.d.ts +5 -11
  19. package/dist/esm/adapters/Cornerstone3D/ArrowAnnotate.js +106 -118
  20. package/dist/esm/adapters/Cornerstone3D/BaseAdapter3D.d.ts +43 -0
  21. package/dist/esm/adapters/Cornerstone3D/BaseAdapter3D.js +92 -0
  22. package/dist/esm/adapters/Cornerstone3D/Bidirectional.d.ts +2 -5
  23. package/dist/esm/adapters/Cornerstone3D/Bidirectional.js +118 -133
  24. package/dist/esm/adapters/Cornerstone3D/CircleROI.d.ts +2 -7
  25. package/dist/esm/adapters/Cornerstone3D/CircleROI.js +85 -85
  26. package/dist/esm/adapters/Cornerstone3D/CobbAngle.d.ts +2 -5
  27. package/dist/esm/adapters/Cornerstone3D/CobbAngle.js +93 -104
  28. package/dist/esm/adapters/Cornerstone3D/CodingScheme.js +5 -5
  29. package/dist/esm/adapters/Cornerstone3D/EllipticalROI.d.ts +2 -7
  30. package/dist/esm/adapters/Cornerstone3D/EllipticalROI.js +148 -149
  31. package/dist/esm/adapters/Cornerstone3D/KeyImage.d.ts +24 -0
  32. package/dist/esm/adapters/Cornerstone3D/KeyImage.js +49 -0
  33. package/dist/esm/adapters/Cornerstone3D/Length.d.ts +2 -10
  34. package/dist/esm/adapters/Cornerstone3D/Length.js +83 -93
  35. package/dist/esm/adapters/Cornerstone3D/MeasurementReport.d.ts +23 -4
  36. package/dist/esm/adapters/Cornerstone3D/MeasurementReport.js +259 -240
  37. package/dist/esm/adapters/Cornerstone3D/ParametricMap/generateToolState.js +8 -4
  38. package/dist/esm/adapters/Cornerstone3D/PlanarFreehandROI.d.ts +3 -5
  39. package/dist/esm/adapters/Cornerstone3D/PlanarFreehandROI.js +99 -115
  40. package/dist/esm/adapters/Cornerstone3D/Probe.d.ts +4 -17
  41. package/dist/esm/adapters/Cornerstone3D/Probe.js +38 -79
  42. package/dist/esm/adapters/Cornerstone3D/RTStruct/RTSS.js +60 -52
  43. package/dist/esm/adapters/Cornerstone3D/RTStruct/index.js +3 -1
  44. package/dist/esm/adapters/Cornerstone3D/RTStruct/utilities/getPatientModule.js +5 -5
  45. package/dist/esm/adapters/Cornerstone3D/RTStruct/utilities/getReferencedFrameOfReferenceSequence.js +14 -10
  46. package/dist/esm/adapters/Cornerstone3D/RTStruct/utilities/getReferencedSeriesSequence.js +17 -11
  47. package/dist/esm/adapters/Cornerstone3D/RTStruct/utilities/getStructureSetModule.js +3 -1
  48. package/dist/esm/adapters/Cornerstone3D/RectangleROI.d.ts +2 -5
  49. package/dist/esm/adapters/Cornerstone3D/RectangleROI.js +73 -85
  50. package/dist/esm/adapters/Cornerstone3D/Segmentation/generateLabelMaps2DFrom3D.js +15 -13
  51. package/dist/esm/adapters/Cornerstone3D/Segmentation/generateSegmentation.js +15 -9
  52. package/dist/esm/adapters/Cornerstone3D/Segmentation/generateToolState.js +10 -10
  53. package/dist/esm/adapters/Cornerstone3D/Segmentation/labelmapImagesFromBuffer.js +134 -151
  54. package/dist/esm/adapters/Cornerstone3D/UltrasoundDirectional.d.ts +2 -5
  55. package/dist/esm/adapters/Cornerstone3D/UltrasoundDirectional.js +68 -84
  56. package/dist/esm/adapters/Cornerstone3D/index.d.ts +4 -0
  57. package/dist/esm/adapters/Cornerstone3D/index.js +21 -17
  58. package/dist/esm/adapters/VTKjs/Segmentation.js +66 -72
  59. package/dist/esm/adapters/VTKjs/index.js +2 -2
  60. package/dist/esm/adapters/helpers/checkIfPerpendicular.js +2 -2
  61. package/dist/esm/adapters/helpers/checkOrientation.js +8 -8
  62. package/dist/esm/adapters/helpers/codeMeaningEquals.js +2 -2
  63. package/dist/esm/adapters/helpers/compareArrays.js +4 -2
  64. package/dist/esm/adapters/helpers/downloadDICOMData.js +6 -4
  65. package/dist/esm/adapters/helpers/getDatasetsFromImages.js +20 -18
  66. package/dist/esm/adapters/helpers/graphicTypeEquals.js +2 -2
  67. package/dist/esm/adapters/helpers/toArray.js +1 -3
  68. package/dist/esm/adapters/index.d.ts +2 -0
  69. package/dist/esm/adapters/index.js +6 -5
  70. package/dist/esm/node_modules/@babel/runtime/helpers/esm/defineProperty.js +18 -0
  71. package/dist/esm/node_modules/@babel/runtime/helpers/esm/toPrimitive.js +14 -0
  72. package/dist/esm/node_modules/@babel/runtime/helpers/esm/toPropertyKey.js +9 -0
  73. package/dist/esm/node_modules/@babel/runtime/helpers/esm/typeof.js +11 -0
  74. package/package.json +4 -4
  75. package/dist/esm/_virtual/_rollupPluginBabelHelpers.js +0 -493
  76. package/dist/esm/adapters/Cornerstone3D/isValidCornerstoneTrackingIdentifier.js +0 -18
@@ -1,24 +1,31 @@
1
- import { asyncToGenerator as _asyncToGenerator, regeneratorRuntime as _regeneratorRuntime, toConsumableArray as _toConsumableArray, createForOfIteratorHelper as _createForOfIteratorHelper, slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
2
- import { utilities, data, normalizers, derivations, log } from 'dcmjs';
1
+ import { utilities, log, data, normalizers, derivations } from 'dcmjs';
3
2
  import ndarray from 'ndarray';
4
3
  import getDatasetsFromImages from '../helpers/getDatasetsFromImages.js';
5
4
  import checkOrientation from '../helpers/checkOrientation.js';
6
5
  import compareArrays from '../helpers/compareArrays.js';
7
6
  import Events from '../enums/Events.js';
8
7
 
9
- var _utilities$orientatio = utilities.orientation,
10
- rotateDirectionCosinesInPlane = _utilities$orientatio.rotateDirectionCosinesInPlane,
11
- flipIOP = _utilities$orientatio.flipImageOrientationPatient,
12
- flipMatrix2D = _utilities$orientatio.flipMatrix2D,
13
- rotateMatrix902D = _utilities$orientatio.rotateMatrix902D;
14
- var BitArray = data.BitArray,
15
- DicomMessage = data.DicomMessage,
16
- DicomMetaDictionary = data.DicomMetaDictionary;
17
- var Normalizer = normalizers.Normalizer;
18
- var SegmentationDerivation = derivations.Segmentation;
19
- var _utilities$compressio = utilities.compression,
20
- encode = _utilities$compressio.encode,
21
- decode = _utilities$compressio.decode;
8
+ const {
9
+ rotateDirectionCosinesInPlane,
10
+ flipImageOrientationPatient: flipIOP,
11
+ flipMatrix2D,
12
+ rotateMatrix902D
13
+ } = utilities.orientation;
14
+ const {
15
+ BitArray,
16
+ DicomMessage,
17
+ DicomMetaDictionary
18
+ } = data;
19
+ const {
20
+ Normalizer
21
+ } = normalizers;
22
+ const {
23
+ Segmentation: SegmentationDerivation
24
+ } = derivations;
25
+ const {
26
+ encode,
27
+ decode
28
+ } = utilities.compression;
22
29
 
23
30
  /**
24
31
  *
@@ -27,7 +34,7 @@ var _utilities$compressio = utilities.compression,
27
34
  * @property {Object[]} segments - The cornerstoneTools segment metadata that corresponds to the
28
35
  * seriesInstanceUid.
29
36
  */
30
- var generateSegmentationDefaultOptions = {
37
+ const generateSegmentationDefaultOptions = {
31
38
  includeSliceSpacing: true,
32
39
  rleEncode: false
33
40
  };
@@ -43,9 +50,9 @@ var generateSegmentationDefaultOptions = {
43
50
  * @returns {Blob}
44
51
  */
45
52
  function generateSegmentation(images, inputLabelmaps3D) {
46
- var userOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
47
- var isMultiframe = images[0].imageId.includes("?frame");
48
- var segmentation = _createSegFromImages(images, isMultiframe, userOptions);
53
+ let userOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
54
+ const isMultiframe = images[0].imageId.includes("?frame");
55
+ const segmentation = _createSegFromImages(images, isMultiframe, userOptions);
49
56
  return fillSegmentation(segmentation, inputLabelmaps3D, userOptions);
50
57
  }
51
58
 
@@ -59,63 +66,63 @@ function generateSegmentation(images, inputLabelmaps3D) {
59
66
  * @returns {object} The filled segmentation object.
60
67
  */
61
68
  function fillSegmentation(segmentation, inputLabelmaps3D) {
62
- var userOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
63
- var options = Object.assign({}, generateSegmentationDefaultOptions, userOptions);
69
+ let userOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
70
+ const options = Object.assign({}, generateSegmentationDefaultOptions, userOptions);
64
71
 
65
72
  // Use another variable so we don't redefine labelmaps3D.
66
- var labelmaps3D = Array.isArray(inputLabelmaps3D) ? inputLabelmaps3D : [inputLabelmaps3D];
67
- var numberOfFrames = 0;
68
- var referencedFramesPerLabelmap = [];
69
- var _loop = function _loop() {
70
- var labelmap3D = labelmaps3D[labelmapIndex];
71
- var labelmaps2D = labelmap3D.labelmaps2D,
72
- metadata = labelmap3D.metadata;
73
- var referencedFramesPerSegment = [];
74
- for (var i = 1; i < metadata.length; i++) {
73
+ const labelmaps3D = Array.isArray(inputLabelmaps3D) ? inputLabelmaps3D : [inputLabelmaps3D];
74
+ let numberOfFrames = 0;
75
+ const referencedFramesPerLabelmap = [];
76
+ for (let labelmapIndex = 0; labelmapIndex < labelmaps3D.length; labelmapIndex++) {
77
+ const labelmap3D = labelmaps3D[labelmapIndex];
78
+ const {
79
+ labelmaps2D,
80
+ metadata
81
+ } = labelmap3D;
82
+ const referencedFramesPerSegment = [];
83
+ for (let i = 1; i < metadata.length; i++) {
75
84
  if (metadata[i]) {
76
85
  referencedFramesPerSegment[i] = [];
77
86
  }
78
87
  }
79
- var _loop2 = function _loop2(_i) {
80
- var labelmap2D = labelmaps2D[_i];
81
- if (labelmaps2D[_i]) {
82
- var segmentsOnLabelmap = labelmap2D.segmentsOnLabelmap;
83
- segmentsOnLabelmap.forEach(function (segmentIndex) {
88
+ for (let i = 0; i < labelmaps2D.length; i++) {
89
+ const labelmap2D = labelmaps2D[i];
90
+ if (labelmaps2D[i]) {
91
+ const {
92
+ segmentsOnLabelmap
93
+ } = labelmap2D;
94
+ segmentsOnLabelmap.forEach(segmentIndex => {
84
95
  if (segmentIndex !== 0) {
85
- referencedFramesPerSegment[segmentIndex].push(_i);
96
+ referencedFramesPerSegment[segmentIndex].push(i);
86
97
  numberOfFrames++;
87
98
  }
88
99
  });
89
100
  }
90
- };
91
- for (var _i = 0; _i < labelmaps2D.length; _i++) {
92
- _loop2(_i);
93
101
  }
94
102
  referencedFramesPerLabelmap[labelmapIndex] = referencedFramesPerSegment;
95
- };
96
- for (var labelmapIndex = 0; labelmapIndex < labelmaps3D.length; labelmapIndex++) {
97
- _loop();
98
103
  }
99
104
  segmentation.setNumberOfFrames(numberOfFrames);
100
- for (var _labelmapIndex = 0; _labelmapIndex < labelmaps3D.length; _labelmapIndex++) {
101
- var referencedFramesPerSegment = referencedFramesPerLabelmap[_labelmapIndex];
102
- var labelmap3D = labelmaps3D[_labelmapIndex];
103
- var metadata = labelmap3D.metadata;
104
- for (var segmentIndex = 1; segmentIndex < referencedFramesPerSegment.length; segmentIndex++) {
105
- var referencedFrameIndicies = referencedFramesPerSegment[segmentIndex];
105
+ for (let labelmapIndex = 0; labelmapIndex < labelmaps3D.length; labelmapIndex++) {
106
+ const referencedFramesPerSegment = referencedFramesPerLabelmap[labelmapIndex];
107
+ const labelmap3D = labelmaps3D[labelmapIndex];
108
+ const {
109
+ metadata
110
+ } = labelmap3D;
111
+ for (let segmentIndex = 1; segmentIndex < referencedFramesPerSegment.length; segmentIndex++) {
112
+ const referencedFrameIndicies = referencedFramesPerSegment[segmentIndex];
106
113
  if (referencedFrameIndicies) {
107
114
  // Frame numbers start from 1.
108
- var referencedFrameNumbers = referencedFrameIndicies.map(function (element) {
115
+ const referencedFrameNumbers = referencedFrameIndicies.map(element => {
109
116
  return element + 1;
110
117
  });
111
- var segmentMetadata = metadata[segmentIndex];
112
- var labelmaps = _getLabelmapsFromReferencedFrameIndicies(labelmap3D, referencedFrameIndicies);
118
+ const segmentMetadata = metadata[segmentIndex];
119
+ const labelmaps = _getLabelmapsFromReferencedFrameIndicies(labelmap3D, referencedFrameIndicies);
113
120
  segmentation.addSegmentFromLabelmap(segmentMetadata, labelmaps, segmentIndex, referencedFrameNumbers);
114
121
  }
115
122
  }
116
123
  }
117
124
  if (options.rleEncode) {
118
- var rleEncodedFrames = encode(segmentation.dataset.PixelData, numberOfFrames, segmentation.dataset.Rows, segmentation.dataset.Columns);
125
+ const rleEncodedFrames = encode(segmentation.dataset.PixelData, numberOfFrames, segmentation.dataset.Rows, segmentation.dataset.Columns);
119
126
 
120
127
  // Must use fractional now to RLE encode, as the DICOM standard only allows BitStored && BitsAllocated
121
128
  // to be 1 for BINARY. This is not ideal and there should be a better format for compression in this manner
@@ -142,10 +149,12 @@ function fillSegmentation(segmentation, inputLabelmaps3D) {
142
149
  return segmentation;
143
150
  }
144
151
  function _getLabelmapsFromReferencedFrameIndicies(labelmap3D, referencedFrameIndicies) {
145
- var labelmaps2D = labelmap3D.labelmaps2D;
146
- var labelmaps = [];
147
- for (var i = 0; i < referencedFrameIndicies.length; i++) {
148
- var frame = referencedFrameIndicies[i];
152
+ const {
153
+ labelmaps2D
154
+ } = labelmap3D;
155
+ const labelmaps = [];
156
+ for (let i = 0; i < referencedFrameIndicies.length; i++) {
157
+ const frame = referencedFrameIndicies[i];
149
158
  labelmaps.push(labelmaps2D[frame].pixelData);
150
159
  }
151
160
  return labelmaps;
@@ -159,7 +168,7 @@ function _getLabelmapsFromReferencedFrameIndicies(labelmap3D, referencedFrameInd
159
168
  * @returns {Object} The Seg derived dataSet.
160
169
  */
161
170
  function _createSegFromImages(images, isMultiframe, options) {
162
- var multiframe = getDatasetsFromImages(images, isMultiframe);
171
+ const multiframe = getDatasetsFromImages(images, isMultiframe);
163
172
  return new SegmentationDerivation([multiframe], options);
164
173
  }
165
174
 
@@ -178,8 +187,129 @@ function _createSegFromImages(images, isMultiframe, options) {
178
187
  * @return {[][][]} 3D list containing the track of segments per frame for each labelMap
179
188
  * (available only for the overlapping case).
180
189
  */
181
- function generateToolState(_x, _x2, _x3, _x4) {
182
- return _generateToolState.apply(this, arguments);
190
+ async function generateToolState(referencedImageIds, arrayBuffer, metadataProvider, options) {
191
+ const {
192
+ skipOverlapping = false,
193
+ tolerance = 1e-3,
194
+ TypedArrayConstructor = Uint8Array,
195
+ maxBytesPerChunk = 199000000,
196
+ eventTarget = null,
197
+ triggerEvent = null
198
+ } = options;
199
+ const dicomData = DicomMessage.readFile(arrayBuffer);
200
+ const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
201
+ dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
202
+ const multiframe = Normalizer.normalizeToDataset([dataset]);
203
+ const imagePlaneModule = metadataProvider.get("imagePlaneModule", referencedImageIds[0]);
204
+ const generalSeriesModule = metadataProvider.get("generalSeriesModule", referencedImageIds[0]);
205
+ const SeriesInstanceUID = generalSeriesModule.seriesInstanceUID;
206
+ if (!imagePlaneModule) {
207
+ console.warn("Insufficient metadata, imagePlaneModule missing.");
208
+ }
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
+
211
+ // Get IOP from ref series, compute supported orientations:
212
+ const validOrientations = getValidOrientations(ImageOrientationPatient);
213
+ const sliceLength = multiframe.Columns * multiframe.Rows;
214
+ const segMetadata = getSegmentMetadata(multiframe, SeriesInstanceUID);
215
+ const TransferSyntaxUID = multiframe._meta.TransferSyntaxUID.Value[0];
216
+ let pixelData;
217
+ let pixelDataChunks;
218
+ if (TransferSyntaxUID === "1.2.840.10008.1.2.5") {
219
+ const rleEncodedFrames = Array.isArray(multiframe.PixelData) ? multiframe.PixelData : [multiframe.PixelData];
220
+ pixelData = decode(rleEncodedFrames, multiframe.Rows, multiframe.Columns);
221
+ if (multiframe.BitsStored === 1) {
222
+ console.warn("No implementation for rle + bitbacking.");
223
+ return;
224
+ }
225
+
226
+ // Todo: need to test this with rle data
227
+ pixelDataChunks = [pixelData];
228
+ } else {
229
+ pixelDataChunks = unpackPixelData(multiframe, {
230
+ maxBytesPerChunk
231
+ });
232
+ if (!pixelDataChunks) {
233
+ throw new Error("Fractional segmentations are not yet supported");
234
+ }
235
+ }
236
+ const orientation = checkOrientation(multiframe, validOrientations, [imagePlaneModule.rows, imagePlaneModule.columns, referencedImageIds.length], tolerance);
237
+
238
+ // Pre-compute the sop UID to imageId index map so that in the for loop
239
+ // we don't have to call metadataProvider.get() for each imageId over
240
+ // and over again.
241
+ const sopUIDImageIdIndexMap = referencedImageIds.reduce((acc, imageId) => {
242
+ const {
243
+ sopInstanceUID
244
+ } = metadataProvider.get("generalImageModule", imageId);
245
+ acc[sopInstanceUID] = imageId;
246
+ return acc;
247
+ }, {});
248
+ let overlapping = false;
249
+ if (!skipOverlapping) {
250
+ overlapping = checkSEGsOverlapping(pixelDataChunks, multiframe, referencedImageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, sopUIDImageIdIndexMap);
251
+ }
252
+ let insertFunction;
253
+ switch (orientation) {
254
+ case "Planar":
255
+ if (overlapping) {
256
+ insertFunction = insertOverlappingPixelDataPlanar;
257
+ } else {
258
+ insertFunction = insertPixelDataPlanar;
259
+ }
260
+ break;
261
+ case "Perpendicular":
262
+ //insertFunction = insertPixelDataPerpendicular;
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
+ }
267
+
268
+ /* if SEGs are overlapping:
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
+
274
+ const segmentsOnFrameArray = [];
275
+ segmentsOnFrameArray[0] = [];
276
+ const segmentsOnFrame = [];
277
+ const arrayBufferLength = sliceLength * referencedImageIds.length * TypedArrayConstructor.BYTES_PER_ELEMENT;
278
+ const labelmapBufferArray = [];
279
+ labelmapBufferArray[0] = new ArrayBuffer(arrayBufferLength);
280
+
281
+ // Pre-compute the indices and metadata so that we don't have to call
282
+ // a function for each imageId in the for loop.
283
+ const imageIdMaps = referencedImageIds.reduce((acc, curr, index) => {
284
+ acc.indices[curr] = index;
285
+ acc.metadata[curr] = metadataProvider.get("instance", curr);
286
+ return acc;
287
+ }, {
288
+ indices: {},
289
+ metadata: {}
290
+ });
291
+
292
+ // This is the centroid calculation for each segment Index, the data structure
293
+ // is a Map with key = segmentIndex and value = {imageIdIndex: centroid, ...}
294
+ // later on we will use this data structure to calculate the centroid of the
295
+ // segment in the labelmapBuffer
296
+ const segmentsPixelIndices = new Map();
297
+ const overlappingSegments = await insertFunction(segmentsOnFrame, segmentsOnFrameArray, labelmapBufferArray, pixelDataChunks, multiframe, referencedImageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, segmentsPixelIndices, sopUIDImageIdIndexMap, imageIdMaps, eventTarget, triggerEvent);
298
+
299
+ // calculate the centroid of each segment
300
+ const centroidXYZ = new Map();
301
+ segmentsPixelIndices.forEach((imageIdIndexBufferIndex, segmentIndex) => {
302
+ const centroids = calculateCentroid(imageIdIndexBufferIndex, multiframe, metadataProvider, referencedImageIds);
303
+ centroidXYZ.set(segmentIndex, centroids);
304
+ });
305
+ return {
306
+ labelmapBufferArray,
307
+ segMetadata,
308
+ segmentsOnFrame,
309
+ segmentsOnFrameArray,
310
+ centroids: centroidXYZ,
311
+ overlappingSegments
312
+ };
183
313
  }
184
314
 
185
315
  // function insertPixelDataPerpendicular(
@@ -364,154 +494,27 @@ function generateToolState(_x, _x2, _x3, _x4) {
364
494
  *
365
495
  * @returns {String} Returns the imageId
366
496
  */
367
- function _generateToolState() {
368
- _generateToolState = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(referencedImageIds, arrayBuffer, metadataProvider, options) {
369
- var _options$skipOverlapp, skipOverlapping, _options$tolerance, tolerance, _options$TypedArrayCo, TypedArrayConstructor, _options$maxBytesPerC, maxBytesPerChunk, _options$eventTarget, eventTarget, _options$triggerEvent, triggerEvent, dicomData, dataset, multiframe, imagePlaneModule, generalSeriesModule, SeriesInstanceUID, ImageOrientationPatient, validOrientations, sliceLength, segMetadata, TransferSyntaxUID, pixelData, pixelDataChunks, rleEncodedFrames, orientation, sopUIDImageIdIndexMap, overlapping, insertFunction, segmentsOnFrameArray, segmentsOnFrame, arrayBufferLength, labelmapBufferArray, imageIdMaps, segmentsPixelIndices, overlappingSegments, centroidXYZ;
370
- return _regeneratorRuntime().wrap(function _callee$(_context) {
371
- while (1) switch (_context.prev = _context.next) {
372
- case 0:
373
- _options$skipOverlapp = options.skipOverlapping, skipOverlapping = _options$skipOverlapp === void 0 ? false : _options$skipOverlapp, _options$tolerance = options.tolerance, tolerance = _options$tolerance === void 0 ? 1e-3 : _options$tolerance, _options$TypedArrayCo = options.TypedArrayConstructor, TypedArrayConstructor = _options$TypedArrayCo === void 0 ? Uint8Array : _options$TypedArrayCo, _options$maxBytesPerC = options.maxBytesPerChunk, maxBytesPerChunk = _options$maxBytesPerC === void 0 ? 199000000 : _options$maxBytesPerC, _options$eventTarget = options.eventTarget, eventTarget = _options$eventTarget === void 0 ? null : _options$eventTarget, _options$triggerEvent = options.triggerEvent, triggerEvent = _options$triggerEvent === void 0 ? null : _options$triggerEvent;
374
- dicomData = DicomMessage.readFile(arrayBuffer);
375
- dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
376
- dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
377
- multiframe = Normalizer.normalizeToDataset([dataset]);
378
- imagePlaneModule = metadataProvider.get("imagePlaneModule", referencedImageIds[0]);
379
- generalSeriesModule = metadataProvider.get("generalSeriesModule", referencedImageIds[0]);
380
- SeriesInstanceUID = generalSeriesModule.seriesInstanceUID;
381
- if (!imagePlaneModule) {
382
- console.warn("Insufficient metadata, imagePlaneModule missing.");
383
- }
384
- ImageOrientationPatient = Array.isArray(imagePlaneModule.rowCosines) ? [].concat(_toConsumableArray(imagePlaneModule.rowCosines), _toConsumableArray(imagePlaneModule.columnCosines)) : [imagePlaneModule.rowCosines.x, imagePlaneModule.rowCosines.y, imagePlaneModule.rowCosines.z, imagePlaneModule.columnCosines.x, imagePlaneModule.columnCosines.y, imagePlaneModule.columnCosines.z]; // Get IOP from ref series, compute supported orientations:
385
- validOrientations = getValidOrientations(ImageOrientationPatient);
386
- sliceLength = multiframe.Columns * multiframe.Rows;
387
- segMetadata = getSegmentMetadata(multiframe, SeriesInstanceUID);
388
- TransferSyntaxUID = multiframe._meta.TransferSyntaxUID.Value[0];
389
- if (!(TransferSyntaxUID === "1.2.840.10008.1.2.5")) {
390
- _context.next = 23;
391
- break;
392
- }
393
- rleEncodedFrames = Array.isArray(multiframe.PixelData) ? multiframe.PixelData : [multiframe.PixelData];
394
- pixelData = decode(rleEncodedFrames, multiframe.Rows, multiframe.Columns);
395
- if (!(multiframe.BitsStored === 1)) {
396
- _context.next = 20;
397
- break;
398
- }
399
- console.warn("No implementation for rle + bitbacking.");
400
- return _context.abrupt("return");
401
- case 20:
402
- // Todo: need to test this with rle data
403
- pixelDataChunks = [pixelData];
404
- _context.next = 26;
405
- break;
406
- case 23:
407
- pixelDataChunks = unpackPixelData(multiframe, {
408
- maxBytesPerChunk: maxBytesPerChunk
409
- });
410
- if (pixelDataChunks) {
411
- _context.next = 26;
412
- break;
413
- }
414
- throw new Error("Fractional segmentations are not yet supported");
415
- case 26:
416
- orientation = checkOrientation(multiframe, validOrientations, [imagePlaneModule.rows, imagePlaneModule.columns, referencedImageIds.length], tolerance); // Pre-compute the sop UID to imageId index map so that in the for loop
417
- // we don't have to call metadataProvider.get() for each imageId over
418
- // and over again.
419
- sopUIDImageIdIndexMap = referencedImageIds.reduce(function (acc, imageId) {
420
- var _metadataProvider$get = metadataProvider.get("generalImageModule", imageId),
421
- sopInstanceUID = _metadataProvider$get.sopInstanceUID;
422
- acc[sopInstanceUID] = imageId;
423
- return acc;
424
- }, {});
425
- overlapping = false;
426
- if (!skipOverlapping) {
427
- overlapping = checkSEGsOverlapping(pixelDataChunks, multiframe, referencedImageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, sopUIDImageIdIndexMap);
428
- }
429
- _context.t0 = orientation;
430
- _context.next = _context.t0 === "Planar" ? 33 : _context.t0 === "Perpendicular" ? 35 : _context.t0 === "Oblique" ? 36 : 37;
431
- break;
432
- case 33:
433
- if (overlapping) {
434
- insertFunction = insertOverlappingPixelDataPlanar;
435
- } else {
436
- insertFunction = insertPixelDataPlanar;
437
- }
438
- return _context.abrupt("break", 37);
439
- case 35:
440
- throw new Error("Segmentations orthogonal to the acquisition plane of the source data are not yet supported.");
441
- case 36:
442
- throw new Error("Segmentations oblique to the acquisition plane of the source data are not yet supported.");
443
- case 37:
444
- /* if SEGs are overlapping:
445
- 1) the labelmapBuffer will contain M volumes which have non-overlapping segments;
446
- 2) segmentsOnFrame will have M * numberOfFrames values to track in which labelMap are the segments;
447
- 3) insertFunction will return the number of LabelMaps
448
- 4) generateToolState return is an array*/
449
- segmentsOnFrameArray = [];
450
- segmentsOnFrameArray[0] = [];
451
- segmentsOnFrame = [];
452
- arrayBufferLength = sliceLength * referencedImageIds.length * TypedArrayConstructor.BYTES_PER_ELEMENT;
453
- labelmapBufferArray = [];
454
- labelmapBufferArray[0] = new ArrayBuffer(arrayBufferLength);
455
-
456
- // Pre-compute the indices and metadata so that we don't have to call
457
- // a function for each imageId in the for loop.
458
- imageIdMaps = referencedImageIds.reduce(function (acc, curr, index) {
459
- acc.indices[curr] = index;
460
- acc.metadata[curr] = metadataProvider.get("instance", curr);
461
- return acc;
462
- }, {
463
- indices: {},
464
- metadata: {}
465
- }); // This is the centroid calculation for each segment Index, the data structure
466
- // is a Map with key = segmentIndex and value = {imageIdIndex: centroid, ...}
467
- // later on we will use this data structure to calculate the centroid of the
468
- // segment in the labelmapBuffer
469
- segmentsPixelIndices = new Map();
470
- _context.next = 47;
471
- return insertFunction(segmentsOnFrame, segmentsOnFrameArray, labelmapBufferArray, pixelDataChunks, multiframe, referencedImageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, segmentsPixelIndices, sopUIDImageIdIndexMap, imageIdMaps, eventTarget, triggerEvent);
472
- case 47:
473
- overlappingSegments = _context.sent;
474
- // calculate the centroid of each segment
475
- centroidXYZ = new Map();
476
- segmentsPixelIndices.forEach(function (imageIdIndexBufferIndex, segmentIndex) {
477
- var centroids = calculateCentroid(imageIdIndexBufferIndex, multiframe, metadataProvider, referencedImageIds);
478
- centroidXYZ.set(segmentIndex, centroids);
479
- });
480
- return _context.abrupt("return", {
481
- labelmapBufferArray: labelmapBufferArray,
482
- segMetadata: segMetadata,
483
- segmentsOnFrame: segmentsOnFrame,
484
- segmentsOnFrameArray: segmentsOnFrameArray,
485
- centroids: centroidXYZ,
486
- overlappingSegments: overlappingSegments
487
- });
488
- case 51:
489
- case "end":
490
- return _context.stop();
491
- }
492
- }, _callee);
493
- }));
494
- return _generateToolState.apply(this, arguments);
495
- }
496
497
  function findReferenceSourceImageId(multiframe, frameSegment, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap) {
497
- var imageId = undefined;
498
+ let imageId = undefined;
498
499
  if (!multiframe) {
499
500
  return imageId;
500
501
  }
501
- var FrameOfReferenceUID = multiframe.FrameOfReferenceUID,
502
- PerFrameFunctionalGroupsSequence = multiframe.PerFrameFunctionalGroupsSequence,
503
- SourceImageSequence = multiframe.SourceImageSequence,
504
- ReferencedSeriesSequence = multiframe.ReferencedSeriesSequence;
502
+ const {
503
+ FrameOfReferenceUID,
504
+ PerFrameFunctionalGroupsSequence,
505
+ SourceImageSequence,
506
+ ReferencedSeriesSequence
507
+ } = multiframe;
505
508
  if (!PerFrameFunctionalGroupsSequence || PerFrameFunctionalGroupsSequence.length === 0) {
506
509
  return imageId;
507
510
  }
508
- var PerFrameFunctionalGroup = PerFrameFunctionalGroupsSequence[frameSegment];
511
+ const PerFrameFunctionalGroup = PerFrameFunctionalGroupsSequence[frameSegment];
509
512
  if (!PerFrameFunctionalGroup) {
510
513
  return imageId;
511
514
  }
512
- var frameSourceImageSequence = undefined;
515
+ let frameSourceImageSequence = undefined;
513
516
  if (PerFrameFunctionalGroup.DerivationImageSequence) {
514
- var DerivationImageSequence = PerFrameFunctionalGroup.DerivationImageSequence;
517
+ let DerivationImageSequence = PerFrameFunctionalGroup.DerivationImageSequence;
515
518
  if (Array.isArray(DerivationImageSequence)) {
516
519
  if (DerivationImageSequence.length !== 0) {
517
520
  DerivationImageSequence = DerivationImageSequence[0];
@@ -537,8 +540,8 @@ function findReferenceSourceImageId(multiframe, frameSegment, imageIds, metadata
537
540
  imageId = getImageIdOfSourceImageBySourceImageSequence(frameSourceImageSequence, sopUIDImageIdIndexMap);
538
541
  }
539
542
  if (imageId === undefined && ReferencedSeriesSequence) {
540
- var referencedSeriesSequence = Array.isArray(ReferencedSeriesSequence) ? ReferencedSeriesSequence[0] : ReferencedSeriesSequence;
541
- var ReferencedSeriesInstanceUID = referencedSeriesSequence.SeriesInstanceUID;
543
+ const referencedSeriesSequence = Array.isArray(ReferencedSeriesSequence) ? ReferencedSeriesSequence[0] : ReferencedSeriesSequence;
544
+ const ReferencedSeriesInstanceUID = referencedSeriesSequence.SeriesInstanceUID;
542
545
  imageId = getImageIdOfSourceImagebyGeometry(ReferencedSeriesInstanceUID, FrameOfReferenceUID, PerFrameFunctionalGroup, imageIds, metadataProvider, tolerance);
543
546
  }
544
547
  return imageId;
@@ -550,18 +553,20 @@ function findReferenceSourceImageId(multiframe, frameSegment, imageIds, metadata
550
553
  */
551
554
 
552
555
  function checkSEGsOverlapping(pixelData, multiframe, imageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, sopUIDImageIdIndexMap) {
553
- var SharedFunctionalGroupsSequence = multiframe.SharedFunctionalGroupsSequence,
554
- PerFrameFunctionalGroupsSequence = multiframe.PerFrameFunctionalGroupsSequence,
555
- SegmentSequence = multiframe.SegmentSequence,
556
- Rows = multiframe.Rows,
557
- Columns = multiframe.Columns;
558
- var numberOfSegs = SegmentSequence.length;
556
+ const {
557
+ SharedFunctionalGroupsSequence,
558
+ PerFrameFunctionalGroupsSequence,
559
+ SegmentSequence,
560
+ Rows,
561
+ Columns
562
+ } = multiframe;
563
+ let numberOfSegs = SegmentSequence.length;
559
564
  if (numberOfSegs < 2) {
560
565
  return false;
561
566
  }
562
- var sharedImageOrientationPatient = SharedFunctionalGroupsSequence.PlaneOrientationSequence ? SharedFunctionalGroupsSequence.PlaneOrientationSequence.ImageOrientationPatient : undefined;
563
- var sliceLength = Columns * Rows;
564
- var groupsLen = PerFrameFunctionalGroupsSequence.length;
567
+ const sharedImageOrientationPatient = SharedFunctionalGroupsSequence.PlaneOrientationSequence ? SharedFunctionalGroupsSequence.PlaneOrientationSequence.ImageOrientationPatient : undefined;
568
+ const sliceLength = Columns * Rows;
569
+ const groupsLen = PerFrameFunctionalGroupsSequence.length;
565
570
 
566
571
  /** sort groupsLen to have all the segments for each frame in an array
567
572
  * frame 2 : 1, 2
@@ -569,91 +574,76 @@ function checkSEGsOverlapping(pixelData, multiframe, imageIds, validOrientations
569
574
  * frame 5 : 4
570
575
  */
571
576
 
572
- var frameSegmentsMapping = new Map();
573
- var _loop3 = function _loop3() {
574
- var segmentIndex = getSegmentIndex(multiframe, frameSegment);
575
- if (segmentIndex === undefined) {
576
- console.warn("Could not retrieve the segment index for frame segment " + frameSegment + ", skipping this frame.");
577
- return 0; // continue
578
- }
579
- var imageId = findReferenceSourceImageId(multiframe, frameSegment, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
580
- if (!imageId) {
581
- console.warn("Image not present in stack, can't import frame : " + frameSegment + ".");
582
- return 0; // continue
583
- }
584
- var imageIdIndex = imageIds.findIndex(function (element) {
585
- return element === imageId;
586
- });
587
- if (frameSegmentsMapping.has(imageIdIndex)) {
588
- var segmentArray = frameSegmentsMapping.get(imageIdIndex);
589
- if (!segmentArray.includes(frameSegment)) {
590
- segmentArray.push(frameSegment);
591
- frameSegmentsMapping.set(imageIdIndex, segmentArray);
592
- }
593
- } else {
594
- frameSegmentsMapping.set(imageIdIndex, [frameSegment]);
577
+ let frameSegmentsMapping = new Map();
578
+ for (let frameSegment = 0; frameSegment < groupsLen; ++frameSegment) {
579
+ const segmentIndex = getSegmentIndex(multiframe, frameSegment);
580
+ if (segmentIndex === undefined) {
581
+ console.warn("Could not retrieve the segment index for frame segment " + frameSegment + ", skipping this frame.");
582
+ continue;
583
+ }
584
+ const imageId = findReferenceSourceImageId(multiframe, frameSegment, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
585
+ if (!imageId) {
586
+ console.warn("Image not present in stack, can't import frame : " + frameSegment + ".");
587
+ continue;
588
+ }
589
+ const imageIdIndex = imageIds.findIndex(element => element === imageId);
590
+ if (frameSegmentsMapping.has(imageIdIndex)) {
591
+ let segmentArray = frameSegmentsMapping.get(imageIdIndex);
592
+ if (!segmentArray.includes(frameSegment)) {
593
+ segmentArray.push(frameSegment);
594
+ frameSegmentsMapping.set(imageIdIndex, segmentArray);
595
595
  }
596
- },
597
- _ret;
598
- for (var frameSegment = 0; frameSegment < groupsLen; ++frameSegment) {
599
- _ret = _loop3();
600
- if (_ret === 0) continue;
596
+ } else {
597
+ frameSegmentsMapping.set(imageIdIndex, [frameSegment]);
598
+ }
601
599
  }
602
- var _iterator = _createForOfIteratorHelper(frameSegmentsMapping.entries()),
603
- _step;
604
- try {
605
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
606
- var _step$value = _slicedToArray(_step.value, 2),
607
- role = _step$value[1];
608
- var temp2DArray = new TypedArrayConstructor(sliceLength).fill(0);
609
- for (var i = 0; i < role.length; ++i) {
610
- var _frameSegment = role[i];
611
- var PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[_frameSegment];
612
- var ImageOrientationPatientI = sharedImageOrientationPatient || PerFrameFunctionalGroups.PlaneOrientationSequence.ImageOrientationPatient;
613
- var view = readFromUnpackedChunks(pixelData, _frameSegment * sliceLength, sliceLength);
614
- var pixelDataI2D = ndarray(view, [Rows, Columns]);
615
- var alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
616
- if (!alignedPixelDataI) {
617
- console.warn("Individual SEG frames are out of plane with respect to the first SEG frame, this is not yet supported, skipping this frame.");
618
- continue;
619
- }
620
- var data = alignedPixelDataI.data;
621
- for (var j = 0, len = data.length; j < len; ++j) {
622
- if (data[j] !== 0) {
623
- temp2DArray[j]++;
624
- if (temp2DArray[j] > 1) {
625
- return true;
626
- }
600
+ for (let [, role] of frameSegmentsMapping.entries()) {
601
+ let temp2DArray = new TypedArrayConstructor(sliceLength).fill(0);
602
+ for (let i = 0; i < role.length; ++i) {
603
+ const frameSegment = role[i];
604
+ const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[frameSegment];
605
+ const ImageOrientationPatientI = sharedImageOrientationPatient || PerFrameFunctionalGroups.PlaneOrientationSequence.ImageOrientationPatient;
606
+ const view = readFromUnpackedChunks(pixelData, frameSegment * sliceLength, sliceLength);
607
+ const pixelDataI2D = ndarray(view, [Rows, Columns]);
608
+ const alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
609
+ if (!alignedPixelDataI) {
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
+ continue;
612
+ }
613
+ const data = alignedPixelDataI.data;
614
+ for (let j = 0, len = data.length; j < len; ++j) {
615
+ if (data[j] !== 0) {
616
+ temp2DArray[j]++;
617
+ if (temp2DArray[j] > 1) {
618
+ return true;
627
619
  }
628
620
  }
629
621
  }
630
622
  }
631
- } catch (err) {
632
- _iterator.e(err);
633
- } finally {
634
- _iterator.f();
635
623
  }
636
624
  return false;
637
625
  }
638
626
  function insertOverlappingPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray, labelmapBufferArray, pixelData, multiframe, imageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, segmentsPixelIndices, sopUIDImageIdIndexMap) {
639
- var SharedFunctionalGroupsSequence = multiframe.SharedFunctionalGroupsSequence,
640
- PerFrameFunctionalGroupsSequence = multiframe.PerFrameFunctionalGroupsSequence,
641
- Rows = multiframe.Rows,
642
- Columns = multiframe.Columns;
643
- var sharedImageOrientationPatient = SharedFunctionalGroupsSequence.PlaneOrientationSequence ? SharedFunctionalGroupsSequence.PlaneOrientationSequence.ImageOrientationPatient : undefined;
644
- var sliceLength = Columns * Rows;
645
- var arrayBufferLength = sliceLength * imageIds.length * TypedArrayConstructor.BYTES_PER_ELEMENT;
627
+ const {
628
+ SharedFunctionalGroupsSequence,
629
+ PerFrameFunctionalGroupsSequence,
630
+ Rows,
631
+ Columns
632
+ } = multiframe;
633
+ const sharedImageOrientationPatient = SharedFunctionalGroupsSequence.PlaneOrientationSequence ? SharedFunctionalGroupsSequence.PlaneOrientationSequence.ImageOrientationPatient : undefined;
634
+ const sliceLength = Columns * Rows;
635
+ const arrayBufferLength = sliceLength * imageIds.length * TypedArrayConstructor.BYTES_PER_ELEMENT;
646
636
  // indicate the number of labelMaps
647
- var M = 1;
637
+ let M = 1;
648
638
 
649
639
  // indicate the current labelMap array index;
650
- var m = 0;
640
+ let m = 0;
651
641
 
652
642
  // temp array for checking overlaps
653
- var tempBuffer = labelmapBufferArray[m].slice(0);
643
+ let tempBuffer = labelmapBufferArray[m].slice(0);
654
644
 
655
645
  // temp list for checking overlaps
656
- var tempSegmentsOnFrame = structuredClone(segmentsOnFrameArray[m]);
646
+ let tempSegmentsOnFrame = structuredClone(segmentsOnFrameArray[m]);
657
647
 
658
648
  /** split overlapping SEGs algorithm for each segment:
659
649
  * A) copy the labelmapBuffer in the array with index 0
@@ -662,81 +652,71 @@ function insertOverlappingPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray,
662
652
  * D) if overlap, repeat increasing the index m up to M (if out of memory, add new buffer in the array and M++);
663
653
  */
664
654
 
665
- var numberOfSegs = multiframe.SegmentSequence.length;
666
- for (var segmentIndexToProcess = 1; segmentIndexToProcess <= numberOfSegs; ++segmentIndexToProcess) {
667
- var _loop4 = function _loop4(_i2) {
668
- var PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[_i2];
669
- var segmentIndex = getSegmentIndex(multiframe, _i2);
670
- if (segmentIndex === undefined) {
671
- throw new Error("Could not retrieve the segment index. Aborting segmentation loading.");
672
- }
673
- if (segmentIndex !== segmentIndexToProcess) {
674
- i = _i2;
675
- return 0; // continue
676
- }
677
- var ImageOrientationPatientI = sharedImageOrientationPatient || PerFrameFunctionalGroups.PlaneOrientationSequence.ImageOrientationPatient;
678
-
679
- // Since we moved to the chunks approach, we need to read the data
680
- // and handle scenarios where the portion of data is in one chunk
681
- // and the other portion is in another chunk
682
- var view = readFromUnpackedChunks(pixelData, _i2 * sliceLength, sliceLength);
683
- var pixelDataI2D = ndarray(view, [Rows, Columns]);
684
- var alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
685
- if (!alignedPixelDataI) {
686
- 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.");
687
- }
688
- var imageId = findReferenceSourceImageId(multiframe, _i2, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
689
- if (!imageId) {
690
- console.warn("Image not present in stack, can't import frame : " + _i2 + ".");
691
- i = _i2;
692
- return 0; // continue
693
- }
694
- var sourceImageMetadata = metadataProvider.get("instance", imageId);
695
- if (Rows !== sourceImageMetadata.Rows || Columns !== sourceImageMetadata.Columns) {
696
- 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. ");
697
- }
698
- var imageIdIndex = imageIds.findIndex(function (element) {
699
- return element === imageId;
700
- });
701
- var byteOffset = sliceLength * imageIdIndex * TypedArrayConstructor.BYTES_PER_ELEMENT;
702
- var labelmap2DView = new TypedArrayConstructor(tempBuffer, byteOffset, sliceLength);
703
- var data = alignedPixelDataI.data;
704
- var segmentOnFrame = false;
705
- for (var j = 0, len = alignedPixelDataI.data.length; j < len; ++j) {
706
- if (data[j]) {
707
- if (labelmap2DView[j] !== 0) {
708
- m++;
709
- if (m >= M) {
710
- labelmapBufferArray[m] = new ArrayBuffer(arrayBufferLength);
711
- segmentsOnFrameArray[m] = [];
712
- M++;
713
- }
714
- tempBuffer = labelmapBufferArray[m].slice(0);
715
- tempSegmentsOnFrame = structuredClone(segmentsOnFrameArray[m]);
716
- _i2 = 0;
717
- break;
718
- } else {
719
- labelmap2DView[j] = segmentIndex;
720
- segmentOnFrame = true;
655
+ let numberOfSegs = multiframe.SegmentSequence.length;
656
+ for (let segmentIndexToProcess = 1; segmentIndexToProcess <= numberOfSegs; ++segmentIndexToProcess) {
657
+ for (let i = 0, groupsLen = PerFrameFunctionalGroupsSequence.length; i < groupsLen; ++i) {
658
+ const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[i];
659
+ const segmentIndex = getSegmentIndex(multiframe, i);
660
+ if (segmentIndex === undefined) {
661
+ throw new Error("Could not retrieve the segment index. Aborting segmentation loading.");
662
+ }
663
+ if (segmentIndex !== segmentIndexToProcess) {
664
+ continue;
665
+ }
666
+ const ImageOrientationPatientI = sharedImageOrientationPatient || PerFrameFunctionalGroups.PlaneOrientationSequence.ImageOrientationPatient;
667
+
668
+ // Since we moved to the chunks approach, we need to read the data
669
+ // and handle scenarios where the portion of data is in one chunk
670
+ // and the other portion is in another chunk
671
+ const view = readFromUnpackedChunks(pixelData, i * sliceLength, sliceLength);
672
+ const pixelDataI2D = ndarray(view, [Rows, Columns]);
673
+ const alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
674
+ if (!alignedPixelDataI) {
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
+ }
677
+ const imageId = findReferenceSourceImageId(multiframe, i, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
678
+ if (!imageId) {
679
+ console.warn("Image not present in stack, can't import frame : " + i + ".");
680
+ continue;
681
+ }
682
+ const sourceImageMetadata = metadataProvider.get("instance", imageId);
683
+ if (Rows !== sourceImageMetadata.Rows || Columns !== sourceImageMetadata.Columns) {
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
+ }
686
+ const imageIdIndex = imageIds.findIndex(element => element === imageId);
687
+ const byteOffset = sliceLength * imageIdIndex * TypedArrayConstructor.BYTES_PER_ELEMENT;
688
+ const labelmap2DView = new TypedArrayConstructor(tempBuffer, byteOffset, sliceLength);
689
+ const data = alignedPixelDataI.data;
690
+ let segmentOnFrame = false;
691
+ for (let j = 0, len = alignedPixelDataI.data.length; j < len; ++j) {
692
+ if (data[j]) {
693
+ if (labelmap2DView[j] !== 0) {
694
+ m++;
695
+ if (m >= M) {
696
+ labelmapBufferArray[m] = new ArrayBuffer(arrayBufferLength);
697
+ segmentsOnFrameArray[m] = [];
698
+ M++;
721
699
  }
700
+ tempBuffer = labelmapBufferArray[m].slice(0);
701
+ tempSegmentsOnFrame = structuredClone(segmentsOnFrameArray[m]);
702
+ i = 0;
703
+ break;
704
+ } else {
705
+ labelmap2DView[j] = segmentIndex;
706
+ segmentOnFrame = true;
722
707
  }
723
708
  }
724
- if (segmentOnFrame) {
725
- if (!tempSegmentsOnFrame[imageIdIndex]) {
726
- tempSegmentsOnFrame[imageIdIndex] = [];
727
- }
728
- tempSegmentsOnFrame[imageIdIndex].push(segmentIndex);
729
- if (!segmentsOnFrame[imageIdIndex]) {
730
- segmentsOnFrame[imageIdIndex] = [];
731
- }
732
- segmentsOnFrame[imageIdIndex].push(segmentIndex);
709
+ }
710
+ if (segmentOnFrame) {
711
+ if (!tempSegmentsOnFrame[imageIdIndex]) {
712
+ tempSegmentsOnFrame[imageIdIndex] = [];
713
+ }
714
+ tempSegmentsOnFrame[imageIdIndex].push(segmentIndex);
715
+ if (!segmentsOnFrame[imageIdIndex]) {
716
+ segmentsOnFrame[imageIdIndex] = [];
733
717
  }
734
- i = _i2;
735
- },
736
- _ret2;
737
- for (var i = 0, groupsLen = PerFrameFunctionalGroupsSequence.length; i < groupsLen; ++i) {
738
- _ret2 = _loop4(i);
739
- if (_ret2 === 0) continue;
718
+ segmentsOnFrame[imageIdIndex].push(segmentIndex);
719
+ }
740
720
  }
741
721
  labelmapBufferArray[m] = tempBuffer.slice(0);
742
722
  segmentsOnFrameArray[m] = structuredClone(tempSegmentsOnFrame);
@@ -747,64 +727,68 @@ function insertOverlappingPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray,
747
727
  tempSegmentsOnFrame = structuredClone(segmentsOnFrameArray[m]);
748
728
  }
749
729
  }
750
- var getSegmentIndex = function getSegmentIndex(multiframe, frame) {
751
- var PerFrameFunctionalGroupsSequence = multiframe.PerFrameFunctionalGroupsSequence,
752
- SharedFunctionalGroupsSequence = multiframe.SharedFunctionalGroupsSequence;
753
- var PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[frame];
730
+ const getSegmentIndex = (multiframe, frame) => {
731
+ const {
732
+ PerFrameFunctionalGroupsSequence,
733
+ SharedFunctionalGroupsSequence
734
+ } = multiframe;
735
+ const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[frame];
754
736
  return PerFrameFunctionalGroups && PerFrameFunctionalGroups.SegmentIdentificationSequence ? PerFrameFunctionalGroups.SegmentIdentificationSequence.ReferencedSegmentNumber : SharedFunctionalGroupsSequence.SegmentIdentificationSequence ? SharedFunctionalGroupsSequence.SegmentIdentificationSequence.ReferencedSegmentNumber : undefined;
755
737
  };
756
738
  function insertPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray, labelmapBufferArray, pixelData, multiframe, imageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, segmentsPixelIndices, sopUIDImageIdIndexMap, imageIdMaps, eventTarget, triggerEvent) {
757
- var SharedFunctionalGroupsSequence = multiframe.SharedFunctionalGroupsSequence,
758
- PerFrameFunctionalGroupsSequence = multiframe.PerFrameFunctionalGroupsSequence,
759
- Rows = multiframe.Rows,
760
- Columns = multiframe.Columns;
761
- var sharedImageOrientationPatient = SharedFunctionalGroupsSequence.PlaneOrientationSequence ? SharedFunctionalGroupsSequence.PlaneOrientationSequence.ImageOrientationPatient : undefined;
762
- var sliceLength = Columns * Rows;
763
- var i = 0;
764
- var groupsLen = PerFrameFunctionalGroupsSequence.length;
765
- var chunkSize = Math.ceil(groupsLen / 10); // 10% of total length
766
-
767
- var shouldTriggerEvent = triggerEvent && eventTarget;
768
- var overlapping = false;
739
+ const {
740
+ SharedFunctionalGroupsSequence,
741
+ PerFrameFunctionalGroupsSequence,
742
+ Rows,
743
+ Columns
744
+ } = multiframe;
745
+ const sharedImageOrientationPatient = SharedFunctionalGroupsSequence.PlaneOrientationSequence ? SharedFunctionalGroupsSequence.PlaneOrientationSequence.ImageOrientationPatient : undefined;
746
+ const sliceLength = Columns * Rows;
747
+ let i = 0;
748
+ const groupsLen = PerFrameFunctionalGroupsSequence.length;
749
+ const chunkSize = Math.ceil(groupsLen / 10); // 10% of total length
750
+
751
+ const shouldTriggerEvent = triggerEvent && eventTarget;
752
+ let overlapping = false;
769
753
  // Below, we chunk the processing of the frames to avoid blocking the main thread
770
754
  // if the segmentation is large. We also use a promise to allow the caller to
771
755
  // wait for the processing to finish.
772
- return new Promise(function (resolve) {
756
+ return new Promise(resolve => {
773
757
  function processInChunks() {
774
758
  // process one chunk
775
- for (var end = Math.min(i + chunkSize, groupsLen); i < end; ++i) {
776
- var PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[i];
777
- var ImageOrientationPatientI = sharedImageOrientationPatient || PerFrameFunctionalGroups.PlaneOrientationSequence.ImageOrientationPatient;
778
- var view = readFromUnpackedChunks(pixelData, i * sliceLength, sliceLength);
779
- var pixelDataI2D = ndarray(view, [Rows, Columns]);
780
- var alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
759
+ for (let end = Math.min(i + chunkSize, groupsLen); i < end; ++i) {
760
+ const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[i];
761
+ const ImageOrientationPatientI = sharedImageOrientationPatient || PerFrameFunctionalGroups.PlaneOrientationSequence.ImageOrientationPatient;
762
+ const view = readFromUnpackedChunks(pixelData, i * sliceLength, sliceLength);
763
+ const pixelDataI2D = ndarray(view, [Rows, Columns]);
764
+ const alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
781
765
  if (!alignedPixelDataI) {
782
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.");
783
767
  }
784
- var segmentIndex = getSegmentIndex(multiframe, i);
768
+ const segmentIndex = getSegmentIndex(multiframe, i);
785
769
  if (segmentIndex === undefined) {
786
770
  throw new Error("Could not retrieve the segment index. Aborting segmentation loading.");
787
771
  }
788
772
  if (!segmentsPixelIndices.has(segmentIndex)) {
789
773
  segmentsPixelIndices.set(segmentIndex, {});
790
774
  }
791
- var imageId = findReferenceSourceImageId(multiframe, i, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
775
+ const imageId = findReferenceSourceImageId(multiframe, i, imageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
792
776
  if (!imageId) {
793
777
  console.warn("Image not present in stack, can't import frame : " + i + ".");
794
778
  continue;
795
779
  }
796
- var sourceImageMetadata = imageIdMaps.metadata[imageId];
780
+ const sourceImageMetadata = imageIdMaps.metadata[imageId];
797
781
  if (Rows !== sourceImageMetadata.Rows || Columns !== sourceImageMetadata.Columns) {
798
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. ");
799
783
  }
800
- var imageIdIndex = imageIdMaps.indices[imageId];
801
- var byteOffset = sliceLength * imageIdIndex * TypedArrayConstructor.BYTES_PER_ELEMENT;
802
- var labelmap2DView = new TypedArrayConstructor(labelmapBufferArray[0], byteOffset, sliceLength);
803
- var data = alignedPixelDataI.data;
804
- var indexCache = [];
805
- for (var j = 0, len = alignedPixelDataI.data.length; j < len; ++j) {
784
+ const imageIdIndex = imageIdMaps.indices[imageId];
785
+ const byteOffset = sliceLength * imageIdIndex * TypedArrayConstructor.BYTES_PER_ELEMENT;
786
+ const labelmap2DView = new TypedArrayConstructor(labelmapBufferArray[0], byteOffset, sliceLength);
787
+ const data = alignedPixelDataI.data;
788
+ const indexCache = [];
789
+ for (let j = 0, len = alignedPixelDataI.data.length; j < len; ++j) {
806
790
  if (data[j]) {
807
- for (var x = j; x < len; ++x) {
791
+ for (let x = j; x < len; ++x) {
808
792
  if (data[x]) {
809
793
  if (!overlapping && labelmap2DView[x] !== 0) {
810
794
  overlapping = true;
@@ -820,16 +804,16 @@ function insertPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray, labelmapBu
820
804
  break;
821
805
  }
822
806
  }
823
- var segmentIndexObject = segmentsPixelIndices.get(segmentIndex);
807
+ const segmentIndexObject = segmentsPixelIndices.get(segmentIndex);
824
808
  segmentIndexObject[imageIdIndex] = indexCache;
825
809
  segmentsPixelIndices.set(segmentIndex, segmentIndexObject);
826
810
  }
827
811
 
828
812
  // trigger an event after each chunk
829
813
  if (shouldTriggerEvent) {
830
- var percentComplete = Math.round(i / groupsLen * 100);
814
+ const percentComplete = Math.round(i / groupsLen * 100);
831
815
  triggerEvent(eventTarget, Events.SEGMENTATION_LOAD_PROGRESS, {
832
- percentComplete: percentComplete
816
+ percentComplete
833
817
  });
834
818
  }
835
819
 
@@ -853,8 +837,8 @@ function insertPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray, labelmapBu
853
837
  * @return {Uint8Array} The unpacked pixelData.
854
838
  */
855
839
  function unpackPixelData(multiframe, options) {
856
- var segType = multiframe.SegmentationType;
857
- var data;
840
+ const segType = multiframe.SegmentationType;
841
+ let data;
858
842
  if (Array.isArray(multiframe.PixelData)) {
859
843
  data = multiframe.PixelData[0];
860
844
  } else {
@@ -869,11 +853,9 @@ function unpackPixelData(multiframe, options) {
869
853
  // MAX 2GB is the limit right now to allocate a buffer
870
854
  return getUnpackedChunks(data, options.maxBytesPerChunk);
871
855
  }
872
- var pixelData = new Uint8Array(data);
873
- var max = multiframe.MaximumFractionalValue;
874
- var onlyMaxAndZero = pixelData.find(function (element) {
875
- return element !== 0 && element !== max;
876
- }) === undefined;
856
+ const pixelData = new Uint8Array(data);
857
+ const max = multiframe.MaximumFractionalValue;
858
+ const onlyMaxAndZero = pixelData.find(element => element !== 0 && element !== max) === undefined;
877
859
  if (!onlyMaxAndZero) {
878
860
  // This is a fractional segmentation, which is not currently supported.
879
861
  return;
@@ -907,8 +889,10 @@ function getUnpackedChunks(data, maxBytesPerChunk) {
907
889
  * @return {String} The corresponding imageId.
908
890
  */
909
891
  function getImageIdOfSourceImageBySourceImageSequence(SourceImageSequence, sopUIDImageIdIndexMap) {
910
- var ReferencedSOPInstanceUID = SourceImageSequence.ReferencedSOPInstanceUID,
911
- ReferencedFrameNumber = SourceImageSequence.ReferencedFrameNumber;
892
+ const {
893
+ ReferencedSOPInstanceUID,
894
+ ReferencedFrameNumber
895
+ } = SourceImageSequence;
912
896
  return ReferencedFrameNumber ? getImageIdOfReferencedFrame(ReferencedSOPInstanceUID, ReferencedFrameNumber, sopUIDImageIdIndexMap) : sopUIDImageIdIndexMap[ReferencedSOPInstanceUID];
913
897
  }
914
898
 
@@ -928,8 +912,8 @@ function getImageIdOfSourceImagebyGeometry(ReferencedSeriesInstanceUID, FrameOfR
928
912
  if (ReferencedSeriesInstanceUID === undefined || PerFrameFunctionalGroup.PlanePositionSequence === undefined || PerFrameFunctionalGroup.PlanePositionSequence[0] === undefined || PerFrameFunctionalGroup.PlanePositionSequence[0].ImagePositionPatient === undefined) {
929
913
  return undefined;
930
914
  }
931
- for (var imageIdsIndexc = 0; imageIdsIndexc < imageIds.length; ++imageIdsIndexc) {
932
- var sourceImageMetadata = metadataProvider.get("instance", imageIds[imageIdsIndexc]);
915
+ for (let imageIdsIndexc = 0; imageIdsIndexc < imageIds.length; ++imageIdsIndexc) {
916
+ const sourceImageMetadata = metadataProvider.get("instance", imageIds[imageIdsIndexc]);
933
917
  if (sourceImageMetadata === undefined || sourceImageMetadata.ImagePositionPatient === undefined || sourceImageMetadata.FrameOfReferenceUID !== FrameOfReferenceUID || sourceImageMetadata.SeriesInstanceUID !== ReferencedSeriesInstanceUID) {
934
918
  continue;
935
919
  }
@@ -950,11 +934,11 @@ function getImageIdOfSourceImagebyGeometry(ReferencedSeriesInstanceUID, FrameOfR
950
934
  * @return {String} The imageId that corresponds to the sopInstanceUid.
951
935
  */
952
936
  function getImageIdOfReferencedFrame(sopInstanceUid, frameNumber, sopUIDImageIdIndexMap) {
953
- var imageId = sopUIDImageIdIndexMap[sopInstanceUid];
937
+ const imageId = sopUIDImageIdIndexMap[sopInstanceUid];
954
938
  if (!imageId) {
955
939
  return;
956
940
  }
957
- var imageIdFrameNumber = Number(imageId.split("frame=")[1]);
941
+ const imageIdFrameNumber = Number(imageId.split("frame=")[1]);
958
942
  return imageIdFrameNumber === frameNumber - 1 ? imageId : undefined;
959
943
  }
960
944
 
@@ -965,7 +949,7 @@ function getImageIdOfReferencedFrame(sopInstanceUid, frameNumber, sopUIDImageIdI
965
949
  * @return {Number[8][6]} An array of valid orientations.
966
950
  */
967
951
  function getValidOrientations(iop) {
968
- var orientations = [];
952
+ const orientations = [];
969
953
 
970
954
  // [0, 1, 2]: 0, 0hf, 0vf
971
955
  // [3, 4, 5]: 90, 90hf, 90vf
@@ -974,7 +958,7 @@ function getValidOrientations(iop) {
974
958
  orientations[0] = iop;
975
959
  orientations[1] = flipIOP.h(iop);
976
960
  orientations[2] = flipIOP.v(iop);
977
- var iop90 = rotateDirectionCosinesInPlane(iop, Math.PI / 2);
961
+ const iop90 = rotateDirectionCosinesInPlane(iop, Math.PI / 2);
978
962
  orientations[3] = iop90;
979
963
  orientations[4] = flipIOP.h(iop90);
980
964
  orientations[5] = flipIOP.v(iop90);
@@ -1032,17 +1016,17 @@ function alignPixelDataWithSourceData(pixelData2D, iop, orientations, tolerance)
1032
1016
  }
1033
1017
  }
1034
1018
  function getSegmentMetadata(multiframe, seriesInstanceUid) {
1035
- var segmentSequence = multiframe.SegmentSequence;
1036
- var data = [];
1019
+ const segmentSequence = multiframe.SegmentSequence;
1020
+ let data = [];
1037
1021
  if (Array.isArray(segmentSequence)) {
1038
- data = [undefined].concat(_toConsumableArray(segmentSequence));
1022
+ data = [undefined, ...segmentSequence];
1039
1023
  } else {
1040
1024
  // Only one segment, will be stored as an object.
1041
1025
  data = [undefined, segmentSequence];
1042
1026
  }
1043
1027
  return {
1044
- seriesInstanceUid: seriesInstanceUid,
1045
- data: data
1028
+ seriesInstanceUid,
1029
+ data
1046
1030
  };
1047
1031
  }
1048
1032
 
@@ -1056,18 +1040,18 @@ function getSegmentMetadata(multiframe, seriesInstanceUid) {
1056
1040
  * @returns {Uint8Array} A new Uint8Array containing the requested bytes.
1057
1041
  */
1058
1042
  function readFromUnpackedChunks(chunks, offset, length) {
1059
- var mapping = getUnpackedOffsetAndLength(chunks, offset, length);
1043
+ const mapping = getUnpackedOffsetAndLength(chunks, offset, length);
1060
1044
 
1061
1045
  // If all the data is in one chunk, we can just slice that chunk
1062
1046
  if (mapping.start.chunkIndex === mapping.end.chunkIndex) {
1063
1047
  return new Uint8Array(chunks[mapping.start.chunkIndex].buffer, mapping.start.offset, length);
1064
1048
  } else {
1065
1049
  // If the data spans multiple chunks, we need to create a new Uint8Array and copy the data from each chunk
1066
- var result = new Uint8Array(length);
1067
- var resultOffset = 0;
1068
- for (var i = mapping.start.chunkIndex; i <= mapping.end.chunkIndex; i++) {
1069
- var start = i === mapping.start.chunkIndex ? mapping.start.offset : 0;
1070
- var end = i === mapping.end.chunkIndex ? mapping.end.offset : chunks[i].length;
1050
+ let result = new Uint8Array(length);
1051
+ let resultOffset = 0;
1052
+ for (let i = mapping.start.chunkIndex; i <= mapping.end.chunkIndex; i++) {
1053
+ let start = i === mapping.start.chunkIndex ? mapping.start.offset : 0;
1054
+ let end = i === mapping.end.chunkIndex ? mapping.end.offset : chunks[i].length;
1071
1055
  result.set(new Uint8Array(chunks[i].buffer, start, end - start), resultOffset);
1072
1056
  resultOffset += end - start;
1073
1057
  }
@@ -1075,9 +1059,7 @@ function readFromUnpackedChunks(chunks, offset, length) {
1075
1059
  }
1076
1060
  }
1077
1061
  function getUnpackedOffsetAndLength(chunks, offset, length) {
1078
- var totalBytes = chunks.reduce(function (total, chunk) {
1079
- return total + chunk.length;
1080
- }, 0);
1062
+ var totalBytes = chunks.reduce((total, chunk) => total + chunk.length, 0);
1081
1063
  if (offset < 0 || offset + length > totalBytes) {
1082
1064
  throw new Error("Offset and length out of bounds");
1083
1065
  }
@@ -1105,61 +1087,51 @@ function getUnpackedOffsetAndLength(chunks, offset, length) {
1105
1087
  };
1106
1088
  }
1107
1089
  function calculateCentroid(imageIdIndexBufferIndex, multiframe, metadataProvider, imageIds) {
1108
- var xAcc = 0;
1109
- var yAcc = 0;
1110
- var zAcc = 0;
1111
- var worldXAcc = 0;
1112
- var worldYAcc = 0;
1113
- var worldZAcc = 0;
1114
- var count = 0;
1115
- for (var _i3 = 0, _Object$entries = Object.entries(imageIdIndexBufferIndex); _i3 < _Object$entries.length; _i3++) {
1116
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i3], 2),
1117
- imageIdIndex = _Object$entries$_i[0],
1118
- bufferIndices = _Object$entries$_i[1];
1119
- var z = Number(imageIdIndex);
1090
+ let xAcc = 0;
1091
+ let yAcc = 0;
1092
+ let zAcc = 0;
1093
+ let worldXAcc = 0;
1094
+ let worldYAcc = 0;
1095
+ let worldZAcc = 0;
1096
+ let count = 0;
1097
+ for (const [imageIdIndex, bufferIndices] of Object.entries(imageIdIndexBufferIndex)) {
1098
+ const z = Number(imageIdIndex);
1120
1099
  if (!bufferIndices || bufferIndices.length === 0) {
1121
1100
  continue;
1122
1101
  }
1123
1102
 
1124
1103
  // Get metadata for this slice
1125
- var imageId = imageIds[z];
1126
- var imagePlaneModule = metadataProvider.get("imagePlaneModule", imageId);
1104
+ const imageId = imageIds[z];
1105
+ const imagePlaneModule = metadataProvider.get("imagePlaneModule", imageId);
1127
1106
  if (!imagePlaneModule) {
1128
1107
  console.debug("Missing imagePlaneModule metadata for centroid calculation");
1129
1108
  continue;
1130
1109
  }
1131
- var imagePositionPatient = imagePlaneModule.imagePositionPatient,
1132
- rowCosines = imagePlaneModule.rowCosines,
1133
- columnCosines = imagePlaneModule.columnCosines,
1134
- rowPixelSpacing = imagePlaneModule.rowPixelSpacing,
1135
- columnPixelSpacing = imagePlaneModule.columnPixelSpacing;
1136
- var _iterator2 = _createForOfIteratorHelper(bufferIndices),
1137
- _step2;
1138
- try {
1139
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
1140
- var bufferIndex = _step2.value;
1141
- var y = Math.floor(bufferIndex / multiframe.Rows);
1142
- var x = bufferIndex % multiframe.Rows;
1143
-
1144
- // Image coordinates
1145
- xAcc += x;
1146
- yAcc += y;
1147
- zAcc += z;
1148
-
1149
- // Calculate world coordinates
1150
- // P(world) = P(image) * IOP * spacing + IPP
1151
- var worldX = imagePositionPatient[0] + x * rowCosines[0] * columnPixelSpacing + y * columnCosines[0] * rowPixelSpacing;
1152
- var worldY = imagePositionPatient[1] + x * rowCosines[1] * columnPixelSpacing + y * columnCosines[1] * rowPixelSpacing;
1153
- var worldZ = imagePositionPatient[2] + x * rowCosines[2] * columnPixelSpacing + y * columnCosines[2] * rowPixelSpacing;
1154
- worldXAcc += worldX;
1155
- worldYAcc += worldY;
1156
- worldZAcc += worldZ;
1157
- count++;
1158
- }
1159
- } catch (err) {
1160
- _iterator2.e(err);
1161
- } finally {
1162
- _iterator2.f();
1110
+ const {
1111
+ imagePositionPatient,
1112
+ rowCosines,
1113
+ columnCosines,
1114
+ rowPixelSpacing,
1115
+ columnPixelSpacing
1116
+ } = imagePlaneModule;
1117
+ for (const bufferIndex of bufferIndices) {
1118
+ const y = Math.floor(bufferIndex / multiframe.Rows);
1119
+ const x = bufferIndex % multiframe.Rows;
1120
+
1121
+ // Image coordinates
1122
+ xAcc += x;
1123
+ yAcc += y;
1124
+ zAcc += z;
1125
+
1126
+ // Calculate world coordinates
1127
+ // P(world) = P(image) * IOP * spacing + IPP
1128
+ const worldX = imagePositionPatient[0] + x * rowCosines[0] * columnPixelSpacing + y * columnCosines[0] * rowPixelSpacing;
1129
+ const worldY = imagePositionPatient[1] + x * rowCosines[1] * columnPixelSpacing + y * columnCosines[1] * rowPixelSpacing;
1130
+ const worldZ = imagePositionPatient[2] + x * rowCosines[2] * columnPixelSpacing + y * columnCosines[2] * rowPixelSpacing;
1131
+ worldXAcc += worldX;
1132
+ worldYAcc += worldY;
1133
+ worldZAcc += worldZ;
1134
+ count++;
1163
1135
  }
1164
1136
  }
1165
1137
  return {
@@ -1173,13 +1145,13 @@ function calculateCentroid(imageIdIndexBufferIndex, multiframe, metadataProvider
1173
1145
  y: worldYAcc / count,
1174
1146
  z: worldZAcc / count
1175
1147
  },
1176
- count: count
1148
+ count
1177
1149
  };
1178
1150
  }
1179
- var Segmentation = {
1180
- generateSegmentation: generateSegmentation,
1181
- generateToolState: generateToolState,
1182
- fillSegmentation: fillSegmentation
1151
+ const Segmentation = {
1152
+ generateSegmentation,
1153
+ generateToolState,
1154
+ fillSegmentation
1183
1155
  };
1184
1156
 
1185
1157
  export { _createSegFromImages, _getLabelmapsFromReferencedFrameIndicies, alignPixelDataWithSourceData, calculateCentroid, checkSEGsOverlapping, Segmentation as default, fillSegmentation, findReferenceSourceImageId, generateSegmentation, generateToolState, getImageIdOfReferencedFrame, getImageIdOfSourceImageBySourceImageSequence, getImageIdOfSourceImagebyGeometry, getSegmentIndex, getSegmentMetadata, getUnpackedChunks, getUnpackedOffsetAndLength, getValidOrientations, insertOverlappingPixelDataPlanar, insertPixelDataPlanar, readFromUnpackedChunks, unpackPixelData };