@cornerstonejs/adapters 3.3.4 → 3.5.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/esm/adapters/Cornerstone3D/Segmentation/compactMergeSegData.d.ts +4 -0
- package/dist/esm/adapters/Cornerstone3D/Segmentation/compactMergeSegData.js +61 -0
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateToolState.d.ts +2 -4
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateToolState.js +0 -2
- package/dist/esm/adapters/Cornerstone3D/Segmentation/labelmapImagesFromBuffer.d.ts +30 -3
- package/dist/esm/adapters/Cornerstone3D/Segmentation/labelmapImagesFromBuffer.js +335 -22
- package/package.json +4 -4
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const checkHasOverlapping = _ref => {
|
|
2
|
+
let {
|
|
3
|
+
largerArray,
|
|
4
|
+
currentTestedArray,
|
|
5
|
+
newArray
|
|
6
|
+
} = _ref;
|
|
7
|
+
return largerArray.some((_, currentImageIndex) => {
|
|
8
|
+
const originalImagePixelData = currentTestedArray[currentImageIndex];
|
|
9
|
+
const newImagePixelData = newArray[currentImageIndex];
|
|
10
|
+
if (!originalImagePixelData || !newImagePixelData) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return originalImagePixelData.some((originalPixel, currentPixelIndex) => {
|
|
14
|
+
const newPixel = newImagePixelData[currentPixelIndex];
|
|
15
|
+
return originalPixel && newPixel;
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const compactMergeSegmentDataWithoutInformationLoss = _ref2 => {
|
|
20
|
+
let {
|
|
21
|
+
arrayOfSegmentData,
|
|
22
|
+
newSegmentData
|
|
23
|
+
} = _ref2;
|
|
24
|
+
if (arrayOfSegmentData.length === 0) {
|
|
25
|
+
arrayOfSegmentData.push(newSegmentData);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (let currentTestedIndex = 0; currentTestedIndex < arrayOfSegmentData.length; currentTestedIndex++) {
|
|
29
|
+
const currentTestedArray = arrayOfSegmentData[currentTestedIndex];
|
|
30
|
+
const originalArrayIsLarger = currentTestedArray.length > newSegmentData.length;
|
|
31
|
+
const largerArray = originalArrayIsLarger ? currentTestedArray : newSegmentData;
|
|
32
|
+
const hasOverlapping = checkHasOverlapping({
|
|
33
|
+
currentTestedArray,
|
|
34
|
+
largerArray,
|
|
35
|
+
newArray: newSegmentData
|
|
36
|
+
});
|
|
37
|
+
if (hasOverlapping) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
largerArray.forEach((_, currentImageIndex) => {
|
|
41
|
+
const originalImagePixelData = currentTestedArray[currentImageIndex];
|
|
42
|
+
const newImagePixelData = newSegmentData[currentImageIndex];
|
|
43
|
+
if (!originalImagePixelData && !newImagePixelData || !newImagePixelData) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!originalImagePixelData) {
|
|
47
|
+
currentTestedArray[currentImageIndex] = newImagePixelData;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const mergedPixelData = originalImagePixelData.map((originalPixel, currentPixelIndex) => {
|
|
51
|
+
const newPixel = newImagePixelData[currentPixelIndex];
|
|
52
|
+
return originalPixel || newPixel;
|
|
53
|
+
});
|
|
54
|
+
currentTestedArray[currentImageIndex] = mergedPixelData;
|
|
55
|
+
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
arrayOfSegmentData.push(newSegmentData);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { compactMergeSegmentDataWithoutInformationLoss };
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
declare function generateToolState(imageIds: any, arrayBuffer: any, metadataProvider: any, skipOverlapping?: boolean, tolerance?: number, cs3dVersion?: number): any;
|
|
2
|
-
declare function createFromDICOMSegBuffer(referencedImageIds: any, arrayBuffer: any, { metadataProvider,
|
|
2
|
+
declare function createFromDICOMSegBuffer(referencedImageIds: any, arrayBuffer: any, { metadataProvider, tolerance }: {
|
|
3
3
|
metadataProvider: any;
|
|
4
|
-
skipOverlapping?: boolean;
|
|
5
4
|
tolerance?: number;
|
|
6
5
|
}): Promise<{
|
|
7
|
-
labelMapImages: any
|
|
6
|
+
labelMapImages: any;
|
|
8
7
|
segMetadata: {
|
|
9
8
|
seriesInstanceUid: any;
|
|
10
9
|
data: any[];
|
|
11
10
|
};
|
|
12
11
|
segmentsOnFrame: any[];
|
|
13
|
-
segmentsOnFrameArray: any[];
|
|
14
12
|
centroids: Map<any, any>;
|
|
15
13
|
overlappingSegments: any;
|
|
16
14
|
}>;
|
|
@@ -10,11 +10,9 @@ function generateToolState(imageIds, arrayBuffer, metadataProvider) {
|
|
|
10
10
|
function createFromDICOMSegBuffer(referencedImageIds, arrayBuffer, _ref) {
|
|
11
11
|
let {
|
|
12
12
|
metadataProvider,
|
|
13
|
-
skipOverlapping = false,
|
|
14
13
|
tolerance = 1e-3
|
|
15
14
|
} = _ref;
|
|
16
15
|
return createLabelmapsFromBufferInternal(referencedImageIds, arrayBuffer, metadataProvider, {
|
|
17
|
-
skipOverlapping,
|
|
18
16
|
tolerance
|
|
19
17
|
});
|
|
20
18
|
}
|
|
@@ -1,13 +1,40 @@
|
|
|
1
1
|
declare function createLabelmapsFromBufferInternal(referencedImageIds: any, arrayBuffer: any, metadataProvider: any, options: any): Promise<{
|
|
2
|
-
labelMapImages: any
|
|
2
|
+
labelMapImages: any;
|
|
3
3
|
segMetadata: {
|
|
4
4
|
seriesInstanceUid: any;
|
|
5
5
|
data: any[];
|
|
6
6
|
};
|
|
7
7
|
segmentsOnFrame: any[];
|
|
8
|
-
segmentsOnFrameArray: any[];
|
|
9
8
|
centroids: Map<any, any>;
|
|
10
9
|
overlappingSegments: any;
|
|
11
10
|
}>;
|
|
12
|
-
export declare function insertPixelDataPlanar(segmentsOnFrame
|
|
11
|
+
export declare function insertPixelDataPlanar({ segmentsOnFrame, labelMapImages, pixelDataChunks, multiframe, referencedImageIds, validOrientations, metadataProvider, tolerance, segmentsPixelIndices, sopUIDImageIdIndexMap, imageIdMaps }: {
|
|
12
|
+
segmentsOnFrame: any;
|
|
13
|
+
labelMapImages: any;
|
|
14
|
+
pixelDataChunks: any;
|
|
15
|
+
multiframe: any;
|
|
16
|
+
referencedImageIds: any;
|
|
17
|
+
validOrientations: any;
|
|
18
|
+
metadataProvider: any;
|
|
19
|
+
tolerance: any;
|
|
20
|
+
segmentsPixelIndices: any;
|
|
21
|
+
sopUIDImageIdIndexMap: any;
|
|
22
|
+
imageIdMaps: any;
|
|
23
|
+
}): Promise<unknown>;
|
|
24
|
+
export declare function insertOverlappingPixelDataPlanar({ segmentsOnFrame, labelMapImages, pixelDataChunks, multiframe, referencedImageIds, validOrientations, metadataProvider, tolerance, segmentsPixelIndices, sopUIDImageIdIndexMap, imageIdMaps }: {
|
|
25
|
+
segmentsOnFrame: any;
|
|
26
|
+
labelMapImages: any;
|
|
27
|
+
pixelDataChunks: any;
|
|
28
|
+
multiframe: any;
|
|
29
|
+
referencedImageIds: any;
|
|
30
|
+
validOrientations: any;
|
|
31
|
+
metadataProvider: any;
|
|
32
|
+
tolerance: any;
|
|
33
|
+
segmentsPixelIndices: any;
|
|
34
|
+
sopUIDImageIdIndexMap: any;
|
|
35
|
+
imageIdMaps: any;
|
|
36
|
+
}): {
|
|
37
|
+
arrayOfLabelMapImages: any;
|
|
38
|
+
hasOverlappingSegments: boolean;
|
|
39
|
+
};
|
|
13
40
|
export { createLabelmapsFromBufferInternal };
|
|
@@ -2,7 +2,8 @@ import { imageLoader } from '@cornerstonejs/core';
|
|
|
2
2
|
import { utilities, data, normalizers } from 'dcmjs';
|
|
3
3
|
import ndarray from 'ndarray';
|
|
4
4
|
import checkOrientation from '../../helpers/checkOrientation.js';
|
|
5
|
-
import { getValidOrientations, getSegmentMetadata, unpackPixelData,
|
|
5
|
+
import { getValidOrientations, getSegmentMetadata, unpackPixelData, calculateCentroid, readFromUnpackedChunks, alignPixelDataWithSourceData, getSegmentIndex, findReferenceSourceImageId } from '../../Cornerstone/Segmentation_4X.js';
|
|
6
|
+
import { compactMergeSegmentDataWithoutInformationLoss } from './compactMergeSegData.js';
|
|
6
7
|
|
|
7
8
|
const {
|
|
8
9
|
DicomMessage,
|
|
@@ -14,9 +15,49 @@ const {
|
|
|
14
15
|
const {
|
|
15
16
|
decode
|
|
16
17
|
} = utilities.compression;
|
|
18
|
+
const updateSegmentsOnFrame = _ref => {
|
|
19
|
+
let {
|
|
20
|
+
segmentsOnFrame,
|
|
21
|
+
imageIdIndex,
|
|
22
|
+
segmentIndex
|
|
23
|
+
} = _ref;
|
|
24
|
+
if (!segmentsOnFrame[imageIdIndex]) {
|
|
25
|
+
segmentsOnFrame[imageIdIndex] = [];
|
|
26
|
+
}
|
|
27
|
+
segmentsOnFrame[imageIdIndex].push(segmentIndex);
|
|
28
|
+
};
|
|
29
|
+
const updateSegmentsPixelIndices = _ref2 => {
|
|
30
|
+
let {
|
|
31
|
+
segmentsPixelIndices,
|
|
32
|
+
segmentIndex,
|
|
33
|
+
imageIdIndex,
|
|
34
|
+
indexCache
|
|
35
|
+
} = _ref2;
|
|
36
|
+
if (!segmentsPixelIndices.has(segmentIndex)) {
|
|
37
|
+
segmentsPixelIndices.set(segmentIndex, {});
|
|
38
|
+
}
|
|
39
|
+
const segmentIndexObject = segmentsPixelIndices.get(segmentIndex);
|
|
40
|
+
segmentIndexObject[imageIdIndex] = indexCache;
|
|
41
|
+
segmentsPixelIndices.set(segmentIndex, segmentIndexObject);
|
|
42
|
+
};
|
|
43
|
+
const extractInfoFromPerFrameFunctionalGroups = _ref3 => {
|
|
44
|
+
let {
|
|
45
|
+
PerFrameFunctionalGroups,
|
|
46
|
+
sequenceIndex,
|
|
47
|
+
sopUIDImageIdIndexMap,
|
|
48
|
+
multiframe
|
|
49
|
+
} = _ref3;
|
|
50
|
+
const referencedSOPInstanceUid = PerFrameFunctionalGroups.DerivationImageSequence[0].SourceImageSequence[0].ReferencedSOPInstanceUID;
|
|
51
|
+
const referencedImageId = sopUIDImageIdIndexMap[referencedSOPInstanceUid];
|
|
52
|
+
const segmentIndex = getSegmentIndex(multiframe, sequenceIndex);
|
|
53
|
+
return {
|
|
54
|
+
referencedSOPInstanceUid,
|
|
55
|
+
referencedImageId,
|
|
56
|
+
segmentIndex
|
|
57
|
+
};
|
|
58
|
+
};
|
|
17
59
|
async function createLabelmapsFromBufferInternal(referencedImageIds, arrayBuffer, metadataProvider, options) {
|
|
18
60
|
const {
|
|
19
|
-
skipOverlapping = false,
|
|
20
61
|
tolerance = 1e-3,
|
|
21
62
|
TypedArrayConstructor = Uint8Array,
|
|
22
63
|
maxBytesPerChunk = 199000000
|
|
@@ -61,26 +102,16 @@ async function createLabelmapsFromBufferInternal(referencedImageIds, arrayBuffer
|
|
|
61
102
|
acc[sopInstanceUID] = imageId;
|
|
62
103
|
return acc;
|
|
63
104
|
}, {});
|
|
64
|
-
let overlapping = false;
|
|
65
|
-
if (!skipOverlapping) {
|
|
66
|
-
overlapping = checkSEGsOverlapping(pixelDataChunks, multiframe, referencedImageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, sopUIDImageIdIndexMap);
|
|
67
|
-
}
|
|
68
105
|
let insertFunction;
|
|
69
106
|
switch (orientation) {
|
|
70
107
|
case "Planar":
|
|
71
|
-
|
|
72
|
-
throw new Error("Overlapping segmentations are not yet supported.");
|
|
73
|
-
} else {
|
|
74
|
-
insertFunction = insertPixelDataPlanar;
|
|
75
|
-
}
|
|
108
|
+
insertFunction = insertPixelDataPlanar;
|
|
76
109
|
break;
|
|
77
110
|
case "Perpendicular":
|
|
78
111
|
throw new Error("Segmentations orthogonal to the acquisition plane of the source data are not yet supported.");
|
|
79
112
|
case "Oblique":
|
|
80
113
|
throw new Error("Segmentations oblique to the acquisition plane of the source data are not yet supported.");
|
|
81
114
|
}
|
|
82
|
-
const segmentsOnFrameArray = [];
|
|
83
|
-
segmentsOnFrameArray[0] = [];
|
|
84
115
|
const segmentsOnFrame = [];
|
|
85
116
|
const imageIdMaps = {
|
|
86
117
|
indices: {},
|
|
@@ -95,22 +126,50 @@ async function createLabelmapsFromBufferInternal(referencedImageIds, arrayBuffer
|
|
|
95
126
|
labelMapImages.push(labelMapImage);
|
|
96
127
|
}
|
|
97
128
|
const segmentsPixelIndices = new Map();
|
|
98
|
-
const
|
|
129
|
+
const {
|
|
130
|
+
hasOverlappingSegments,
|
|
131
|
+
arrayOfLabelMapImages
|
|
132
|
+
} = await insertFunction({
|
|
133
|
+
segmentsOnFrame,
|
|
134
|
+
labelMapImages,
|
|
135
|
+
pixelDataChunks,
|
|
136
|
+
multiframe,
|
|
137
|
+
referencedImageIds,
|
|
138
|
+
validOrientations,
|
|
139
|
+
metadataProvider,
|
|
140
|
+
tolerance,
|
|
141
|
+
segmentsPixelIndices,
|
|
142
|
+
sopUIDImageIdIndexMap,
|
|
143
|
+
imageIdMaps,
|
|
144
|
+
TypedArrayConstructor
|
|
145
|
+
});
|
|
99
146
|
const centroidXYZ = new Map();
|
|
100
147
|
segmentsPixelIndices.forEach((imageIdIndexBufferIndex, segmentIndex) => {
|
|
101
148
|
const centroids = calculateCentroid(imageIdIndexBufferIndex, multiframe, metadataProvider, referencedImageIds);
|
|
102
149
|
centroidXYZ.set(segmentIndex, centroids);
|
|
103
150
|
});
|
|
104
151
|
return {
|
|
105
|
-
labelMapImages:
|
|
152
|
+
labelMapImages: arrayOfLabelMapImages,
|
|
106
153
|
segMetadata,
|
|
107
154
|
segmentsOnFrame,
|
|
108
|
-
segmentsOnFrameArray,
|
|
109
155
|
centroids: centroidXYZ,
|
|
110
|
-
overlappingSegments
|
|
156
|
+
overlappingSegments: hasOverlappingSegments
|
|
111
157
|
};
|
|
112
158
|
}
|
|
113
|
-
function insertPixelDataPlanar(
|
|
159
|
+
function insertPixelDataPlanar(_ref4) {
|
|
160
|
+
let {
|
|
161
|
+
segmentsOnFrame,
|
|
162
|
+
labelMapImages,
|
|
163
|
+
pixelDataChunks,
|
|
164
|
+
multiframe,
|
|
165
|
+
referencedImageIds,
|
|
166
|
+
validOrientations,
|
|
167
|
+
metadataProvider,
|
|
168
|
+
tolerance,
|
|
169
|
+
segmentsPixelIndices,
|
|
170
|
+
sopUIDImageIdIndexMap,
|
|
171
|
+
imageIdMaps
|
|
172
|
+
} = _ref4;
|
|
114
173
|
const {
|
|
115
174
|
SharedFunctionalGroupsSequence,
|
|
116
175
|
PerFrameFunctionalGroupsSequence,
|
|
@@ -125,7 +184,7 @@ function insertPixelDataPlanar(segmentsOnFrame, labelMapImages, pixelData, multi
|
|
|
125
184
|
for (let i = 0; i < groupsLen; ++i) {
|
|
126
185
|
const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[i];
|
|
127
186
|
const ImageOrientationPatientI = sharedImageOrientationPatient || PerFrameFunctionalGroups.PlaneOrientationSequence.ImageOrientationPatient;
|
|
128
|
-
const view = readFromUnpackedChunks(
|
|
187
|
+
const view = readFromUnpackedChunks(pixelDataChunks, i * sliceLength, sliceLength);
|
|
129
188
|
const pixelDataI2D = ndarray(view, [Rows, Columns]);
|
|
130
189
|
const alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
|
|
131
190
|
if (!alignedPixelDataI) {
|
|
@@ -138,7 +197,7 @@ function insertPixelDataPlanar(segmentsOnFrame, labelMapImages, pixelData, multi
|
|
|
138
197
|
if (!segmentsPixelIndices.has(segmentIndex)) {
|
|
139
198
|
segmentsPixelIndices.set(segmentIndex, {});
|
|
140
199
|
}
|
|
141
|
-
const imageId = findReferenceSourceImageId(multiframe, i,
|
|
200
|
+
const imageId = findReferenceSourceImageId(multiframe, i, referencedImageIds, metadataProvider, tolerance, sopUIDImageIdIndexMap);
|
|
142
201
|
if (!imageId) {
|
|
143
202
|
console.warn("Image not present in stack, can't import frame : " + i + ".");
|
|
144
203
|
continue;
|
|
@@ -158,6 +217,19 @@ function insertPixelDataPlanar(segmentsOnFrame, labelMapImages, pixelData, multi
|
|
|
158
217
|
if (data[x]) {
|
|
159
218
|
if (!overlapping && labelmap2DView[x] !== 0) {
|
|
160
219
|
overlapping = true;
|
|
220
|
+
return resolve(insertOverlappingPixelDataPlanar({
|
|
221
|
+
segmentsOnFrame,
|
|
222
|
+
labelMapImages,
|
|
223
|
+
pixelDataChunks,
|
|
224
|
+
multiframe,
|
|
225
|
+
referencedImageIds,
|
|
226
|
+
validOrientations,
|
|
227
|
+
metadataProvider,
|
|
228
|
+
tolerance,
|
|
229
|
+
segmentsPixelIndices,
|
|
230
|
+
sopUIDImageIdIndexMap,
|
|
231
|
+
imageIdMaps
|
|
232
|
+
}));
|
|
161
233
|
}
|
|
162
234
|
labelmap2DView[x] = segmentIndex;
|
|
163
235
|
indexCache.push(x);
|
|
@@ -174,8 +246,249 @@ function insertPixelDataPlanar(segmentsOnFrame, labelMapImages, pixelData, multi
|
|
|
174
246
|
segmentIndexObject[imageIdIndex] = indexCache;
|
|
175
247
|
segmentsPixelIndices.set(segmentIndex, segmentIndexObject);
|
|
176
248
|
}
|
|
177
|
-
resolve(
|
|
249
|
+
resolve({
|
|
250
|
+
hasOverlappingSegments: overlapping,
|
|
251
|
+
arrayOfLabelMapImages: [labelMapImages]
|
|
252
|
+
});
|
|
178
253
|
});
|
|
179
254
|
}
|
|
255
|
+
const getAlignedPixelData = _ref5 => {
|
|
256
|
+
let {
|
|
257
|
+
sharedImageOrientationPatient,
|
|
258
|
+
PerFrameFunctionalGroups,
|
|
259
|
+
pixelDataChunks,
|
|
260
|
+
sequenceIndex,
|
|
261
|
+
sliceLength,
|
|
262
|
+
Rows,
|
|
263
|
+
Columns,
|
|
264
|
+
validOrientations,
|
|
265
|
+
tolerance
|
|
266
|
+
} = _ref5;
|
|
267
|
+
const ImageOrientationPatientI = sharedImageOrientationPatient || PerFrameFunctionalGroups.PlaneOrientationSequence.ImageOrientationPatient;
|
|
268
|
+
const view = readFromUnpackedChunks(pixelDataChunks, sequenceIndex * sliceLength, sliceLength);
|
|
269
|
+
const pixelDataI2D = ndarray(view, [Rows, Columns]);
|
|
270
|
+
const alignedPixelDataI = alignPixelDataWithSourceData(pixelDataI2D, ImageOrientationPatientI, validOrientations, tolerance);
|
|
271
|
+
if (!alignedPixelDataI) {
|
|
272
|
+
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.");
|
|
273
|
+
}
|
|
274
|
+
return alignedPixelDataI;
|
|
275
|
+
};
|
|
276
|
+
const checkImageDimensions = _ref6 => {
|
|
277
|
+
let {
|
|
278
|
+
metadataProvider,
|
|
279
|
+
imageId,
|
|
280
|
+
Rows,
|
|
281
|
+
Columns
|
|
282
|
+
} = _ref6;
|
|
283
|
+
const sourceImageMetadata = metadataProvider.get("instance", imageId);
|
|
284
|
+
if (Rows !== sourceImageMetadata.Rows || Columns !== sourceImageMetadata.Columns) {
|
|
285
|
+
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. ");
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
const getArrayOfLabelMapImagesWithSegmentData = _ref7 => {
|
|
289
|
+
let {
|
|
290
|
+
arrayOfSegmentData,
|
|
291
|
+
referencedImageIds
|
|
292
|
+
} = _ref7;
|
|
293
|
+
let largestArray = [];
|
|
294
|
+
for (let i = 0; i < arrayOfSegmentData.length; i++) {
|
|
295
|
+
const segmentData = arrayOfSegmentData[i];
|
|
296
|
+
if (segmentData.length > largestArray.length) {
|
|
297
|
+
largestArray = segmentData;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return arrayOfSegmentData.map(arr => {
|
|
301
|
+
const labelMapImages = referencedImageIds.map((referencedImageId, i) => {
|
|
302
|
+
const hasEmptySegmentData = !arr[i];
|
|
303
|
+
const labelMapImage = imageLoader.createAndCacheDerivedLabelmapImage(referencedImageId);
|
|
304
|
+
const pixelData = labelMapImage.getPixelData();
|
|
305
|
+
if (!hasEmptySegmentData) {
|
|
306
|
+
for (let j = 0; j < pixelData.length; j++) {
|
|
307
|
+
pixelData[j] = arr[i][j];
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return labelMapImage;
|
|
311
|
+
}).filter(Boolean);
|
|
312
|
+
return labelMapImages;
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
function insertOverlappingPixelDataPlanar(_ref8) {
|
|
316
|
+
let {
|
|
317
|
+
segmentsOnFrame,
|
|
318
|
+
labelMapImages,
|
|
319
|
+
pixelDataChunks,
|
|
320
|
+
multiframe,
|
|
321
|
+
referencedImageIds,
|
|
322
|
+
validOrientations,
|
|
323
|
+
metadataProvider,
|
|
324
|
+
tolerance,
|
|
325
|
+
segmentsPixelIndices,
|
|
326
|
+
sopUIDImageIdIndexMap,
|
|
327
|
+
imageIdMaps
|
|
328
|
+
} = _ref8;
|
|
329
|
+
const {
|
|
330
|
+
SharedFunctionalGroupsSequence,
|
|
331
|
+
PerFrameFunctionalGroupsSequence,
|
|
332
|
+
Rows,
|
|
333
|
+
Columns
|
|
334
|
+
} = multiframe;
|
|
335
|
+
const sharedImageOrientationPatient = SharedFunctionalGroupsSequence.PlaneOrientationSequence ? SharedFunctionalGroupsSequence.PlaneOrientationSequence.ImageOrientationPatient : undefined;
|
|
336
|
+
const sliceLength = Columns * Rows;
|
|
337
|
+
const arrayOfSegmentData = getArrayOfSegmentData({
|
|
338
|
+
sliceLength,
|
|
339
|
+
Rows,
|
|
340
|
+
Columns,
|
|
341
|
+
validOrientations,
|
|
342
|
+
metadataProvider,
|
|
343
|
+
imageIdMaps,
|
|
344
|
+
segmentsOnFrame,
|
|
345
|
+
tolerance,
|
|
346
|
+
pixelDataChunks,
|
|
347
|
+
PerFrameFunctionalGroupsSequence,
|
|
348
|
+
labelMapImages,
|
|
349
|
+
sopUIDImageIdIndexMap,
|
|
350
|
+
multiframe,
|
|
351
|
+
sharedImageOrientationPatient,
|
|
352
|
+
segmentsPixelIndices
|
|
353
|
+
});
|
|
354
|
+
const arrayOfLabelMapImagesWithSegmentData = getArrayOfLabelMapImagesWithSegmentData({
|
|
355
|
+
arrayOfSegmentData,
|
|
356
|
+
referencedImageIds
|
|
357
|
+
});
|
|
358
|
+
return {
|
|
359
|
+
arrayOfLabelMapImages: arrayOfLabelMapImagesWithSegmentData,
|
|
360
|
+
hasOverlappingSegments: true
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
const getArrayOfSegmentData = _ref9 => {
|
|
364
|
+
let {
|
|
365
|
+
sliceLength,
|
|
366
|
+
Rows,
|
|
367
|
+
Columns,
|
|
368
|
+
validOrientations,
|
|
369
|
+
metadataProvider,
|
|
370
|
+
imageIdMaps,
|
|
371
|
+
segmentsOnFrame,
|
|
372
|
+
tolerance,
|
|
373
|
+
pixelDataChunks,
|
|
374
|
+
PerFrameFunctionalGroupsSequence,
|
|
375
|
+
labelMapImages,
|
|
376
|
+
sopUIDImageIdIndexMap,
|
|
377
|
+
multiframe,
|
|
378
|
+
sharedImageOrientationPatient,
|
|
379
|
+
segmentsPixelIndices
|
|
380
|
+
} = _ref9;
|
|
381
|
+
const arrayOfSegmentData = [];
|
|
382
|
+
const numberOfSegments = multiframe.SegmentSequence.length;
|
|
383
|
+
for (let currentSegmentIndex = 1; currentSegmentIndex <= numberOfSegments; ++currentSegmentIndex) {
|
|
384
|
+
const segmentData = getSegmentData({
|
|
385
|
+
PerFrameFunctionalGroupsSequence,
|
|
386
|
+
labelMapImages,
|
|
387
|
+
sopUIDImageIdIndexMap,
|
|
388
|
+
multiframe,
|
|
389
|
+
segmentIndex: currentSegmentIndex,
|
|
390
|
+
sliceLength,
|
|
391
|
+
Rows,
|
|
392
|
+
Columns,
|
|
393
|
+
validOrientations,
|
|
394
|
+
tolerance,
|
|
395
|
+
pixelDataChunks,
|
|
396
|
+
sharedImageOrientationPatient,
|
|
397
|
+
metadataProvider,
|
|
398
|
+
imageIdMaps,
|
|
399
|
+
segmentsOnFrame,
|
|
400
|
+
segmentsPixelIndices
|
|
401
|
+
});
|
|
402
|
+
compactMergeSegmentDataWithoutInformationLoss({
|
|
403
|
+
arrayOfSegmentData,
|
|
404
|
+
newSegmentData: segmentData
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
return arrayOfSegmentData;
|
|
408
|
+
};
|
|
409
|
+
const getSegmentData = _ref10 => {
|
|
410
|
+
let {
|
|
411
|
+
PerFrameFunctionalGroupsSequence,
|
|
412
|
+
labelMapImages,
|
|
413
|
+
sopUIDImageIdIndexMap,
|
|
414
|
+
multiframe,
|
|
415
|
+
segmentIndex,
|
|
416
|
+
sliceLength,
|
|
417
|
+
Rows,
|
|
418
|
+
Columns,
|
|
419
|
+
validOrientations,
|
|
420
|
+
tolerance,
|
|
421
|
+
pixelDataChunks,
|
|
422
|
+
sharedImageOrientationPatient,
|
|
423
|
+
metadataProvider,
|
|
424
|
+
imageIdMaps,
|
|
425
|
+
segmentsOnFrame,
|
|
426
|
+
segmentsPixelIndices
|
|
427
|
+
} = _ref10;
|
|
428
|
+
const segmentData = [];
|
|
429
|
+
for (let currentLabelMapImageIndex = 0; currentLabelMapImageIndex < labelMapImages.length; currentLabelMapImageIndex++) {
|
|
430
|
+
const currentLabelMapImage = labelMapImages[currentLabelMapImageIndex];
|
|
431
|
+
const referencedImageId = currentLabelMapImage.referencedImageId;
|
|
432
|
+
const PerFrameFunctionalGroupsIndex = PerFrameFunctionalGroupsSequence.findIndex((PerFrameFunctionalGroups, currentSequenceIndex) => {
|
|
433
|
+
const {
|
|
434
|
+
segmentIndex: groupsSegmentIndex,
|
|
435
|
+
referencedImageId: groupsReferenceImageId
|
|
436
|
+
} = extractInfoFromPerFrameFunctionalGroups({
|
|
437
|
+
PerFrameFunctionalGroups,
|
|
438
|
+
sequenceIndex: currentSequenceIndex,
|
|
439
|
+
sopUIDImageIdIndexMap,
|
|
440
|
+
multiframe
|
|
441
|
+
});
|
|
442
|
+
const isCorrectPerFrameFunctionalGroup = groupsSegmentIndex === segmentIndex && groupsReferenceImageId === currentLabelMapImage.referencedImageId;
|
|
443
|
+
return isCorrectPerFrameFunctionalGroup;
|
|
444
|
+
});
|
|
445
|
+
if (PerFrameFunctionalGroupsIndex === -1) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence[PerFrameFunctionalGroupsIndex];
|
|
449
|
+
const alignedPixelDataI = getAlignedPixelData({
|
|
450
|
+
sharedImageOrientationPatient,
|
|
451
|
+
PerFrameFunctionalGroups,
|
|
452
|
+
pixelDataChunks,
|
|
453
|
+
sequenceIndex: PerFrameFunctionalGroupsIndex,
|
|
454
|
+
sliceLength,
|
|
455
|
+
Rows,
|
|
456
|
+
Columns,
|
|
457
|
+
validOrientations,
|
|
458
|
+
tolerance
|
|
459
|
+
});
|
|
460
|
+
checkImageDimensions({
|
|
461
|
+
metadataProvider,
|
|
462
|
+
Rows,
|
|
463
|
+
Columns,
|
|
464
|
+
imageId: referencedImageId
|
|
465
|
+
});
|
|
466
|
+
const indexCache = [];
|
|
467
|
+
const segmentationDataForImageId = alignedPixelDataI.data.map((pixel, pixelIndex) => {
|
|
468
|
+
const pixelValue = pixel ? segmentIndex : 0;
|
|
469
|
+
if (pixelValue) {
|
|
470
|
+
indexCache.push(pixelIndex);
|
|
471
|
+
}
|
|
472
|
+
return pixel ? segmentIndex : 0;
|
|
473
|
+
});
|
|
474
|
+
const hasWrittenSegmentationData = indexCache.length > 0;
|
|
475
|
+
if (hasWrittenSegmentationData) {
|
|
476
|
+
segmentData[currentLabelMapImageIndex] = segmentationDataForImageId;
|
|
477
|
+
}
|
|
478
|
+
const imageIdIndex = imageIdMaps.indices[referencedImageId];
|
|
479
|
+
updateSegmentsOnFrame({
|
|
480
|
+
imageIdIndex,
|
|
481
|
+
segmentIndex,
|
|
482
|
+
segmentsOnFrame
|
|
483
|
+
});
|
|
484
|
+
updateSegmentsPixelIndices({
|
|
485
|
+
imageIdIndex,
|
|
486
|
+
segmentIndex,
|
|
487
|
+
segmentsPixelIndices,
|
|
488
|
+
indexCache
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
return segmentData;
|
|
492
|
+
};
|
|
180
493
|
|
|
181
|
-
export { createLabelmapsFromBufferInternal, insertPixelDataPlanar };
|
|
494
|
+
export { createLabelmapsFromBufferInternal, insertOverlappingPixelDataPlanar, insertPixelDataPlanar };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/adapters",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Adapters for Cornerstone3D to/from formats including DICOM SR and others",
|
|
5
5
|
"module": "./dist/esm/index.js",
|
|
6
6
|
"types": "./dist/esm/index.d.ts",
|
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
"ndarray": "^1.0.19"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
|
-
"@cornerstonejs/core": "^3.
|
|
88
|
-
"@cornerstonejs/tools": "^3.
|
|
87
|
+
"@cornerstonejs/core": "^3.5.0",
|
|
88
|
+
"@cornerstonejs/tools": "^3.5.0"
|
|
89
89
|
},
|
|
90
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "f184a6ef9eaa97b3a4311e73fdc1de269e77a349"
|
|
91
91
|
}
|