@cornerstonejs/core 1.41.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.
- package/dist/cjs/RenderingEngine/BaseVolumeViewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/StackViewport.js +2 -1
- package/dist/cjs/RenderingEngine/StackViewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/helpers/createVolumeActor.js +2 -1
- package/dist/cjs/RenderingEngine/helpers/createVolumeActor.js.map +1 -1
- package/dist/cjs/cache/cache.d.ts +6 -3
- package/dist/cjs/cache/cache.js +43 -6
- package/dist/cjs/cache/cache.js.map +1 -1
- package/dist/cjs/cache/classes/ImageVolume.d.ts +25 -5
- package/dist/cjs/cache/classes/ImageVolume.js +302 -13
- package/dist/cjs/cache/classes/ImageVolume.js.map +1 -1
- package/dist/cjs/eventTarget.d.ts +1 -0
- package/dist/cjs/eventTarget.js +13 -1
- package/dist/cjs/eventTarget.js.map +1 -1
- package/dist/cjs/init.js +2 -0
- package/dist/cjs/init.js.map +1 -1
- package/dist/cjs/loaders/imageLoader.js +6 -2
- package/dist/cjs/loaders/imageLoader.js.map +1 -1
- package/dist/cjs/loaders/volumeLoader.d.ts +12 -9
- package/dist/cjs/loaders/volumeLoader.js +50 -5
- package/dist/cjs/loaders/volumeLoader.js.map +1 -1
- package/dist/cjs/types/Cornerstone3DConfig.d.ts +1 -0
- package/dist/cjs/types/IDynamicImageVolume.d.ts +2 -2
- package/dist/cjs/types/IImage.d.ts +5 -0
- package/dist/cjs/types/IImageVolume.d.ts +7 -2
- package/dist/cjs/types/ILoadObject.d.ts +2 -2
- package/dist/cjs/types/IVolume.d.ts +3 -26
- package/dist/cjs/types/ImageVolumeProps.d.ts +6 -0
- package/dist/cjs/types/ImageVolumeProps.js +3 -0
- package/dist/cjs/types/ImageVolumeProps.js.map +1 -0
- package/dist/cjs/types/VolumeProps.d.ts +27 -0
- package/dist/cjs/types/VolumeProps.js +3 -0
- package/dist/cjs/types/VolumeProps.js.map +1 -0
- package/dist/cjs/types/index.d.ts +4 -2
- package/dist/cjs/utilities/VoxelManager.d.ts +2 -2
- package/dist/cjs/utilities/cacheUtils.d.ts +2 -0
- package/dist/cjs/utilities/cacheUtils.js +96 -0
- package/dist/cjs/utilities/cacheUtils.js.map +1 -0
- package/dist/cjs/utilities/convertStackToVolumeViewport.d.ts +12 -0
- package/dist/cjs/utilities/convertStackToVolumeViewport.js +61 -0
- package/dist/cjs/utilities/convertStackToVolumeViewport.js.map +1 -0
- package/dist/cjs/utilities/convertVolumeToStackViewport.d.ts +9 -0
- package/dist/cjs/utilities/convertVolumeToStackViewport.js +95 -0
- package/dist/cjs/utilities/convertVolumeToStackViewport.js.map +1 -0
- package/dist/cjs/utilities/createSigmoidRGBTransferFunction.js +25 -2
- package/dist/cjs/utilities/createSigmoidRGBTransferFunction.js.map +1 -1
- package/dist/cjs/utilities/generateVolumePropsFromImageIds.d.ts +3 -0
- package/dist/cjs/utilities/generateVolumePropsFromImageIds.js +124 -0
- package/dist/cjs/utilities/generateVolumePropsFromImageIds.js.map +1 -0
- package/dist/cjs/utilities/index.d.ts +6 -1
- package/dist/cjs/utilities/index.js +12 -1
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/roundNumber.d.ts +4 -0
- package/dist/cjs/utilities/roundNumber.js +36 -0
- package/dist/cjs/utilities/roundNumber.js.map +1 -0
- package/dist/esm/RenderingEngine/BaseVolumeViewport.js.map +1 -1
- package/dist/esm/RenderingEngine/StackViewport.js +2 -1
- package/dist/esm/RenderingEngine/StackViewport.js.map +1 -1
- package/dist/esm/RenderingEngine/helpers/createVolumeActor.js +1 -1
- package/dist/esm/RenderingEngine/helpers/createVolumeActor.js.map +1 -1
- package/dist/esm/cache/cache.js +43 -6
- package/dist/esm/cache/cache.js.map +1 -1
- package/dist/esm/cache/classes/ImageVolume.js +279 -14
- package/dist/esm/cache/classes/ImageVolume.js.map +1 -1
- package/dist/esm/eventTarget.js +13 -1
- package/dist/esm/eventTarget.js.map +1 -1
- package/dist/esm/init.js +2 -0
- package/dist/esm/init.js.map +1 -1
- package/dist/esm/loaders/imageLoader.js +5 -2
- package/dist/esm/loaders/imageLoader.js.map +1 -1
- package/dist/esm/loaders/volumeLoader.js +50 -5
- package/dist/esm/loaders/volumeLoader.js.map +1 -1
- package/dist/esm/types/ImageVolumeProps.js +2 -0
- package/dist/esm/types/ImageVolumeProps.js.map +1 -0
- package/dist/esm/types/VolumeProps.js +2 -0
- package/dist/esm/types/VolumeProps.js.map +1 -0
- package/dist/esm/utilities/cacheUtils.js +65 -0
- package/dist/esm/utilities/cacheUtils.js.map +1 -0
- package/dist/esm/utilities/convertStackToVolumeViewport.js +49 -0
- package/dist/esm/utilities/convertStackToVolumeViewport.js.map +1 -0
- package/dist/esm/utilities/convertVolumeToStackViewport.js +58 -0
- package/dist/esm/utilities/convertVolumeToStackViewport.js.map +1 -0
- package/dist/esm/utilities/createSigmoidRGBTransferFunction.js +1 -1
- package/dist/esm/utilities/createSigmoidRGBTransferFunction.js.map +1 -1
- package/dist/esm/utilities/generateVolumePropsFromImageIds.js +118 -0
- package/dist/esm/utilities/generateVolumePropsFromImageIds.js.map +1 -0
- package/dist/esm/utilities/index.js +6 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/roundNumber.js +33 -0
- package/dist/esm/utilities/roundNumber.js.map +1 -0
- package/dist/types/RenderingEngine/BaseVolumeViewport.d.ts.map +1 -1
- package/dist/types/RenderingEngine/StackViewport.d.ts.map +1 -1
- package/dist/types/cache/cache.d.ts +6 -3
- package/dist/types/cache/cache.d.ts.map +1 -1
- package/dist/types/cache/classes/ImageVolume.d.ts +25 -5
- package/dist/types/cache/classes/ImageVolume.d.ts.map +1 -1
- package/dist/types/eventTarget.d.ts +1 -0
- package/dist/types/eventTarget.d.ts.map +1 -1
- package/dist/types/loaders/imageLoader.d.ts.map +1 -1
- package/dist/types/loaders/volumeLoader.d.ts +12 -9
- package/dist/types/loaders/volumeLoader.d.ts.map +1 -1
- package/dist/types/types/Cornerstone3DConfig.d.ts +1 -0
- package/dist/types/types/Cornerstone3DConfig.d.ts.map +1 -1
- package/dist/types/types/IDynamicImageVolume.d.ts +2 -2
- package/dist/types/types/IDynamicImageVolume.d.ts.map +1 -1
- package/dist/types/types/IImage.d.ts +5 -0
- package/dist/types/types/IImage.d.ts.map +1 -1
- package/dist/types/types/IImageVolume.d.ts +7 -2
- package/dist/types/types/IImageVolume.d.ts.map +1 -1
- package/dist/types/types/ILoadObject.d.ts +2 -2
- package/dist/types/types/ILoadObject.d.ts.map +1 -1
- package/dist/types/types/IVolume.d.ts +3 -26
- package/dist/types/types/IVolume.d.ts.map +1 -1
- package/dist/types/types/ImageVolumeProps.d.ts +7 -0
- package/dist/types/types/ImageVolumeProps.d.ts.map +1 -0
- package/dist/types/types/VolumeProps.d.ts +28 -0
- package/dist/types/types/VolumeProps.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +4 -2
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/utilities/VoxelManager.d.ts +2 -2
- package/dist/types/utilities/VoxelManager.d.ts.map +1 -1
- package/dist/types/utilities/cacheUtils.d.ts +3 -0
- package/dist/types/utilities/cacheUtils.d.ts.map +1 -0
- package/dist/types/utilities/convertStackToVolumeViewport.d.ts +13 -0
- package/dist/types/utilities/convertStackToVolumeViewport.d.ts.map +1 -0
- package/dist/types/utilities/convertVolumeToStackViewport.d.ts +10 -0
- package/dist/types/utilities/convertVolumeToStackViewport.d.ts.map +1 -0
- package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts +4 -0
- package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts.map +1 -0
- package/dist/types/utilities/index.d.ts +6 -1
- package/dist/types/utilities/index.d.ts.map +1 -1
- package/dist/types/utilities/roundNumber.d.ts +5 -0
- package/dist/types/utilities/roundNumber.d.ts.map +1 -0
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +2 -2
- package/src/RenderingEngine/BaseVolumeViewport.ts +0 -1
- package/src/RenderingEngine/StackViewport.ts +3 -1
- package/src/RenderingEngine/helpers/createVolumeActor.ts +1 -1
- package/src/cache/cache.ts +91 -7
- package/src/cache/classes/ImageVolume.ts +535 -21
- package/src/eventTarget.ts +19 -1
- package/src/init.ts +2 -2
- package/src/loaders/imageLoader.ts +6 -2
- package/src/loaders/volumeLoader.ts +118 -23
- package/src/types/Cornerstone3DConfig.ts +12 -0
- package/src/types/IDynamicImageVolume.ts +2 -2
- package/src/types/IImage.ts +6 -0
- package/src/types/IImageVolume.ts +14 -2
- package/src/types/ILoadObject.ts +2 -2
- package/src/types/IVolume.ts +4 -41
- package/src/types/ImageVolumeProps.ts +15 -0
- package/src/types/VolumeProps.ts +57 -0
- package/src/types/index.ts +5 -2
- package/src/utilities/VoxelManager.ts +2 -2
- package/src/utilities/cacheUtils.ts +121 -0
- package/src/utilities/convertStackToVolumeViewport.ts +115 -0
- package/src/utilities/convertVolumeToStackViewport.ts +125 -0
- package/src/utilities/createSigmoidRGBTransferFunction.ts +1 -1
- package/src/utilities/generateVolumePropsFromImageIds.ts +183 -0
- package/src/utilities/index.ts +11 -0
- 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 {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 = (<
|
|
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
|
-
):
|
|
154
|
+
): IVolumeLoadObject {
|
|
143
155
|
const colonIndex = volumeId.indexOf(':');
|
|
144
156
|
const scheme = volumeId.substring(0, colonIndex);
|
|
145
|
-
|
|
157
|
+
let loader = volumeLoaders[scheme];
|
|
146
158
|
|
|
147
159
|
if (loader === undefined || loader === null) {
|
|
148
|
-
if (
|
|
149
|
-
|
|
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
|
-
|
|
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<
|
|
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:
|
|
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:
|
|
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<
|
|
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
|
-
):
|
|
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
|
|
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:
|
|
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:
|
|
462
|
-
):
|
|
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,
|
|
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():
|
|
15
|
+
getScalarDataArrays(): PixelDataTypedArray[];
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export default IDynamicImageVolume;
|
package/src/types/IImage.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
2
2
|
import {
|
|
3
3
|
Metadata,
|
|
4
|
-
|
|
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():
|
|
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;
|
package/src/types/ILoadObject.ts
CHANGED
|
@@ -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<
|
|
22
|
+
promise: Promise<IImageVolume>;
|
|
23
23
|
/** optional cancel function for loading*/
|
|
24
24
|
cancelFn?: () => void;
|
|
25
25
|
/** optional decache function */
|
package/src/types/IVolume.ts
CHANGED
|
@@ -1,45 +1,8 @@
|
|
|
1
|
-
import
|
|
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
|
-
*
|
|
4
|
+
* Backwards compatibility for IVolume
|
|
10
5
|
*/
|
|
11
|
-
|
|
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
|
|
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 };
|
package/src/types/index.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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:
|
|
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
|
+
}
|