@cornerstonejs/core 1.42.0 → 1.42.1

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 (157) hide show
  1. package/dist/cjs/RenderingEngine/BaseVolumeViewport.js.map +1 -1
  2. package/dist/cjs/RenderingEngine/StackViewport.js +2 -1
  3. package/dist/cjs/RenderingEngine/StackViewport.js.map +1 -1
  4. package/dist/cjs/RenderingEngine/helpers/createVolumeActor.js +2 -1
  5. package/dist/cjs/RenderingEngine/helpers/createVolumeActor.js.map +1 -1
  6. package/dist/cjs/cache/cache.d.ts +6 -3
  7. package/dist/cjs/cache/cache.js +43 -6
  8. package/dist/cjs/cache/cache.js.map +1 -1
  9. package/dist/cjs/cache/classes/ImageVolume.d.ts +25 -5
  10. package/dist/cjs/cache/classes/ImageVolume.js +302 -13
  11. package/dist/cjs/cache/classes/ImageVolume.js.map +1 -1
  12. package/dist/cjs/eventTarget.d.ts +1 -0
  13. package/dist/cjs/eventTarget.js +13 -1
  14. package/dist/cjs/eventTarget.js.map +1 -1
  15. package/dist/cjs/init.js +2 -0
  16. package/dist/cjs/init.js.map +1 -1
  17. package/dist/cjs/loaders/imageLoader.js +6 -2
  18. package/dist/cjs/loaders/imageLoader.js.map +1 -1
  19. package/dist/cjs/loaders/volumeLoader.d.ts +12 -9
  20. package/dist/cjs/loaders/volumeLoader.js +50 -5
  21. package/dist/cjs/loaders/volumeLoader.js.map +1 -1
  22. package/dist/cjs/types/Cornerstone3DConfig.d.ts +1 -0
  23. package/dist/cjs/types/IDynamicImageVolume.d.ts +2 -2
  24. package/dist/cjs/types/IImage.d.ts +5 -0
  25. package/dist/cjs/types/IImageVolume.d.ts +7 -2
  26. package/dist/cjs/types/ILoadObject.d.ts +2 -2
  27. package/dist/cjs/types/IVolume.d.ts +3 -26
  28. package/dist/cjs/types/ImageVolumeProps.d.ts +6 -0
  29. package/dist/cjs/types/ImageVolumeProps.js +3 -0
  30. package/dist/cjs/types/ImageVolumeProps.js.map +1 -0
  31. package/dist/cjs/types/VolumeProps.d.ts +27 -0
  32. package/dist/cjs/types/VolumeProps.js +3 -0
  33. package/dist/cjs/types/VolumeProps.js.map +1 -0
  34. package/dist/cjs/types/index.d.ts +4 -2
  35. package/dist/cjs/utilities/VoxelManager.d.ts +2 -2
  36. package/dist/cjs/utilities/cacheUtils.d.ts +2 -0
  37. package/dist/cjs/utilities/cacheUtils.js +96 -0
  38. package/dist/cjs/utilities/cacheUtils.js.map +1 -0
  39. package/dist/cjs/utilities/convertStackToVolumeViewport.d.ts +12 -0
  40. package/dist/cjs/utilities/convertStackToVolumeViewport.js +61 -0
  41. package/dist/cjs/utilities/convertStackToVolumeViewport.js.map +1 -0
  42. package/dist/cjs/utilities/convertVolumeToStackViewport.d.ts +9 -0
  43. package/dist/cjs/utilities/convertVolumeToStackViewport.js +95 -0
  44. package/dist/cjs/utilities/convertVolumeToStackViewport.js.map +1 -0
  45. package/dist/cjs/utilities/generateVolumePropsFromImageIds.d.ts +3 -0
  46. package/dist/cjs/utilities/generateVolumePropsFromImageIds.js +124 -0
  47. package/dist/cjs/utilities/generateVolumePropsFromImageIds.js.map +1 -0
  48. package/dist/cjs/utilities/index.d.ts +6 -1
  49. package/dist/cjs/utilities/index.js +12 -1
  50. package/dist/cjs/utilities/index.js.map +1 -1
  51. package/dist/cjs/utilities/roundNumber.d.ts +4 -0
  52. package/dist/cjs/utilities/roundNumber.js +36 -0
  53. package/dist/cjs/utilities/roundNumber.js.map +1 -0
  54. package/dist/esm/RenderingEngine/BaseVolumeViewport.js.map +1 -1
  55. package/dist/esm/RenderingEngine/StackViewport.js +2 -1
  56. package/dist/esm/RenderingEngine/StackViewport.js.map +1 -1
  57. package/dist/esm/RenderingEngine/helpers/createVolumeActor.js +1 -1
  58. package/dist/esm/RenderingEngine/helpers/createVolumeActor.js.map +1 -1
  59. package/dist/esm/cache/cache.js +43 -6
  60. package/dist/esm/cache/cache.js.map +1 -1
  61. package/dist/esm/cache/classes/ImageVolume.js +279 -14
  62. package/dist/esm/cache/classes/ImageVolume.js.map +1 -1
  63. package/dist/esm/eventTarget.js +13 -1
  64. package/dist/esm/eventTarget.js.map +1 -1
  65. package/dist/esm/init.js +2 -0
  66. package/dist/esm/init.js.map +1 -1
  67. package/dist/esm/loaders/imageLoader.js +5 -2
  68. package/dist/esm/loaders/imageLoader.js.map +1 -1
  69. package/dist/esm/loaders/volumeLoader.js +50 -5
  70. package/dist/esm/loaders/volumeLoader.js.map +1 -1
  71. package/dist/esm/types/ImageVolumeProps.js +2 -0
  72. package/dist/esm/types/ImageVolumeProps.js.map +1 -0
  73. package/dist/esm/types/VolumeProps.js +2 -0
  74. package/dist/esm/types/VolumeProps.js.map +1 -0
  75. package/dist/esm/utilities/cacheUtils.js +65 -0
  76. package/dist/esm/utilities/cacheUtils.js.map +1 -0
  77. package/dist/esm/utilities/convertStackToVolumeViewport.js +49 -0
  78. package/dist/esm/utilities/convertStackToVolumeViewport.js.map +1 -0
  79. package/dist/esm/utilities/convertVolumeToStackViewport.js +58 -0
  80. package/dist/esm/utilities/convertVolumeToStackViewport.js.map +1 -0
  81. package/dist/esm/utilities/generateVolumePropsFromImageIds.js +118 -0
  82. package/dist/esm/utilities/generateVolumePropsFromImageIds.js.map +1 -0
  83. package/dist/esm/utilities/index.js +6 -1
  84. package/dist/esm/utilities/index.js.map +1 -1
  85. package/dist/esm/utilities/roundNumber.js +33 -0
  86. package/dist/esm/utilities/roundNumber.js.map +1 -0
  87. package/dist/types/RenderingEngine/BaseVolumeViewport.d.ts.map +1 -1
  88. package/dist/types/RenderingEngine/StackViewport.d.ts.map +1 -1
  89. package/dist/types/cache/cache.d.ts +6 -3
  90. package/dist/types/cache/cache.d.ts.map +1 -1
  91. package/dist/types/cache/classes/ImageVolume.d.ts +25 -5
  92. package/dist/types/cache/classes/ImageVolume.d.ts.map +1 -1
  93. package/dist/types/eventTarget.d.ts +1 -0
  94. package/dist/types/eventTarget.d.ts.map +1 -1
  95. package/dist/types/loaders/imageLoader.d.ts.map +1 -1
  96. package/dist/types/loaders/volumeLoader.d.ts +12 -9
  97. package/dist/types/loaders/volumeLoader.d.ts.map +1 -1
  98. package/dist/types/types/Cornerstone3DConfig.d.ts +1 -0
  99. package/dist/types/types/Cornerstone3DConfig.d.ts.map +1 -1
  100. package/dist/types/types/IDynamicImageVolume.d.ts +2 -2
  101. package/dist/types/types/IDynamicImageVolume.d.ts.map +1 -1
  102. package/dist/types/types/IImage.d.ts +5 -0
  103. package/dist/types/types/IImage.d.ts.map +1 -1
  104. package/dist/types/types/IImageVolume.d.ts +7 -2
  105. package/dist/types/types/IImageVolume.d.ts.map +1 -1
  106. package/dist/types/types/ILoadObject.d.ts +2 -2
  107. package/dist/types/types/ILoadObject.d.ts.map +1 -1
  108. package/dist/types/types/IVolume.d.ts +3 -26
  109. package/dist/types/types/IVolume.d.ts.map +1 -1
  110. package/dist/types/types/ImageVolumeProps.d.ts +7 -0
  111. package/dist/types/types/ImageVolumeProps.d.ts.map +1 -0
  112. package/dist/types/types/VolumeProps.d.ts +28 -0
  113. package/dist/types/types/VolumeProps.d.ts.map +1 -0
  114. package/dist/types/types/index.d.ts +4 -2
  115. package/dist/types/types/index.d.ts.map +1 -1
  116. package/dist/types/utilities/VoxelManager.d.ts +2 -2
  117. package/dist/types/utilities/VoxelManager.d.ts.map +1 -1
  118. package/dist/types/utilities/cacheUtils.d.ts +3 -0
  119. package/dist/types/utilities/cacheUtils.d.ts.map +1 -0
  120. package/dist/types/utilities/convertStackToVolumeViewport.d.ts +13 -0
  121. package/dist/types/utilities/convertStackToVolumeViewport.d.ts.map +1 -0
  122. package/dist/types/utilities/convertVolumeToStackViewport.d.ts +10 -0
  123. package/dist/types/utilities/convertVolumeToStackViewport.d.ts.map +1 -0
  124. package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts +4 -0
  125. package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts.map +1 -0
  126. package/dist/types/utilities/index.d.ts +6 -1
  127. package/dist/types/utilities/index.d.ts.map +1 -1
  128. package/dist/types/utilities/roundNumber.d.ts +5 -0
  129. package/dist/types/utilities/roundNumber.d.ts.map +1 -0
  130. package/dist/umd/index.js +1 -1
  131. package/dist/umd/index.js.map +1 -1
  132. package/package.json +2 -2
  133. package/src/RenderingEngine/BaseVolumeViewport.ts +0 -1
  134. package/src/RenderingEngine/StackViewport.ts +3 -1
  135. package/src/RenderingEngine/helpers/createVolumeActor.ts +1 -1
  136. package/src/cache/cache.ts +91 -7
  137. package/src/cache/classes/ImageVolume.ts +535 -21
  138. package/src/eventTarget.ts +19 -1
  139. package/src/init.ts +2 -2
  140. package/src/loaders/imageLoader.ts +6 -2
  141. package/src/loaders/volumeLoader.ts +118 -23
  142. package/src/types/Cornerstone3DConfig.ts +12 -0
  143. package/src/types/IDynamicImageVolume.ts +2 -2
  144. package/src/types/IImage.ts +6 -0
  145. package/src/types/IImageVolume.ts +14 -2
  146. package/src/types/ILoadObject.ts +2 -2
  147. package/src/types/IVolume.ts +4 -41
  148. package/src/types/ImageVolumeProps.ts +15 -0
  149. package/src/types/VolumeProps.ts +57 -0
  150. package/src/types/index.ts +5 -2
  151. package/src/utilities/VoxelManager.ts +2 -2
  152. package/src/utilities/cacheUtils.ts +121 -0
  153. package/src/utilities/convertStackToVolumeViewport.ts +115 -0
  154. package/src/utilities/convertVolumeToStackViewport.ts +125 -0
  155. package/src/utilities/generateVolumePropsFromImageIds.ts +183 -0
  156. package/src/utilities/index.ts +11 -0
  157. package/src/utilities/roundNumber.ts +56 -0
@@ -6,20 +6,32 @@ import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
6
6
  import cloneDeep from 'lodash.clonedeep';
7
7
 
8
8
  import { ImageVolume } from '../cache/classes/ImageVolume';
9
- import type * as Types from '../types';
10
9
  import cache from '../cache/cache';
11
10
  import Events from '../enums/Events';
12
11
  import eventTarget from '../eventTarget';
13
12
  import triggerEvent from '../utilities/triggerEvent';
14
- import { getBufferConfiguration, uuidv4 } from '../utilities';
13
+ import {
14
+ generateVolumePropsFromImageIds,
15
+ getBufferConfiguration,
16
+ uuidv4,
17
+ } from '../utilities';
15
18
  import {
16
19
  Point3,
17
20
  Metadata,
18
21
  EventTypes,
19
22
  Mat3,
23
+ IImageVolume,
24
+ VolumeLoaderFn,
25
+ IDynamicImageVolume,
20
26
  PixelDataTypedArray,
27
+ IVolumeLoadObject,
28
+ PixelDataTypedArrayString,
21
29
  } from '../types';
22
30
  import { getConfiguration } from '../init';
31
+ import {
32
+ performCacheOptimizationForVolume,
33
+ setupCacheOptimizationEventListener,
34
+ } from '../utilities/cacheUtils';
23
35
 
24
36
  interface VolumeLoaderOptions {
25
37
  imageIds: Array<string>;
@@ -28,7 +40,7 @@ interface VolumeLoaderOptions {
28
40
  interface DerivedVolumeOptions {
29
41
  volumeId: string;
30
42
  targetBuffer?: {
31
- type: 'Float32Array' | 'Uint8Array' | 'Uint16Array' | 'Int16Array';
43
+ type: PixelDataTypedArrayString;
32
44
  sharedArrayBuffer?: boolean;
33
45
  };
34
46
  }
@@ -46,7 +58,7 @@ interface LocalVolumeOptions {
46
58
  */
47
59
  function addScalarDataToImageData(
48
60
  imageData: vtkImageDataType,
49
- scalarData: Types.VolumeScalarData,
61
+ scalarData: PixelDataTypedArray,
50
62
  dataArrayAttrs
51
63
  ) {
52
64
  const scalarArray = vtkDataArray.newInstance({
@@ -63,7 +75,7 @@ function addScalarDataToImageData(
63
75
  */
64
76
  function addScalarDataArraysToImageData(
65
77
  imageData: vtkImageDataType,
66
- scalarDataArrays: Types.VolumeScalarData[],
78
+ scalarDataArrays: PixelDataTypedArray[],
67
79
  dataArrayAttrs
68
80
  ) {
69
81
  scalarDataArrays.forEach((scalarData, i) => {
@@ -81,7 +93,7 @@ function addScalarDataArraysToImageData(
81
93
  }
82
94
 
83
95
  function createInternalVTKRepresentation(
84
- volume: Types.IImageVolume
96
+ volume: IImageVolume
85
97
  ): vtkImageDataType {
86
98
  const { dimensions, metadata, spacing, direction, origin } = volume;
87
99
  const { PhotometricInterpretation } = metadata;
@@ -101,7 +113,7 @@ function createInternalVTKRepresentation(
101
113
 
102
114
  // Add scalar data to 3D or 4D volume
103
115
  if (volume.isDynamicVolume()) {
104
- const scalarDataArrays = (<Types.IDynamicImageVolume>(
116
+ const scalarDataArrays = (<IDynamicImageVolume>(
105
117
  volume
106
118
  )).getScalarDataArrays();
107
119
 
@@ -139,23 +151,28 @@ let unknownVolumeLoader;
139
151
  function loadVolumeFromVolumeLoader(
140
152
  volumeId: string,
141
153
  options?: VolumeLoaderOptions
142
- ): Types.IVolumeLoadObject {
154
+ ): IVolumeLoadObject {
143
155
  const colonIndex = volumeId.indexOf(':');
144
156
  const scheme = volumeId.substring(0, colonIndex);
145
- const loader = volumeLoaders[scheme];
157
+ let loader = volumeLoaders[scheme];
146
158
 
147
159
  if (loader === undefined || loader === null) {
148
- if (unknownVolumeLoader !== undefined) {
149
- return unknownVolumeLoader(volumeId, options);
160
+ if (
161
+ unknownVolumeLoader == null ||
162
+ typeof unknownVolumeLoader !== 'function'
163
+ ) {
164
+ throw new Error(
165
+ `No volume loader for scheme ${scheme} has been registered`
166
+ );
150
167
  }
151
168
 
152
- throw new Error(
153
- 'loadVolumeFromVolumeLoader: no volume loader for volumeId'
154
- );
169
+ loader = unknownVolumeLoader;
155
170
  }
156
171
 
157
172
  const volumeLoadObject = loader(volumeId, options);
158
173
 
174
+ setupCacheOptimizationEventListener(volumeId);
175
+
159
176
  // Broadcast a volume loaded event once the image is loaded
160
177
  volumeLoadObject.promise.then(
161
178
  function (volume) {
@@ -186,7 +203,7 @@ function loadVolumeFromVolumeLoader(
186
203
  export function loadVolume(
187
204
  volumeId: string,
188
205
  options: VolumeLoaderOptions = { imageIds: [] }
189
- ): Promise<Types.IImageVolume> {
206
+ ): Promise<IImageVolume> {
190
207
  if (volumeId === undefined) {
191
208
  throw new Error('loadVolume: parameter volumeId must not be undefined');
192
209
  }
@@ -199,7 +216,7 @@ export function loadVolume(
199
216
 
200
217
  volumeLoadObject = loadVolumeFromVolumeLoader(volumeId, options);
201
218
 
202
- return volumeLoadObject.promise.then((volume: Types.IImageVolume) => {
219
+ return volumeLoadObject.promise.then((volume: IImageVolume) => {
203
220
  volume.imageData = createInternalVTKRepresentation(volume);
204
221
  return volume;
205
222
  });
@@ -232,7 +249,7 @@ export async function createAndCacheVolume(
232
249
 
233
250
  volumeLoadObject = loadVolumeFromVolumeLoader(volumeId, options);
234
251
 
235
- volumeLoadObject.promise.then((volume: Types.IImageVolume) => {
252
+ volumeLoadObject.promise.then((volume: IImageVolume) => {
236
253
  volume.imageData = createInternalVTKRepresentation(volume);
237
254
  });
238
255
 
@@ -258,7 +275,7 @@ export async function createAndCacheVolume(
258
275
  export async function createAndCacheDerivedVolume(
259
276
  referencedVolumeId: string,
260
277
  options: DerivedVolumeOptions
261
- ): Promise<ImageVolume> {
278
+ ): Promise<IImageVolume> {
262
279
  const referencedVolume = cache.getVolume(referencedVolumeId);
263
280
 
264
281
  if (!referencedVolume) {
@@ -330,6 +347,7 @@ export async function createAndCacheDerivedVolume(
330
347
  scalarData: volumeScalarData,
331
348
  sizeInBytes: numBytes,
332
349
  referencedVolumeId,
350
+ imageIds: [],
333
351
  });
334
352
 
335
353
  const volumeLoadObject = {
@@ -355,7 +373,7 @@ export function createLocalVolume(
355
373
  options: LocalVolumeOptions,
356
374
  volumeId: string,
357
375
  preventCache = false
358
- ): ImageVolume {
376
+ ): IImageVolume {
359
377
  const { scalarData, metadata, dimensions, spacing, origin, direction } =
360
378
  options;
361
379
 
@@ -381,7 +399,7 @@ export function createLocalVolume(
381
399
  const cachedVolume = cache.getVolume(volumeId);
382
400
 
383
401
  if (cachedVolume) {
384
- return cachedVolume as ImageVolume;
402
+ return cachedVolume as IImageVolume;
385
403
  }
386
404
 
387
405
  const scalarLength = dimensions[0] * dimensions[1] * dimensions[2];
@@ -418,6 +436,7 @@ export function createLocalVolume(
418
436
  imageData: imageData,
419
437
  scalarData,
420
438
  sizeInBytes: numBytes,
439
+ imageIds: [],
421
440
  });
422
441
 
423
442
  if (preventCache) {
@@ -432,6 +451,78 @@ export function createLocalVolume(
432
451
  return derivedVolume;
433
452
  }
434
453
 
454
+ export async function createAndCacheVolumeFromImages(
455
+ volumeId: string,
456
+ imageIds: string[],
457
+ options: {
458
+ preventCache?: boolean;
459
+ additionalDetails?: Record<string, any>;
460
+ } = {}
461
+ ): Promise<IImageVolume> {
462
+ const { preventCache = false } = options;
463
+
464
+ if (imageIds === undefined) {
465
+ throw new Error(
466
+ 'createAndCacheVolumeFromImages: parameter imageIds must not be undefined'
467
+ );
468
+ }
469
+
470
+ if (volumeId === undefined) {
471
+ throw new Error(
472
+ 'createAndCacheVolumeFromImages: parameter volumeId must not be undefined'
473
+ );
474
+ }
475
+
476
+ const cachedVolume = cache.getVolume(volumeId);
477
+
478
+ if (cachedVolume) {
479
+ return Promise.resolve(cachedVolume);
480
+ }
481
+
482
+ const volumeProps = generateVolumePropsFromImageIds(imageIds, volumeId);
483
+
484
+ // volume is an empty volume, we need to load the data from the imageIds
485
+ // into the volume scalarData
486
+
487
+ // it is important to get the imageIds from the volumeProps
488
+ // since they are sorted
489
+ const imagePromises = volumeProps.imageIds.map((imageId, imageIdIndex) => {
490
+ const imageLoadObject = cache.getImageLoadObject(imageId);
491
+
492
+ return imageLoadObject.promise.then((image) => {
493
+ const pixelData = image.getPixelData();
494
+ const offset = imageIdIndex * image.rows * image.columns;
495
+
496
+ (volumeProps.scalarData as PixelDataTypedArray).set(pixelData, offset);
497
+ });
498
+ });
499
+
500
+ await Promise.all(imagePromises);
501
+
502
+ const volume = new ImageVolume({
503
+ ...volumeProps,
504
+ referencedImageIds: imageIds,
505
+ ...options,
506
+ });
507
+
508
+ // since we generated the volume from images, we can optimize the cache
509
+ // by replacing the pixelData of the images with a view of the volume's
510
+ // scalarData
511
+ performCacheOptimizationForVolume(volume);
512
+
513
+ const volumeLoadObject = {
514
+ promise: Promise.resolve(volume),
515
+ };
516
+
517
+ if (preventCache) {
518
+ return volumeLoadObject.promise;
519
+ }
520
+
521
+ cache.putVolumeLoadObject(volumeId, volumeLoadObject);
522
+
523
+ return volumeLoadObject.promise;
524
+ }
525
+
435
526
  /**
436
527
  * Registers an volumeLoader plugin with cornerstone for the specified scheme
437
528
  *
@@ -440,7 +531,7 @@ export function createLocalVolume(
440
531
  */
441
532
  export function registerVolumeLoader(
442
533
  scheme: string,
443
- volumeLoader: Types.VolumeLoaderFn
534
+ volumeLoader: VolumeLoaderFn
444
535
  ): void {
445
536
  volumeLoaders[scheme] = volumeLoader;
446
537
  }
@@ -458,11 +549,15 @@ export function getVolumeLoaderSchemes(): string[] {
458
549
  * @returns The previous Unknown Volume Loader
459
550
  */
460
551
  export function registerUnknownVolumeLoader(
461
- volumeLoader: Types.VolumeLoaderFn
462
- ): Types.VolumeLoaderFn | undefined {
552
+ volumeLoader: VolumeLoaderFn
553
+ ): VolumeLoaderFn | undefined {
463
554
  const oldVolumeLoader = unknownVolumeLoader;
464
555
 
465
556
  unknownVolumeLoader = volumeLoader;
466
557
 
467
558
  return oldVolumeLoader;
468
559
  }
560
+
561
+ export function getUnknownVolumeLoaderSchema(): string {
562
+ return unknownVolumeLoader.name;
563
+ }
@@ -51,6 +51,18 @@ type Cornerstone3DConfig = {
51
51
  */
52
52
  strictZSpacingForVolumeViewport: boolean;
53
53
  };
54
+ /**
55
+ * This flag controls whether to enable cache optimization or not. Basically,
56
+ * when we have a stack viewport (image stack) and we convert it to a volume
57
+ * the volume will be cached as well as the stack. However, if we can optimize this
58
+ * by going back to the image cache and create a view at the correct offset
59
+ * of the bigger volume array buffer, this will save memory. This will get enabled
60
+ * if cornerstone3D is configured to use SharedArrayBuffer, the reason is that
61
+ * when we modify the image cache then the images are referring to a different
62
+ * buffer (SharedArrayBuffer) and some systems don't support shared array
63
+ * buffers.
64
+ */
65
+ enableCacheOptimization: boolean;
54
66
  };
55
67
 
56
68
  export default Cornerstone3DConfig;
@@ -1,4 +1,4 @@
1
- import { IImageVolume, VolumeScalarData } from '../types';
1
+ import { IImageVolume, PixelDataTypedArray } from '../types';
2
2
 
3
3
  /**
4
4
  * Cornerstone ImageVolume interface. Todo: we should define new IVolume class
@@ -12,7 +12,7 @@ interface IDynamicImageVolume extends IImageVolume {
12
12
  /** Returns the number of time points */
13
13
  get numTimePoints(): number;
14
14
  /** return scalar data arrays (one per timepoint) */
15
- getScalarDataArrays(): VolumeScalarData[];
15
+ getScalarDataArrays(): PixelDataTypedArray[];
16
16
  }
17
17
 
18
18
  export default IDynamicImageVolume;
@@ -120,6 +120,12 @@ interface IImage {
120
120
 
121
121
  imageQualityStatus?: ImageQualityStatus;
122
122
  calibration?: IImageCalibration;
123
+ imageFrame?: any;
124
+
125
+ bufferView?: {
126
+ buffer: ArrayBuffer;
127
+ offset: number;
128
+ };
123
129
  }
124
130
 
125
131
  export default IImage;
@@ -1,7 +1,7 @@
1
1
  import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
2
2
  import {
3
3
  Metadata,
4
- VolumeScalarData,
4
+ PixelDataTypedArray,
5
5
  Point3,
6
6
  IImageLoadObject,
7
7
  Mat3,
@@ -49,8 +49,12 @@ interface IImageVolume {
49
49
  imageIds: Array<string>;
50
50
  /** volume referencedVolumeId (if it is derived from another volume) */
51
51
  referencedVolumeId?: string; // if volume is derived from another volume
52
+ /** volume referencedImageIds (if it is derived from set of images in the image cache) */
53
+ referencedImageIds?: Array<string>;
52
54
  /** whether the metadata for the pixel spacing is not undefined */
53
55
  hasPixelSpacing: boolean;
56
+ /** Property to store additional information */
57
+ additionalDetails?: Record<string, any>;
54
58
  /** return true if it is a 4D volume or false if it is 3D volume */
55
59
  isDynamicVolume(): boolean;
56
60
  /** method to convert the volume data in the volume cache, to separate images in the image cache */
@@ -63,7 +67,9 @@ interface IImageVolume {
63
67
  cancelLoading?: () => void;
64
68
 
65
69
  /** return the volume scalar data */
66
- getScalarData(): VolumeScalarData;
70
+ getScalarData(): PixelDataTypedArray;
71
+
72
+ convertToImageSlicesAndCache(): string[];
67
73
 
68
74
  /** return the index of a given imageId */
69
75
  getImageIdIndex(imageId: string): number;
@@ -73,6 +79,12 @@ interface IImageVolume {
73
79
 
74
80
  /** destroy the volume and make it unusable */
75
81
  destroy(): void;
82
+
83
+ /** decache */
84
+ decache?: () => void;
85
+
86
+ /** */
87
+ get imageCacheOffsetMap(): Map<string, any>;
76
88
  }
77
89
 
78
90
  export default IImageVolume;
@@ -1,6 +1,6 @@
1
- import { ImageVolume } from './../cache/classes/ImageVolume';
2
1
  import IGeometry from './IGeometry';
3
2
  import IImage from './IImage';
3
+ import IImageVolume from './IImageVolume';
4
4
 
5
5
  /**
6
6
  * ImageLoadObject interface which any imageLoader should return
@@ -19,7 +19,7 @@ export interface IImageLoadObject {
19
19
  */
20
20
  export interface IVolumeLoadObject {
21
21
  /** promise that resolves to an ImageVolume */
22
- promise: Promise<ImageVolume>;
22
+ promise: Promise<IImageVolume>;
23
23
  /** optional cancel function for loading*/
24
24
  cancelFn?: () => void;
25
25
  /** optional decache function */
@@ -1,45 +1,8 @@
1
- import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
2
- import type Point3 from './Point3';
3
- import type Metadata from './Metadata';
4
- import Mat3 from './Mat3';
5
-
6
- type VolumeScalarData = Float32Array | Uint8Array | Uint16Array | Int16Array;
1
+ import { ImageVolumeProps } from './ImageVolumeProps';
7
2
 
8
3
  /**
9
- * Cornerstone ImageVolume interface.
4
+ * Backwards compatibility for IVolume
10
5
  */
11
- interface IVolume {
12
- /** unique identifier for the volume in the cache */
13
- volumeId: string;
14
- /** volume metadata */
15
- metadata: Metadata;
16
- /** volume dimensions */
17
- dimensions: Point3;
18
- /** volume spacing */
19
- spacing: Point3;
20
- /** volume origin */
21
- origin: Point3;
22
- /** volume direction */
23
- direction: Mat3;
24
- /** volume scalarData */
25
- scalarData: VolumeScalarData | Array<VolumeScalarData>;
26
- /** volume size in bytes */
27
- sizeInBytes?: number;
28
- /** volume image data as vtkImageData */
29
- imageData?: vtkImageData;
30
- /** referencedVolumeId if volume is derived from another volume */
31
- referencedVolumeId?: string;
32
- /** volume scaling metadata */
33
- scaling?: {
34
- PT?: {
35
- // @TODO: Do these values exist?
36
- SUVlbmFactor?: number;
37
- SUVbsaFactor?: number;
38
- // accessed in ProbeTool
39
- suvbwToSuvlbm?: number;
40
- suvbwToSuvbsa?: number;
41
- };
42
- };
43
- }
6
+ type IVolume = ImageVolumeProps;
44
7
 
45
- export { IVolume as default, IVolume, VolumeScalarData };
8
+ export { IVolume };
@@ -0,0 +1,15 @@
1
+ import { VolumeProps } from '.';
2
+
3
+ /**
4
+ * ImageVolume which is considered a special case of a Volume, which is
5
+ * constructed out of set of images (imageIds). Unlike Volume which can be
6
+ * constructed from any type of volumetric data, such as nifti or nrrd,
7
+ */
8
+ interface ImageVolumeProps extends VolumeProps {
9
+ /** imageIds of the volume (if it is built of separate imageIds) */
10
+ imageIds: Array<string>;
11
+ /** if the volume is created from a stack, the imageIds of the stack */
12
+ referencedImageIds?: Array<string>;
13
+ }
14
+
15
+ export { ImageVolumeProps };
@@ -0,0 +1,57 @@
1
+ import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
2
+ import type Point3 from './Point3';
3
+ import type Metadata from './Metadata';
4
+ import Mat3 from './Mat3';
5
+ import { PixelDataTypedArray } from './PixelDataTypedArray';
6
+
7
+ /**
8
+ * Properties required to instantiate a Volume object.
9
+ * This includes all the necessary data and metadata to define
10
+ * a volume in 3D/4D space.
11
+ */
12
+ interface VolumeProps {
13
+ /** Unique identifier for the volume */
14
+ volumeId: string;
15
+
16
+ /** Metadata describing the volume */
17
+ metadata: Metadata;
18
+
19
+ /** Dimensions of the volume (width, height, depth) */
20
+ dimensions: Point3;
21
+
22
+ /** Spacing between volume points in 3D world space */
23
+ spacing: Point3;
24
+
25
+ /** Origin point of the volume in world space */
26
+ origin: Point3;
27
+
28
+ /** Direction of the volume in world space */
29
+ direction: Mat3;
30
+
31
+ /** Image data representing the volume */
32
+ imageData?: vtkImageData;
33
+
34
+ /** Scalar data representing the volume's intensity values */
35
+ scalarData: PixelDataTypedArray | Array<PixelDataTypedArray>;
36
+
37
+ /** Size of the volume data in bytes (optional) */
38
+ sizeInBytes?: number;
39
+
40
+ /** Property to store additional information */
41
+ additionalDetails?: Record<string, any>;
42
+
43
+ /** Scaling parameters if the volume contains scaled data (optional) */
44
+ scaling?: {
45
+ PT?: {
46
+ SUVlbmFactor?: number;
47
+ SUVbsaFactor?: number;
48
+ suvbwToSuvlbm?: number;
49
+ suvbwToSuvbsa?: number;
50
+ };
51
+ };
52
+
53
+ /** Optional ID of a referenced volume if this volume is derived from another */
54
+ referencedVolumeId?: string;
55
+ }
56
+
57
+ export { VolumeProps };
@@ -3,7 +3,7 @@ import type Cornerstone3DConfig from './Cornerstone3DConfig';
3
3
  import type ICamera from './ICamera';
4
4
  import type IEnabledElement from './IEnabledElement';
5
5
  import type ICache from './ICache';
6
- import type { IVolume, VolumeScalarData } from './IVolume';
6
+ import type { IVolume } from './IVolume';
7
7
  import type { VOI, VOIRange } from './voi';
8
8
  import type DisplayArea from './displayArea';
9
9
  import type ImageLoaderFn from './ImageLoaderFn';
@@ -106,6 +106,8 @@ import type {
106
106
  VideoViewportInput,
107
107
  } from './VideoViewportTypes';
108
108
  import type BoundsIJK from './BoundsIJK';
109
+ import type { ImageVolumeProps } from './ImageVolumeProps';
110
+ import type { VolumeProps } from './VolumeProps';
109
111
 
110
112
  export type {
111
113
  // config
@@ -118,9 +120,9 @@ export type {
118
120
  IEnabledElement,
119
121
  ICache,
120
122
  IVolume,
121
- VolumeScalarData,
122
123
  IViewportId,
123
124
  IImageVolume,
125
+ ImageVolumeProps,
124
126
  IDynamicImageVolume,
125
127
  IRenderingEngine,
126
128
  ScalingParameters,
@@ -216,4 +218,5 @@ export type {
216
218
  BoundsIJK,
217
219
  Color,
218
220
  ColorLUT,
221
+ VolumeProps,
219
222
  };
@@ -1,4 +1,4 @@
1
- import type { BoundsIJK, Point3, VolumeScalarData } from '../types';
1
+ import type { BoundsIJK, Point3, PixelDataTypedArray } from '../types';
2
2
 
3
3
  /**
4
4
  * This is a simple, standard interface to values associated with a voxel.
@@ -12,7 +12,7 @@ export default class VoxelManager<T> {
12
12
  ] as BoundsIJK;
13
13
 
14
14
  // Provide direct access to the underlying data, if any
15
- public scalarData: VolumeScalarData;
15
+ public scalarData: PixelDataTypedArray;
16
16
  public map: Map<number, T>;
17
17
  public sourceVoxelManager: VoxelManager<T>;
18
18
  public isInObject: (pointIPS, pointIJK) => boolean;
@@ -0,0 +1,121 @@
1
+ import cache, { ImageVolume } from '../cache';
2
+ import { Events } from '../enums';
3
+ import eventTarget from '../eventTarget';
4
+ import { getConfiguration, getShouldUseSharedArrayBuffer } from '../init';
5
+
6
+ /**
7
+ * This function will check if the cache optimization is enabled and if it is
8
+ * it will check if the created volume was derived from an already cached stack
9
+ * of images, if so it will go back to the image cache and create a view at the
10
+ * correct offset of the bigger volume array buffer, this will save memory.
11
+ *
12
+ * @param volumeId - The volumeId that will be checked for cache optimization
13
+ */
14
+ export function setupCacheOptimizationEventListener(volumeId) {
15
+ const { enableCacheOptimization } = getConfiguration();
16
+ const shouldUseSAB = getShouldUseSharedArrayBuffer();
17
+
18
+ const performOptimization = enableCacheOptimization && shouldUseSAB;
19
+ if (!performOptimization) {
20
+ return;
21
+ }
22
+
23
+ eventTarget.addEventListenerOnce(
24
+ Events.IMAGE_VOLUME_LOADING_COMPLETED,
25
+ (evt) => {
26
+ if (evt.detail.volumeId !== volumeId) {
27
+ return;
28
+ }
29
+
30
+ const volume = cache.getVolume(volumeId);
31
+
32
+ performCacheOptimizationForVolume(volume);
33
+ }
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Performs cache optimization for a volume by replacing the pixel data of each image
39
+ * in the image cache (if found) with a view of the volume's scalar data.
40
+ * @param options - The options for cache optimization.
41
+ * @param options.volumeId - The ID of the volume.
42
+ */
43
+ export function performCacheOptimizationForVolume(volume) {
44
+ if (!(volume instanceof ImageVolume)) {
45
+ return;
46
+ }
47
+
48
+ const scalarData = volume.getScalarData();
49
+
50
+ volume.imageCacheOffsetMap.size > 0
51
+ ? _processImageCacheOffsetMap(volume, scalarData)
52
+ : _processVolumeImages(volume, scalarData);
53
+ }
54
+
55
+ /**
56
+ * This function will process the volume images and replace the pixel data of each
57
+ * image in the image cache (if found) with a view of the volume's scalar data.
58
+ * This function is used when the volume is derived from an already cached stack
59
+ * of images.
60
+ *
61
+ * @param volume - The volume to process.
62
+ * @param scalarData - The scalar data to use for the volume.
63
+ */
64
+ function _processImageCacheOffsetMap(volume, scalarData) {
65
+ volume.imageCacheOffsetMap.forEach(({ offset }, imageId) => {
66
+ const image = cache.getImage(imageId);
67
+ if (!image) {
68
+ return;
69
+ }
70
+
71
+ _updateImageWithScalarDataView(image, scalarData, offset);
72
+ cache.decrementImageCacheSize(image.sizeInBytes);
73
+ });
74
+ }
75
+
76
+ /**
77
+ * This function will process the volume images and replace the pixel data of each
78
+ * image in the image cache (if found) with a view of the volume's scalar data.
79
+ * This function is used when the volume is not derived from an already cached stack
80
+ * of images.
81
+ *
82
+ * @param volume - The volume to process.
83
+ * @param scalarData - The scalar data to use for the volume.
84
+ */
85
+ function _processVolumeImages(volume, scalarData) {
86
+ volume.imageIds.forEach((imageId) => {
87
+ const image = cache.getImage(imageId);
88
+ if (!image) {
89
+ return;
90
+ }
91
+
92
+ const index = volume.getImageIdIndex(imageId);
93
+ const offset = index * image.getPixelData().byteLength;
94
+
95
+ _updateImageWithScalarDataView(image, scalarData, offset);
96
+ cache.decrementImageCacheSize(image.sizeInBytes);
97
+ });
98
+ }
99
+
100
+ function _updateImageWithScalarDataView(image, scalarData, offset) {
101
+ const pixelData = image.imageFrame
102
+ ? image.imageFrame.pixelData
103
+ : image.getPixelData();
104
+
105
+ const view = new pixelData.constructor(
106
+ scalarData.buffer,
107
+ offset,
108
+ pixelData.length
109
+ );
110
+
111
+ image.getPixelData = () => view;
112
+
113
+ if (image.imageFrame) {
114
+ image.imageFrame.pixelData = view;
115
+ }
116
+
117
+ image.bufferView = {
118
+ buffer: scalarData.buffer,
119
+ offset,
120
+ };
121
+ }