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.
Files changed (103) hide show
  1. package/.vscode/settings.json +4 -0
  2. package/README.md +78 -48
  3. package/bundler/webpack.common.js +27 -0
  4. package/bundler/webpack.dev.js +23 -0
  5. package/bundler/webpack.prod.js +19 -0
  6. package/decs.d.ts +12 -0
  7. package/dist/imaging/MetaDataReadable.d.ts +39 -0
  8. package/dist/imaging/MetaDataTypes.d.ts +3488 -0
  9. package/dist/imaging/imageAnonymization.d.ts +12 -0
  10. package/dist/imaging/imageColormaps.d.ts +47 -0
  11. package/dist/imaging/imageContours.d.ts +18 -0
  12. package/dist/imaging/imageIo.d.ts +42 -0
  13. package/dist/imaging/imageLayers.d.ts +56 -0
  14. package/dist/imaging/imageLoading.d.ts +65 -0
  15. package/dist/imaging/imageParsing.d.ts +46 -0
  16. package/dist/imaging/imagePresets.d.ts +43 -0
  17. package/dist/imaging/imageRendering.d.ts +238 -0
  18. package/dist/imaging/imageReslice.d.ts +14 -0
  19. package/dist/imaging/imageStore.d.ts +121 -0
  20. package/dist/imaging/imageTags.d.ts +22 -0
  21. package/dist/imaging/imageTools.d.ts +20 -0
  22. package/dist/imaging/imageUtils.d.ts +165 -0
  23. package/dist/imaging/loaders/commonLoader.d.ts +103 -0
  24. package/dist/imaging/loaders/dicomLoader.d.ts +29 -0
  25. package/dist/imaging/loaders/fileLoader.d.ts +33 -0
  26. package/dist/imaging/loaders/multiframeLoader.d.ts +37 -0
  27. package/dist/imaging/loaders/nrrdLoader.d.ts +112 -0
  28. package/dist/imaging/loaders/resliceLoader.d.ts +15 -0
  29. package/dist/imaging/monitors/memory.d.ts +41 -0
  30. package/dist/imaging/monitors/performance.d.ts +23 -0
  31. package/dist/imaging/parsers/ecg.d.ts +15 -0
  32. package/dist/imaging/parsers/nrrd.d.ts +3 -0
  33. package/dist/imaging/tools/custom/4dSliceScrollTool.d.ts +12 -0
  34. package/dist/imaging/tools/custom/contourTool.d.ts +409 -0
  35. package/dist/imaging/tools/custom/diameterTool.d.ts +18 -0
  36. package/dist/imaging/tools/custom/editMaskTool.d.ts +22 -0
  37. package/dist/imaging/tools/custom/ellipticalRoiOverlayTool.d.ts +45 -0
  38. package/dist/imaging/tools/custom/polygonSegmentationMixin.d.ts +54 -0
  39. package/dist/imaging/tools/custom/polylineScissorsTool.d.ts +11 -0
  40. package/dist/imaging/tools/custom/rectangleRoiOverlayTool.d.ts +45 -0
  41. package/dist/imaging/tools/custom/seedTool.d.ts +0 -0
  42. package/dist/imaging/tools/custom/setLabelMap3D.d.ts +39 -0
  43. package/dist/imaging/tools/custom/thresholdsBrushTool.d.ts +19 -0
  44. package/dist/imaging/tools/default.d.ts +53 -0
  45. package/dist/imaging/tools/interaction.d.ts +30 -0
  46. package/dist/imaging/tools/io.d.ts +38 -0
  47. package/dist/imaging/tools/main.d.ts +81 -0
  48. package/dist/imaging/tools/segmentation.d.ts +125 -0
  49. package/dist/imaging/tools/state.d.ts +17 -0
  50. package/dist/imaging/tools/strategies/eraseFreehand.d.ts +16 -0
  51. package/dist/imaging/tools/strategies/fillFreehand.d.ts +16 -0
  52. package/dist/imaging/tools/strategies/index.d.ts +2 -0
  53. package/dist/index.d.ts +34 -0
  54. package/dist/larvitar.js +89801 -0
  55. package/dist/larvitar.js.map +1 -0
  56. package/imaging/MetaDataReadable.ts +40 -0
  57. package/imaging/MetaDataTypes.ts +3490 -0
  58. package/imaging/dataDictionary.json +5328 -5328
  59. package/imaging/{imageAnonymization.js → imageAnonymization.ts} +41 -13
  60. package/imaging/{imageColormaps.js → imageColormaps.ts} +48 -30
  61. package/imaging/{imageContours.js → imageContours.ts} +24 -22
  62. package/imaging/{imageIo.js → imageIo.ts} +89 -52
  63. package/imaging/{imageLayers.js → imageLayers.ts} +31 -14
  64. package/imaging/{imageLoading.js → imageLoading.ts} +108 -45
  65. package/imaging/{imageParsing.js → imageParsing.ts} +158 -80
  66. package/imaging/{imagePresets.js → imagePresets.ts} +44 -11
  67. package/imaging/imageRendering.ts +1091 -0
  68. package/imaging/{imageReslice.js → imageReslice.ts} +18 -9
  69. package/imaging/imageStore.ts +487 -0
  70. package/imaging/imageTags.ts +609 -0
  71. package/imaging/imageTools.js +2 -1
  72. package/imaging/{imageUtils.js → imageUtils.ts} +211 -701
  73. package/imaging/loaders/{commonLoader.js → commonLoader.ts} +73 -24
  74. package/imaging/loaders/{dicomLoader.js → dicomLoader.ts} +25 -5
  75. package/imaging/loaders/{fileLoader.js → fileLoader.ts} +5 -5
  76. package/imaging/loaders/{multiframeLoader.js → multiframeLoader.ts} +145 -90
  77. package/imaging/loaders/{nrrdLoader.js → nrrdLoader.ts} +230 -64
  78. package/imaging/loaders/{resliceLoader.js → resliceLoader.ts} +51 -20
  79. package/imaging/monitors/{memory.js → memory.ts} +54 -8
  80. package/imaging/monitors/performance.ts +34 -0
  81. package/imaging/parsers/ecg.ts +51 -0
  82. package/imaging/tools/README.md +27 -0
  83. package/imaging/tools/custom/4dSliceScrollTool.js +47 -46
  84. package/imaging/tools/custom/ellipticalRoiOverlayTool.js +534 -0
  85. package/imaging/tools/custom/polylineScissorsTool.js +1 -1
  86. package/imaging/tools/custom/rectangleRoiOverlayTool.js +564 -0
  87. package/imaging/tools/{setLabelMap3D.js → custom/setLabelMap3D.ts} +19 -25
  88. package/imaging/tools/{default.js → default.ts} +114 -30
  89. package/imaging/tools/{interaction.js → interaction.ts} +42 -23
  90. package/imaging/tools/{io.js → io.ts} +47 -31
  91. package/imaging/tools/{main.js → main.ts} +105 -40
  92. package/imaging/tools/{segmentation.js → segmentation.ts} +95 -68
  93. package/imaging/tools/{state.js → state.ts} +7 -12
  94. package/imaging/tools/types.d.ts +243 -0
  95. package/imaging/types.d.ts +197 -0
  96. package/{index.js → index.ts} +43 -14
  97. package/jsdoc.json +1 -1
  98. package/package.json +32 -14
  99. package/tsconfig.json +102 -0
  100. package/imaging/imageRendering.js +0 -860
  101. package/imaging/imageStore.js +0 -322
  102. package/modules/vuex/larvitar.js +0 -187
  103. /package/imaging/tools/{polygonSegmentationMixin.js → custom/polygonSegmentationMixin.js} +0 -0
@@ -8,6 +8,7 @@ import cornerstone from "cornerstone-core";
8
8
  import { default as cornerstoneDICOMImageLoader } from "cornerstone-wado-image-loader";
9
9
  import { each, clone, range, findKey, filter, pickBy } from "lodash";
10
10
  import { v4 as uuidv4 } from "uuid";
11
+ import { ImageLoader } from "cornerstone-core";
11
12
 
12
13
  // internal libraries
13
14
  import {
@@ -21,6 +22,16 @@ import {
21
22
  getLarvitarImageTracker,
22
23
  getLarvitarManager
23
24
  } from "./commonLoader";
25
+ import type {
26
+ Image,
27
+ Instance,
28
+ Volume,
29
+ LarvitarManager,
30
+ ImageFrame,
31
+ ImageTracker,
32
+ MetaData
33
+ } from "../types";
34
+ import { DataSet } from "dicom-parser";
24
35
 
25
36
  // global module variables
26
37
  let customImageLoaderCounter = 0;
@@ -35,6 +46,79 @@ let customImageLoaderCounter = 0;
35
46
  * getNrrdSerieDimensions()
36
47
  */
37
48
 
49
+ type NrrdInputVolume = {
50
+ header: {
51
+ sizes: number[];
52
+ "space directions": number[][]; // a property with a space in the name ?? Seriously ??
53
+ "space origin": [number, number];
54
+ kinds: string[];
55
+ type: string;
56
+ };
57
+ data: Uint16Array; // TODO-ts: other typed arrays ?
58
+ };
59
+
60
+ export type NrrdSeries = {
61
+ currentImageIdIndex: number;
62
+ imageIds: string[];
63
+ instances: { [key: string]: Instance };
64
+ instanceUIDs: { [key: string]: string };
65
+ numberOfImages: number;
66
+ seriesDescription: string;
67
+ seriesUID: string;
68
+ customLoader: string;
69
+ nrrdHeader: NrrdHeader;
70
+ bytes: number;
71
+ dataSet?: DataSet;
72
+ metadata?: MetaData;
73
+ };
74
+
75
+ type NrrdHeader = {
76
+ volume: Volume;
77
+ intercept: number;
78
+ slope: number;
79
+ repr: string;
80
+ phase: string;
81
+ study_description: string;
82
+ series_description: string;
83
+ acquisition_date: string;
84
+ [imageId: string]: string | number | Volume | NrrdInstance; // TODO-ts: fix this: we need just NrrdInstance
85
+ };
86
+
87
+ type NrrdInstance = {
88
+ instanceUID: string;
89
+ seriesDescription: string;
90
+ seriesModality: string;
91
+ patientName: string;
92
+ bitsAllocated: number;
93
+ pixelRepresentation: string;
94
+ };
95
+
96
+ // TODO-ts: why it's different from cornerstone type ?
97
+ // type Image = {
98
+ // imageId: string;
99
+ // rows: number;
100
+ // columns: number;
101
+ // minPixelValue: number;
102
+ // maxPixelValue: number;
103
+ // slope: number;
104
+ // intercept: number;
105
+ // windowCenter: number;
106
+ // windowWidth: number;
107
+ // render?: Function;
108
+ // getPixelData?: Function;
109
+ // getCanvas?: Function;
110
+ // color: boolean;
111
+ // columnPixelSpacing: number;
112
+ // rowPixelSpacing: number;
113
+ // invert: boolean;
114
+ // sizeInBytes: number;
115
+ // height: number;
116
+ // width: number;
117
+ // decodeTimeInMS?: number;
118
+ // webWorkerTimeInMS?: number;
119
+ // metadata: {[key: string]: MetadataValue};
120
+ // }
121
+
38
122
  /**
39
123
  * Build the data structure for the provided image orientation
40
124
  * @instance
@@ -44,12 +128,17 @@ let customImageLoaderCounter = 0;
44
128
  * @param {Object} custom_header A custom header object
45
129
  * @return {Object} volume data
46
130
  */
47
- export const buildNrrdImage = function (volume, seriesId, custom_header) {
131
+ export const buildNrrdImage = function (
132
+ volume: NrrdInputVolume,
133
+ seriesId: string,
134
+ custom_header: NrrdHeader
135
+ ) {
136
+ //TODO-ts: better definition
48
137
  let t0 = performance.now();
49
138
  // standard image structure
50
- let image = {};
51
- let manager = getLarvitarManager();
52
- let imageTracker = getLarvitarImageTracker();
139
+ let image: Partial<NrrdSeries> = {};
140
+ let manager = getLarvitarManager() as LarvitarManager;
141
+ let imageTracker = getLarvitarImageTracker() as ImageTracker;
53
142
  image.currentImageIdIndex = 0;
54
143
  image.imageIds = [];
55
144
  image.instances = {};
@@ -57,8 +146,8 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
57
146
  image.seriesDescription = "";
58
147
  image.seriesUID = seriesId;
59
148
 
60
- let header = {};
61
- header["volume"] = {};
149
+ let header: Partial<NrrdHeader> = {};
150
+ header.volume = {} as Volume;
62
151
  // need to extract header from nrrd file format
63
152
  // sizes, spaceDirections and spaceOrigin
64
153
 
@@ -96,9 +185,9 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
96
185
  header.volume.sliceThickness = spacing_z;
97
186
  header.volume.repr =
98
187
  volume.header.type[0].toUpperCase() + volume.header.type.slice(1);
99
- header.volume.intercept = custom_header ? custom_header.intercept : null;
100
- header.volume.slope = custom_header ? custom_header.slope : null;
101
- header.volume.phase = custom_header ? custom_header.phase : null;
188
+ header.volume.intercept = custom_header ? custom_header.intercept : 0;
189
+ header.volume.slope = custom_header ? custom_header.slope : 1;
190
+ header.volume.phase = custom_header ? custom_header.phase : "";
102
191
  header.volume.study_description = custom_header
103
192
  ? custom_header.study_description
104
193
  : "";
@@ -112,9 +201,13 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
112
201
  let rows = volume.header.sizes[index + 1];
113
202
  let cols = volume.header.sizes[index + 0];
114
203
  let frames = volume.header.sizes[index + 2];
115
- let iop = volume.header["space directions"][index + 0].concat(
204
+ let iopArr = volume.header["space directions"][index + 0].concat(
116
205
  volume.header["space directions"][index + 1]
117
206
  );
207
+ if (iopArr.length !== 6) {
208
+ throw new Error("Invalid Image Orientation");
209
+ }
210
+ let iop = iopArr as [number, number, number, number, number, number];
118
211
  let firstIpp = header.volume.imagePosition;
119
212
  let w = getNormalOrientation(iop);
120
213
  let ps = header.volume.pixelSpacing;
@@ -122,7 +215,7 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
122
215
  let intercept = header.volume.intercept;
123
216
  let slope = header.volume.slope;
124
217
 
125
- let metadata = {
218
+ let metadata: Partial<Instance["metadata"]> = {
126
219
  x00280010: rows, // Rows
127
220
  x00280011: cols, // Columns
128
221
  x00200037: iop, // ImageOrientationPatient
@@ -131,30 +224,34 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
131
224
  x00281052: intercept ? [intercept] : [0],
132
225
  x00281053: slope ? [slope] : [1],
133
226
  x00200052: header.volume.imageIds
134
- ? header[header.volume.imageIds[0]].instanceUID
227
+ ? (header[header.volume.imageIds[0]] as NrrdInstance).instanceUID
135
228
  : null,
136
229
  x0008103e: header.volume.imageIds
137
- ? header[header.volume.imageIds[0]].seriesDescription
230
+ ? (header[header.volume.imageIds[0]] as NrrdInstance).seriesDescription
138
231
  : null,
139
232
  x00080060: header.volume.imageIds
140
- ? header[header.volume.imageIds[0]].seriesModality
233
+ ? (header[header.volume.imageIds[0]] as NrrdInstance).seriesModality
141
234
  : null,
142
235
  x00100010: header.volume.imageIds
143
- ? header[header.volume.imageIds[0]].patientName
236
+ ? (header[header.volume.imageIds[0]] as NrrdInstance).patientName
144
237
  : null,
145
238
  x00280100: header.volume.imageIds
146
- ? header[header.volume.imageIds[0]].bitsAllocated
239
+ ? (header[header.volume.imageIds[0]] as NrrdInstance).bitsAllocated
147
240
  : null,
148
241
  x00280103: header.volume.imageIds
149
- ? header[header.volume.imageIds[0]].pixelRepresentation
242
+ ? (header[header.volume.imageIds[0]] as NrrdInstance).pixelRepresentation
150
243
  : null,
151
244
  repr: header.volume.repr || null
152
245
  };
153
246
 
154
247
  // compute default ww/wl values here to use them also for resliced images
155
248
  let minMax = cornerstoneDICOMImageLoader.getMinMax(volume.data);
156
- let maxVoi = minMax.max * metadata.x00281053[0] + metadata.x00281052[0];
157
- let minVoi = minMax.min * metadata.x00281053[0] + metadata.x00281052[0];
249
+ let maxVoi =
250
+ minMax.max * (metadata.x00281053 as number[])[0] +
251
+ (metadata.x00281052 as number[])[0];
252
+ let minVoi =
253
+ minMax.min * (metadata.x00281053 as number[])[0] +
254
+ (metadata.x00281052 as number[])[0];
158
255
  let ww = maxVoi - minVoi;
159
256
  let wl = (maxVoi + minVoi) / 2;
160
257
 
@@ -162,35 +259,49 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
162
259
  metadata.x00280107 = minMax.max;
163
260
 
164
261
  // extract the pixelData of each frame, store the data into the image object
165
- each(range(frames), function (sliceIndex) {
262
+ each(range(frames), function (sliceIndex: number) {
166
263
  let sliceSize = rows * cols;
167
264
  let sliceBuffer = volume.data.subarray(
168
265
  sliceSize * sliceIndex,
169
266
  sliceSize * (sliceIndex + 1)
170
267
  );
268
+
269
+ if (!metadata) {
270
+ throw new Error("Metadata not found");
271
+ }
272
+
273
+ // @ts-ignore: TODO this is concepptually wrong, we already know the Pixel Representation
274
+ // (see above, line 241), this function just returns the same value again
171
275
  let r = getPixelRepresentation(metadata);
172
276
  let typedArray = getTypedArrayFromDataType(r);
277
+
278
+ if (!typedArray) {
279
+ throw new Error("Typed array not found");
280
+ }
281
+
173
282
  let pixelData = new typedArray(sliceBuffer);
174
283
  // assign these values to the metadata of all images
175
284
  metadata.x00281050 = wl;
176
285
  metadata.x00281051 = ww;
177
286
 
178
287
  let imageId = getNrrdImageId("nrrdLoader");
288
+ if (!imageTracker) {
289
+ throw new Error("Image tracker not initialized");
290
+ }
179
291
  imageTracker[imageId] = seriesId;
180
292
 
181
293
  // store file references
182
- image.imageIds.push(imageId);
183
- image.instances[imageId] = {
184
- instanceId: uuidv4(),
185
- frame: sliceIndex
186
- };
187
- let frameMetadata = clone(metadata);
188
-
294
+ image.imageIds!.push(imageId);
295
+ let frameMetadata: MetaData = clone(metadata);
189
296
  frameMetadata.x00200032 = firstIpp.map(function (val, i) {
190
297
  return val + thickness * sliceIndex * w[i];
191
298
  });
192
- image.instances[imageId].metadata = frameMetadata;
193
- image.instances[imageId].pixelData = pixelData;
299
+ image.instances![imageId] = {
300
+ instanceId: uuidv4(),
301
+ frame: sliceIndex,
302
+ metadata: frameMetadata,
303
+ pixelData: pixelData
304
+ };
194
305
  });
195
306
 
196
307
  let middleSlice = Math.floor(image.imageIds.length / 2);
@@ -199,9 +310,13 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
199
310
  // specify custom loader type and attach original header
200
311
  image.customLoader = "nrrdLoader";
201
312
  header.volume.imageIds = image.imageIds;
202
- image.nrrdHeader = header;
313
+ image.nrrdHeader = header as NrrdHeader;
314
+
315
+ if (!manager) {
316
+ throw new Error("Larvitar manager not initialized");
317
+ }
203
318
 
204
- manager[seriesId] = image;
319
+ manager[seriesId] = image as NrrdSeries;
205
320
 
206
321
  let t1 = performance.now();
207
322
  console.log(`Call to buildNrrdImage took ${t1 - t0} milliseconds.`);
@@ -215,7 +330,7 @@ export const buildNrrdImage = function (volume, seriesId, custom_header) {
215
330
  * @param {String} customLoaderName The custom loader name
216
331
  * @return {String} the custom image id
217
332
  */
218
- export const getNrrdImageId = function (customLoaderName) {
333
+ export const getNrrdImageId = function (customLoaderName: string) {
219
334
  let imageId = customLoaderName + "://" + customImageLoaderCounter;
220
335
  customImageLoaderCounter++;
221
336
  return imageId;
@@ -228,11 +343,15 @@ export const getNrrdImageId = function (customLoaderName) {
228
343
  * @param {String} imageId The image id
229
344
  * @return {Object} custom image object
230
345
  */
231
- export const loadNrrdImage = function (imageId) {
232
- let manager = getLarvitarManager();
233
- let imageTracker = getLarvitarImageTracker();
346
+ export const loadNrrdImage: ImageLoader = function (imageId: string) {
347
+ let manager = getLarvitarManager() as LarvitarManager;
348
+ let imageTracker = getLarvitarImageTracker() as ImageTracker;
349
+ if (!manager || !imageTracker) {
350
+ throw new Error("Larvitar manager or image tracker not initialized");
351
+ }
234
352
  let seriesId = imageTracker[imageId];
235
353
  let instance = manager[seriesId].instances[imageId];
354
+ //@ts-ignore TODO-ts: fix this why is different typed array?
236
355
  return createCustomImage(imageId, instance.metadata, instance.pixelData);
237
356
  };
238
357
 
@@ -246,13 +365,13 @@ export const loadNrrdImage = function (imageId) {
246
365
  * @return {String} image id
247
366
  */
248
367
  export const getImageIdFromSlice = function (
249
- sliceNumber,
250
- orientation,
251
- seriesId
368
+ sliceNumber: number,
369
+ orientation: string,
370
+ seriesId: string
252
371
  ) {
253
372
  var prefix = "nrrdLoader://";
254
373
  var serieImageTracker;
255
- let imageTracker = getLarvitarImageTracker();
374
+ let imageTracker = getLarvitarImageTracker() as ImageTracker;
256
375
 
257
376
  if (seriesId) {
258
377
  serieImageTracker = pickBy(imageTracker, image => {
@@ -262,12 +381,18 @@ export const getImageIdFromSlice = function (
262
381
  serieImageTracker = imageTracker;
263
382
  }
264
383
 
265
- var firstImageId = findKey(serieImageTracker, entry => {
384
+ var firstImageIdStr = findKey(serieImageTracker, entry => {
266
385
  return entry[1] == orientation;
267
386
  });
268
387
 
269
- var imageIndex =
270
- parseInt(firstImageId.split("//").pop()) + parseInt(sliceNumber);
388
+ let firstImageId = firstImageIdStr?.split("//").pop();
389
+
390
+ if (firstImageId == undefined) {
391
+ console.error("cannot find imageId for orientation: " + orientation);
392
+ return "";
393
+ }
394
+
395
+ var imageIndex = parseInt(firstImageId) + parseInt(sliceNumber.toString());
271
396
 
272
397
  var imageId = prefix.concat(imageIndex.toString());
273
398
 
@@ -283,16 +408,29 @@ export const getImageIdFromSlice = function (
283
408
  * @param {String} seriesId The series id
284
409
  * @return {Integer} The image slice number
285
410
  */
286
- export const getSliceNumberFromImageId = function (imageId, orientation) {
287
- let imageTracker = getLarvitarImageTracker();
288
- var firstImageId = findKey(imageTracker, entry => {
411
+ export const getSliceNumberFromImageId = function (
412
+ imageId: string,
413
+ orientation: string
414
+ ) {
415
+ let imageTracker = getLarvitarImageTracker() as ImageTracker;
416
+ var firstImageIdStr = findKey(imageTracker, entry => {
289
417
  return entry[1] == orientation;
290
418
  });
291
419
 
420
+ if (firstImageIdStr == undefined) {
421
+ console.error("cannot find imageId for orientation: " + orientation);
422
+ return 0;
423
+ }
424
+
292
425
  var imageNumber = imageId.split("//").pop() || imageId;
426
+ let firstImageId = firstImageIdStr.split("//").pop();
293
427
 
294
- var imageIndex =
295
- parseInt(imageNumber) - parseInt(firstImageId.split("//").pop());
428
+ if (firstImageId == undefined) {
429
+ console.error("cannot find imageId for orientation: " + orientation);
430
+ return 0;
431
+ }
432
+
433
+ var imageIndex = parseInt(imageNumber) - parseInt(firstImageId);
296
434
 
297
435
  return imageIndex;
298
436
  };
@@ -304,7 +442,7 @@ export const getSliceNumberFromImageId = function (imageId, orientation) {
304
442
  * @return {Object} Series dimension for each view
305
443
  */
306
444
  export const getNrrdSerieDimensions = function () {
307
- let imageTracker = getLarvitarImageTracker();
445
+ let imageTracker = getLarvitarImageTracker() as ImageTracker;
308
446
  var dim_axial = filter(imageTracker, img => {
309
447
  return img[1] == "axial";
310
448
  });
@@ -334,11 +472,17 @@ export const getNrrdSerieDimensions = function () {
334
472
  * @param {Object} dataSet The dataset
335
473
  * @return {String} The image id
336
474
  */
337
- let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
475
+ let createCustomImage = function (
476
+ imageId: string,
477
+ metadata: MetaData,
478
+ pixelData: Uint8ClampedArray,
479
+ dataSet?: any
480
+ ) {
481
+ //TODO-ts check this
338
482
  let canvas = window.document.createElement("canvas");
339
483
  let lastImageIdDrawn = "";
340
484
 
341
- let imageFrame = getImageFrame(metadata, dataSet);
485
+ let imageFrame = getImageFrame(metadata, dataSet) as ImageFrame;
342
486
 
343
487
  // This function uses the pixelData received as argument without manipulating
344
488
  // them: if the image is compressed, the decompress function should be called
@@ -361,7 +505,7 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
361
505
  );
362
506
  }
363
507
 
364
- let image = {
508
+ let image: Partial<Image> = {
365
509
  imageId: imageId,
366
510
  color: cornerstoneDICOMImageLoader.isColorImage(
367
511
  imageFrame.photometricInterpretation
@@ -369,7 +513,7 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
369
513
  columnPixelSpacing: pixelSpacing ? pixelSpacing[1] : undefined,
370
514
  columns: imageFrame.columns,
371
515
  height: imageFrame.rows,
372
- intercept: rescaleIntercept ? rescaleIntercept[0] : 0,
516
+ intercept: rescaleIntercept ? (rescaleIntercept as number[])[0] : 0,
373
517
  invert: imageFrame.photometricInterpretation === "MONOCHROME1",
374
518
  minPixelValue: imageFrame.smallestPixelValue,
375
519
  maxPixelValue: imageFrame.largestPixelValue,
@@ -377,17 +521,21 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
377
521
  rowPixelSpacing: pixelSpacing ? pixelSpacing[0] : undefined,
378
522
  rows: imageFrame.rows,
379
523
  sizeInBytes: getSizeInBytes(),
380
- slope: rescaleSlope ? rescaleSlope[0] : 1,
524
+ slope: rescaleSlope ? (rescaleSlope as number[])[0] : 1,
381
525
  width: imageFrame.columns,
382
- windowCenter: windowCenter ? windowCenter[0] : undefined,
383
- windowWidth: windowWidth ? windowWidth[0] : undefined,
526
+ windowCenter: windowCenter ? (windowCenter as number[])[0] : undefined,
527
+ windowWidth: windowWidth ? (windowWidth as number[])[0] : undefined,
384
528
  decodeTimeInMS: undefined,
385
529
  webWorkerTimeInMS: undefined
386
530
  };
387
531
 
388
532
  // add function to return pixel data
389
533
  image.getPixelData = function () {
390
- return imageFrame.pixelData;
534
+ if (!imageFrame.pixelData) {
535
+ console.warn('no pixel data for imageId "' + imageId);
536
+ return [];
537
+ }
538
+ return Array.from(imageFrame.pixelData);
391
539
  };
392
540
 
393
541
  // convert color space
@@ -397,6 +545,11 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
397
545
  canvas.width = imageFrame.columns;
398
546
 
399
547
  let context = canvas.getContext("2d");
548
+
549
+ if (!context) {
550
+ throw new Error("Unable to get canvas context");
551
+ }
552
+
400
553
  let imageData = context.createImageData(
401
554
  imageFrame.columns,
402
555
  imageFrame.rows
@@ -415,10 +568,13 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
415
568
  return canvas;
416
569
  }
417
570
 
418
- canvas.height = image.rows;
419
- canvas.width = image.columns;
571
+ canvas.height = image.rows || 0;
572
+ canvas.width = image.columns || 0;
420
573
  let context = canvas.getContext("2d");
421
- context.putImageData(imageFrame.imageData, 0, 0);
574
+ if (!context) {
575
+ throw new Error("Unable to get canvas context");
576
+ }
577
+ context.putImageData(imageFrame.imageData!, 0, 0);
422
578
  lastImageIdDrawn = imageId;
423
579
  return canvas;
424
580
  };
@@ -436,13 +592,23 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
436
592
  // set the ww/wc to cover the dynamic range of the image if no values are supplied
437
593
  if (image.windowCenter === undefined || image.windowWidth === undefined) {
438
594
  if (image.color) {
439
- image.windowWidth = 255;
440
- image.windowCenter = 128;
441
- } else {
595
+ image.windowWidth = 255.0;
596
+ image.windowCenter = 127.5;
597
+ } else if (
598
+ image.maxPixelValue != null &&
599
+ image.minPixelValue != null &&
600
+ image.slope != null &&
601
+ image.intercept != null
602
+ ) {
442
603
  let maxVoi = image.maxPixelValue * image.slope + image.intercept;
443
604
  let minVoi = image.minPixelValue * image.slope + image.intercept;
444
605
  image.windowWidth = maxVoi - minVoi;
445
606
  image.windowCenter = (maxVoi + minVoi) / 2;
607
+ } else {
608
+ console.error(
609
+ "Unable to calculate default window width/center for imageId: " +
610
+ imageId
611
+ );
446
612
  }
447
613
  }
448
614
 
@@ -451,8 +617,8 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
451
617
  // function to store custom image pixelData and metadata.
452
618
  image.metadata = metadata;
453
619
 
454
- let promise = new Promise(function (resolve) {
455
- resolve(image);
620
+ let promise: Promise<Image> = new Promise(function (resolve) {
621
+ resolve(image as Image);
456
622
  });
457
623
 
458
624
  // Return an object containing the Promise to cornerstone so it can setup callbacks to be
@@ -13,6 +13,7 @@ import {
13
13
  getLarvitarImageTracker,
14
14
  getLarvitarManager
15
15
  } from "./commonLoader";
16
+ import type { Image, ImageFrame, MetaData } from "../types";
16
17
 
17
18
  /*
18
19
  * This module provides the following functions to be exported:
@@ -20,18 +21,19 @@ import {
20
21
  */
21
22
 
22
23
  /**
23
- * Custom Loader for WadoImageLoader
24
+ * Custom Loader for DICOMImageLoader
24
25
  * @instance
25
26
  * @function loadReslicedImage
26
27
  * @param {String} imageId The Id of the image
27
28
  * @returns {Object} custom image object
28
29
  */
29
- export const loadReslicedImage = function (imageId) {
30
+ export const loadReslicedImage = function (imageId: string) {
30
31
  let manager = getLarvitarManager();
31
32
  let imageTracker = getLarvitarImageTracker();
32
33
  let seriesId = imageTracker[imageId];
33
34
  let instance = manager[seriesId].instances[imageId];
34
35
  var reslicedPixeldata = instance.pixelData;
36
+ //@ts-ignore deprecated
35
37
  return createCustomImage(imageId, instance.metadata, reslicedPixeldata);
36
38
  };
37
39
 
@@ -47,11 +49,16 @@ export const loadReslicedImage = function (imageId) {
47
49
  * @param {Object} dataSet dataset object
48
50
  * @returns {Object} custom image object
49
51
  */
50
- let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
52
+ let createCustomImage = function (
53
+ imageId: string,
54
+ metadata: MetaData,
55
+ pixelData: Uint8ClampedArray,
56
+ dataSet?: any // deprecated
57
+ ) {
51
58
  let canvas = window.document.createElement("canvas");
52
59
  let lastImageIdDrawn = "";
53
60
 
54
- let imageFrame = getImageFrame(metadata, dataSet);
61
+ let imageFrame = getImageFrame(metadata, dataSet) as ImageFrame;
55
62
 
56
63
  // This function uses the pixelData received as argument without manipulating
57
64
  // them: if the image is compressed, the decompress function should be called
@@ -74,33 +81,39 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
74
81
  );
75
82
  }
76
83
 
77
- let image = {
84
+ let image: Partial<Image> = {
78
85
  imageId: imageId,
79
86
  color: cornerstoneDICOMImageLoader.isColorImage(
80
87
  imageFrame.photometricInterpretation
81
88
  ),
82
- columnPixelSpacing: pixelSpacing ? pixelSpacing[1] : undefined,
89
+ columnPixelSpacing: pixelSpacing
90
+ ? (pixelSpacing as number[])[1]
91
+ : undefined,
83
92
  columns: imageFrame.columns,
84
93
  height: imageFrame.rows,
85
- intercept: rescaleIntercept ? rescaleIntercept : 0,
94
+ intercept: rescaleIntercept ? (rescaleIntercept as number) : 0,
86
95
  invert: imageFrame.photometricInterpretation === "MONOCHROME1",
87
96
  minPixelValue: imageFrame.smallestPixelValue,
88
97
  maxPixelValue: imageFrame.largestPixelValue,
89
98
  render: undefined, // set below
90
- rowPixelSpacing: pixelSpacing ? pixelSpacing[0] : undefined,
99
+ rowPixelSpacing: pixelSpacing ? (pixelSpacing as number[])[0] : undefined,
91
100
  rows: imageFrame.rows,
92
101
  sizeInBytes: getSizeInBytes(),
93
- slope: rescaleSlope ? rescaleSlope : 1,
102
+ slope: rescaleSlope ? (rescaleSlope as number) : 1,
94
103
  width: imageFrame.columns,
95
- windowCenter: windowCenter ? windowCenter : undefined,
96
- windowWidth: windowWidth ? windowWidth : undefined,
104
+ windowCenter: windowCenter ? (windowCenter as number) : undefined,
105
+ windowWidth: windowWidth ? (windowWidth as number) : undefined,
97
106
  decodeTimeInMS: undefined,
98
107
  webWorkerTimeInMS: undefined
99
108
  };
100
109
 
101
110
  // add function to return pixel data
102
111
  image.getPixelData = function () {
103
- return imageFrame.pixelData;
112
+ if (!imageFrame.pixelData) {
113
+ console.warn('no pixel data for imageId "' + imageId);
114
+ return [];
115
+ }
116
+ return Array.from(imageFrame.pixelData);
104
117
  };
105
118
 
106
119
  // convert color space
@@ -110,6 +123,11 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
110
123
  canvas.width = imageFrame.columns;
111
124
 
112
125
  let context = canvas.getContext("2d");
126
+
127
+ if (!context) {
128
+ throw new Error("Unable to get canvas context");
129
+ }
130
+
113
131
  let imageData = context.createImageData(
114
132
  imageFrame.columns,
115
133
  imageFrame.rows
@@ -128,10 +146,13 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
128
146
  return canvas;
129
147
  }
130
148
 
131
- canvas.height = image.rows;
132
- canvas.width = image.columns;
149
+ canvas.height = image.rows || 0;
150
+ canvas.width = image.columns || 0;
133
151
  let context = canvas.getContext("2d");
134
- context.putImageData(imageFrame.imageData, 0, 0);
152
+ if (!context) {
153
+ throw new Error("Unable to get canvas context");
154
+ }
155
+ context.putImageData(imageFrame.imageData!, 0, 0);
135
156
  lastImageIdDrawn = imageId;
136
157
  return canvas;
137
158
  };
@@ -147,13 +168,23 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
147
168
  // set the ww/wc to cover the dynamic range of the image if no values are supplied
148
169
  if (image.windowCenter === undefined || image.windowWidth === undefined) {
149
170
  if (image.color) {
150
- image.windowWidth = 255;
151
- image.windowCenter = 128;
152
- } else {
171
+ image.windowWidth = 255.0;
172
+ image.windowCenter = 127.5;
173
+ } else if (
174
+ image.maxPixelValue &&
175
+ image.minPixelValue &&
176
+ image.slope &&
177
+ image.intercept
178
+ ) {
153
179
  let maxVoi = image.maxPixelValue * image.slope + image.intercept;
154
180
  let minVoi = image.minPixelValue * image.slope + image.intercept;
155
181
  image.windowWidth = maxVoi - minVoi;
156
182
  image.windowCenter = (maxVoi + minVoi) / 2;
183
+ } else {
184
+ console.error(
185
+ "Unable to calculate default window width/center for imageId: " +
186
+ imageId
187
+ );
157
188
  }
158
189
  }
159
190
 
@@ -162,8 +193,8 @@ let createCustomImage = function (imageId, metadata, pixelData, dataSet) {
162
193
  // function to store custom image pixelData and metadata.
163
194
  image.metadata = metadata;
164
195
 
165
- let promise = new Promise(function (resolve) {
166
- resolve(image);
196
+ let promise: Promise<Image> = new Promise(function (resolve) {
197
+ resolve(image as Image);
167
198
  });
168
199
 
169
200
  // Return an object containing the Promise to cornerstone so it can setup callbacks to be