@cornerstonejs/adapters 5.4.10 → 5.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/adapters/Cornerstone/Segmentation_4X.d.ts +2 -2
- package/dist/esm/adapters/Cornerstone/Segmentation_4X.js +215 -69
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateSegmentation.d.ts +10 -1
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateSegmentation.js +286 -5
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateToolState.d.ts +20 -1
- package/dist/esm/adapters/Cornerstone3D/Segmentation/generateToolState.js +37 -7
- package/dist/esm/adapters/Cornerstone3D/Segmentation/index.d.ts +2 -2
- package/dist/esm/adapters/Cornerstone3D/Segmentation/index.js +2 -1
- package/dist/esm/adapters/Cornerstone3D/Segmentation/labelmapImagesFromBuffer.d.ts +45 -3
- package/dist/esm/adapters/Cornerstone3D/Segmentation/labelmapImagesFromBuffer.js +577 -145
- package/dist/esm/adapters/Cornerstone3D/Segmentation/perFrameFunctionalGroups.d.ts +17 -0
- package/dist/esm/adapters/Cornerstone3D/Segmentation/perFrameFunctionalGroups.js +116 -0
- package/dist/esm/adapters/Cornerstone3D/encodePixelData.d.ts +46 -0
- package/dist/esm/adapters/Cornerstone3D/encodePixelData.js +440 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +7 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function generateSegmentation(images: object[], inputLabelmaps3D: any | any[], userOptions?: any): Blob;
|
|
2
|
-
export function fillSegmentation(segmentation: any, inputLabelmaps3D: any, userOptions?: {}): object;
|
|
2
|
+
export function fillSegmentation(segmentation: any, inputLabelmaps3D: any, userOptions?: {}, images?: any[], metadata?: any): object;
|
|
3
3
|
export function _getLabelmapsFromReferencedFrameIndicies(labelmap3D: any, referencedFrameIndicies: any): any[];
|
|
4
4
|
export function _createSegFromImages(images: any[], isMultiframe: boolean, options: any): any;
|
|
5
5
|
export function generateToolState(referencedImageIds: string[], arrayBuffer: ArrayBuffer, metadataProvider: any, options: obj): [];
|
|
@@ -18,7 +18,7 @@ export function getSegmentMetadata(multiframe: any, seriesInstanceUid: any): {
|
|
|
18
18
|
seriesInstanceUid: any;
|
|
19
19
|
data: any[];
|
|
20
20
|
};
|
|
21
|
-
export function readFromUnpackedChunks(chunks:
|
|
21
|
+
export function readFromUnpackedChunks(chunks: Uint8Array[] | Uint16Array[], offset: number, length: number): Uint8Array | Uint16Array;
|
|
22
22
|
export function getUnpackedOffsetAndLength(chunks: any, offset: any, length: any): {
|
|
23
23
|
start: {
|
|
24
24
|
chunkIndex: number;
|
|
@@ -4,6 +4,8 @@ import ndarray from 'ndarray';
|
|
|
4
4
|
import getDatasetsFromImages from '../helpers/getDatasetsFromImages.js';
|
|
5
5
|
import checkOrientation from '../helpers/checkOrientation.js';
|
|
6
6
|
import { utilities as utilities$1 } from '@cornerstonejs/core';
|
|
7
|
+
import { getReferencedSourceImageSequenceItem, applyPerFrameFunctionalGroups } from '../Cornerstone3D/Segmentation/perFrameFunctionalGroups.js';
|
|
8
|
+
import { getBitmapFramesFromDataset, encodeFramesToTransferSyntax, RLE_LOSSLESS_TRANSFER_SYNTAX_UID as RLE_LOSSLESS_TRANSFER_SYNTAX_UID$1 } from '../Cornerstone3D/encodePixelData.js';
|
|
7
9
|
import { Events } from '../enums/Events.js';
|
|
8
10
|
|
|
9
11
|
const _utilities$orientatio = utilities.orientation,
|
|
@@ -19,6 +21,8 @@ const SegmentationDerivation = derivations.Segmentation;
|
|
|
19
21
|
const _utilities$compressio = utilities.compression,
|
|
20
22
|
encode = _utilities$compressio.encode,
|
|
21
23
|
decode = _utilities$compressio.decode;
|
|
24
|
+
const RLE_LOSSLESS_TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.5';
|
|
25
|
+
const EXPLICIT_VR_LITTLE_ENDIAN_TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.1';
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
28
|
*
|
|
@@ -29,7 +33,7 @@ const _utilities$compressio = utilities.compression,
|
|
|
29
33
|
*/
|
|
30
34
|
const generateSegmentationDefaultOptions = {
|
|
31
35
|
includeSliceSpacing: true,
|
|
32
|
-
|
|
36
|
+
transferSyntaxUid: RLE_LOSSLESS_TRANSFER_SYNTAX_UID
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
/**
|
|
@@ -49,89 +53,200 @@ function generateSegmentation(images, inputLabelmaps3D) {
|
|
|
49
53
|
return fillSegmentation(segmentation, inputLabelmaps3D, userOptions);
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
/**
|
|
57
|
+
* The set of non-zero segment values actually present in a frame's pixel data,
|
|
58
|
+
* collected in a single pass. `segmentsOnLabelmap` can be stale, so presence is
|
|
59
|
+
* verified against the pixels — but once per frame, not with one full-frame
|
|
60
|
+
* scan per listed segment.
|
|
61
|
+
*/
|
|
62
|
+
function segmentsPresentInFrame(pixelData) {
|
|
63
|
+
const presentSegments = new Set();
|
|
64
|
+
if (!pixelData) {
|
|
65
|
+
return presentSegments;
|
|
66
|
+
}
|
|
67
|
+
for (let i = 0; i < pixelData.length; i++) {
|
|
68
|
+
const value = pixelData[i];
|
|
69
|
+
if (value !== 0) {
|
|
70
|
+
presentSegments.add(value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return presentSegments;
|
|
74
|
+
}
|
|
75
|
+
|
|
52
76
|
/**
|
|
53
77
|
* Fills a given segmentation object with data from the input labelmaps3D
|
|
54
78
|
*
|
|
55
79
|
* @param segmentation - The segmentation object to be filled.
|
|
56
80
|
* @param inputLabelmaps3D - An array of 3D labelmaps, or a single 3D labelmap.
|
|
57
81
|
* @param userOptions - Optional configuration settings. Will override the default options.
|
|
82
|
+
* - `transferSyntaxUid` — the output transfer syntax. Defaults to RLE Lossless
|
|
83
|
+
* (`1.2.840.10008.1.2.5`); Explicit VR Little Endian
|
|
84
|
+
* (`1.2.840.10008.1.2.1`) is also supported. Any other value throws.
|
|
85
|
+
* - `rleEncode` — **obsolete / ignored.** This option is no longer read. It
|
|
86
|
+
* never worked correctly in prior versions (the old path did not produce a
|
|
87
|
+
* valid RLE-encoded SEG), so it has been dropped rather than fixed. RLE
|
|
88
|
+
* output is now selected via `transferSyntaxUid` (RLE Lossless is the
|
|
89
|
+
* default), and encoding is handled by `encodeFramesToTransferSyntax`.
|
|
58
90
|
*
|
|
59
91
|
* @returns {object} The filled segmentation object.
|
|
60
92
|
*/
|
|
61
93
|
function fillSegmentation(segmentation, inputLabelmaps3D) {
|
|
94
|
+
var _ref, _options$transferSynt;
|
|
62
95
|
let userOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
96
|
+
let images = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
|
|
97
|
+
let metadata = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
|
|
63
98
|
const options = Object.assign({}, generateSegmentationDefaultOptions, userOptions);
|
|
64
99
|
|
|
65
100
|
// Use another variable so we don't redefine labelmaps3D.
|
|
66
101
|
const labelmaps3D = Array.isArray(inputLabelmaps3D) ? inputLabelmaps3D : [inputLabelmaps3D];
|
|
67
|
-
let numberOfFrames = 0;
|
|
68
102
|
const referencedFramesPerLabelmap = [];
|
|
103
|
+
const frameDescriptors = [];
|
|
69
104
|
for (let labelmapIndex = 0; labelmapIndex < labelmaps3D.length; labelmapIndex++) {
|
|
70
105
|
const labelmap3D = labelmaps3D[labelmapIndex];
|
|
71
106
|
const labelmaps2D = labelmap3D.labelmaps2D,
|
|
72
|
-
|
|
107
|
+
segmentMetadata = labelmap3D.metadata;
|
|
73
108
|
const referencedFramesPerSegment = [];
|
|
74
|
-
for (let i = 1; i <
|
|
75
|
-
if (
|
|
109
|
+
for (let i = 1; i < segmentMetadata.length; i++) {
|
|
110
|
+
if (segmentMetadata[i]) {
|
|
76
111
|
referencedFramesPerSegment[i] = [];
|
|
77
112
|
}
|
|
78
113
|
}
|
|
79
114
|
for (let i = 0; i < labelmaps2D.length; i++) {
|
|
80
115
|
const labelmap2D = labelmaps2D[i];
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
segmentsOnLabelmap.forEach(segmentIndex => {
|
|
84
|
-
if (segmentIndex !== 0) {
|
|
85
|
-
referencedFramesPerSegment[segmentIndex].push(i);
|
|
86
|
-
numberOfFrames++;
|
|
87
|
-
}
|
|
88
|
-
});
|
|
116
|
+
if (!(labelmap2D !== null && labelmap2D !== void 0 && labelmap2D.pixelData)) {
|
|
117
|
+
continue;
|
|
89
118
|
}
|
|
119
|
+
const segmentsOnLabelmap = labelmap2D.segmentsOnLabelmap;
|
|
120
|
+
const presentSegments = segmentsPresentInFrame(labelmap2D.pixelData);
|
|
121
|
+
segmentsOnLabelmap.forEach(segmentIndex => {
|
|
122
|
+
if (segmentIndex !== 0 && segmentMetadata[segmentIndex] && referencedFramesPerSegment[segmentIndex] && presentSegments.has(segmentIndex)) {
|
|
123
|
+
referencedFramesPerSegment[segmentIndex].push(i);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
90
126
|
}
|
|
91
127
|
referencedFramesPerLabelmap[labelmapIndex] = referencedFramesPerSegment;
|
|
92
128
|
}
|
|
129
|
+
let numberOfFrames = 0;
|
|
130
|
+
for (let labelmapIndex = 0; labelmapIndex < labelmaps3D.length; labelmapIndex++) {
|
|
131
|
+
const referencedFramesPerSegment = referencedFramesPerLabelmap[labelmapIndex];
|
|
132
|
+
const labelmap3D = labelmaps3D[labelmapIndex];
|
|
133
|
+
const segmentMetadata = labelmap3D.metadata;
|
|
134
|
+
for (let segmentIndex = 1; segmentIndex < referencedFramesPerSegment.length; segmentIndex++) {
|
|
135
|
+
const referencedFrameIndicies = referencedFramesPerSegment[segmentIndex];
|
|
136
|
+
if (!(referencedFrameIndicies !== null && referencedFrameIndicies !== void 0 && referencedFrameIndicies.length) || !segmentMetadata[segmentIndex]) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const labelmaps = _getLabelmapsFromReferencedFrameIndicies(labelmap3D, referencedFrameIndicies);
|
|
140
|
+
if (!labelmaps.length || !labelmaps.some(frame => (frame === null || frame === void 0 ? void 0 : frame.length) && frame.some(v => v !== 0))) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
numberOfFrames += referencedFrameIndicies.length;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (numberOfFrames === 0) {
|
|
147
|
+
throw new Error('No non-empty segmentation frames found for SEG export');
|
|
148
|
+
}
|
|
93
149
|
segmentation.setNumberOfFrames(numberOfFrames);
|
|
94
150
|
for (let labelmapIndex = 0; labelmapIndex < labelmaps3D.length; labelmapIndex++) {
|
|
95
151
|
const referencedFramesPerSegment = referencedFramesPerLabelmap[labelmapIndex];
|
|
96
152
|
const labelmap3D = labelmaps3D[labelmapIndex];
|
|
97
|
-
const
|
|
153
|
+
const segmentMetadata = labelmap3D.metadata;
|
|
98
154
|
for (let segmentIndex = 1; segmentIndex < referencedFramesPerSegment.length; segmentIndex++) {
|
|
99
155
|
const referencedFrameIndicies = referencedFramesPerSegment[segmentIndex];
|
|
100
|
-
if (referencedFrameIndicies) {
|
|
101
|
-
|
|
102
|
-
const referencedFrameNumbers = referencedFrameIndicies.map(element => {
|
|
103
|
-
return element + 1;
|
|
104
|
-
});
|
|
105
|
-
const segmentMetadata = metadata[segmentIndex];
|
|
106
|
-
const labelmaps = _getLabelmapsFromReferencedFrameIndicies(labelmap3D, referencedFrameIndicies);
|
|
107
|
-
segmentation.addSegmentFromLabelmap(segmentMetadata, labelmaps, segmentIndex, referencedFrameNumbers);
|
|
156
|
+
if (!(referencedFrameIndicies !== null && referencedFrameIndicies !== void 0 && referencedFrameIndicies.length) || !segmentMetadata[segmentIndex]) {
|
|
157
|
+
continue;
|
|
108
158
|
}
|
|
159
|
+
const labelmaps = _getLabelmapsFromReferencedFrameIndicies(labelmap3D, referencedFrameIndicies);
|
|
160
|
+
if (!labelmaps.length || !labelmaps.some(frame => (frame === null || frame === void 0 ? void 0 : frame.length) && frame.some(v => v !== 0))) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Frame numbers start from 1.
|
|
165
|
+
const referencedFrameNumbers = referencedFrameIndicies.map(element => element + 1);
|
|
166
|
+
referencedFrameNumbers.forEach(frameNumber => {
|
|
167
|
+
frameDescriptors.push({
|
|
168
|
+
referencedSegmentNumber: segmentIndex,
|
|
169
|
+
sourceFrameIndex: frameNumber - 1
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
segmentation.addSegmentFromLabelmap(segmentMetadata[segmentIndex], labelmaps, segmentIndex, referencedFrameNumbers);
|
|
109
173
|
}
|
|
110
174
|
}
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
175
|
+
if (frameDescriptors.length && images !== null && images !== void 0 && images.length && metadata) {
|
|
176
|
+
// Every SEG frame must reference a resolvable source SOP Instance UID.
|
|
177
|
+
// Reject rather than silently dropping frames: a dropped frame would make
|
|
178
|
+
// the per-frame functional groups disagree with the encoded PixelData and
|
|
179
|
+
// produce a SEG whose source-image references are unreliable.
|
|
180
|
+
const perFrameInputs = frameDescriptors.map(desc => {
|
|
181
|
+
const sourceImageSequenceItem = getReferencedSourceImageSequenceItem(images[desc.sourceFrameIndex], metadata);
|
|
182
|
+
if (!(sourceImageSequenceItem !== null && sourceImageSequenceItem !== void 0 && sourceImageSequenceItem.ReferencedSOPInstanceUID)) {
|
|
183
|
+
throw new Error("Cannot resolve a source ReferencedSOPInstanceUID for SEG frame " + "(sourceFrameIndex ".concat(desc.sourceFrameIndex, ", segment ") + "".concat(desc.referencedSegmentNumber, "). Refusing to write a SEG with ") + "unreliable source image references.");
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
referencedSegmentNumber: desc.referencedSegmentNumber,
|
|
187
|
+
sourceImageSequenceItem
|
|
188
|
+
};
|
|
124
189
|
});
|
|
125
|
-
segmentation.dataset
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
segmentation.dataset.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
190
|
+
applyPerFrameFunctionalGroups(segmentation.dataset, perFrameInputs);
|
|
191
|
+
}
|
|
192
|
+
const transferSyntaxUid = (_ref = (_options$transferSynt = options.transferSyntaxUid) !== null && _options$transferSynt !== void 0 ? _options$transferSynt : options.transferSyntaxUID) !== null && _ref !== void 0 ? _ref : RLE_LOSSLESS_TRANSFER_SYNTAX_UID;
|
|
193
|
+
if (transferSyntaxUid === RLE_LOSSLESS_TRANSFER_SYNTAX_UID) {
|
|
194
|
+
const isBinaryBitmap = segmentation.dataset.SegmentationType === 'BINARY' && Number(segmentation.dataset.BitsAllocated) === 1;
|
|
195
|
+
if (isBinaryBitmap) {
|
|
196
|
+
segmentation.bitPackPixelData();
|
|
197
|
+
const _getBitmapFramesFromD = getBitmapFramesFromDataset(segmentation.dataset),
|
|
198
|
+
frames = _getBitmapFramesFromD.frames,
|
|
199
|
+
bitsAllocated = _getBitmapFramesFromD.bitsAllocated;
|
|
200
|
+
const _encodeFramesToTransf = encodeFramesToTransferSyntax({
|
|
201
|
+
transferSyntaxUID: RLE_LOSSLESS_TRANSFER_SYNTAX_UID$1,
|
|
202
|
+
frames,
|
|
203
|
+
bitsAllocated,
|
|
204
|
+
columns: Number(segmentation.dataset.Columns) || undefined
|
|
205
|
+
}),
|
|
206
|
+
pixelData = _encodeFramesToTransf.pixelData,
|
|
207
|
+
pixelDataVR = _encodeFramesToTransf.pixelDataVR;
|
|
208
|
+
segmentation.dataset.PixelData = pixelData;
|
|
209
|
+
segmentation.dataset._vrMap.PixelData = pixelDataVR;
|
|
210
|
+
if (!options.skipTransferSyntaxMeta) {
|
|
211
|
+
segmentation.dataset._meta.TransferSyntaxUID = {
|
|
212
|
+
Value: [RLE_LOSSLESS_TRANSFER_SYNTAX_UID],
|
|
213
|
+
vr: 'UI'
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
segmentation.dataset.SpecificCharacterSet = 'ISO_IR 192';
|
|
217
|
+
} else {
|
|
218
|
+
const rleEncodedFrames = encode(segmentation.dataset.PixelData, numberOfFrames, segmentation.dataset.Rows, segmentation.dataset.Columns);
|
|
219
|
+
|
|
220
|
+
// Fractional/8-bit labelmaps: legacy dcmjs row RLE (not valid for 1-bit packed binary).
|
|
221
|
+
segmentation.assignToDataset({
|
|
222
|
+
BitsAllocated: '8',
|
|
223
|
+
BitsStored: '8',
|
|
224
|
+
HighBit: '7',
|
|
225
|
+
SegmentationType: 'FRACTIONAL',
|
|
226
|
+
SegmentationFractionalType: 'PROBABILITY',
|
|
227
|
+
MaximumFractionalValue: '255'
|
|
228
|
+
});
|
|
229
|
+
if (!options.skipTransferSyntaxMeta) {
|
|
230
|
+
segmentation.dataset._meta.TransferSyntaxUID = {
|
|
231
|
+
Value: [RLE_LOSSLESS_TRANSFER_SYNTAX_UID],
|
|
232
|
+
vr: 'UI'
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
segmentation.dataset.SpecificCharacterSet = 'ISO_IR 192';
|
|
236
|
+
segmentation.dataset._vrMap.PixelData = 'OB';
|
|
237
|
+
segmentation.dataset.PixelData = rleEncodedFrames;
|
|
238
|
+
}
|
|
239
|
+
} else if (transferSyntaxUid === EXPLICIT_VR_LITTLE_ENDIAN_TRANSFER_SYNTAX_UID) {
|
|
240
|
+
// For explicit VR little endian, at least bitpack the data.
|
|
134
241
|
segmentation.bitPackPixelData();
|
|
242
|
+
if (!options.skipTransferSyntaxMeta) {
|
|
243
|
+
segmentation.dataset._meta.TransferSyntaxUID = {
|
|
244
|
+
Value: [EXPLICIT_VR_LITTLE_ENDIAN_TRANSFER_SYNTAX_UID],
|
|
245
|
+
vr: 'UI'
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
throw new Error("Unsupported SEG transfer syntax: ".concat(transferSyntaxUid, ". ") + "Supported: ".concat(RLE_LOSSLESS_TRANSFER_SYNTAX_UID, ", ").concat(EXPLICIT_VR_LITTLE_ENDIAN_TRANSFER_SYNTAX_UID));
|
|
135
250
|
}
|
|
136
251
|
return segmentation;
|
|
137
252
|
}
|
|
@@ -581,9 +696,9 @@ function checkSEGsOverlapping(pixelData, multiframe, imageIds, validOrientations
|
|
|
581
696
|
frameSegmentsMapping.set(imageIdIndex, [frameSegment]);
|
|
582
697
|
}
|
|
583
698
|
}
|
|
584
|
-
for (let
|
|
585
|
-
var
|
|
586
|
-
let role =
|
|
699
|
+
for (let _ref2 of frameSegmentsMapping.entries()) {
|
|
700
|
+
var _ref3 = _slicedToArray(_ref2, 2);
|
|
701
|
+
let role = _ref3[1];
|
|
587
702
|
let temp2DArray = new TypedArrayConstructor(sliceLength).fill(0);
|
|
588
703
|
for (let i = 0; i < role.length; ++i) {
|
|
589
704
|
const frameSegment = role[i];
|
|
@@ -711,11 +826,29 @@ function insertOverlappingPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray,
|
|
|
711
826
|
tempSegmentsOnFrame = structuredClone(segmentsOnFrameArray[m]);
|
|
712
827
|
}
|
|
713
828
|
}
|
|
829
|
+
function getReferencedSegmentNumberFromIdentificationSequence(segmentIdentificationSequence) {
|
|
830
|
+
if (!segmentIdentificationSequence) {
|
|
831
|
+
return undefined;
|
|
832
|
+
}
|
|
833
|
+
const normalized = Array.isArray(segmentIdentificationSequence) ? segmentIdentificationSequence[0] : segmentIdentificationSequence;
|
|
834
|
+
return normalized === null || normalized === void 0 ? void 0 : normalized.ReferencedSegmentNumber;
|
|
835
|
+
}
|
|
836
|
+
function getSharedFunctionalGroupsSequence(multiframe) {
|
|
837
|
+
const shared = multiframe === null || multiframe === void 0 ? void 0 : multiframe.SharedFunctionalGroupsSequence;
|
|
838
|
+
if (Array.isArray(shared)) {
|
|
839
|
+
return shared.length > 0 ? shared[0] : undefined;
|
|
840
|
+
}
|
|
841
|
+
return shared;
|
|
842
|
+
}
|
|
714
843
|
const getSegmentIndex = (multiframe, frame) => {
|
|
715
|
-
const PerFrameFunctionalGroupsSequence = multiframe.PerFrameFunctionalGroupsSequence
|
|
716
|
-
|
|
717
|
-
const
|
|
718
|
-
|
|
844
|
+
const PerFrameFunctionalGroupsSequence = multiframe.PerFrameFunctionalGroupsSequence;
|
|
845
|
+
const PerFrameFunctionalGroups = PerFrameFunctionalGroupsSequence === null || PerFrameFunctionalGroupsSequence === void 0 ? void 0 : PerFrameFunctionalGroupsSequence[frame];
|
|
846
|
+
const fromPerFrame = getReferencedSegmentNumberFromIdentificationSequence(PerFrameFunctionalGroups === null || PerFrameFunctionalGroups === void 0 ? void 0 : PerFrameFunctionalGroups.SegmentIdentificationSequence);
|
|
847
|
+
if (fromPerFrame !== undefined) {
|
|
848
|
+
return fromPerFrame;
|
|
849
|
+
}
|
|
850
|
+
const shared = getSharedFunctionalGroupsSequence(multiframe);
|
|
851
|
+
return getReferencedSegmentNumberFromIdentificationSequence(shared === null || shared === void 0 ? void 0 : shared.SegmentIdentificationSequence);
|
|
719
852
|
};
|
|
720
853
|
function insertPixelDataPlanar(segmentsOnFrame, segmentsOnFrameArray, labelmapBufferArray, pixelData, multiframe, imageIds, validOrientations, metadataProvider, tolerance, TypedArrayConstructor, segmentsPixelIndices, sopUIDImageIdIndexMap, imageIdMaps, eventTarget, triggerEvent) {
|
|
721
854
|
const SharedFunctionalGroupsSequence = multiframe.SharedFunctionalGroupsSequence,
|
|
@@ -894,13 +1027,14 @@ function getImageIdOfSourceImageBySourceImageSequence(SourceImageSequence, sopUI
|
|
|
894
1027
|
} else if (baseImageId.includes('dicomfile:')) {
|
|
895
1028
|
// dicomfile base 1, despite having frame=
|
|
896
1029
|
return baseImageId.replace(/frame=\d+/, "frame=".concat(ReferencedFrameNumber));
|
|
897
|
-
} else if (baseImageId.includes('frame=')) {
|
|
898
|
-
|
|
1030
|
+
} else if (baseImageId.includes('frame=') || baseImageId.includes('wadouri:')) {
|
|
1031
|
+
// OHIF local/wadouri use 1-based ?frame= / &frame= (same as dicomfile:).
|
|
1032
|
+
return baseImageId.replace(/frame=\d+/, "frame=".concat(ReferencedFrameNumber));
|
|
899
1033
|
} else {
|
|
900
1034
|
if (baseImageId.includes('wadors:')) {
|
|
901
1035
|
return "".concat(baseImageId, "/frames/").concat(ReferencedFrameNumber);
|
|
902
1036
|
} else {
|
|
903
|
-
return "".concat(baseImageId, "?frame=").concat(ReferencedFrameNumber
|
|
1037
|
+
return "".concat(baseImageId, "?frame=").concat(ReferencedFrameNumber);
|
|
904
1038
|
}
|
|
905
1039
|
}
|
|
906
1040
|
}
|
|
@@ -1049,28 +1183,40 @@ function getSegmentMetadata(multiframe, seriesInstanceUid) {
|
|
|
1049
1183
|
}
|
|
1050
1184
|
|
|
1051
1185
|
/**
|
|
1052
|
-
* Reads a range of bytes from an array of
|
|
1053
|
-
*
|
|
1186
|
+
* Reads a range of SAMPLES (typed-array elements, not bytes) from an array of
|
|
1187
|
+
* typed-array chunks and aggregates them into a new typed array of the same
|
|
1188
|
+
* element type. Chunks may be Uint8Array (8-bit / packed binary) or
|
|
1189
|
+
* Uint16Array (16-bit labelmaps); offsets and lengths are in elements so the
|
|
1190
|
+
* same sample-indexed math works for both widths.
|
|
1054
1191
|
*
|
|
1055
|
-
* @param {
|
|
1056
|
-
* @param {number} offset - The offset of the first
|
|
1057
|
-
* @param {number} length - The number of
|
|
1058
|
-
* @returns {Uint8Array} A
|
|
1192
|
+
* @param {Uint8Array[]|Uint16Array[]} chunks - Typed-array chunks (all the same type).
|
|
1193
|
+
* @param {number} offset - The offset of the first sample to read.
|
|
1194
|
+
* @param {number} length - The number of samples to read.
|
|
1195
|
+
* @returns {Uint8Array|Uint16Array} A view/copy containing the requested samples.
|
|
1059
1196
|
*/
|
|
1060
1197
|
function readFromUnpackedChunks(chunks, offset, length) {
|
|
1198
|
+
var _chunks$0$constructor, _chunks$, _TypedArray$BYTES_PER;
|
|
1061
1199
|
const mapping = getUnpackedOffsetAndLength(chunks, offset, length);
|
|
1062
|
-
|
|
1200
|
+
const TypedArray = (_chunks$0$constructor = (_chunks$ = chunks[0]) === null || _chunks$ === void 0 ? void 0 : _chunks$.constructor) !== null && _chunks$0$constructor !== void 0 ? _chunks$0$constructor : Uint8Array;
|
|
1201
|
+
const bytesPerElement = (_TypedArray$BYTES_PER = TypedArray.BYTES_PER_ELEMENT) !== null && _TypedArray$BYTES_PER !== void 0 ? _TypedArray$BYTES_PER : 1;
|
|
1202
|
+
|
|
1203
|
+
// Chunks are typically subarray views that share one backing ArrayBuffer, so
|
|
1204
|
+
// `chunk.buffer` is the whole buffer (byte 0 = chunk 0) while the computed
|
|
1205
|
+
// offsets are chunk-relative. Add each chunk's own byteOffset so a view into
|
|
1206
|
+
// chunk N reads chunk N's bytes and not chunk 0's; element offsets are scaled
|
|
1207
|
+
// by the chunk's element width so 16-bit chunks are not read as bytes.
|
|
1063
1208
|
// If all the data is in one chunk, we can just slice that chunk
|
|
1064
1209
|
if (mapping.start.chunkIndex === mapping.end.chunkIndex) {
|
|
1065
|
-
|
|
1210
|
+
const chunk = chunks[mapping.start.chunkIndex];
|
|
1211
|
+
return new TypedArray(chunk.buffer, chunk.byteOffset + mapping.start.offset * bytesPerElement, length);
|
|
1066
1212
|
} else {
|
|
1067
|
-
// If the data spans multiple chunks, we need to create a new
|
|
1068
|
-
let result = new
|
|
1213
|
+
// If the data spans multiple chunks, we need to create a new typed array and copy the data from each chunk
|
|
1214
|
+
let result = new TypedArray(length);
|
|
1069
1215
|
let resultOffset = 0;
|
|
1070
1216
|
for (let i = mapping.start.chunkIndex; i <= mapping.end.chunkIndex; i++) {
|
|
1071
1217
|
let start = i === mapping.start.chunkIndex ? mapping.start.offset : 0;
|
|
1072
1218
|
let end = i === mapping.end.chunkIndex ? mapping.end.offset : chunks[i].length;
|
|
1073
|
-
result.set(new
|
|
1219
|
+
result.set(new TypedArray(chunks[i].buffer, chunks[i].byteOffset + start * bytesPerElement, end - start), resultOffset);
|
|
1074
1220
|
resultOffset += end - start;
|
|
1075
1221
|
}
|
|
1076
1222
|
return result;
|
|
@@ -1112,10 +1258,10 @@ function calculateCentroid(imageIdIndexBufferIndex, multiframe, metadataProvider
|
|
|
1112
1258
|
let worldYAcc = 0;
|
|
1113
1259
|
let worldZAcc = 0;
|
|
1114
1260
|
let count = 0;
|
|
1115
|
-
for (const
|
|
1116
|
-
var
|
|
1117
|
-
const imageIdIndex =
|
|
1118
|
-
const bufferIndices =
|
|
1261
|
+
for (const _ref4 of Object.entries(imageIdIndexBufferIndex)) {
|
|
1262
|
+
var _ref5 = _slicedToArray(_ref4, 2);
|
|
1263
|
+
const imageIdIndex = _ref5[0];
|
|
1264
|
+
const bufferIndices = _ref5[1];
|
|
1119
1265
|
const z = Number(imageIdIndex);
|
|
1120
1266
|
if (!bufferIndices || bufferIndices.length === 0) {
|
|
1121
1267
|
continue;
|
|
@@ -4,5 +4,14 @@ interface IOptions {
|
|
|
4
4
|
type Options = IOptions & {
|
|
5
5
|
[key: string]: number | boolean | unknown | string;
|
|
6
6
|
};
|
|
7
|
+
declare function toLabelmap3DArray(inputLabelmaps3D: any): any[];
|
|
8
|
+
declare function collectNonEmptyFrameIndices(labelmap3DArray: any): number[];
|
|
9
|
+
declare function resolveReferencedImagesForExport(images: Array<{
|
|
10
|
+
imageId?: string;
|
|
11
|
+
} | undefined | null>, frameIndices: number[]): {
|
|
12
|
+
imageId?: string;
|
|
13
|
+
}[];
|
|
14
|
+
declare function maxSegmentValue(labelmap3DArray: any, frameIndices: number[]): number;
|
|
15
|
+
declare function collectSegmentSequence(labelmap3DArray: any): Record<string, unknown>[];
|
|
7
16
|
declare function generateSegmentation(images: any, labelmaps: any, metadata: any, options?: Options): any;
|
|
8
|
-
export { generateSegmentation };
|
|
17
|
+
export { generateSegmentation, toLabelmap3DArray, collectNonEmptyFrameIndices, resolveReferencedImagesForExport, maxSegmentValue, collectSegmentSequence, };
|