larvitar 1.5.13 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +4 -0
- package/README.md +78 -48
- package/bundler/webpack.common.js +27 -0
- package/bundler/webpack.dev.js +23 -0
- package/bundler/webpack.prod.js +19 -0
- package/decs.d.ts +12 -0
- package/dist/imaging/MetaDataReadable.d.ts +39 -0
- package/dist/imaging/MetaDataTypes.d.ts +3488 -0
- package/dist/imaging/imageAnonymization.d.ts +12 -0
- package/dist/imaging/imageColormaps.d.ts +47 -0
- package/dist/imaging/imageContours.d.ts +18 -0
- package/dist/imaging/imageIo.d.ts +42 -0
- package/dist/imaging/imageLayers.d.ts +56 -0
- package/dist/imaging/imageLoading.d.ts +65 -0
- package/dist/imaging/imageParsing.d.ts +46 -0
- package/dist/imaging/imagePresets.d.ts +43 -0
- package/dist/imaging/imageRendering.d.ts +238 -0
- package/dist/imaging/imageReslice.d.ts +14 -0
- package/dist/imaging/imageStore.d.ts +121 -0
- package/dist/imaging/imageTags.d.ts +22 -0
- package/dist/imaging/imageTools.d.ts +20 -0
- package/dist/imaging/imageUtils.d.ts +165 -0
- package/dist/imaging/loaders/commonLoader.d.ts +103 -0
- package/dist/imaging/loaders/dicomLoader.d.ts +29 -0
- package/dist/imaging/loaders/fileLoader.d.ts +33 -0
- package/dist/imaging/loaders/multiframeLoader.d.ts +37 -0
- package/dist/imaging/loaders/nrrdLoader.d.ts +112 -0
- package/dist/imaging/loaders/resliceLoader.d.ts +15 -0
- package/dist/imaging/monitors/memory.d.ts +41 -0
- package/dist/imaging/monitors/performance.d.ts +23 -0
- package/dist/imaging/parsers/ecg.d.ts +15 -0
- package/dist/imaging/parsers/nrrd.d.ts +3 -0
- package/dist/imaging/tools/custom/4dSliceScrollTool.d.ts +12 -0
- package/dist/imaging/tools/custom/contourTool.d.ts +409 -0
- package/dist/imaging/tools/custom/diameterTool.d.ts +18 -0
- package/dist/imaging/tools/custom/editMaskTool.d.ts +22 -0
- package/dist/imaging/tools/custom/ellipticalRoiOverlayTool.d.ts +45 -0
- package/dist/imaging/tools/custom/polygonSegmentationMixin.d.ts +54 -0
- package/dist/imaging/tools/custom/polylineScissorsTool.d.ts +11 -0
- package/dist/imaging/tools/custom/rectangleRoiOverlayTool.d.ts +45 -0
- package/dist/imaging/tools/custom/seedTool.d.ts +0 -0
- package/dist/imaging/tools/custom/setLabelMap3D.d.ts +39 -0
- package/dist/imaging/tools/custom/thresholdsBrushTool.d.ts +19 -0
- package/dist/imaging/tools/default.d.ts +53 -0
- package/dist/imaging/tools/interaction.d.ts +30 -0
- package/dist/imaging/tools/io.d.ts +38 -0
- package/dist/imaging/tools/main.d.ts +81 -0
- package/dist/imaging/tools/segmentation.d.ts +125 -0
- package/dist/imaging/tools/state.d.ts +17 -0
- package/dist/imaging/tools/strategies/eraseFreehand.d.ts +16 -0
- package/dist/imaging/tools/strategies/fillFreehand.d.ts +16 -0
- package/dist/imaging/tools/strategies/index.d.ts +2 -0
- package/dist/index.d.ts +34 -0
- package/dist/larvitar.js +89801 -0
- package/dist/larvitar.js.map +1 -0
- package/imaging/MetaDataReadable.ts +40 -0
- package/imaging/MetaDataTypes.ts +3490 -0
- package/imaging/dataDictionary.json +5328 -5328
- package/imaging/{imageAnonymization.js → imageAnonymization.ts} +41 -13
- package/imaging/{imageColormaps.js → imageColormaps.ts} +48 -30
- package/imaging/{imageContours.js → imageContours.ts} +24 -22
- package/imaging/{imageIo.js → imageIo.ts} +89 -52
- package/imaging/{imageLayers.js → imageLayers.ts} +31 -14
- package/imaging/{imageLoading.js → imageLoading.ts} +108 -45
- package/imaging/{imageParsing.js → imageParsing.ts} +158 -80
- package/imaging/{imagePresets.js → imagePresets.ts} +44 -11
- package/imaging/imageRendering.ts +1091 -0
- package/imaging/{imageReslice.js → imageReslice.ts} +18 -9
- package/imaging/imageStore.ts +487 -0
- package/imaging/imageTags.ts +609 -0
- package/imaging/imageTools.js +2 -1
- package/imaging/{imageUtils.js → imageUtils.ts} +211 -701
- package/imaging/loaders/{commonLoader.js → commonLoader.ts} +73 -24
- package/imaging/loaders/{dicomLoader.js → dicomLoader.ts} +25 -5
- package/imaging/loaders/{fileLoader.js → fileLoader.ts} +5 -5
- package/imaging/loaders/{multiframeLoader.js → multiframeLoader.ts} +145 -90
- package/imaging/loaders/{nrrdLoader.js → nrrdLoader.ts} +230 -64
- package/imaging/loaders/{resliceLoader.js → resliceLoader.ts} +51 -20
- package/imaging/monitors/{memory.js → memory.ts} +54 -8
- package/imaging/monitors/performance.ts +34 -0
- package/imaging/parsers/ecg.ts +51 -0
- package/imaging/tools/README.md +27 -0
- package/imaging/tools/custom/4dSliceScrollTool.js +47 -46
- package/imaging/tools/custom/ellipticalRoiOverlayTool.js +534 -0
- package/imaging/tools/custom/polylineScissorsTool.js +1 -1
- package/imaging/tools/custom/rectangleRoiOverlayTool.js +564 -0
- package/imaging/tools/{setLabelMap3D.js → custom/setLabelMap3D.ts} +19 -25
- package/imaging/tools/{default.js → default.ts} +114 -30
- package/imaging/tools/{interaction.js → interaction.ts} +42 -23
- package/imaging/tools/{io.js → io.ts} +47 -31
- package/imaging/tools/{main.js → main.ts} +105 -40
- package/imaging/tools/{segmentation.js → segmentation.ts} +95 -68
- package/imaging/tools/{state.js → state.ts} +7 -12
- package/imaging/tools/types.d.ts +243 -0
- package/imaging/types.d.ts +197 -0
- package/{index.js → index.ts} +43 -14
- package/jsdoc.json +1 -1
- package/package.json +32 -14
- package/tsconfig.json +102 -0
- package/imaging/imageRendering.js +0 -860
- package/imaging/imageStore.js +0 -322
- package/modules/vuex/larvitar.js +0 -187
- /package/imaging/tools/{polygonSegmentationMixin.js → custom/polygonSegmentationMixin.js} +0 -0
|
@@ -21,16 +21,27 @@ import {
|
|
|
21
21
|
} from "lodash";
|
|
22
22
|
import { v4 as uuidv4 } from "uuid";
|
|
23
23
|
import cornerstone from "cornerstone-core";
|
|
24
|
-
import { convertBytes } from "dicom-character-set";
|
|
25
24
|
|
|
26
25
|
// internal libraries
|
|
27
26
|
import { getDicomImageId } from "./loaders/dicomLoader";
|
|
28
27
|
import TAG_DICT from "./dataDictionary.json";
|
|
29
28
|
import { getSeriesDataFromLarvitarManager } from "./loaders/commonLoader";
|
|
29
|
+
import type {
|
|
30
|
+
CustomDataSet,
|
|
31
|
+
MetaData,
|
|
32
|
+
ReslicedInstance,
|
|
33
|
+
Series
|
|
34
|
+
} from "./types";
|
|
35
|
+
import { getTagValue } from "./imageTags";
|
|
36
|
+
import { MetaDataTypes } from "./MetaDataTypes";
|
|
30
37
|
|
|
31
38
|
// global module variables
|
|
32
39
|
// variables used to manage the reslice functionality
|
|
33
|
-
const resliceTable
|
|
40
|
+
const resliceTable: {
|
|
41
|
+
[key: string]: {
|
|
42
|
+
[key: string]: [number, number, number];
|
|
43
|
+
};
|
|
44
|
+
} = {
|
|
34
45
|
sagittal: { coronal: [-2, 1, 0], axial: [-2, 0, -1] },
|
|
35
46
|
coronal: { sagittal: [2, 1, -0], axial: [0, 2, -1] },
|
|
36
47
|
axial: { sagittal: [1, -2, -0], coronal: [0, -2, 1] }
|
|
@@ -43,7 +54,6 @@ const resliceTable = {
|
|
|
43
54
|
* getMaxPixelValue(defaultValue, pixelData)
|
|
44
55
|
* getPixelRepresentation(dataset)
|
|
45
56
|
* getTypedArrayFromDataType(dataType)
|
|
46
|
-
* getPixelTypedArray(dataset, pixelDataElement)
|
|
47
57
|
* getSortedStack(seriesData, sortPriorities, returnSuccessMethod)
|
|
48
58
|
* getSortedUIDs(seriesData)
|
|
49
59
|
* randomId()
|
|
@@ -52,7 +62,6 @@ const resliceTable = {
|
|
|
52
62
|
* getCmprMetadata(reslicedSeriesId, imageLoaderName, header)
|
|
53
63
|
* getReslicedPixeldata(imageId, originalData, reslicedData)
|
|
54
64
|
* getDistanceBetweenSlices(seriesData, sliceIndex1, sliceIndex2)
|
|
55
|
-
* parseTag(dataSet, propertyName, element)
|
|
56
65
|
* isElement(o)
|
|
57
66
|
* getImageMetadata(dataSet, imageId)
|
|
58
67
|
*/
|
|
@@ -70,7 +79,9 @@ const resliceTable = {
|
|
|
70
79
|
* @function getNormalOrientation
|
|
71
80
|
* @param {Array} el - The image_orientation dicom tag
|
|
72
81
|
*/
|
|
73
|
-
export const getNormalOrientation = function (
|
|
82
|
+
export const getNormalOrientation = function (
|
|
83
|
+
el: [number, number, number, number, number, number]
|
|
84
|
+
) {
|
|
74
85
|
let a = [el[0], el[1], el[2]];
|
|
75
86
|
let b = [el[3], el[4], el[5]];
|
|
76
87
|
|
|
@@ -90,7 +101,10 @@ export const getNormalOrientation = function (el) {
|
|
|
90
101
|
* @param {Number} value - The min value
|
|
91
102
|
* @param {Array} pixelData - Pixel data array
|
|
92
103
|
*/
|
|
93
|
-
export const getMinPixelValue = function (
|
|
104
|
+
export const getMinPixelValue = function (
|
|
105
|
+
value: number,
|
|
106
|
+
pixelData: Uint16Array
|
|
107
|
+
) {
|
|
94
108
|
if (value !== undefined) {
|
|
95
109
|
return value;
|
|
96
110
|
}
|
|
@@ -110,7 +124,10 @@ export const getMinPixelValue = function (value, pixelData) {
|
|
|
110
124
|
* @param {Number} value - The max value
|
|
111
125
|
* @param {Array} pixelData - Pixel data array
|
|
112
126
|
*/
|
|
113
|
-
export const getMaxPixelValue = function (
|
|
127
|
+
export const getMaxPixelValue = function (
|
|
128
|
+
value: string,
|
|
129
|
+
pixelData: Uint16Array
|
|
130
|
+
) {
|
|
114
131
|
if (value !== undefined) {
|
|
115
132
|
return value;
|
|
116
133
|
}
|
|
@@ -131,7 +148,7 @@ export const getMaxPixelValue = function (value, pixelData) {
|
|
|
131
148
|
* @param {Object} dataSet - The dataset
|
|
132
149
|
* @returns {String} The pixel representation in the form Sint / Uint + bytelength
|
|
133
150
|
*/
|
|
134
|
-
export const getPixelRepresentation = function (dataSet) {
|
|
151
|
+
export const getPixelRepresentation = function (dataSet: CustomDataSet) {
|
|
135
152
|
if (dataSet.repr) {
|
|
136
153
|
return dataSet.repr;
|
|
137
154
|
} else {
|
|
@@ -156,50 +173,17 @@ export const getPixelRepresentation = function (dataSet) {
|
|
|
156
173
|
* @param {Object} dataType - The data type
|
|
157
174
|
* @returns {TypedArray} The typed array
|
|
158
175
|
*/
|
|
159
|
-
export const getTypedArrayFromDataType = function (dataType) {
|
|
160
|
-
let repr = dataType.toLowerCase();
|
|
176
|
+
export const getTypedArrayFromDataType = function (dataType: string) {
|
|
177
|
+
let repr = dataType.toLowerCase() as keyof typeof TYPES_TO_TYPEDARRAY;
|
|
161
178
|
let typedArray = has(TYPES_TO_TYPEDARRAY, repr)
|
|
162
179
|
? TYPES_TO_TYPEDARRAY[repr]
|
|
163
180
|
: null;
|
|
164
181
|
if (!typedArray) {
|
|
165
|
-
console.error("
|
|
182
|
+
console.error("invalid data type: ", dataType);
|
|
166
183
|
}
|
|
167
184
|
return typedArray;
|
|
168
185
|
};
|
|
169
186
|
|
|
170
|
-
/**
|
|
171
|
-
* Create and return a typed array from the pixel data
|
|
172
|
-
* @instance
|
|
173
|
-
* @function getPixelTypedArray
|
|
174
|
-
* @param {Object} dataSet - The cornerstone serie object
|
|
175
|
-
* @param {Object} pixelDataElement - The dataset metadata (dataSet.elements.x7fe00010)
|
|
176
|
-
* @returns {TypedArray} The pixel array as proper typed array
|
|
177
|
-
*/
|
|
178
|
-
export const getPixelTypedArray = function (dataSet, pixelDataElement) {
|
|
179
|
-
let buffer = dataSet.byteArray.buffer;
|
|
180
|
-
let offset = pixelDataElement.dataOffset;
|
|
181
|
-
let r = getPixelRepresentation(dataSet);
|
|
182
|
-
let typedArray = getTypedArrayFromDataType(r);
|
|
183
|
-
switch (typedArray) {
|
|
184
|
-
case Uint16Array:
|
|
185
|
-
length = pixelDataElement.length / 2;
|
|
186
|
-
break;
|
|
187
|
-
case Int16Array:
|
|
188
|
-
length = pixelDataElement.length / 2;
|
|
189
|
-
break;
|
|
190
|
-
case Uint32Array:
|
|
191
|
-
length = pixelDataElement.length / 4;
|
|
192
|
-
break;
|
|
193
|
-
case Int32Array:
|
|
194
|
-
length = pixelDataElement.length / 4;
|
|
195
|
-
break;
|
|
196
|
-
default:
|
|
197
|
-
length = pixelDataElement.length;
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
return new typedArray(buffer, offset, length);
|
|
201
|
-
};
|
|
202
|
-
|
|
203
187
|
/**
|
|
204
188
|
* Sort the array of images ids of a series trying with:
|
|
205
189
|
* - content time order, if the series has cardiacNumberOfImages tag > 1
|
|
@@ -214,31 +198,30 @@ export const getPixelTypedArray = function (dataSet, pixelDataElement) {
|
|
|
214
198
|
* @return {Object} The sorted stack
|
|
215
199
|
*/
|
|
216
200
|
export const getSortedStack = function (
|
|
217
|
-
seriesData,
|
|
218
|
-
sortPriorities
|
|
219
|
-
returnSuccessMethod
|
|
201
|
+
seriesData: Series,
|
|
202
|
+
sortPriorities: Array<"imagePosition" | "contentTime" | "instanceNumber">,
|
|
203
|
+
returnSuccessMethod: boolean
|
|
220
204
|
) {
|
|
221
|
-
let tryToSort = function (
|
|
205
|
+
let tryToSort = function (
|
|
206
|
+
data: Series,
|
|
207
|
+
methods: typeof sortPriorities
|
|
208
|
+
): string[] {
|
|
222
209
|
if (isEmpty(methods)) {
|
|
223
210
|
if (returnSuccessMethod === true) {
|
|
224
|
-
return sorted
|
|
211
|
+
return sorted!;
|
|
225
212
|
} else {
|
|
226
|
-
return sorted
|
|
213
|
+
return sorted!;
|
|
227
214
|
}
|
|
228
215
|
}
|
|
229
216
|
|
|
230
217
|
let sortMethod = methods.shift();
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return sorted;
|
|
239
|
-
}
|
|
240
|
-
} catch (ex) {
|
|
241
|
-
return tryToSort(data, methods);
|
|
218
|
+
var sorted = sortBy(data.imageIds, function (imageId: string) {
|
|
219
|
+
return sortStackCallback(data, imageId, sortMethod!);
|
|
220
|
+
});
|
|
221
|
+
if (returnSuccessMethod === true) {
|
|
222
|
+
return sorted;
|
|
223
|
+
} else {
|
|
224
|
+
return sorted;
|
|
242
225
|
}
|
|
243
226
|
};
|
|
244
227
|
|
|
@@ -254,10 +237,11 @@ export const getSortedStack = function (
|
|
|
254
237
|
* @param {Object} seriesData - The dataset
|
|
255
238
|
* @return {Object} The sorted instanceUIDs
|
|
256
239
|
*/
|
|
257
|
-
export const getSortedUIDs = function (seriesData) {
|
|
258
|
-
let instanceUIDs = {};
|
|
259
|
-
forEach(seriesData.imageIds, function (imageId) {
|
|
260
|
-
|
|
240
|
+
export const getSortedUIDs = function (seriesData: Series) {
|
|
241
|
+
let instanceUIDs: { [key: string]: string } = {};
|
|
242
|
+
forEach(seriesData.imageIds, function (imageId: string) {
|
|
243
|
+
let instanceUID = seriesData.instances[imageId].metadata.instanceUID!;
|
|
244
|
+
instanceUIDs[instanceUID] = imageId;
|
|
261
245
|
});
|
|
262
246
|
return instanceUIDs;
|
|
263
247
|
};
|
|
@@ -281,35 +265,53 @@ export const randomId = function () {
|
|
|
281
265
|
* @param {Bool} isArray - True if tag value is an array
|
|
282
266
|
* @return {Number} - Tag mean value
|
|
283
267
|
*/
|
|
284
|
-
export const getMeanValue = function (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
meanValue
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
268
|
+
export const getMeanValue = function (
|
|
269
|
+
series: Series,
|
|
270
|
+
tag: keyof MetaData,
|
|
271
|
+
isArray: boolean
|
|
272
|
+
) {
|
|
273
|
+
let meanValue = isArray ? ([] as number[]) : (0 as number);
|
|
274
|
+
|
|
275
|
+
forEach(series.imageIds, function (imageId: string) {
|
|
276
|
+
let tagValue = series.instances[imageId].metadata[
|
|
277
|
+
tag
|
|
278
|
+
] as MetaData[typeof tag];
|
|
279
|
+
if (Array.isArray(tagValue)) {
|
|
280
|
+
tagValue; //exclude array of metadatatypes
|
|
281
|
+
meanValue = meanValue as number[];
|
|
282
|
+
|
|
283
|
+
if (tagValue.length === 2) {
|
|
284
|
+
tagValue = tagValue as [number, number];
|
|
285
|
+
meanValue[0] = meanValue[0] ? meanValue[0] + tagValue[0] : tagValue[0];
|
|
286
|
+
meanValue[1] = meanValue[1] ? meanValue[1] + tagValue[1] : tagValue[1];
|
|
287
|
+
} else if (tagValue.length === 3) {
|
|
288
|
+
tagValue = tagValue as [number, number, number];
|
|
289
|
+
meanValue[0] = meanValue[0] ? meanValue[0] + tagValue[0] : tagValue[0];
|
|
290
|
+
meanValue[1] = meanValue[1] ? meanValue[1] + tagValue[1] : tagValue[1];
|
|
291
|
+
meanValue[2] = meanValue[2] ? meanValue[2] + tagValue[2] : tagValue[2];
|
|
292
|
+
} else if (tagValue.length === 6) {
|
|
293
|
+
tagValue = tagValue as [number, number, number, number, number, number];
|
|
294
|
+
meanValue[0] = meanValue[0] ? meanValue[0] + tagValue[0] : tagValue[0];
|
|
295
|
+
meanValue[1] = meanValue[1] ? meanValue[1] + tagValue[1] : tagValue[1];
|
|
296
|
+
meanValue[2] = meanValue[2] ? meanValue[2] + tagValue[2] : tagValue[2];
|
|
297
|
+
meanValue[3] = meanValue[3] ? meanValue[3] + tagValue[3] : tagValue[3];
|
|
298
|
+
meanValue[4] = meanValue[4] ? meanValue[4] + tagValue[4] : tagValue[4];
|
|
299
|
+
meanValue[5] = meanValue[5] ? meanValue[5] + tagValue[5] : tagValue[5];
|
|
300
|
+
}
|
|
303
301
|
} else {
|
|
302
|
+
meanValue = meanValue as number;
|
|
303
|
+
tagValue = parseFloat(tagValue as string);
|
|
304
304
|
meanValue += tagValue;
|
|
305
305
|
}
|
|
306
306
|
});
|
|
307
307
|
|
|
308
308
|
if (isArray) {
|
|
309
|
+
meanValue = meanValue as number[];
|
|
309
310
|
for (let i = 0; i < meanValue.length; i++) {
|
|
310
311
|
meanValue[i] /= series.imageIds.length;
|
|
311
312
|
}
|
|
312
313
|
} else {
|
|
314
|
+
meanValue = meanValue as number;
|
|
313
315
|
meanValue /= series.imageIds.length;
|
|
314
316
|
}
|
|
315
317
|
return meanValue;
|
|
@@ -327,11 +329,11 @@ export const getMeanValue = function (series, tag, isArray) {
|
|
|
327
329
|
* @return {Object} - Cornerstone series object, filled only with metadata
|
|
328
330
|
*/
|
|
329
331
|
export const getReslicedMetadata = function (
|
|
330
|
-
reslicedSeriesId,
|
|
331
|
-
fromOrientation,
|
|
332
|
-
toOrientation,
|
|
333
|
-
seriesData,
|
|
334
|
-
imageLoaderName
|
|
332
|
+
reslicedSeriesId: string,
|
|
333
|
+
fromOrientation: "axial" | "coronal" | "sagittal",
|
|
334
|
+
toOrientation: "axial" | "coronal" | "sagittal",
|
|
335
|
+
seriesData: Series,
|
|
336
|
+
imageLoaderName: string
|
|
335
337
|
) {
|
|
336
338
|
// get reslice metadata and apply the reslice algorithm
|
|
337
339
|
let permuteTable = resliceTable[fromOrientation][toOrientation];
|
|
@@ -340,20 +342,20 @@ export const getReslicedMetadata = function (
|
|
|
340
342
|
});
|
|
341
343
|
|
|
342
344
|
// orthogonal reslice algorithm
|
|
343
|
-
let reslicedImageIds = [];
|
|
344
|
-
let reslicedInstances = {};
|
|
345
|
+
let reslicedImageIds: string[] = [];
|
|
346
|
+
let reslicedInstances: { [key: string]: ReslicedInstance } = {};
|
|
345
347
|
|
|
346
348
|
let sampleMetadata = seriesData.instances[seriesData.imageIds[0]].metadata;
|
|
347
349
|
|
|
348
350
|
let fromSize = [
|
|
349
|
-
sampleMetadata.x00280011
|
|
350
|
-
sampleMetadata.x00280010
|
|
351
|
+
sampleMetadata.x00280011!,
|
|
352
|
+
sampleMetadata.x00280010!,
|
|
351
353
|
seriesData.imageIds.length
|
|
352
354
|
];
|
|
353
355
|
let toSize = permuteValues(permuteAbsTable, fromSize);
|
|
354
356
|
let fromSpacing = spacingArray(seriesData, sampleMetadata);
|
|
355
|
-
let toSpacing = permuteValues(permuteAbsTable, fromSpacing);
|
|
356
|
-
let reslicedIOP = getReslicedIOP(sampleMetadata.x00200037
|
|
357
|
+
let toSpacing = permuteValues(permuteAbsTable, fromSpacing as number[]);
|
|
358
|
+
let reslicedIOP = getReslicedIOP(sampleMetadata.x00200037!, permuteTable);
|
|
357
359
|
|
|
358
360
|
for (let f = 0; f < toSize[2]; f++) {
|
|
359
361
|
let reslicedImageId = getDicomImageId(imageLoaderName);
|
|
@@ -361,16 +363,16 @@ export const getReslicedMetadata = function (
|
|
|
361
363
|
|
|
362
364
|
let instanceId = uuidv4();
|
|
363
365
|
let reslicedIPP = getReslicedIPP(
|
|
364
|
-
sampleMetadata.x00200032,
|
|
365
|
-
sampleMetadata.x00200037
|
|
366
|
+
sampleMetadata.x00200032 as [number, number, number],
|
|
367
|
+
sampleMetadata.x00200037!,
|
|
366
368
|
reslicedIOP,
|
|
367
369
|
permuteTable,
|
|
368
370
|
f,
|
|
369
371
|
fromSize,
|
|
370
372
|
toSize,
|
|
371
|
-
fromSpacing
|
|
373
|
+
fromSpacing as number[]
|
|
372
374
|
);
|
|
373
|
-
let metadata = extend(clone(sampleMetadata), {
|
|
375
|
+
let metadata: MetaData = extend(clone(sampleMetadata), {
|
|
374
376
|
// pixel representation
|
|
375
377
|
x00280100: sampleMetadata.x00280100,
|
|
376
378
|
x00280103: sampleMetadata.x00280103,
|
|
@@ -413,12 +415,10 @@ export const getReslicedMetadata = function (
|
|
|
413
415
|
// new image orientation
|
|
414
416
|
x00200037: reslicedIOP,
|
|
415
417
|
// new image position
|
|
416
|
-
x00200032: reslicedIPP
|
|
417
|
-
x00280106: sampleMetadata.x00280106,
|
|
418
|
-
x00280107: sampleMetadata.x00280107
|
|
418
|
+
x00200032: reslicedIPP
|
|
419
419
|
});
|
|
420
420
|
|
|
421
|
-
// set human readable metadata
|
|
421
|
+
// set human readable metadata.
|
|
422
422
|
metadata.seriesUID = reslicedSeriesId;
|
|
423
423
|
metadata.rows = metadata.x00280010;
|
|
424
424
|
metadata.cols = metadata.x00280011;
|
|
@@ -454,12 +454,12 @@ export const getReslicedMetadata = function (
|
|
|
454
454
|
* @return {Object} - Cornerstone series object, filled only with metadata
|
|
455
455
|
*/
|
|
456
456
|
export const getCmprMetadata = function (
|
|
457
|
-
reslicedSeriesId,
|
|
458
|
-
imageLoaderName,
|
|
459
|
-
header
|
|
457
|
+
reslicedSeriesId: string,
|
|
458
|
+
imageLoaderName: string,
|
|
459
|
+
header: any // TODO-ts : type
|
|
460
460
|
) {
|
|
461
|
-
let reslicedImageIds = [];
|
|
462
|
-
let reslicedInstances = {};
|
|
461
|
+
let reslicedImageIds: string[] = [];
|
|
462
|
+
let reslicedInstances: { [key: string]: ReslicedInstance } = {};
|
|
463
463
|
|
|
464
464
|
for (let f = 0; f < header.frames_number; f++) {
|
|
465
465
|
let reslicedImageId = getDicomImageId(imageLoaderName);
|
|
@@ -467,7 +467,7 @@ export const getCmprMetadata = function (
|
|
|
467
467
|
|
|
468
468
|
let instanceId = uuidv4();
|
|
469
469
|
|
|
470
|
-
let metadata = {
|
|
470
|
+
let metadata: MetaData = {
|
|
471
471
|
// pixel representation
|
|
472
472
|
x00280100: header.repr,
|
|
473
473
|
// Bits Allocated
|
|
@@ -477,7 +477,7 @@ export const getCmprMetadata = function (
|
|
|
477
477
|
x00280011: header.cols, // cols
|
|
478
478
|
// resliced series spacing
|
|
479
479
|
x00280030: [header.spacing[1], header.spacing[0]],
|
|
480
|
-
x00180050: [header.distance_btw_slices],
|
|
480
|
+
x00180050: [header.distance_btw_slices] as number[],
|
|
481
481
|
// remove min and max pixelvalue from metadata before calling the createCustomImage function:
|
|
482
482
|
// need to recalculate the min and max pixel values on the new instance pixelData
|
|
483
483
|
x00280106: undefined,
|
|
@@ -506,8 +506,8 @@ export const getCmprMetadata = function (
|
|
|
506
506
|
// data needed to obtain a good rendering
|
|
507
507
|
x00281050: [header.wwwl[1] / 2], // [wl]
|
|
508
508
|
x00281051: [header.wwwl[0]], // [ww]
|
|
509
|
-
x00281052:
|
|
510
|
-
x00281053:
|
|
509
|
+
x00281052: header.intercept,
|
|
510
|
+
x00281053: header.slope,
|
|
511
511
|
// new image orientation (IOP)
|
|
512
512
|
x00200037: header.iop ? header.iop.slice(f * 6, (f + 1) * 6) : null,
|
|
513
513
|
// new image position (IPP)
|
|
@@ -536,27 +536,30 @@ export const getCmprMetadata = function (
|
|
|
536
536
|
* @return {Object} - A single resliced slice pixel array
|
|
537
537
|
*/
|
|
538
538
|
export const getReslicedPixeldata = function (
|
|
539
|
-
imageId,
|
|
540
|
-
originalData,
|
|
541
|
-
reslicedData
|
|
539
|
+
imageId: string,
|
|
540
|
+
originalData: Series,
|
|
541
|
+
reslicedData: Series
|
|
542
542
|
) {
|
|
543
543
|
// resliced metadata must be already available
|
|
544
|
-
let reslicedInstance = reslicedData.instances[imageId];
|
|
544
|
+
let reslicedInstance = reslicedData.instances[imageId] as ReslicedInstance;
|
|
545
545
|
let reslicedMetadata = reslicedInstance.metadata;
|
|
546
|
+
if (!reslicedInstance.permuteTable) {
|
|
547
|
+
throw new Error("Resliced permuteTable not available");
|
|
548
|
+
}
|
|
546
549
|
let permuteAbsTable = reslicedInstance.permuteTable.map(function (v) {
|
|
547
550
|
return Math.abs(v);
|
|
548
551
|
});
|
|
549
552
|
|
|
550
553
|
// compute resliced series pixelData, use the correct typedarray
|
|
551
|
-
let rows = reslicedMetadata.x00280010;
|
|
552
|
-
let cols = reslicedMetadata.x00280011;
|
|
553
|
-
let reslicedSlice = getTypedArray(reslicedMetadata, rows * cols);
|
|
554
|
+
let rows = reslicedMetadata.x00280010 as number;
|
|
555
|
+
let cols = reslicedMetadata.x00280011 as number;
|
|
556
|
+
let reslicedSlice = getTypedArray(reslicedMetadata as any, rows * cols); // TODO-ts : type of reslicedMetadata?
|
|
554
557
|
|
|
555
558
|
let frame = indexOf(reslicedData.imageIds, imageId);
|
|
556
559
|
let originalInstance = originalData.instances[originalData.imageIds[0]];
|
|
557
|
-
let fromCols = originalInstance.metadata.x00280011;
|
|
560
|
+
let fromCols = originalInstance.metadata.x00280011 as number;
|
|
558
561
|
|
|
559
|
-
function getPixelValue(ijf) {
|
|
562
|
+
function getPixelValue(ijf: [number, number, number]) {
|
|
560
563
|
let i = ijf[0];
|
|
561
564
|
let j = ijf[1];
|
|
562
565
|
let f = ijf[2];
|
|
@@ -577,7 +580,7 @@ export const getReslicedPixeldata = function (
|
|
|
577
580
|
|
|
578
581
|
for (let j = 0; j < rows; j++) {
|
|
579
582
|
for (let i = 0; i < cols; i++) {
|
|
580
|
-
let ijf = [0, 0, 0];
|
|
583
|
+
let ijf: [number, number, number] = [0, 0, 0];
|
|
581
584
|
ijf[permuteAbsTable[0]] = i;
|
|
582
585
|
ijf[permuteAbsTable[1]] = j;
|
|
583
586
|
ijf[permuteAbsTable[2]] = frame;
|
|
@@ -607,9 +610,9 @@ export const getReslicedPixeldata = function (
|
|
|
607
610
|
* @return {Number} - The distance value
|
|
608
611
|
*/
|
|
609
612
|
export const getDistanceBetweenSlices = function (
|
|
610
|
-
seriesData,
|
|
611
|
-
sliceIndex1,
|
|
612
|
-
sliceIndex2
|
|
613
|
+
seriesData: Series,
|
|
614
|
+
sliceIndex1: number,
|
|
615
|
+
sliceIndex2: number
|
|
613
616
|
) {
|
|
614
617
|
if (seriesData.imageIds.length <= 1) {
|
|
615
618
|
return 0;
|
|
@@ -618,12 +621,8 @@ export const getDistanceBetweenSlices = function (
|
|
|
618
621
|
let imageId1 = seriesData.imageIds[sliceIndex1];
|
|
619
622
|
let instance1 = seriesData.instances[imageId1];
|
|
620
623
|
let metadata1 = instance1.metadata;
|
|
621
|
-
let imageOrientation = metadata1.
|
|
622
|
-
|
|
623
|
-
: metadata1.x00200037;
|
|
624
|
-
let imagePosition = metadata1.imagePosition
|
|
625
|
-
? metadata1.imagePosition
|
|
626
|
-
: metadata1.x00200032;
|
|
624
|
+
let imageOrientation = metadata1.x00200037;
|
|
625
|
+
let imagePosition = metadata1.x00200032 as [number, number, number];
|
|
627
626
|
|
|
628
627
|
if (imageOrientation && imagePosition) {
|
|
629
628
|
let normal = getNormalOrientation(imageOrientation);
|
|
@@ -635,223 +634,17 @@ export const getDistanceBetweenSlices = function (
|
|
|
635
634
|
let imageId2 = seriesData.imageIds[sliceIndex2];
|
|
636
635
|
let instance2 = seriesData.instances[imageId2];
|
|
637
636
|
let metadata2 = instance2.metadata;
|
|
638
|
-
let imagePosition2 = metadata2.
|
|
639
|
-
? metadata2.imagePosition
|
|
640
|
-
: metadata2.x00200032;
|
|
637
|
+
let imagePosition2 = metadata2.x00200032!;
|
|
641
638
|
|
|
642
639
|
let d2 =
|
|
643
640
|
normal[0] * imagePosition2[0] +
|
|
644
641
|
normal[1] * imagePosition2[1] +
|
|
645
|
-
normal[2] * imagePosition2[2]
|
|
642
|
+
normal[2] * imagePosition2[2]!;
|
|
646
643
|
|
|
647
644
|
return Math.abs(d1 - d2);
|
|
648
645
|
}
|
|
649
646
|
};
|
|
650
647
|
|
|
651
|
-
/**
|
|
652
|
-
* Parse a DICOM Tag according to its type
|
|
653
|
-
* @instance
|
|
654
|
-
* @function parseTag
|
|
655
|
-
* @param {Object} dataSet - The parsed dataset object from dicom parser
|
|
656
|
-
* @param {String} propertyName - The tag name
|
|
657
|
-
* @param {Object} element - The parsed dataset element
|
|
658
|
-
* @return {String} - The DICOM Tag value
|
|
659
|
-
*/
|
|
660
|
-
export const parseTag = function (dataSet, propertyName, element) {
|
|
661
|
-
// GET VR
|
|
662
|
-
var tagData = dataSet.elements[propertyName] || {};
|
|
663
|
-
var vr = tagData.vr;
|
|
664
|
-
if (!vr) {
|
|
665
|
-
// use dicom dict to get VR
|
|
666
|
-
var tag = getDICOMTag(propertyName);
|
|
667
|
-
if (tag && tag.vr) {
|
|
668
|
-
vr = tag.vr;
|
|
669
|
-
} else {
|
|
670
|
-
return element;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
var value;
|
|
675
|
-
|
|
676
|
-
if (isStringVr(vr)) {
|
|
677
|
-
// We ask the dataset to give us the element's data in string form.
|
|
678
|
-
// Most elements are strings but some aren't so we do a quick check
|
|
679
|
-
// to make sure it actually has all ascii characters so we know it is
|
|
680
|
-
// reasonable to display it.
|
|
681
|
-
var str = dataSet.string(propertyName);
|
|
682
|
-
if (str === undefined) {
|
|
683
|
-
return undefined;
|
|
684
|
-
} else {
|
|
685
|
-
// the string will be undefined if the element is present but has no data
|
|
686
|
-
// (i.e. attribute is of type 2 or 3) so we only display the string if it has
|
|
687
|
-
// data. Note that the length of the element will be 0 to indicate "no data"
|
|
688
|
-
// so we don't put anything here for the value in that case.
|
|
689
|
-
value = str;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// A string of characters representing an Integer in base-10 (decimal),
|
|
693
|
-
// shall contain only the characters 0 - 9, with an optional leading "+" or "-".
|
|
694
|
-
// It may be padded with leading and/or trailing spaces. Embedded spaces
|
|
695
|
-
// are not allowed. The integer, n, represented shall be in the range:
|
|
696
|
-
// -231 <= n <= (231 - 1).
|
|
697
|
-
if (vr === "IS") {
|
|
698
|
-
value = parseInt(value);
|
|
699
|
-
}
|
|
700
|
-
// A string of characters representing either a fixed point number
|
|
701
|
-
// or a floating point number. A fixed point number shall contain only
|
|
702
|
-
// the characters 0-9 with an optional leading "+" or "-" and an optional "."
|
|
703
|
-
// to mark the decimal point. A floating point number shall be conveyed
|
|
704
|
-
// as defined in ANSI X3.9, with an "E" or "e" to indicate the start
|
|
705
|
-
// of the exponent. Decimal Strings may be padded with leading or trailing spaces.
|
|
706
|
-
// Embedded spaces are not allowed.
|
|
707
|
-
else if (vr === "DS") {
|
|
708
|
-
value = value.split("\\").map(Number);
|
|
709
|
-
if (propertyName == "x00281050" || propertyName == "x00281051") {
|
|
710
|
-
value = value.length > 0 ? value[0] : value;
|
|
711
|
-
} else {
|
|
712
|
-
value = value.length == 1 ? value[0] : value;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
// A string of characters of the format YYYYMMDD; where YYYY shall contain year,
|
|
716
|
-
// MM shall contain the month, and DD shall contain the day,
|
|
717
|
-
// interpreted as a date of the Gregorian calendar system.
|
|
718
|
-
else if (vr === "DA") {
|
|
719
|
-
value = parseDateTag(value, false);
|
|
720
|
-
}
|
|
721
|
-
// A concatenated date-time character string in the format:
|
|
722
|
-
// YYYYMMDDHHMMSS.FFFFFF
|
|
723
|
-
else if (vr === "DT") {
|
|
724
|
-
value = parseDateTimeTag(value);
|
|
725
|
-
}
|
|
726
|
-
// A string of characters of the format HHMMSS.FFFFFF; where HH contains hours
|
|
727
|
-
// (range "00" - "23"), MM contains minutes (range "00" - "59"),
|
|
728
|
-
// SS contains seconds (range "00" - "60"), and FFFFFF contains a fractional
|
|
729
|
-
// part of a second as small as 1 millionth of a second (range "000000" - "999999").
|
|
730
|
-
else if (vr === "TM") {
|
|
731
|
-
value = parseTimeTag(value);
|
|
732
|
-
}
|
|
733
|
-
// Specific Character Set (0008,0005) identifies the Character Set that expands or
|
|
734
|
-
// replaces the Basic Graphic Set (ISO 646) for values of Data Elements that have
|
|
735
|
-
// Value Representation of SH, LO, ST, PN, LT, UC or UT.
|
|
736
|
-
// If the Attribute Specific Character Set (0008,0005) is not present or has only
|
|
737
|
-
// a single value, Code Extension techniques are not used. Defined Terms for the
|
|
738
|
-
// Attribute Specific Character Set (0008,0005), when single valued, are derived
|
|
739
|
-
// from the International Registration Number as per ISO 2375
|
|
740
|
-
// (e.g., ISO_IR 100 for Latin alphabet No. 1).
|
|
741
|
-
// See https://github.com/radialogica/dicom-character-set
|
|
742
|
-
else if (
|
|
743
|
-
vr == "PN" ||
|
|
744
|
-
vr == "SH" ||
|
|
745
|
-
vr == "LO" ||
|
|
746
|
-
vr == "ST" ||
|
|
747
|
-
vr == "LT" ||
|
|
748
|
-
vr == "UC" ||
|
|
749
|
-
vr == "UT"
|
|
750
|
-
) {
|
|
751
|
-
// get character set
|
|
752
|
-
let characterSet = dataSet.string("x00080005");
|
|
753
|
-
if (characterSet) {
|
|
754
|
-
let data = dataSet.elements[propertyName];
|
|
755
|
-
let arr = new Uint8Array(
|
|
756
|
-
dataSet.byteArray.buffer,
|
|
757
|
-
data.dataOffset,
|
|
758
|
-
data.length
|
|
759
|
-
);
|
|
760
|
-
value = convertBytes(characterSet, arr, {
|
|
761
|
-
vr: vr
|
|
762
|
-
});
|
|
763
|
-
arr = null;
|
|
764
|
-
}
|
|
765
|
-
if (vr == "PN") {
|
|
766
|
-
// PatientName tag value is: "LastName^FirstName^MiddleName".
|
|
767
|
-
// Spaces inside each name component are permitted. If you don't know
|
|
768
|
-
// any of the three components, just leave it empty.
|
|
769
|
-
// Actually you may even append a name prefix (^professor) and
|
|
770
|
-
// a name suffix (^senior) so you have a maximum of 5 components.
|
|
771
|
-
value = parsePatientNameTag(value);
|
|
772
|
-
}
|
|
773
|
-
value = value.replace(/\0/g, ""); // remove null char (\u0000)
|
|
774
|
-
}
|
|
775
|
-
// A string of characters with one of the following formats
|
|
776
|
-
// -- nnnD, nnnW, nnnM, nnnY; where nnn shall contain the number of days for D,
|
|
777
|
-
// weeks for W, months for M, or years for Y.
|
|
778
|
-
else if (vr == "AS") {
|
|
779
|
-
value = parseAgeTag(value);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// A string of characters with leading or trailing spaces (20H) being non-significant.
|
|
783
|
-
else if (vr === "CS") {
|
|
784
|
-
if (propertyName === "x00041500") {
|
|
785
|
-
value = parseDICOMFileIDTag(value);
|
|
786
|
-
} else {
|
|
787
|
-
value = value.split("\\").join(", ");
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
} else if (vr === "US") {
|
|
791
|
-
value = dataSet.uint16(propertyName);
|
|
792
|
-
} else if (vr === "SS") {
|
|
793
|
-
value = dataSet.int16(propertyName);
|
|
794
|
-
} else if (vr === "US|SS") {
|
|
795
|
-
value = dataSet.int16(propertyName);
|
|
796
|
-
} else if (vr === "UL") {
|
|
797
|
-
value = dataSet.uint32(propertyName);
|
|
798
|
-
} else if (vr === "SL") {
|
|
799
|
-
value = dataSet.int32(propertyName);
|
|
800
|
-
} else if (vr == "FD") {
|
|
801
|
-
value = dataSet.double(propertyName);
|
|
802
|
-
} else if (vr == "FL") {
|
|
803
|
-
value = dataSet.float(propertyName);
|
|
804
|
-
} else if (
|
|
805
|
-
vr === "OB" ||
|
|
806
|
-
vr === "OW" ||
|
|
807
|
-
vr === "OW|OB" ||
|
|
808
|
-
vr === "US|OW" ||
|
|
809
|
-
vr === "UN" ||
|
|
810
|
-
vr === "OF" ||
|
|
811
|
-
vr === "UT"
|
|
812
|
-
) {
|
|
813
|
-
// If it is some other length and we have no string
|
|
814
|
-
if (element.length === 2) {
|
|
815
|
-
value =
|
|
816
|
-
"binary data of length " +
|
|
817
|
-
element.length +
|
|
818
|
-
" as uint16: " +
|
|
819
|
-
dataSet.uint16(propertyName);
|
|
820
|
-
} else if (element.length === 4) {
|
|
821
|
-
value =
|
|
822
|
-
"binary data of length " +
|
|
823
|
-
element.length +
|
|
824
|
-
" as uint32: " +
|
|
825
|
-
dataSet.uint32(propertyName);
|
|
826
|
-
} else {
|
|
827
|
-
value = "binary data of length " + element.length + " and VR " + vr;
|
|
828
|
-
}
|
|
829
|
-
} else if (vr === "AT") {
|
|
830
|
-
var group = dataSet.uint16(propertyName, 0);
|
|
831
|
-
if (group) {
|
|
832
|
-
var groupHexStr = ("0000" + group.toString(16)).substr(-4);
|
|
833
|
-
var elm = dataSet.uint16(propertyName, 1);
|
|
834
|
-
var elmHexStr = ("0000" + elm.toString(16)).substr(-4);
|
|
835
|
-
value = "x" + groupHexStr + elmHexStr;
|
|
836
|
-
} else {
|
|
837
|
-
value = "";
|
|
838
|
-
}
|
|
839
|
-
} else if (vr === "SQ") {
|
|
840
|
-
// parse the nested tags
|
|
841
|
-
var subTags = map(element, function (obj) {
|
|
842
|
-
return map(obj, function (v, k) {
|
|
843
|
-
return parseTag(dataSet, k, v);
|
|
844
|
-
});
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
value = subTags;
|
|
848
|
-
} else {
|
|
849
|
-
// If it is some other length and we have no string
|
|
850
|
-
value = "no display code for VR " + vr;
|
|
851
|
-
}
|
|
852
|
-
return value;
|
|
853
|
-
};
|
|
854
|
-
|
|
855
648
|
/**
|
|
856
649
|
* @instance
|
|
857
650
|
* @function getImageMetadata
|
|
@@ -859,7 +652,10 @@ export const parseTag = function (dataSet, propertyName, element) {
|
|
|
859
652
|
* @param {String} instanceUID - The SOPInstanceUID
|
|
860
653
|
* @return {Array} - List of metadata objects: tag, name and value
|
|
861
654
|
*/
|
|
862
|
-
export const getImageMetadata = function (
|
|
655
|
+
export const getImageMetadata = function (
|
|
656
|
+
seriesId: string,
|
|
657
|
+
instanceUID: string
|
|
658
|
+
) {
|
|
863
659
|
const seriesData = getSeriesDataFromLarvitarManager(seriesId);
|
|
864
660
|
if (seriesData === undefined || seriesData === null) {
|
|
865
661
|
console.log(`Invalid Series ID: ${seriesId}`);
|
|
@@ -873,14 +669,15 @@ export const getImageMetadata = function (seriesId, instanceUID) {
|
|
|
873
669
|
|
|
874
670
|
let metadata = seriesData.instances[imageId].metadata;
|
|
875
671
|
// get elements from metadata where the key starts with x and is length 7
|
|
876
|
-
let metadata_keys =
|
|
877
|
-
return key.length === 9 && key[0] === "x";
|
|
878
|
-
});
|
|
672
|
+
let metadata_keys = Object.keys(metadata);
|
|
879
673
|
// loop metadata using metadata_keys and return list of key value pairs
|
|
880
|
-
let metadata_list = map(metadata_keys, function (key) {
|
|
674
|
+
let metadata_list = map(metadata_keys, function (key: string) {
|
|
881
675
|
// if value is a dictionary return empty string
|
|
676
|
+
let KEY = key as keyof MetaDataTypes;
|
|
882
677
|
const value =
|
|
883
|
-
metadata[
|
|
678
|
+
metadata[KEY] && metadata[KEY]!.constructor == Object
|
|
679
|
+
? ""
|
|
680
|
+
: metadata[KEY];
|
|
884
681
|
// convert key removing x and adding comma at position 4
|
|
885
682
|
const tagKey = (
|
|
886
683
|
"(" +
|
|
@@ -889,7 +686,14 @@ export const getImageMetadata = function (seriesId, instanceUID) {
|
|
|
889
686
|
key.slice(5) +
|
|
890
687
|
")"
|
|
891
688
|
).toUpperCase();
|
|
892
|
-
|
|
689
|
+
|
|
690
|
+
if (!Object.keys(TAG_DICT).includes(tagKey)) {
|
|
691
|
+
console.debug(`Invalid tag key: ${tagKey}`);
|
|
692
|
+
}
|
|
693
|
+
// force type to keyof typeof TAG_DICT after having checked that it is a valid key
|
|
694
|
+
const name = TAG_DICT[tagKey as keyof typeof TAG_DICT]
|
|
695
|
+
? TAG_DICT[tagKey as keyof typeof TAG_DICT].name
|
|
696
|
+
: "";
|
|
893
697
|
return {
|
|
894
698
|
tag: tagKey,
|
|
895
699
|
name: name,
|
|
@@ -911,34 +715,31 @@ export const getImageMetadata = function (seriesId, instanceUID) {
|
|
|
911
715
|
* @param {String} method - Orientation target
|
|
912
716
|
* @return {Number} - The sorting value (float)
|
|
913
717
|
*/
|
|
914
|
-
let sortStackCallback = function (
|
|
718
|
+
let sortStackCallback = function (
|
|
719
|
+
seriesData: Series,
|
|
720
|
+
imageId: string,
|
|
721
|
+
method: "instanceNumber" | "contentTime" | "imagePosition"
|
|
722
|
+
) {
|
|
915
723
|
switch (method) {
|
|
916
724
|
case "instanceNumber":
|
|
917
|
-
var instanceNumber = seriesData.instances[imageId].metadata.x00200013
|
|
918
|
-
instanceNumber = parseInt(instanceNumber);
|
|
725
|
+
var instanceNumber = seriesData.instances[imageId].metadata.x00200013!;
|
|
919
726
|
return instanceNumber;
|
|
920
727
|
|
|
921
728
|
case "contentTime":
|
|
922
729
|
return seriesData.instances[imageId].metadata.x00080033;
|
|
923
730
|
|
|
924
731
|
case "imagePosition":
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
var o = seriesData.instances[imageId].metadata.imageOrientation;
|
|
932
|
-
o = map(o, function (value) {
|
|
933
|
-
return parseFloat(value);
|
|
934
|
-
});
|
|
732
|
+
let p = seriesData.instances[imageId].metadata.imagePosition!;
|
|
733
|
+
let pStr = p?.map(String);
|
|
734
|
+
let o = seriesData.instances[imageId].metadata.imageOrientation!;
|
|
735
|
+
let oStr = o?.map(String);
|
|
935
736
|
|
|
936
|
-
var v1, v2, v3;
|
|
737
|
+
var v1, v2, v3: number;
|
|
937
738
|
v1 = o[0] * o[0] + o[3] * o[3];
|
|
938
739
|
v2 = o[1] * o[1] + o[4] * o[4];
|
|
939
740
|
v3 = o[2] * o[2] + o[5] * o[5];
|
|
940
741
|
|
|
941
|
-
var sortIndex;
|
|
742
|
+
var sortIndex = -1;
|
|
942
743
|
if (v1 <= v2 && v2 <= v3) {
|
|
943
744
|
sortIndex = 0;
|
|
944
745
|
}
|
|
@@ -948,89 +749,17 @@ let sortStackCallback = function (seriesData, imageId, method) {
|
|
|
948
749
|
if (v3 <= v1 && v3 <= v2) {
|
|
949
750
|
sortIndex = 2;
|
|
950
751
|
}
|
|
752
|
+
|
|
753
|
+
if (!sortIndex) {
|
|
754
|
+
throw new Error("Invalid sort index");
|
|
755
|
+
}
|
|
756
|
+
|
|
951
757
|
return p[sortIndex];
|
|
952
758
|
default:
|
|
953
759
|
break;
|
|
954
760
|
}
|
|
955
761
|
};
|
|
956
762
|
|
|
957
|
-
/**
|
|
958
|
-
* Get the dicom tag code from dicom image
|
|
959
|
-
* @instance
|
|
960
|
-
* @function getDICOMTagCode
|
|
961
|
-
* @param {String} dicomTag - The original DICOM tag code
|
|
962
|
-
* @return {String} - The human readable DICOM tag code
|
|
963
|
-
*/
|
|
964
|
-
let getDICOMTagCode = function (code) {
|
|
965
|
-
let re = /x(\w{4})(\w{4})/;
|
|
966
|
-
let result = re.exec(code);
|
|
967
|
-
if (!result) {
|
|
968
|
-
return code;
|
|
969
|
-
}
|
|
970
|
-
let newCode = "(" + result[1] + "," + result[2] + ")";
|
|
971
|
-
newCode = newCode.toUpperCase();
|
|
972
|
-
return newCode;
|
|
973
|
-
};
|
|
974
|
-
|
|
975
|
-
/**
|
|
976
|
-
* Get the dicom tag from dicom tag code
|
|
977
|
-
* @instance
|
|
978
|
-
* @function getDICOMTag
|
|
979
|
-
* @param {String} dicomTagCode - The original DICOM tag code
|
|
980
|
-
* @return {String} - The human readable DICOM tag
|
|
981
|
-
*/
|
|
982
|
-
let getDICOMTag = function (code) {
|
|
983
|
-
let newCode = getDICOMTagCode(code);
|
|
984
|
-
let tag = TAG_DICT[newCode];
|
|
985
|
-
return tag;
|
|
986
|
-
};
|
|
987
|
-
|
|
988
|
-
/**
|
|
989
|
-
* Convert date from dicom tag
|
|
990
|
-
* @instance
|
|
991
|
-
* @function formatDate
|
|
992
|
-
* @param {Date} dicomDate - A date from a DICOM tag
|
|
993
|
-
* @return {String} - The human readable date
|
|
994
|
-
*/
|
|
995
|
-
let formatDate = function (date) {
|
|
996
|
-
let yyyy = date.slice(0, 4);
|
|
997
|
-
let mm = date.slice(4, 6);
|
|
998
|
-
let dd = date.slice(6, 8);
|
|
999
|
-
return (
|
|
1000
|
-
yyyy + "-" + (mm[1] ? mm : "0" + mm[0]) + "-" + (dd[1] ? dd : "0" + dd[0])
|
|
1001
|
-
);
|
|
1002
|
-
};
|
|
1003
|
-
|
|
1004
|
-
/**
|
|
1005
|
-
* Convert datetime from dicom tag
|
|
1006
|
-
* @instance
|
|
1007
|
-
* @function formatDateTime
|
|
1008
|
-
* @param {Date} dicomDateTime - A dateTime from a DICOM tag
|
|
1009
|
-
* @return {String} - The human readable dateTime
|
|
1010
|
-
*/
|
|
1011
|
-
let formatDateTime = function (date) {
|
|
1012
|
-
let yyyy = date.slice(0, 4);
|
|
1013
|
-
let mm = date.slice(4, 6);
|
|
1014
|
-
let dd = date.slice(6, 8);
|
|
1015
|
-
let hh = date.slice(8, 10);
|
|
1016
|
-
let m = date.slice(10, 12);
|
|
1017
|
-
let ss = date.slice(12, 14);
|
|
1018
|
-
|
|
1019
|
-
return (
|
|
1020
|
-
yyyy +
|
|
1021
|
-
"-" +
|
|
1022
|
-
(mm[1] ? mm : "0" + mm[0]) +
|
|
1023
|
-
"-" +
|
|
1024
|
-
(dd[1] ? dd : "0" + dd[0]) +
|
|
1025
|
-
"/" +
|
|
1026
|
-
hh +
|
|
1027
|
-
":" +
|
|
1028
|
-
m +
|
|
1029
|
-
":" +
|
|
1030
|
-
ss
|
|
1031
|
-
);
|
|
1032
|
-
};
|
|
1033
|
-
|
|
1034
763
|
/**
|
|
1035
764
|
* Generate a random number and convert it to base 36 (0-9a-z)
|
|
1036
765
|
* @instance
|
|
@@ -1049,7 +778,7 @@ let rand = function () {
|
|
|
1049
778
|
* @param {Array} sourceArray - The source array
|
|
1050
779
|
* @return {Array} - The converted array
|
|
1051
780
|
*/
|
|
1052
|
-
let permuteValues = function (convertArray, sourceArray) {
|
|
781
|
+
let permuteValues = function (convertArray: number[], sourceArray: number[]) {
|
|
1053
782
|
let outputArray = new Array(convertArray.length);
|
|
1054
783
|
for (let i = 0; i < convertArray.length; i++) {
|
|
1055
784
|
outputArray[i] = sourceArray[convertArray[i]];
|
|
@@ -1065,7 +794,7 @@ let permuteValues = function (convertArray, sourceArray) {
|
|
|
1065
794
|
* @param {Number} x - The number to check
|
|
1066
795
|
* @return {Boolean} - Is negative boolean response
|
|
1067
796
|
*/
|
|
1068
|
-
let isNegativeSign = function (x) {
|
|
797
|
+
let isNegativeSign = function (x: number) {
|
|
1069
798
|
return 1 / x !== 1 / Math.abs(x);
|
|
1070
799
|
};
|
|
1071
800
|
|
|
@@ -1077,9 +806,12 @@ let isNegativeSign = function (x) {
|
|
|
1077
806
|
* @param {Number} size - The size of the array
|
|
1078
807
|
* @return {Array} - The typed array
|
|
1079
808
|
*/
|
|
1080
|
-
let getTypedArray = function (tags, size) {
|
|
809
|
+
let getTypedArray = function (tags: CustomDataSet, size: number) {
|
|
1081
810
|
let r = getPixelRepresentation(tags);
|
|
1082
811
|
let typedArray = getTypedArrayFromDataType(r);
|
|
812
|
+
if (!typedArray) {
|
|
813
|
+
throw new Error("Invalid typed array");
|
|
814
|
+
}
|
|
1083
815
|
return new typedArray(size);
|
|
1084
816
|
};
|
|
1085
817
|
|
|
@@ -1091,7 +823,10 @@ let getTypedArray = function (tags, size) {
|
|
|
1091
823
|
* @param {Array} permuteTable - The matrix transformation
|
|
1092
824
|
* @return {Array} - The resliced image orientation array
|
|
1093
825
|
*/
|
|
1094
|
-
let getReslicedIOP = function (
|
|
826
|
+
let getReslicedIOP = function (
|
|
827
|
+
iop: [number, number, number, number, number, number],
|
|
828
|
+
permuteTable: number[]
|
|
829
|
+
) {
|
|
1095
830
|
if (!iop) {
|
|
1096
831
|
return null;
|
|
1097
832
|
}
|
|
@@ -1126,29 +861,29 @@ let getReslicedIOP = function (iop, permuteTable) {
|
|
|
1126
861
|
* @return {Array} - The resliced image position array
|
|
1127
862
|
*/
|
|
1128
863
|
let getReslicedIPP = function (
|
|
1129
|
-
ipp,
|
|
1130
|
-
iop,
|
|
1131
|
-
reslicedIOP,
|
|
1132
|
-
permuteTable,
|
|
1133
|
-
imageIndex,
|
|
1134
|
-
fromSize,
|
|
1135
|
-
toSize,
|
|
1136
|
-
fromSpacing
|
|
864
|
+
ipp: [number, number, number],
|
|
865
|
+
iop: [number, number, number, number, number, number],
|
|
866
|
+
reslicedIOP: [number, number, number, number, number, number],
|
|
867
|
+
permuteTable: number[],
|
|
868
|
+
imageIndex: number,
|
|
869
|
+
fromSize: number[],
|
|
870
|
+
toSize: number[],
|
|
871
|
+
fromSpacing: number[]
|
|
1137
872
|
) {
|
|
1138
873
|
// compute resliced ipp
|
|
1139
874
|
let reslicedIPP = [];
|
|
1140
875
|
|
|
1141
|
-
// iop data
|
|
876
|
+
// iop data types??
|
|
1142
877
|
let u = iop.slice(0, 3);
|
|
1143
878
|
let v = iop.slice(3, 6);
|
|
1144
879
|
let w = getNormalOrientation(iop);
|
|
1145
|
-
let absW = map(w, function (v) {
|
|
880
|
+
let absW = map(w, function (v: number) {
|
|
1146
881
|
return Math.abs(v);
|
|
1147
882
|
});
|
|
1148
883
|
let majorOriginalIndex = indexOf(absW, max(absW));
|
|
1149
884
|
|
|
1150
885
|
let normalReslicedIop = getNormalOrientation(reslicedIOP);
|
|
1151
|
-
normalReslicedIop = map(normalReslicedIop, function (v) {
|
|
886
|
+
normalReslicedIop = map(normalReslicedIop, function (v: number) {
|
|
1152
887
|
return Math.abs(v);
|
|
1153
888
|
});
|
|
1154
889
|
|
|
@@ -1161,10 +896,11 @@ let getReslicedIPP = function (
|
|
|
1161
896
|
if (isNegativeSign(permuteTable[1])) {
|
|
1162
897
|
ipp = ipp.map(function (val, i) {
|
|
1163
898
|
return val + fromSize[2] * fromSpacing[2] * w[i];
|
|
1164
|
-
});
|
|
899
|
+
}) as [number, number, number];
|
|
1165
900
|
}
|
|
1166
901
|
|
|
1167
|
-
let spacing
|
|
902
|
+
let spacing: number;
|
|
903
|
+
let versor: number[];
|
|
1168
904
|
// to sagittal
|
|
1169
905
|
if (majorIndex == 0) {
|
|
1170
906
|
// original x spacing
|
|
@@ -1203,7 +939,7 @@ let getReslicedIPP = function (
|
|
|
1203
939
|
return val + index * spacing * versor[i];
|
|
1204
940
|
});
|
|
1205
941
|
|
|
1206
|
-
return reslicedIPP;
|
|
942
|
+
return reslicedIPP as [number, number, number];
|
|
1207
943
|
};
|
|
1208
944
|
|
|
1209
945
|
/**
|
|
@@ -1214,9 +950,12 @@ let getReslicedIPP = function (
|
|
|
1214
950
|
* @param {Array} reslicedIPP - The resliced image position array
|
|
1215
951
|
* @return {Array} - The slice location as normal orientation vector
|
|
1216
952
|
*/
|
|
1217
|
-
let getReslicedSliceLocation = function (
|
|
953
|
+
let getReslicedSliceLocation = function (
|
|
954
|
+
reslicedIOP: [number, number, number, number, number, number],
|
|
955
|
+
reslicedIPP: [number, number, number]
|
|
956
|
+
) {
|
|
1218
957
|
let normalReslicedIop = getNormalOrientation(reslicedIOP);
|
|
1219
|
-
normalReslicedIop = map(normalReslicedIop, function (v) {
|
|
958
|
+
normalReslicedIop = map(normalReslicedIop, function (v: number) {
|
|
1220
959
|
return Math.abs(v);
|
|
1221
960
|
});
|
|
1222
961
|
|
|
@@ -1232,22 +971,23 @@ let getReslicedSliceLocation = function (reslicedIOP, reslicedIPP) {
|
|
|
1232
971
|
* @param {Object} sampleMetadata - The medatata object
|
|
1233
972
|
* @return {Array} - The spacing array
|
|
1234
973
|
*/
|
|
1235
|
-
let spacingArray = function (
|
|
974
|
+
let spacingArray = function (
|
|
975
|
+
seriesData: Series,
|
|
976
|
+
sampleMetadata: MetaDataTypes
|
|
977
|
+
) {
|
|
1236
978
|
// the spacingArray is as follows:
|
|
1237
979
|
// [0]: column pixelSpacing value (x00280030[1])
|
|
1238
980
|
// [1]: row pixelSpacing value (x00280030[0])
|
|
1239
981
|
// [2]: distance between slices, given the series imageOrientationPatient and
|
|
1240
982
|
// imagePositionPatient of the first two slices
|
|
1241
983
|
|
|
1242
|
-
let distanceBetweenSlices = sampleMetadata
|
|
1243
|
-
? sampleMetadata
|
|
984
|
+
let distanceBetweenSlices = sampleMetadata["x00180050"]
|
|
985
|
+
? sampleMetadata["x00180050"]
|
|
1244
986
|
: getDistanceBetweenSlices(seriesData, 0, 1);
|
|
1245
987
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
distanceBetweenSlices
|
|
1250
|
-
];
|
|
988
|
+
let spacing = sampleMetadata.x00280030!;
|
|
989
|
+
|
|
990
|
+
return [spacing[1], spacing[0], distanceBetweenSlices as number];
|
|
1251
991
|
};
|
|
1252
992
|
|
|
1253
993
|
/**
|
|
@@ -1258,7 +998,10 @@ let spacingArray = function (seriesData, sampleMetadata) {
|
|
|
1258
998
|
* @param {Array} sourceArray - The source array
|
|
1259
999
|
* @return {Array} - The permuted array array
|
|
1260
1000
|
*/
|
|
1261
|
-
let permuteSignedArrays = function (
|
|
1001
|
+
let permuteSignedArrays = function (
|
|
1002
|
+
convertArray: number[],
|
|
1003
|
+
sourceArray: number[][]
|
|
1004
|
+
) {
|
|
1262
1005
|
let outputArray = new Array(convertArray.length);
|
|
1263
1006
|
for (let i = 0; i < convertArray.length; i++) {
|
|
1264
1007
|
let sourceIndex = Math.abs(convertArray[i]);
|
|
@@ -1274,239 +1017,6 @@ let permuteSignedArrays = function (convertArray, sourceArray) {
|
|
|
1274
1017
|
return outputArray;
|
|
1275
1018
|
};
|
|
1276
1019
|
|
|
1277
|
-
/**
|
|
1278
|
-
* Check if argument is a valid Date Object
|
|
1279
|
-
* @instance
|
|
1280
|
-
* @function isValidDate
|
|
1281
|
-
* @param {Date} d - The date object to be checked
|
|
1282
|
-
* @return {Boolean} - Boolean result
|
|
1283
|
-
*/
|
|
1284
|
-
const isValidDate = function (d) {
|
|
1285
|
-
return d instanceof Date && !isNaN(d);
|
|
1286
|
-
};
|
|
1287
|
-
|
|
1288
|
-
/**
|
|
1289
|
-
* Check if argument is a string of concatenated vrs
|
|
1290
|
-
* @instance
|
|
1291
|
-
* @function isStringVr
|
|
1292
|
-
* @param {String} vr - The string to be checked
|
|
1293
|
-
* @return {Boolean} - Boolean result
|
|
1294
|
-
*/
|
|
1295
|
-
const isStringVr = function (vr) {
|
|
1296
|
-
// vr can be a string of concatenated vrs
|
|
1297
|
-
vr = vr || "";
|
|
1298
|
-
vr = vr.split("|")[0];
|
|
1299
|
-
|
|
1300
|
-
if (
|
|
1301
|
-
vr === "AT" ||
|
|
1302
|
-
vr === "FL" ||
|
|
1303
|
-
vr === "FD" ||
|
|
1304
|
-
vr === "OB" ||
|
|
1305
|
-
vr === "OF" ||
|
|
1306
|
-
vr === "OW" ||
|
|
1307
|
-
vr === "SI" ||
|
|
1308
|
-
vr === "SQ" ||
|
|
1309
|
-
vr === "SS" ||
|
|
1310
|
-
vr === "UL" ||
|
|
1311
|
-
vr === "US"
|
|
1312
|
-
) {
|
|
1313
|
-
return false;
|
|
1314
|
-
}
|
|
1315
|
-
return true;
|
|
1316
|
-
};
|
|
1317
|
-
|
|
1318
|
-
/**
|
|
1319
|
-
* Parse a dicom date tag into human readable format
|
|
1320
|
-
* @instance
|
|
1321
|
-
* @function parseDateTag
|
|
1322
|
-
* @param {String} tagValue - The string to be parsed
|
|
1323
|
-
* @return {String} - The parsed result
|
|
1324
|
-
*/
|
|
1325
|
-
const parseDateTag = function (tagValue) {
|
|
1326
|
-
if (!tagValue) return;
|
|
1327
|
-
let year = tagValue.substring(0, 4);
|
|
1328
|
-
let month = tagValue.substring(4, 6);
|
|
1329
|
-
let day = tagValue.substring(6, 8);
|
|
1330
|
-
let date = new Date(year, month - 1, day);
|
|
1331
|
-
if (isValidDate(date) === true) {
|
|
1332
|
-
return date.toISOString();
|
|
1333
|
-
} else {
|
|
1334
|
-
return tagValue;
|
|
1335
|
-
}
|
|
1336
|
-
};
|
|
1337
|
-
|
|
1338
|
-
/**
|
|
1339
|
-
* Parse a dicom datetime tag into human readable format
|
|
1340
|
-
* @instance
|
|
1341
|
-
* @function parseDateTimeTag
|
|
1342
|
-
* @param {String} tagValue - The string to be parsed
|
|
1343
|
-
* @return {String} - The parsed result
|
|
1344
|
-
*/
|
|
1345
|
-
const parseDateTimeTag = function (tagValue) {
|
|
1346
|
-
if (!tagValue) return;
|
|
1347
|
-
let year = tagValue.substring(0, 4);
|
|
1348
|
-
let month = tagValue.substring(4, 6);
|
|
1349
|
-
let day = tagValue.substring(6, 8);
|
|
1350
|
-
let hour = tagValue.substring(8, 10);
|
|
1351
|
-
let min = tagValue.substring(10, 12);
|
|
1352
|
-
let sec = tagValue.substring(12, 14);
|
|
1353
|
-
let msec = tagValue.substring(15, 21);
|
|
1354
|
-
let date = new Date(year, month - 1, day, hour, min, sec, msec);
|
|
1355
|
-
if (isValidDate(date) === true) {
|
|
1356
|
-
return date.toISOString();
|
|
1357
|
-
} else {
|
|
1358
|
-
return tagValue;
|
|
1359
|
-
}
|
|
1360
|
-
};
|
|
1361
|
-
|
|
1362
|
-
/**
|
|
1363
|
-
* Parse a dicom time tag into human readable format
|
|
1364
|
-
* @instance
|
|
1365
|
-
* @function parseTimeTag
|
|
1366
|
-
* @param {String} tagValue - The string to be parsed
|
|
1367
|
-
* @return {String} - The parsed result
|
|
1368
|
-
*/
|
|
1369
|
-
const parseTimeTag = function (tagValue) {
|
|
1370
|
-
if (!tagValue) return;
|
|
1371
|
-
let hour = tagValue.substring(0, 2);
|
|
1372
|
-
let min = tagValue.substring(2, 4);
|
|
1373
|
-
let sec = tagValue.substring(4, 6);
|
|
1374
|
-
let msec = tagValue.substring(7, 13) ? tagValue.substring(7, 13) : "0";
|
|
1375
|
-
let result = hour + ":" + min + ":" + sec + "." + msec;
|
|
1376
|
-
return result;
|
|
1377
|
-
};
|
|
1378
|
-
|
|
1379
|
-
/**
|
|
1380
|
-
* Parse a dicom patient tag into human readable format
|
|
1381
|
-
* @instance
|
|
1382
|
-
* @function parsePatientNameTag
|
|
1383
|
-
* @param {String} tagValue - The string to be parsed
|
|
1384
|
-
* @return {String} - The parsed result
|
|
1385
|
-
*/
|
|
1386
|
-
const parsePatientNameTag = function (tagValue) {
|
|
1387
|
-
if (!tagValue) return;
|
|
1388
|
-
return tagValue.replace(/\^/gi, " ");
|
|
1389
|
-
};
|
|
1390
|
-
|
|
1391
|
-
/**
|
|
1392
|
-
* Parse a dicom age tag into human readable format
|
|
1393
|
-
* @instance
|
|
1394
|
-
* @function parseAgeTag
|
|
1395
|
-
* @param {String} tagValue - The string to be parsed
|
|
1396
|
-
* @return {String} - The parsed result
|
|
1397
|
-
*/
|
|
1398
|
-
const parseAgeTag = function (tagValue) {
|
|
1399
|
-
if (!tagValue) return;
|
|
1400
|
-
let regs = /(\d{3})(D|W|M|Y)/gim.exec(tagValue);
|
|
1401
|
-
if (regs) {
|
|
1402
|
-
return parseInt(regs[1]) + " " + regs[2];
|
|
1403
|
-
}
|
|
1404
|
-
};
|
|
1405
|
-
|
|
1406
|
-
/**
|
|
1407
|
-
* Parse a dicom fileID tag into human readable format
|
|
1408
|
-
* @instance
|
|
1409
|
-
* @function parseDICOMFileIDTag
|
|
1410
|
-
* @param {String} tagValue - The string to be parsed
|
|
1411
|
-
* @return {String} - The parsed result
|
|
1412
|
-
*/
|
|
1413
|
-
const parseDICOMFileIDTag = function (tagValue) {
|
|
1414
|
-
// The DICOM File Service does not specify any "separator" between
|
|
1415
|
-
// the Components of the File ID. This is a Value Representation issue that
|
|
1416
|
-
// may be addressed in a specific manner by each Media Format Layer.
|
|
1417
|
-
// In DICOM IODs, File ID Components are generally handled as multiple
|
|
1418
|
-
// Values and separated by "backslashes".
|
|
1419
|
-
// There is no requirement that Media Format Layers use this separator.
|
|
1420
|
-
if (!tagValue) return;
|
|
1421
|
-
return tagValue.split("\\").join(path.sep);
|
|
1422
|
-
};
|
|
1423
|
-
|
|
1424
|
-
/**
|
|
1425
|
-
* Extract tag value according to its value rapresentation, see
|
|
1426
|
-
* {@link http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html}
|
|
1427
|
-
* @instance
|
|
1428
|
-
* @function getTagValue
|
|
1429
|
-
* @param {Object} dataSet - the dataset
|
|
1430
|
-
* @param {String} tag - the desired tag key
|
|
1431
|
-
* @return {Number | Array | String} - the desired tag value
|
|
1432
|
-
*/
|
|
1433
|
-
const getTagValue = function (dataSet, tag) {
|
|
1434
|
-
// tag value rapresentation
|
|
1435
|
-
if (!getDICOMTag(tag)) {
|
|
1436
|
-
return null;
|
|
1437
|
-
}
|
|
1438
|
-
let vr = getDICOMTag(tag).vr;
|
|
1439
|
-
|
|
1440
|
-
// parse value according to vr map
|
|
1441
|
-
let vrParsingMap = {
|
|
1442
|
-
// Date
|
|
1443
|
-
// string of characters of the format YYYYMMDD; where YYYY shall contain year,
|
|
1444
|
-
// MM shall contain the month, and DD shall contain the day,
|
|
1445
|
-
// interpreted as a date of the Gregorian calendar system.
|
|
1446
|
-
DA: function () {
|
|
1447
|
-
let dateString = dataSet.string(tag);
|
|
1448
|
-
return dateString ? formatDate(dateString) : "";
|
|
1449
|
-
},
|
|
1450
|
-
// Decimal String
|
|
1451
|
-
// A string of characters representing either a fixed point number
|
|
1452
|
-
// or a floating point number.
|
|
1453
|
-
DS: function () {
|
|
1454
|
-
let array = dataSet.string(tag)
|
|
1455
|
-
? dataSet.string(tag).split("\\").map(Number)
|
|
1456
|
-
: null;
|
|
1457
|
-
if (!array) {
|
|
1458
|
-
return null;
|
|
1459
|
-
}
|
|
1460
|
-
return array.length === 1 ? array[0] : array;
|
|
1461
|
-
},
|
|
1462
|
-
// Date Time
|
|
1463
|
-
// A concatenated date-time character string in the format:
|
|
1464
|
-
// YYYYMMDDHHMMSS.FFFFFF&ZZXX
|
|
1465
|
-
DT: function () {
|
|
1466
|
-
let dateString = dataSet.string(tag);
|
|
1467
|
-
return formatDateTime(dateString);
|
|
1468
|
-
},
|
|
1469
|
-
// Person Name
|
|
1470
|
-
// A character string encoded using a 5 component convention.
|
|
1471
|
-
// The character code 5CH (the BACKSLASH "\" in ISO-IR 6) shall
|
|
1472
|
-
// not be present, as it is used as the delimiter between values
|
|
1473
|
-
// in multiple valued data elements. The string may be padded
|
|
1474
|
-
// with trailing spaces. For human use, the five components
|
|
1475
|
-
// in their order of occurrence are: family name complex,
|
|
1476
|
-
// given name complex, middle name, name prefix, name suffix.
|
|
1477
|
-
PN: function () {
|
|
1478
|
-
let pn = dataSet.string(tag) ? dataSet.string(tag).split("^") : null;
|
|
1479
|
-
if (!pn) {
|
|
1480
|
-
return null;
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
let pns = [pn[3], pn[0], pn[1], pn[2], pn[4]];
|
|
1484
|
-
return pns.join(" ").trim();
|
|
1485
|
-
},
|
|
1486
|
-
// Signed Short
|
|
1487
|
-
// Signed binary integer 16 bits long in 2's complement form
|
|
1488
|
-
SS: function () {
|
|
1489
|
-
return dataSet.uint16(tag);
|
|
1490
|
-
},
|
|
1491
|
-
// Unique Identifier
|
|
1492
|
-
// A character string containing a UID that is used to uniquely
|
|
1493
|
-
// identify a wide letiety of items. The UID is a series of numeric
|
|
1494
|
-
// components separated by the period "." character.
|
|
1495
|
-
UI: function () {
|
|
1496
|
-
return dataSet.string(tag);
|
|
1497
|
-
},
|
|
1498
|
-
// Unsigned Short
|
|
1499
|
-
// Unsigned binary integer 16 bits long.
|
|
1500
|
-
US: function () {
|
|
1501
|
-
return dataSet.uint16(tag);
|
|
1502
|
-
},
|
|
1503
|
-
"US|SS": function () {
|
|
1504
|
-
return dataSet.uint16(tag);
|
|
1505
|
-
}
|
|
1506
|
-
};
|
|
1507
|
-
return vrParsingMap[vr] ? vrParsingMap[vr]() : dataSet.string(tag);
|
|
1508
|
-
};
|
|
1509
|
-
|
|
1510
1020
|
/**
|
|
1511
1021
|
* Object used to convert data type to typed array
|
|
1512
1022
|
* @object
|
|
@@ -1558,7 +1068,7 @@ const TYPES_TO_TYPEDARRAY = {
|
|
|
1558
1068
|
* @param {Object} o - The div tag
|
|
1559
1069
|
* @return {Boolean} - True if is an element otherwise returns False
|
|
1560
1070
|
*/
|
|
1561
|
-
export const isElement = function (o) {
|
|
1071
|
+
export const isElement = function (o: any) {
|
|
1562
1072
|
return typeof HTMLElement === "object"
|
|
1563
1073
|
? o instanceof HTMLElement //DOM2
|
|
1564
1074
|
: o &&
|