larvitar 2.0.5 → 2.0.6
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/README.md +2 -2
- package/dist/larvitar.js +1 -1
- package/package.json +6 -2
- package/.github/workflows/build-docs.yml +0 -59
- package/.github/workflows/codeql-analysis.yml +0 -71
- package/.github/workflows/deploy.yml +0 -37
- package/.vscode/settings.json +0 -4
- package/CODE_OF_CONDUCT.md +0 -76
- package/MIGRATION.md +0 -25
- package/bundler/webpack.common.js +0 -27
- package/bundler/webpack.dev.js +0 -23
- package/bundler/webpack.prod.js +0 -19
- package/decs.d.ts +0 -12
- package/imaging/MetaDataReadable.ts +0 -42
- package/imaging/MetaDataTypes.ts +0 -3491
- package/imaging/dataDictionary.json +0 -21866
- package/imaging/imageAnonymization.ts +0 -135
- package/imaging/imageColormaps.ts +0 -217
- package/imaging/imageContours.ts +0 -196
- package/imaging/imageIo.ts +0 -251
- package/imaging/imageLayers.ts +0 -121
- package/imaging/imageLoading.ts +0 -299
- package/imaging/imageParsing.ts +0 -444
- package/imaging/imagePresets.ts +0 -156
- package/imaging/imageRendering.ts +0 -1091
- package/imaging/imageReslice.ts +0 -87
- package/imaging/imageStore.ts +0 -487
- package/imaging/imageTags.ts +0 -609
- package/imaging/imageTools.js +0 -708
- package/imaging/imageUtils.ts +0 -1079
- package/imaging/loaders/commonLoader.ts +0 -275
- package/imaging/loaders/dicomLoader.ts +0 -66
- package/imaging/loaders/fileLoader.ts +0 -71
- package/imaging/loaders/multiframeLoader.ts +0 -435
- package/imaging/loaders/nrrdLoader.ts +0 -630
- package/imaging/loaders/resliceLoader.ts +0 -205
- package/imaging/monitors/memory.ts +0 -151
- package/imaging/monitors/performance.ts +0 -34
- package/imaging/parsers/ecg.ts +0 -54
- package/imaging/parsers/nrrd.js +0 -485
- package/imaging/tools/README.md +0 -27
- package/imaging/tools/custom/4dSliceScrollTool.js +0 -146
- package/imaging/tools/custom/BorderMagnifyTool.js +0 -99
- package/imaging/tools/custom/contourTool.js +0 -1884
- package/imaging/tools/custom/diameterTool.js +0 -141
- package/imaging/tools/custom/editMaskTool.js +0 -141
- package/imaging/tools/custom/ellipticalRoiOverlayTool.js +0 -534
- package/imaging/tools/custom/polygonSegmentationMixin.js +0 -245
- package/imaging/tools/custom/polylineScissorsTool.js +0 -59
- package/imaging/tools/custom/rectangleRoiOverlayTool.js +0 -564
- package/imaging/tools/custom/seedTool.js +0 -342
- package/imaging/tools/custom/setLabelMap3D.ts +0 -242
- package/imaging/tools/custom/thresholdsBrushTool.js +0 -161
- package/imaging/tools/default.ts +0 -594
- package/imaging/tools/interaction.ts +0 -266
- package/imaging/tools/io.ts +0 -229
- package/imaging/tools/main.ts +0 -427
- package/imaging/tools/segmentation.ts +0 -532
- package/imaging/tools/segmentations.md +0 -38
- package/imaging/tools/state.ts +0 -74
- package/imaging/tools/strategies/eraseFreehand.js +0 -76
- package/imaging/tools/strategies/fillFreehand.js +0 -79
- package/imaging/tools/strategies/index.js +0 -2
- package/imaging/tools/types.d.ts +0 -243
- package/imaging/types.d.ts +0 -200
- package/imaging/waveforms/ecg.ts +0 -191
- package/index.ts +0 -431
- package/jsdoc.json +0 -52
- package/rollup.config.js +0 -51
- package/template/.gitkeep +0 -0
- package/tsconfig.json +0 -102
package/imaging/parsers/nrrd.js
DELETED
|
@@ -1,485 +0,0 @@
|
|
|
1
|
-
import pako from "pako";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This is the mapping from the NRRD datatype as written in the NRRD header
|
|
5
|
-
* to the JS typed array equivalent.
|
|
6
|
-
*/
|
|
7
|
-
const NRRD_TYPES_TO_TYPEDARRAY = {
|
|
8
|
-
"signed char": Int8Array,
|
|
9
|
-
int8: Int8Array,
|
|
10
|
-
int8_t: Int8Array,
|
|
11
|
-
uchar: Uint8Array,
|
|
12
|
-
"unsigned char": Uint8Array,
|
|
13
|
-
uint8: Uint8Array,
|
|
14
|
-
uint8_t: Uint8Array,
|
|
15
|
-
short: Int16Array,
|
|
16
|
-
"short int": Int16Array,
|
|
17
|
-
"signed short": Int16Array,
|
|
18
|
-
"signed short int": Int16Array,
|
|
19
|
-
int16: Int16Array,
|
|
20
|
-
int16_t: Int16Array,
|
|
21
|
-
ushort: Uint16Array,
|
|
22
|
-
"unsigned short": Uint16Array,
|
|
23
|
-
"unsigned short int": Uint16Array,
|
|
24
|
-
uint16: Uint16Array,
|
|
25
|
-
uint16_t: Uint16Array,
|
|
26
|
-
int: Int32Array,
|
|
27
|
-
"signed int": Int32Array,
|
|
28
|
-
int32: Int32Array,
|
|
29
|
-
int32_t: Int32Array,
|
|
30
|
-
uint: Uint32Array,
|
|
31
|
-
"unsigned int": Uint32Array,
|
|
32
|
-
uint32: Uint32Array,
|
|
33
|
-
uint32_t: Uint32Array,
|
|
34
|
-
// OK for Node/V8/Chrome but not Firefox
|
|
35
|
-
// longlong: BigInt64Array,
|
|
36
|
-
// "long long": BigInt64Array,
|
|
37
|
-
// "long long int": BigInt64Array,
|
|
38
|
-
// "signed long long": BigInt64Array,
|
|
39
|
-
// "signed long long int": BigInt64Array,
|
|
40
|
-
// int64: BigInt64Array,
|
|
41
|
-
// int64_t: BigInt64Array,
|
|
42
|
-
// ulonglong: BigUint64Array,
|
|
43
|
-
// "unsigned long long": BigUint64Array,
|
|
44
|
-
// "unsigned long long int": BigUint64Array,
|
|
45
|
-
// uint64: BigUint64Array,
|
|
46
|
-
// uint64_t: BigUint64Array,
|
|
47
|
-
float: Float32Array,
|
|
48
|
-
double: Float64Array
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const NRRD_TYPES_TO_VIEW_GET = {
|
|
52
|
-
"signed char": "getInt8",
|
|
53
|
-
int8: "getInt8",
|
|
54
|
-
int8_t: "getInt8",
|
|
55
|
-
uchar: "getUint8",
|
|
56
|
-
"unsigned char": "getUint8",
|
|
57
|
-
uint8: "getUint8",
|
|
58
|
-
uint8_t: "getUint8",
|
|
59
|
-
short: "getInt16",
|
|
60
|
-
"short int": "getInt16",
|
|
61
|
-
"signed short": "getInt16",
|
|
62
|
-
"signed short int": "getInt16",
|
|
63
|
-
int16: "getInt16",
|
|
64
|
-
int16_t: "getInt16",
|
|
65
|
-
ushort: "getUint16",
|
|
66
|
-
"unsigned short": "getUint16",
|
|
67
|
-
"unsigned short int": "getUint16",
|
|
68
|
-
uint16: "getUint16",
|
|
69
|
-
uint16_t: "getUint16",
|
|
70
|
-
int: "getInt32",
|
|
71
|
-
"signed int": "getInt32",
|
|
72
|
-
int32: "getInt32",
|
|
73
|
-
int32_t: "getInt32",
|
|
74
|
-
uint: "getUint32",
|
|
75
|
-
"unsigned int": "getUint32",
|
|
76
|
-
uint32: "getUint32",
|
|
77
|
-
uint32_t: "getUint32",
|
|
78
|
-
// OK for Node/V8/Chrome but not Firefox
|
|
79
|
-
// longlong: null,
|
|
80
|
-
// "long long": null,
|
|
81
|
-
// "long long int": null,
|
|
82
|
-
// "signed long long": null,
|
|
83
|
-
// "signed long long int": null,
|
|
84
|
-
// int64: null,
|
|
85
|
-
// int64_t: null,
|
|
86
|
-
// ulonglong: null,
|
|
87
|
-
// "unsigned long long": null,
|
|
88
|
-
// "unsigned long long int": null,
|
|
89
|
-
// uint64: null,
|
|
90
|
-
// uint64_t: null,
|
|
91
|
-
float: "getFloat32",
|
|
92
|
-
double: "getFloat64"
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const SPACE_TO_SPACEDIMENSIONS = {
|
|
96
|
-
"right-anterior-superior": 3,
|
|
97
|
-
ras: 3,
|
|
98
|
-
"left-anterior-superior": 3,
|
|
99
|
-
las: 3,
|
|
100
|
-
"left-posterior-superior": 3,
|
|
101
|
-
lps: 3,
|
|
102
|
-
"right-anterior-superior-time": 4,
|
|
103
|
-
rast: 4,
|
|
104
|
-
"left-anterior-superior-time": 4,
|
|
105
|
-
last: 4,
|
|
106
|
-
"left-posterior-superior-time": 4,
|
|
107
|
-
lpst: 4,
|
|
108
|
-
"scanner-xyz": 3,
|
|
109
|
-
"scanner-xyz-time": 4,
|
|
110
|
-
"3d-right-handed": 3,
|
|
111
|
-
"3d-left-handed": 3,
|
|
112
|
-
"3d-right-handed-time": 4,
|
|
113
|
-
"3d-left-handed-time": 4
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// in NRRD, some "kinds" have to respect a certain size. For example, the kind
|
|
117
|
-
// "quaternion" has to be of size 4 (xyzw).
|
|
118
|
-
// When the value is 'null', then no enforcement is made.
|
|
119
|
-
// Note: the fields have been turned to lowercase here
|
|
120
|
-
const KIND_TO_SIZE = {
|
|
121
|
-
domain: null,
|
|
122
|
-
space: null,
|
|
123
|
-
time: null,
|
|
124
|
-
list: null,
|
|
125
|
-
point: null,
|
|
126
|
-
vector: null,
|
|
127
|
-
"covariant-vector": null,
|
|
128
|
-
normal: null,
|
|
129
|
-
stub: 1,
|
|
130
|
-
scalar: 1,
|
|
131
|
-
complex: 2,
|
|
132
|
-
"2-vector": 2,
|
|
133
|
-
"3-color": 3,
|
|
134
|
-
"rgb-color": 3,
|
|
135
|
-
"hsv-color": 3,
|
|
136
|
-
"xyz-color": 3,
|
|
137
|
-
"4-color": 4,
|
|
138
|
-
"rgba-color": 4,
|
|
139
|
-
"3-vector": 3,
|
|
140
|
-
"3-gradient": 3,
|
|
141
|
-
"3-normal": 3,
|
|
142
|
-
"4-vector": 4,
|
|
143
|
-
quaternion: 4,
|
|
144
|
-
"2d-symmetric-matrix": 3,
|
|
145
|
-
"2d-masked-symmetric-matrix": 4,
|
|
146
|
-
"2d-matrix": 4,
|
|
147
|
-
"2d-masked-matrix": 4,
|
|
148
|
-
"3d-symmetric-matrix": 6,
|
|
149
|
-
"3d-masked-symmetric-matrix": 7,
|
|
150
|
-
"3d-matrix": 9,
|
|
151
|
-
"3d-masked-matrix": 10,
|
|
152
|
-
"???": null
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Parse a buffer of a NRRD file.
|
|
157
|
-
* Throws an exception if the file is not a proper NRRD file.
|
|
158
|
-
* @instance
|
|
159
|
-
* @function parse
|
|
160
|
-
* @param {ArrayBuffer} nrrdBuffer - the NRRD file buffer
|
|
161
|
-
* @param {Object} options - the option object
|
|
162
|
-
* @param {boolean} options.headerOnly - Parses only the header if true, parses header and data if false (default: false)
|
|
163
|
-
* @return {Object} NRRD header and data such as {header: Object, data: TypedArray }
|
|
164
|
-
*/
|
|
165
|
-
export const parse = function (nrrdBuffer, options) {
|
|
166
|
-
let magicControl = "NRRD000";
|
|
167
|
-
let magicTest = String.fromCharCode.apply(
|
|
168
|
-
null,
|
|
169
|
-
new Uint8Array(nrrdBuffer, 0, magicControl.length)
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
if (magicControl !== magicTest) {
|
|
173
|
-
throw new Error("This file is not a NRRD file");
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
let { header, dataByteOffset } = parseHeader(nrrdBuffer);
|
|
177
|
-
|
|
178
|
-
if ("headerOnly" in options && options.headerOnly) {
|
|
179
|
-
return { header: header, data: null };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
let data = parseData(nrrdBuffer, header, dataByteOffset);
|
|
183
|
-
return { header: header, data: data };
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* @private
|
|
188
|
-
* Parses the header
|
|
189
|
-
*/
|
|
190
|
-
function parseHeader(nrrdBuffer) {
|
|
191
|
-
let byteArrayHeader = [];
|
|
192
|
-
let dataStartPosition = null;
|
|
193
|
-
let view = new DataView(nrrdBuffer);
|
|
194
|
-
|
|
195
|
-
for (let i = 0; i < nrrdBuffer.byteLength; i++) {
|
|
196
|
-
byteArrayHeader.push(String.fromCharCode(view.getUint8(i)));
|
|
197
|
-
|
|
198
|
-
if (
|
|
199
|
-
i > 0 &&
|
|
200
|
-
byteArrayHeader[i - 1] === "\n" &&
|
|
201
|
-
byteArrayHeader[i] === "\n"
|
|
202
|
-
) {
|
|
203
|
-
dataStartPosition = i + 1;
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (dataStartPosition === null) {
|
|
209
|
-
throw new Error("The NRRD header is corrupted.");
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
let comments = [];
|
|
213
|
-
|
|
214
|
-
let headerLines = byteArrayHeader
|
|
215
|
-
.join("")
|
|
216
|
-
.trim()
|
|
217
|
-
.split(/\r\n|\n/)
|
|
218
|
-
.map(l => l.trim());
|
|
219
|
-
|
|
220
|
-
let preMap = headerLines
|
|
221
|
-
.slice(1)
|
|
222
|
-
.filter(s => {
|
|
223
|
-
// removing empty lines
|
|
224
|
-
return s.length > 0;
|
|
225
|
-
})
|
|
226
|
-
.filter(s => {
|
|
227
|
-
// removing comments
|
|
228
|
-
if (s[0] === "#") {
|
|
229
|
-
comments.push(s.slice(1).trim());
|
|
230
|
-
}
|
|
231
|
-
return s[0] !== "#";
|
|
232
|
-
})
|
|
233
|
-
.map(s => {
|
|
234
|
-
let keyVal = s.split(":");
|
|
235
|
-
return {
|
|
236
|
-
key: keyVal[0].trim(),
|
|
237
|
-
val: keyVal[1].trim()
|
|
238
|
-
};
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
let nrrdHeader = {};
|
|
242
|
-
|
|
243
|
-
preMap.forEach(field => {
|
|
244
|
-
nrrdHeader[field.key] = field.val;
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// parsing each fields of the header
|
|
248
|
-
if (nrrdHeader["sizes"]) {
|
|
249
|
-
nrrdHeader["sizes"] = nrrdHeader.sizes.split(/\s+/).map(n => parseInt(n));
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (nrrdHeader["space dimension"]) {
|
|
253
|
-
nrrdHeader["space dimension"] = parseInt(nrrdHeader["space dimension"]);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (nrrdHeader["space"]) {
|
|
257
|
-
nrrdHeader["space dimension"] =
|
|
258
|
-
SPACE_TO_SPACEDIMENSIONS[nrrdHeader["space"].toLowerCase()];
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (nrrdHeader["dimension"]) {
|
|
262
|
-
nrrdHeader["dimension"] = parseInt(nrrdHeader["dimension"]);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (nrrdHeader["space directions"]) {
|
|
266
|
-
nrrdHeader["space directions"] = nrrdHeader["space directions"]
|
|
267
|
-
.split(/\s+/)
|
|
268
|
-
.map(triple => {
|
|
269
|
-
if (triple.trim() === "none") {
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
return triple
|
|
273
|
-
.slice(1, triple.length - 1)
|
|
274
|
-
.split(",")
|
|
275
|
-
.map(n => parseFloat(n));
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
if (nrrdHeader["space directions"].length !== nrrdHeader["dimension"]) {
|
|
279
|
-
throw new Error(
|
|
280
|
-
'"space direction" property has to contain as many elements as dimensions. Non-spatial dimesnsions must be refered as "none". See http://teem.sourceforge.net/nrrd/format.html#spacedirections for more info.'
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (nrrdHeader["space units"]) {
|
|
286
|
-
nrrdHeader["space units"] = nrrdHeader["space units"].split(/\s+/);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (nrrdHeader["space origin"]) {
|
|
290
|
-
nrrdHeader["space origin"] = nrrdHeader["space origin"]
|
|
291
|
-
.slice(1, nrrdHeader["space origin"].length - 1)
|
|
292
|
-
.split(",")
|
|
293
|
-
.map(n => parseFloat(n));
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (nrrdHeader["measurement frame"]) {
|
|
297
|
-
nrrdHeader["measurement frame"] = nrrdHeader["measurement frame"]
|
|
298
|
-
.split(/\s+/)
|
|
299
|
-
.map(triple => {
|
|
300
|
-
if (triple.trim() === "none") {
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
303
|
-
return triple
|
|
304
|
-
.slice(1, triple.length - 1)
|
|
305
|
-
.split(",")
|
|
306
|
-
.map(n => parseFloat(n));
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (nrrdHeader["kinds"]) {
|
|
311
|
-
nrrdHeader["kinds"] = nrrdHeader["kinds"].split(/\s+/);
|
|
312
|
-
|
|
313
|
-
if (nrrdHeader["kinds"].length !== nrrdHeader["sizes"].length) {
|
|
314
|
-
throw new Error(
|
|
315
|
-
`The "kinds" property is expected to have has many elements as the "size" property.`
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
nrrdHeader["kinds"].forEach((k, i) => {
|
|
320
|
-
let expectedLength = KIND_TO_SIZE[k.toLowerCase()];
|
|
321
|
-
let foundLength = nrrdHeader["sizes"][i];
|
|
322
|
-
if (expectedLength !== null && expectedLength !== foundLength) {
|
|
323
|
-
throw new Error(
|
|
324
|
-
`The kind "${k}" expect a size of ${expectedLength} but ${foundLength} found`
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (nrrdHeader["min"]) {
|
|
331
|
-
nrrdHeader["min"] = parseFloat(nrrdHeader["min"]);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (nrrdHeader["max"]) {
|
|
335
|
-
nrrdHeader["max"] = parseFloat(nrrdHeader["max"]);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
if (nrrdHeader["old min"]) {
|
|
339
|
-
nrrdHeader["old min"] = parseFloat(nrrdHeader["old min"]);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (nrrdHeader["old max"]) {
|
|
343
|
-
nrrdHeader["old max"] = parseFloat(nrrdHeader["old max"]);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (nrrdHeader["spacings"]) {
|
|
347
|
-
nrrdHeader["spacings"] = nrrdHeader["spacings"]
|
|
348
|
-
.split(/\s+/)
|
|
349
|
-
.map(n => parseFloat(n));
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (nrrdHeader["thicknesses"]) {
|
|
353
|
-
nrrdHeader["thicknesses"] = nrrdHeader["thicknesses"]
|
|
354
|
-
.split(/\s+/)
|
|
355
|
-
.map(n => parseFloat(n));
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (nrrdHeader["axis mins"]) {
|
|
359
|
-
nrrdHeader["axis mins"] = nrrdHeader["axis mins"]
|
|
360
|
-
.split(/\s+/)
|
|
361
|
-
.map(n => parseFloat(n));
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (nrrdHeader["axismins"]) {
|
|
365
|
-
nrrdHeader["axismins"] = nrrdHeader["axismins"]
|
|
366
|
-
.split(/\s+/)
|
|
367
|
-
.map(n => parseFloat(n));
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (nrrdHeader["axis maxs"]) {
|
|
371
|
-
nrrdHeader["axis maxs"] = nrrdHeader["axis maxs"]
|
|
372
|
-
.split(/\s+/)
|
|
373
|
-
.map(n => parseFloat(n));
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (nrrdHeader["axismaxs"]) {
|
|
377
|
-
nrrdHeader["axismaxs"] = nrrdHeader["axismaxs"]
|
|
378
|
-
.split(/\s+/)
|
|
379
|
-
.map(n => parseFloat(n));
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (nrrdHeader["centers"]) {
|
|
383
|
-
nrrdHeader["centers"] = nrrdHeader["centers"].split(/\s+/).map(mode => {
|
|
384
|
-
if (mode === "cell" || mode === "node") {
|
|
385
|
-
return mode;
|
|
386
|
-
} else {
|
|
387
|
-
return null;
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
if (nrrdHeader["labels"]) {
|
|
393
|
-
nrrdHeader["labels"] = nrrdHeader["labels"].split(/\s+/);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// some additional metadata that are not part of the header will be added here
|
|
397
|
-
nrrdHeader.extra = {};
|
|
398
|
-
|
|
399
|
-
// adding the comments from lines starting with #
|
|
400
|
-
nrrdHeader.extra.comments = comments;
|
|
401
|
-
|
|
402
|
-
// having the stride can be handy.
|
|
403
|
-
nrrdHeader.extra.stride = [1];
|
|
404
|
-
for (let i = 1; i < nrrdHeader.sizes.length; i++) {
|
|
405
|
-
nrrdHeader.extra.stride.push(
|
|
406
|
-
nrrdHeader.extra.stride[i - 1] * nrrdHeader.sizes[i - 1]
|
|
407
|
-
);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
return {
|
|
411
|
-
header: nrrdHeader,
|
|
412
|
-
dataByteOffset: dataStartPosition
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* @private
|
|
418
|
-
* Parses the data
|
|
419
|
-
*/
|
|
420
|
-
function parseData(nrrdBuffer, header, dataByteOffset) {
|
|
421
|
-
let dataBuffer = null;
|
|
422
|
-
let arrayType = NRRD_TYPES_TO_TYPEDARRAY[header.type];
|
|
423
|
-
let nbElementsFromHeader = header.sizes.reduce((prev, curr) => prev * curr);
|
|
424
|
-
let min = +Infinity;
|
|
425
|
-
let max = -Infinity;
|
|
426
|
-
let data = null;
|
|
427
|
-
|
|
428
|
-
let isTextEncoded =
|
|
429
|
-
header.encoding === "ascii" ||
|
|
430
|
-
header.encoding === "txt" ||
|
|
431
|
-
header.encoding === "text";
|
|
432
|
-
|
|
433
|
-
if (header.encoding === "raw") {
|
|
434
|
-
dataBuffer = new Uint8Array(nrrdBuffer).slice(dataByteOffset).buffer;
|
|
435
|
-
} else if (isTextEncoded) {
|
|
436
|
-
let numbers = String.fromCharCode
|
|
437
|
-
.apply(null, new Uint8Array(nrrdBuffer, dataByteOffset))
|
|
438
|
-
.split(/\r\n|\n|\s/)
|
|
439
|
-
.map(s => s.trim())
|
|
440
|
-
.filter(s => s !== "")
|
|
441
|
-
.map(s => {
|
|
442
|
-
let numValue = parseFloat(s);
|
|
443
|
-
min = Math.min(min, numValue);
|
|
444
|
-
max = Math.max(max, numValue);
|
|
445
|
-
return numValue;
|
|
446
|
-
});
|
|
447
|
-
data = new arrayType(numbers);
|
|
448
|
-
} else if (header.encoding === "gzip" || header.encoding === "gz") {
|
|
449
|
-
dataBuffer = pako.inflate(new Uint8Array(nrrdBuffer).slice(dataByteOffset))
|
|
450
|
-
.buffer;
|
|
451
|
-
} else {
|
|
452
|
-
throw new Error('Only "raw", "ascii" and "gzip" encoding are supported.');
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (isTextEncoded) {
|
|
456
|
-
if (nbElementsFromHeader !== data.length) {
|
|
457
|
-
throw new Error("Unconsistency in data buffer length");
|
|
458
|
-
}
|
|
459
|
-
} else {
|
|
460
|
-
let nbElementsFromBufferAndType =
|
|
461
|
-
dataBuffer.byteLength / arrayType.BYTES_PER_ELEMENT;
|
|
462
|
-
|
|
463
|
-
if (nbElementsFromHeader !== nbElementsFromBufferAndType) {
|
|
464
|
-
throw new Error("Unconsistency in data buffer length");
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
data = new arrayType(nbElementsFromHeader);
|
|
468
|
-
let dataView = new DataView(dataBuffer);
|
|
469
|
-
let viewMethod = NRRD_TYPES_TO_VIEW_GET[header.type];
|
|
470
|
-
let littleEndian = header.endian === "little" ? true : false;
|
|
471
|
-
|
|
472
|
-
for (let i = 0; i < nbElementsFromHeader; i++) {
|
|
473
|
-
data[i] = dataView[viewMethod](
|
|
474
|
-
i * arrayType.BYTES_PER_ELEMENT,
|
|
475
|
-
littleEndian
|
|
476
|
-
);
|
|
477
|
-
min = Math.min(min, data[i]);
|
|
478
|
-
max = Math.max(max, data[i]);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
header.extra.min = min;
|
|
483
|
-
header.extra.max = max;
|
|
484
|
-
return data;
|
|
485
|
-
}
|
package/imaging/tools/README.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Tools Management
|
|
2
|
-
|
|
3
|
-
## Default tools
|
|
4
|
-
|
|
5
|
-
In `default.ts` the list of Larvitar default tools is exported as `DEFAULT_TOOLS`, along with their default configuration, that extendes the cornerstoneTools configuration with these properties:
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
{
|
|
9
|
-
name : toolName (string),
|
|
10
|
-
viewports : "all" or [array of target viewports],
|
|
11
|
-
configuration : configuration {object},
|
|
12
|
-
options : options {object},
|
|
13
|
-
class : cornerstone tool library class name (ie "LengthTool" for Length tool),
|
|
14
|
-
sync : cornerstone synchronizer name (ie "wwwcSynchronizer" for Wwwc sync tool),
|
|
15
|
-
cleanable : if true, this tool will be removed when calling "no tools",
|
|
16
|
-
defaultActive : if true, this tool will be activated when calling "addDefaultTools",
|
|
17
|
-
shortcut : keyboard shortcut [not implemented],
|
|
18
|
-
type : tool category inside Larvitar (one of: "utils", "annotation", "segmentation", "overlay"),
|
|
19
|
-
description: a string that describes the tool (eg to be shown in a tooltip)
|
|
20
|
-
}
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
These are the tools that will be added calling `addDefaultTools`. User can override defaults calling `setDefaultToolsProps`.
|
|
24
|
-
|
|
25
|
-
## External Tools
|
|
26
|
-
|
|
27
|
-
User can add custom tools calling `registerExternalTool`. The tool will be registered in the dvTools object and in DEFAULT*TOOLS array. If done \_before* adding the tools with `addDefaultTools`, the tool will be added automatically along with the default ones. Otherwise, the user can simply add its tool using `addTool`.
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/** @module imaging/tools/custom/4dSliceScrollTool
|
|
2
|
-
* @desc This file provides functionalities for an alternative scroll tool
|
|
3
|
-
* to handle 4d exam and navigate to the correct slice
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// external libraries
|
|
9
|
-
import cornerstoneTools from "cornerstone-tools";
|
|
10
|
-
const external = cornerstoneTools.external;
|
|
11
|
-
|
|
12
|
-
const BaseTool = cornerstoneTools.importInternal("base/BaseTool");
|
|
13
|
-
const scroll = cornerstoneTools.importInternal("util/scroll");
|
|
14
|
-
const scrollToIndex = cornerstoneTools.importInternal("util/scrollToIndex");
|
|
15
|
-
const getToolState = cornerstoneTools.getToolState;
|
|
16
|
-
const clip = cornerstoneTools.importInternal("util/clip");
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @public
|
|
20
|
-
* @class 4DScrollMouseWheelTool
|
|
21
|
-
* @memberof Tools
|
|
22
|
-
*
|
|
23
|
-
* @classdesc Tool for scrolling through a series using the mouse wheel.
|
|
24
|
-
* @extends Tools.Base.BaseTool
|
|
25
|
-
*/
|
|
26
|
-
export default class Slice4DScrollMouseWheelTool extends BaseTool {
|
|
27
|
-
constructor(props = {}) {
|
|
28
|
-
const defaultProps = {
|
|
29
|
-
name: "Slice4DScrollMouseWheel",
|
|
30
|
-
supportedInteractionTypes: ["MouseWheel"],
|
|
31
|
-
configuration: {
|
|
32
|
-
loop: false,
|
|
33
|
-
allowSkipping: true,
|
|
34
|
-
invert: false
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
super(props, defaultProps);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
mouseWheelCallback(evt) {
|
|
41
|
-
const { direction: images, element } = evt.detail;
|
|
42
|
-
const { loop, allowSkipping, invert, framesNumber } = this.configuration;
|
|
43
|
-
const direction = invert
|
|
44
|
-
? images * framesNumber * -1
|
|
45
|
-
: images * framesNumber;
|
|
46
|
-
scroll(element, direction, loop, allowSkipping);
|
|
47
|
-
// scroll4DSlices(element, direction, loop, allowSkipping);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Scrolls through the slice of a 4D stack.
|
|
52
|
-
* @export @public @method
|
|
53
|
-
* @name scroll4DSlices
|
|
54
|
-
*
|
|
55
|
-
* @param {HTMLElement} element The element to scroll.
|
|
56
|
-
* @param {number} images The number of images to scroll through.
|
|
57
|
-
* @param {type} [loop = false] Whether to loop the scrolling.
|
|
58
|
-
* @param {type} [allowSkipping = true] Whether frames can be skipped.
|
|
59
|
-
* @returns {void}
|
|
60
|
-
*/
|
|
61
|
-
const scroll4DSlices = function (
|
|
62
|
-
element,
|
|
63
|
-
images,
|
|
64
|
-
loop,
|
|
65
|
-
allowSkipping,
|
|
66
|
-
framesNumber
|
|
67
|
-
) {
|
|
68
|
-
const toolData = getToolState(element, "stack");
|
|
69
|
-
|
|
70
|
-
if (!toolData || !toolData.data || !toolData.data.length) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const stackData = toolData.data[0];
|
|
75
|
-
|
|
76
|
-
if (!stackData.pending) {
|
|
77
|
-
stackData.pending = [];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let newImageIdIndex = stackData.currentImageIdIndex + images; //+ 1 + framesNumber;
|
|
81
|
-
if (loop) {
|
|
82
|
-
const nbImages = stackData.imageIds.length;
|
|
83
|
-
newImageIdIndex %= nbImages;
|
|
84
|
-
} else {
|
|
85
|
-
newImageIdIndex = clip(newImageIdIndex, 0, stackData.imageIds.length - 1);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (allowSkipping) {
|
|
89
|
-
scrollToIndex(element, newImageIdIndex);
|
|
90
|
-
} else {
|
|
91
|
-
const pendingEvent = {
|
|
92
|
-
index: newImageIdIndex
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
stackData.pending.push(pendingEvent);
|
|
96
|
-
scrollWithoutSkipping(stackData, pendingEvent, element);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Recursively scrolls the stack until the desired image is reached.
|
|
102
|
-
* @private
|
|
103
|
-
* @method
|
|
104
|
-
* @name scrollWithoutSkipping
|
|
105
|
-
*
|
|
106
|
-
* @param {type} stackData Data object containing information about the stack.
|
|
107
|
-
* @param {Object} pendingEvent The event to process next.
|
|
108
|
-
* @param {HTMLElement} element The element being scrolled through.
|
|
109
|
-
* @returns {void}
|
|
110
|
-
*/
|
|
111
|
-
function scrollWithoutSkipping(stackData, pendingEvent, element) {
|
|
112
|
-
if (stackData.pending[0] === pendingEvent) {
|
|
113
|
-
if (stackData.currentImageIdIndex === pendingEvent.index) {
|
|
114
|
-
stackData.pending.splice(stackData.pending.indexOf(pendingEvent), 1);
|
|
115
|
-
|
|
116
|
-
if (stackData.pending.length > 0) {
|
|
117
|
-
scrollWithoutSkipping(stackData, stackData.pending[0], element);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const newImageHandler = function (event) {
|
|
124
|
-
const index = stackData.imageIds.indexOf(event.detail.image.imageId);
|
|
125
|
-
|
|
126
|
-
if (index === pendingEvent.index) {
|
|
127
|
-
stackData.pending.splice(stackData.pending.indexOf(pendingEvent), 1);
|
|
128
|
-
element.removeEventListener(
|
|
129
|
-
external.cornerstone.EVENTS.NEW_IMAGE,
|
|
130
|
-
newImageHandler
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
if (stackData.pending.length > 0) {
|
|
134
|
-
scrollWithoutSkipping(stackData, stackData.pending[0], element);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
element.addEventListener(
|
|
140
|
-
external.cornerstone.EVENTS.NEW_IMAGE,
|
|
141
|
-
newImageHandler
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
scrollToIndex(element, pendingEvent.index);
|
|
145
|
-
}
|
|
146
|
-
}
|