larvitar 2.0.5 → 2.0.7
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/imaging/imageRendering.d.ts +1 -71
- package/dist/imaging/imageStore.d.ts +5 -0
- package/dist/imaging/loaders/commonLoader.d.ts +4 -4
- package/dist/imaging/loaders/nrrdLoader.d.ts +1 -51
- package/dist/larvitar.js +13 -1
- package/dist/larvitar.js.map +1 -1
- package/imaging/tools/types.d.ts +19 -19
- package/imaging/types.d.ts +110 -2
- package/package.json +7 -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/dist/imaging/MetaDataReadable.d.ts +0 -41
- package/dist/imaging/MetaDataTypes.d.ts +0 -3489
- 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/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/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/{MetaDataReadable.ts → MetaDataReadable.d.ts} +0 -0
- /package/imaging/{MetaDataTypes.ts → MetaDataTypes.d.ts} +0 -0
package/imaging/imageParsing.ts
DELETED
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
/** @module imaging/imageParsing
|
|
2
|
-
* @desc This file provides functionalities for parsing DICOM image files
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
// external libraries
|
|
6
|
-
import { DataSet, parseDicom } from "dicom-parser";
|
|
7
|
-
import { forEach, each, has, pick } from "lodash";
|
|
8
|
-
import { v4 as uuidv4 } from "uuid";
|
|
9
|
-
|
|
10
|
-
// internal libraries
|
|
11
|
-
import { getPixelRepresentation, randomId } from "./imageUtils";
|
|
12
|
-
import { parseTag } from "./imageTags";
|
|
13
|
-
import { updateLoadedStack } from "./imageLoading";
|
|
14
|
-
import { checkMemoryAllocation } from "./monitors/memory";
|
|
15
|
-
import { ImageObject, Instance, MetaData, Series } from "./types";
|
|
16
|
-
import { getLarvitarManager } from "./loaders/commonLoader";
|
|
17
|
-
import type { MetaDataTypes } from "./MetaDataTypes";
|
|
18
|
-
import { NrrdSeries } from "./loaders/nrrdLoader";
|
|
19
|
-
|
|
20
|
-
// global module variables
|
|
21
|
-
var t0: number; // t0 variable for timing debugging purpose
|
|
22
|
-
|
|
23
|
-
/*
|
|
24
|
-
* This module provides the following functions to be exported:
|
|
25
|
-
* readFiles(fileList)
|
|
26
|
-
* readFile(file)
|
|
27
|
-
* parseDataSet(dataSet, metadata, customFilter)
|
|
28
|
-
* clearImageParsing(seriesStack)
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Reset series stack object and its internal data
|
|
33
|
-
* @instance
|
|
34
|
-
* @function clearImageParsing
|
|
35
|
-
* @param {Object} seriesStack - Parsed series stack object
|
|
36
|
-
*/
|
|
37
|
-
export const clearImageParsing = function (
|
|
38
|
-
seriesStack: ReturnType<typeof getLarvitarManager> | null
|
|
39
|
-
) {
|
|
40
|
-
each(seriesStack, function (stack: Series | NrrdSeries) {
|
|
41
|
-
each(stack.instances, function (instance: Instance) {
|
|
42
|
-
if (instance.dataSet) {
|
|
43
|
-
// @ts-ignore
|
|
44
|
-
instance.dataSet.byteArray = null;
|
|
45
|
-
}
|
|
46
|
-
instance.dataSet = null;
|
|
47
|
-
instance.file = null;
|
|
48
|
-
// @ts-ignore
|
|
49
|
-
instance.metadata = null;
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
seriesStack = null;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Read dicom files and return allSeriesStack object
|
|
57
|
-
* @instance
|
|
58
|
-
* @function readFiles
|
|
59
|
-
* @param {Array} entries - List of file objects
|
|
60
|
-
* @returns {Promise} - Return a promise which will resolve to a image object list or fail if an error occurs
|
|
61
|
-
*/
|
|
62
|
-
export const readFiles = function (entries: File[]) {
|
|
63
|
-
return parseFiles(entries);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Read a single dicom file and return parsed object
|
|
68
|
-
* @instance
|
|
69
|
-
* @function readFile
|
|
70
|
-
* @param {File} entry - File object
|
|
71
|
-
* @returns {Promise} - Return a promise which will resolve to a image object or fail if an error occurs
|
|
72
|
-
*/
|
|
73
|
-
export const readFile = function (entry: File) {
|
|
74
|
-
return parseFile(entry);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/* Internal module functions */
|
|
78
|
-
|
|
79
|
-
//This type is used to instantiate metadata and nested objects in imageParsing in order to allow dynamic setting
|
|
80
|
-
//of MetaDataTypes objects' properties.
|
|
81
|
-
//In other words, metadata : ExtendedMetaDataTypes is useful and substitutes metadata : MetaDataTypes when metadata values
|
|
82
|
-
//aren't called explicitly using metadata['xGGGGEEEEE']
|
|
83
|
-
//and are instead called using TAG as a variable (i.e. metadata[TAG]).
|
|
84
|
-
//It is important to highlight that even if we set metadata[TAG]=tagValue; whose type (of tagValue) is the correct one (returned by ParseTag).
|
|
85
|
-
//if we then extract x=metadata[TAG]; we obtain tagValue : unknown and a casting with : MetaDataType[typeof TAG] is necessary.
|
|
86
|
-
|
|
87
|
-
type ExtendedMetaDataTypes = MetaDataTypes & {
|
|
88
|
-
[key: string]: unknown;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Parse metadata from dicom parser dataSet object
|
|
93
|
-
* @instance
|
|
94
|
-
* @function parseDataSet
|
|
95
|
-
* @param {Object} dataSet - dicom parser dataSet object
|
|
96
|
-
* @param {Object} metadata - Initialized metadata object
|
|
97
|
-
* @param {Array} customFilter - Optional filter: {tags:[], frameId: 0}
|
|
98
|
-
*/
|
|
99
|
-
// This function iterates through dataSet recursively and store tag values into metadata object
|
|
100
|
-
|
|
101
|
-
export const parseDataSet = function (
|
|
102
|
-
dataSet: DataSet,
|
|
103
|
-
metadata: ExtendedMetaDataTypes,
|
|
104
|
-
customFilter?: { tags: string[]; frameId: number }
|
|
105
|
-
) {
|
|
106
|
-
// customFilter= {tags:[], frameId:xxx}
|
|
107
|
-
// the dataSet.elements object contains properties for each element parsed. The name of the property
|
|
108
|
-
// is based on the elements tag and looks like 'xGGGGEEEE' where GGGG is the group number and EEEE is the
|
|
109
|
-
// element number both with lowercase hexadecimal letters. For example, the Series Description DICOM element 0008,103E would
|
|
110
|
-
// be named 'x0008103e'. Here we iterate over each property (element) so we can build a string describing its
|
|
111
|
-
// contents to add to the output array
|
|
112
|
-
try {
|
|
113
|
-
let elements = dataSet.elements;
|
|
114
|
-
|
|
115
|
-
customFilter && has(customFilter, "tags")
|
|
116
|
-
? pick(dataSet.elements, customFilter.tags)
|
|
117
|
-
: dataSet.elements;
|
|
118
|
-
for (let propertyName in elements) {
|
|
119
|
-
let element = elements[propertyName]; //metadata
|
|
120
|
-
const TAG = propertyName as keyof ExtendedMetaDataTypes;
|
|
121
|
-
// Here we check for Sequence items and iterate over them if present. items will not be set in the
|
|
122
|
-
// element object for elements that don't have SQ VR type. Note that implicit little endian
|
|
123
|
-
// sequences will are currently not parsed.
|
|
124
|
-
if (element.items) {
|
|
125
|
-
let nestedArray: MetaDataTypes[] = [];
|
|
126
|
-
|
|
127
|
-
// iterates over nested elements (nested metadata)
|
|
128
|
-
element.items.forEach(function (item) {
|
|
129
|
-
let nestedObject: ExtendedMetaDataTypes = {};
|
|
130
|
-
for (let nestedPropertyName in item.dataSet!.elements) {
|
|
131
|
-
let TAG_tagValue = nestedPropertyName as keyof MetaDataTypes;
|
|
132
|
-
|
|
133
|
-
let tagValue = parseTag<MetaDataTypes[typeof TAG_tagValue]>(
|
|
134
|
-
item.dataSet!,
|
|
135
|
-
nestedPropertyName,
|
|
136
|
-
item.dataSet!.elements[nestedPropertyName]
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
let TAG_nested = nestedPropertyName as keyof ExtendedMetaDataTypes;
|
|
140
|
-
nestedObject[TAG_nested] = tagValue;
|
|
141
|
-
//see MetaDataTypes.ts last property to understand how this dynamic value setting is possible
|
|
142
|
-
}
|
|
143
|
-
nestedArray.push(nestedObject);
|
|
144
|
-
});
|
|
145
|
-
metadata[TAG] = nestedArray;
|
|
146
|
-
} else {
|
|
147
|
-
let TAG_tagValue = propertyName as keyof MetaDataTypes;
|
|
148
|
-
let tagValue = parseTag<MetaDataTypes[typeof TAG_tagValue]>(
|
|
149
|
-
dataSet,
|
|
150
|
-
propertyName,
|
|
151
|
-
element
|
|
152
|
-
);
|
|
153
|
-
let TAG = propertyName as keyof ExtendedMetaDataTypes;
|
|
154
|
-
// identify duplicated tags (keep the first occurency and store the others in another tag eg x00280010_uuid)
|
|
155
|
-
if (metadata[TAG] !== undefined) {
|
|
156
|
-
console.debug(
|
|
157
|
-
`Identified duplicated tag "${propertyName}", values are:`,
|
|
158
|
-
metadata[TAG],
|
|
159
|
-
tagValue
|
|
160
|
-
);
|
|
161
|
-
let TAG_uuidv4 = (propertyName +
|
|
162
|
-
"_" +
|
|
163
|
-
uuidv4()) as keyof ExtendedMetaDataTypes;
|
|
164
|
-
metadata[TAG_uuidv4] = tagValue;
|
|
165
|
-
} else {
|
|
166
|
-
metadata[TAG] = tagValue;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
} catch (err) {
|
|
171
|
-
console.log(err);
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Manage the parsing process waiting for the parsed object before proceeding with the next parse request
|
|
177
|
-
* @inner
|
|
178
|
-
* @function parseNextFile
|
|
179
|
-
* @param {Array} parsingQueue - Array of queued files to be parsed
|
|
180
|
-
* @param {Object} allSeriesStack - Series stack object to be populated
|
|
181
|
-
* @param {string} uuid - Series uuid to be used if series instance uuid is missing
|
|
182
|
-
* @param {Function} resolve - Promise resolve function
|
|
183
|
-
* @param {Function} reject - Promise reject function
|
|
184
|
-
*/
|
|
185
|
-
let parseNextFile = function (
|
|
186
|
-
parsingQueue: File[],
|
|
187
|
-
allSeriesStack: ReturnType<typeof getLarvitarManager>,
|
|
188
|
-
uuid: string,
|
|
189
|
-
resolve: Function,
|
|
190
|
-
reject: Function
|
|
191
|
-
) {
|
|
192
|
-
// initialize t0 on first file of the queue
|
|
193
|
-
if (
|
|
194
|
-
Object.keys(allSeriesStack).length === 0 &&
|
|
195
|
-
allSeriesStack.constructor === Object
|
|
196
|
-
) {
|
|
197
|
-
t0 = performance.now();
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (parsingQueue.length === 0) {
|
|
201
|
-
let t1 = performance.now();
|
|
202
|
-
console.log(`Call to readFiles took ${t1 - t0} milliseconds.`);
|
|
203
|
-
resolve(allSeriesStack);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// remove and return first item from queue
|
|
208
|
-
let file = parsingQueue.shift() as File | undefined | null;
|
|
209
|
-
|
|
210
|
-
if (!file) {
|
|
211
|
-
console.warn("File is undefined or null");
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Check if there is enough memory to parse the file
|
|
216
|
-
if (checkMemoryAllocation(file.size) === false) {
|
|
217
|
-
// do not parse the file and stop parsing
|
|
218
|
-
clearImageParsing(allSeriesStack);
|
|
219
|
-
let t1 = performance.now();
|
|
220
|
-
console.log(`Call to readFiles took ${t1 - t0} milliseconds.`);
|
|
221
|
-
file = null;
|
|
222
|
-
reject("Available memory is not enough");
|
|
223
|
-
return;
|
|
224
|
-
} else {
|
|
225
|
-
// parse the file and wait for results
|
|
226
|
-
parseFile(file)
|
|
227
|
-
.then((seriesData: ImageObject | null) => {
|
|
228
|
-
// use generated series uid if not found in dicom file
|
|
229
|
-
seriesData!.metadata.seriesUID = seriesData!.metadata.seriesUID || uuid;
|
|
230
|
-
// add file to cornerstoneDICOMImageLoader file manager
|
|
231
|
-
updateLoadedStack(seriesData!, allSeriesStack);
|
|
232
|
-
// proceed with the next file to parse
|
|
233
|
-
parseNextFile(parsingQueue, allSeriesStack, uuid, resolve, reject);
|
|
234
|
-
seriesData = null;
|
|
235
|
-
file = null;
|
|
236
|
-
})
|
|
237
|
-
.catch(err => {
|
|
238
|
-
console.error(err);
|
|
239
|
-
parseNextFile(parsingQueue, allSeriesStack, uuid, resolve, reject);
|
|
240
|
-
file = null;
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Push files in queue and start parsing next file
|
|
247
|
-
* @inner
|
|
248
|
-
* @function parseFiles
|
|
249
|
-
* @param {Array} fileList - Array of file objects
|
|
250
|
-
* @returns {Promise} - Return a promise which will resolve to a image object list or fail if an error occurs
|
|
251
|
-
*/
|
|
252
|
-
const parseFiles = function (fileList: File[]) {
|
|
253
|
-
let allSeriesStack = {};
|
|
254
|
-
let parsingQueue: File[] = [];
|
|
255
|
-
|
|
256
|
-
forEach(fileList, function (file: File) {
|
|
257
|
-
if (!file.name.startsWith(".") && !file.name.startsWith("DICOMDIR")) {
|
|
258
|
-
parsingQueue.push(file);
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
return new Promise((resolve, reject) => {
|
|
262
|
-
const uuid = uuidv4();
|
|
263
|
-
parseNextFile(parsingQueue, allSeriesStack, uuid, resolve, reject);
|
|
264
|
-
});
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Parse a single DICOM File (metaData and pixelData)
|
|
269
|
-
* @inner
|
|
270
|
-
* @function parseFile
|
|
271
|
-
* @param {File} file - File object to be parsed
|
|
272
|
-
* @returns {Promise} - Return a promise which will resolve to a image object or fail if an error occurs
|
|
273
|
-
*/
|
|
274
|
-
const parseFile = function (file: File) {
|
|
275
|
-
const parsePromise = new Promise<ImageObject>((resolve, reject) => {
|
|
276
|
-
let reader = new FileReader();
|
|
277
|
-
reader.onload = function () {
|
|
278
|
-
let arrayBuffer = reader.result;
|
|
279
|
-
// Here we have the file data as an ArrayBuffer.
|
|
280
|
-
// dicomParser requires as input a Uint8Array so we create that here.
|
|
281
|
-
|
|
282
|
-
if (!arrayBuffer || typeof arrayBuffer === "string") {
|
|
283
|
-
reject("Error reading file");
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
let byteArray: Uint8Array | null = new Uint8Array(arrayBuffer);
|
|
288
|
-
let dataSet;
|
|
289
|
-
|
|
290
|
-
// this try-catch is used to handle non-DICOM files: log error but continue parsing the other files
|
|
291
|
-
try {
|
|
292
|
-
dataSet = parseDicom(byteArray);
|
|
293
|
-
byteArray = null;
|
|
294
|
-
let metadata: MetaData = {};
|
|
295
|
-
parseDataSet(dataSet, metadata);
|
|
296
|
-
|
|
297
|
-
let temporalPositionIdentifier = metadata["x00200100"]; // Temporal order of a dynamic or functional set of Images.
|
|
298
|
-
let numberOfTemporalPositions = metadata["x00200105"]; // Total number of temporal positions prescribed.
|
|
299
|
-
const is4D =
|
|
300
|
-
temporalPositionIdentifier !== undefined &&
|
|
301
|
-
(numberOfTemporalPositions as number) > 1
|
|
302
|
-
? true
|
|
303
|
-
: false;
|
|
304
|
-
|
|
305
|
-
let modality = metadata["x00080060"] as string;
|
|
306
|
-
let singleFrameModalities = [
|
|
307
|
-
"CR",
|
|
308
|
-
"DX",
|
|
309
|
-
"MG",
|
|
310
|
-
"PX",
|
|
311
|
-
"RF",
|
|
312
|
-
"XA",
|
|
313
|
-
"US",
|
|
314
|
-
"IVUS",
|
|
315
|
-
"OCT"
|
|
316
|
-
];
|
|
317
|
-
// US XA RF IVUS OCT DX CR PX MG
|
|
318
|
-
// Overwrite SOPInstanceUID to manage single stack images (US, XA).
|
|
319
|
-
// Usually different SeriesInstanceUID means different series and that value
|
|
320
|
-
// is used into the application to group different instances into the same series,
|
|
321
|
-
// but if a DICOM file contains a multiframe series, then the SeriesInstanceUID
|
|
322
|
-
// can be shared by other files of the same study.
|
|
323
|
-
// In these cases, the SOPInstanceUID (unique) is used as SeriesInstanceUID.
|
|
324
|
-
let uniqueId = singleFrameModalities.includes(modality)
|
|
325
|
-
? metadata["x00080018"]
|
|
326
|
-
: metadata["x0020000e"];
|
|
327
|
-
let seriesInstanceUID = metadata["x0020000e"];
|
|
328
|
-
let pixelSpacing = metadata["x00280030"];
|
|
329
|
-
let imageOrientation = metadata["x00200037"];
|
|
330
|
-
let imagePosition = metadata["x00200032"];
|
|
331
|
-
let sliceThickness = metadata["x00180050"];
|
|
332
|
-
let numberOfFrames = metadata["x00280008"];
|
|
333
|
-
let isMultiframe = (numberOfFrames as number) > 1 ? true : false;
|
|
334
|
-
let waveform = metadata["x50003000"] ? true : false;
|
|
335
|
-
|
|
336
|
-
if (dataSet.warnings.length > 0) {
|
|
337
|
-
// warnings
|
|
338
|
-
reject(dataSet.warnings);
|
|
339
|
-
} else {
|
|
340
|
-
let pixelDataElement = dataSet.elements.x7fe00010;
|
|
341
|
-
let SOPUID = metadata["x00080016"];
|
|
342
|
-
if (pixelDataElement) {
|
|
343
|
-
// done, pixelDataElement found
|
|
344
|
-
let instanceUID = metadata["x00080018"] || randomId();
|
|
345
|
-
let imageObject: Partial<ImageObject> = {
|
|
346
|
-
// data needed for rendering
|
|
347
|
-
file: file,
|
|
348
|
-
dataSet: dataSet
|
|
349
|
-
};
|
|
350
|
-
imageObject.metadata = metadata as MetaData;
|
|
351
|
-
imageObject.metadata.anonymized = false;
|
|
352
|
-
imageObject.metadata.larvitarSeriesInstanceUID = uniqueId;
|
|
353
|
-
imageObject.metadata.sopClassUID = metadata["x00080016"];
|
|
354
|
-
imageObject.metadata.seriesUID = seriesInstanceUID;
|
|
355
|
-
imageObject.metadata.instanceUID = instanceUID;
|
|
356
|
-
imageObject.metadata.studyUID = metadata["x0020000d"];
|
|
357
|
-
imageObject.metadata.accessionNumber = metadata["x00080050"];
|
|
358
|
-
imageObject.metadata.studyDescription = metadata["x00081030"];
|
|
359
|
-
imageObject.metadata.patientName = metadata["x00100010"] as string;
|
|
360
|
-
imageObject.metadata.patientBirthdate = metadata["x00100030"];
|
|
361
|
-
imageObject.metadata.seriesDescription = metadata[
|
|
362
|
-
"x0008103e"
|
|
363
|
-
] as string;
|
|
364
|
-
imageObject.metadata.seriesDate = metadata["x00080021"];
|
|
365
|
-
imageObject.metadata.seriesModality = metadata["x00080060"]
|
|
366
|
-
?.toString()
|
|
367
|
-
.toLowerCase();
|
|
368
|
-
imageObject.metadata.intercept = metadata["x00281052"];
|
|
369
|
-
imageObject.metadata.slope = metadata["x00281053"];
|
|
370
|
-
imageObject.metadata.pixelSpacing = pixelSpacing;
|
|
371
|
-
imageObject.metadata.sliceThickness = sliceThickness;
|
|
372
|
-
imageObject.metadata.imageOrientation = imageOrientation;
|
|
373
|
-
imageObject.metadata.imagePosition = imagePosition;
|
|
374
|
-
imageObject.metadata.rows = metadata["x00280010"];
|
|
375
|
-
imageObject.metadata.cols = metadata["x00280011"];
|
|
376
|
-
imageObject.metadata.numberOfSlices = metadata["x00540081"]
|
|
377
|
-
? metadata["x00540081"] // number of slices
|
|
378
|
-
: metadata["x00201002"]; // number of instances
|
|
379
|
-
imageObject.metadata.numberOfFrames = numberOfFrames;
|
|
380
|
-
if (isMultiframe) {
|
|
381
|
-
imageObject.metadata.frameTime = metadata["x00181063"];
|
|
382
|
-
imageObject.metadata.frameDelay = metadata["x00181066"];
|
|
383
|
-
if (metadata["x00186060"]) {
|
|
384
|
-
imageObject.metadata.rWaveTimeVector = metadata["x00186060"];
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
imageObject.metadata.isMultiframe = isMultiframe;
|
|
388
|
-
if (is4D) {
|
|
389
|
-
imageObject.metadata.temporalPositionIdentifier =
|
|
390
|
-
metadata["x00200100"];
|
|
391
|
-
imageObject.metadata.numberOfTemporalPositions =
|
|
392
|
-
metadata["x00200105"];
|
|
393
|
-
imageObject.metadata.contentTime = metadata["x00080033"];
|
|
394
|
-
}
|
|
395
|
-
imageObject.metadata.is4D = is4D;
|
|
396
|
-
imageObject.metadata.waveform = waveform;
|
|
397
|
-
imageObject.metadata.windowCenter = metadata["x00281050"];
|
|
398
|
-
imageObject.metadata.windowWidth = metadata["x00281051"];
|
|
399
|
-
imageObject.metadata.minPixelValue = metadata["x00280106"];
|
|
400
|
-
imageObject.metadata.maxPixelValue = metadata["x00280107"];
|
|
401
|
-
imageObject.metadata.length = pixelDataElement.length;
|
|
402
|
-
imageObject.metadata.repr = getPixelRepresentation(dataSet);
|
|
403
|
-
resolve(imageObject as ImageObject);
|
|
404
|
-
} else if (SOPUID == "1.2.840.10008.5.1.4.1.1.104.1") {
|
|
405
|
-
let pdfObject: Partial<ImageObject> = {
|
|
406
|
-
// data needed for rendering
|
|
407
|
-
file: file,
|
|
408
|
-
dataSet: dataSet
|
|
409
|
-
};
|
|
410
|
-
pdfObject.metadata = metadata;
|
|
411
|
-
pdfObject.metadata.larvitarSeriesInstanceUID = uniqueId;
|
|
412
|
-
pdfObject.metadata.seriesUID = seriesInstanceUID;
|
|
413
|
-
pdfObject.instanceUID =
|
|
414
|
-
metadata["x00080018"]?.toString() || randomId();
|
|
415
|
-
pdfObject.metadata.studyUID = metadata["x0020000d"];
|
|
416
|
-
pdfObject.metadata.accessionNumber = metadata["x00080050"];
|
|
417
|
-
pdfObject.metadata.studyDescription = metadata["x00081030"];
|
|
418
|
-
pdfObject.metadata.patientName = metadata["x00100010"] as string;
|
|
419
|
-
pdfObject.metadata.patientBirthdate = metadata["x00100030"];
|
|
420
|
-
pdfObject.metadata.seriesDate = metadata["x00080021"];
|
|
421
|
-
pdfObject.metadata.seriesModality = metadata["x00080060"]
|
|
422
|
-
?.toString()
|
|
423
|
-
.toLowerCase();
|
|
424
|
-
pdfObject.metadata.mimeType = metadata["x00420012"];
|
|
425
|
-
pdfObject.metadata.is4D = false;
|
|
426
|
-
pdfObject.metadata.numberOfFrames = 0;
|
|
427
|
-
pdfObject.metadata.numberOfSlices = 0;
|
|
428
|
-
pdfObject.metadata.numberOfTemporalPositions = 0;
|
|
429
|
-
resolve(pdfObject as ImageObject);
|
|
430
|
-
} else {
|
|
431
|
-
// done, no pixelData
|
|
432
|
-
reject("no pixelData");
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
} catch (err) {
|
|
436
|
-
reject(
|
|
437
|
-
`Larvitar: can not read file "${file.name}" \nParsing error: ${err}`
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
reader.readAsArrayBuffer(file);
|
|
442
|
-
});
|
|
443
|
-
return parsePromise;
|
|
444
|
-
};
|
package/imaging/imagePresets.ts
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/** @module imaging/imagePresets
|
|
2
|
-
* @desc This file provides functionalities for
|
|
3
|
-
* image presets for ww and wc
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// external libraries
|
|
7
|
-
import cornerstone from "cornerstone-core";
|
|
8
|
-
import { each, find } from "lodash";
|
|
9
|
-
|
|
10
|
-
// internal libraries
|
|
11
|
-
import { set as setStore } from "./imageStore";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Object used to list image presets
|
|
15
|
-
* @object
|
|
16
|
-
*/
|
|
17
|
-
const IMAGE_PRESETS = [
|
|
18
|
-
{ name: "CT: Abdomen", ww: 350, wl: 50 },
|
|
19
|
-
{ name: "CT: Bone", ww: 2500, wl: 500 },
|
|
20
|
-
{ name: "CT: Cerebrum", ww: 80, wl: 0 },
|
|
21
|
-
{ name: "CT: Covid-19", ww: 240, wl: -860 },
|
|
22
|
-
{ name: "CT: Liver", ww: 150, wl: 50 },
|
|
23
|
-
{ name: "CT: Lung", ww: 1500, wl: -500 },
|
|
24
|
-
{ name: "CT: Mediastinum", ww: 300, wl: 50 },
|
|
25
|
-
{ name: "CT: Pelvis", ww: 400, wl: 40 }
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
/*
|
|
29
|
-
* This module provides the following functions to be exported:
|
|
30
|
-
* getImagePresets()
|
|
31
|
-
* setImagePreset(name)
|
|
32
|
-
* setImageCustomPreset(viewportNames, customValues)
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Get Image presets object
|
|
37
|
-
* @instance
|
|
38
|
-
* @function getImagePresets
|
|
39
|
-
*/
|
|
40
|
-
export const getImagePresets = function () {
|
|
41
|
-
return IMAGE_PRESETS;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Set Image presets
|
|
46
|
-
* @instance
|
|
47
|
-
* @function setImagePreset
|
|
48
|
-
* @param {Array} viewportNames - List of viewports where to apply preset
|
|
49
|
-
* @param {String} preset - The image preset name or the preset object
|
|
50
|
-
*/
|
|
51
|
-
export const setImagePreset = function (
|
|
52
|
-
viewportNames: string[],
|
|
53
|
-
preset: string | (typeof IMAGE_PRESETS)[0]
|
|
54
|
-
) {
|
|
55
|
-
if (!Array.isArray(viewportNames)) {
|
|
56
|
-
console.error(
|
|
57
|
-
"Invalid parameter, viewportNames has to be an array of viewport names."
|
|
58
|
-
);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
let image_preset =
|
|
62
|
-
typeof preset === "string" ? find(IMAGE_PRESETS, { name: preset }) : preset;
|
|
63
|
-
|
|
64
|
-
if (!image_preset) {
|
|
65
|
-
console.error("Invalid image preset");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
each(viewportNames, function (viewportName: string) {
|
|
70
|
-
let element = document.getElementById(viewportName);
|
|
71
|
-
let enabledElement;
|
|
72
|
-
|
|
73
|
-
if (!element) {
|
|
74
|
-
console.warn("No element with id", viewportName);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
enabledElement = cornerstone.getEnabledElement(element);
|
|
80
|
-
} catch {
|
|
81
|
-
console.warn("No enabledElement with id", viewportName);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let viewport = cornerstone.getViewport(element);
|
|
86
|
-
|
|
87
|
-
if (!viewport) {
|
|
88
|
-
console.warn("No viewport with id", viewportName);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
viewport.voi.windowWidth = image_preset!.ww;
|
|
93
|
-
viewport.voi.windowCenter = image_preset!.wl;
|
|
94
|
-
cornerstone.setViewport(element, viewport);
|
|
95
|
-
// sync ww and wc values in store
|
|
96
|
-
setStore([
|
|
97
|
-
"contrast",
|
|
98
|
-
viewportName,
|
|
99
|
-
viewport.voi.windowWidth,
|
|
100
|
-
viewport.voi.windowCenter
|
|
101
|
-
]);
|
|
102
|
-
});
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Set Image presets
|
|
107
|
-
* @instance
|
|
108
|
-
* @function setImageCustomPreset
|
|
109
|
-
* @param {Array} viewportNames - List of viewports where to apply preset
|
|
110
|
-
* @param {Object} customValues - {wl: value, ww: value}
|
|
111
|
-
*/
|
|
112
|
-
export const setImageCustomPreset = function (
|
|
113
|
-
viewportNames: string[],
|
|
114
|
-
customValues: { wl: number; ww: number }
|
|
115
|
-
) {
|
|
116
|
-
if (!Array.isArray(viewportNames)) {
|
|
117
|
-
console.error(
|
|
118
|
-
"Invalid parameter, viewportNames has to be an array of viewport names."
|
|
119
|
-
);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
each(viewportNames, function (viewportName: string) {
|
|
123
|
-
let element = document.getElementById(viewportName);
|
|
124
|
-
let enabledElement;
|
|
125
|
-
|
|
126
|
-
if (!element) {
|
|
127
|
-
console.warn("No element with id", viewportName);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
enabledElement = cornerstone.getEnabledElement(element);
|
|
133
|
-
} catch {
|
|
134
|
-
console.warn("No enabledElement with id", viewportName);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
let viewport = cornerstone.getViewport(element);
|
|
139
|
-
|
|
140
|
-
if (!viewport) {
|
|
141
|
-
console.warn("No viewport with id", viewportName);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
viewport.voi.windowWidth = customValues.ww;
|
|
146
|
-
viewport.voi.windowCenter = customValues.wl;
|
|
147
|
-
cornerstone.setViewport(element, viewport);
|
|
148
|
-
// sync ww and wc values in store
|
|
149
|
-
setStore([
|
|
150
|
-
"contrast",
|
|
151
|
-
viewportName,
|
|
152
|
-
viewport.voi.windowWidth,
|
|
153
|
-
viewport.voi.windowCenter
|
|
154
|
-
]);
|
|
155
|
-
});
|
|
156
|
-
};
|