@cornerstonejs/core 2.0.0-beta.19 → 2.0.0-beta.20
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/esm/RenderingEngine/BaseVolumeViewport.d.ts +1 -1
- package/dist/esm/RenderingEngine/BaseVolumeViewport.js +3 -2
- package/dist/esm/RenderingEngine/StackViewport.d.ts +2 -2
- package/dist/esm/RenderingEngine/StackViewport.js +61 -10
- package/dist/esm/RenderingEngine/VideoViewport.d.ts +1 -1
- package/dist/esm/RenderingEngine/VideoViewport.js +2 -2
- package/dist/esm/RenderingEngine/Viewport.d.ts +2 -2
- package/dist/esm/RenderingEngine/Viewport.js +1 -1
- package/dist/esm/cache/cache.d.ts +2 -0
- package/dist/esm/cache/cache.js +52 -24
- package/dist/esm/cache/classes/ImageVolume.d.ts +0 -1
- package/dist/esm/cache/classes/ImageVolume.js +61 -70
- package/dist/esm/loaders/imageLoader.js +2 -1
- package/dist/esm/loaders/volumeLoader.js +11 -1
- package/dist/esm/types/CPUIImageData.d.ts +1 -0
- package/dist/esm/types/IImage.d.ts +1 -0
- package/dist/esm/types/IImageData.d.ts +1 -0
- package/dist/esm/types/IViewport.d.ts +4 -3
- package/dist/esm/utilities/cacheUtils.js +38 -30
- package/dist/esm/utilities/convertStackToVolumeViewport.js +2 -6
- package/dist/esm/utilities/convertVolumeToStackViewport.js +3 -29
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -62,7 +62,7 @@ declare abstract class BaseVolumeViewport extends Viewport implements IVolumeVie
|
|
|
62
62
|
getImageIds: (volumeId?: string) => Array<string>;
|
|
63
63
|
abstract getCurrentImageId(): string;
|
|
64
64
|
protected getVolumeId(specifier?: ViewReferenceSpecifier): string;
|
|
65
|
-
|
|
65
|
+
getViewReferenceId(specifier?: ViewReferenceSpecifier): string;
|
|
66
66
|
abstract setBlendMode(blendMode: BlendModes, filterActorUIDs?: Array<string>, immediate?: boolean): void;
|
|
67
67
|
abstract setSlabThickness(slabThickness: number, filterActorUIDs?: Array<string>): void;
|
|
68
68
|
abstract resetSlabThickness(): void;
|
|
@@ -713,7 +713,7 @@ class BaseVolumeViewport extends Viewport {
|
|
|
713
713
|
initializeColorTransferFunction(volumeInputArray) {
|
|
714
714
|
const selectedVolumeId = volumeInputArray[0].volumeId;
|
|
715
715
|
const colorTransferFunction = this._getOrCreateColorTransferFunction(selectedVolumeId);
|
|
716
|
-
if (!this.initialTransferFunctionNodes) {
|
|
716
|
+
if (!this.initialTransferFunctionNodes && colorTransferFunction) {
|
|
717
717
|
this.initialTransferFunctionNodes = getTransferFunctionNodes(colorTransferFunction);
|
|
718
718
|
}
|
|
719
719
|
}
|
|
@@ -788,6 +788,7 @@ class BaseVolumeViewport extends Viewport {
|
|
|
788
788
|
imageData: actor.getMapper().getInputData(),
|
|
789
789
|
metadata: {
|
|
790
790
|
Modality: volume?.metadata?.Modality,
|
|
791
|
+
FrameOfReferenceUID: volume?.metadata?.FrameOfReferenceUID,
|
|
791
792
|
},
|
|
792
793
|
scaling: volume?.scaling,
|
|
793
794
|
hasPixelSpacing: true,
|
|
@@ -853,7 +854,7 @@ class BaseVolumeViewport extends Viewport {
|
|
|
853
854
|
return actorEntries.find((actorEntry) => actorEntry.actor.getClassName() === 'vtkVolume' &&
|
|
854
855
|
actorEntry.uid === specifier.volumeId)?.uid;
|
|
855
856
|
}
|
|
856
|
-
|
|
857
|
+
getViewReferenceId(specifier = {}) {
|
|
857
858
|
let { volumeId, sliceIndex: sliceIndex } = specifier;
|
|
858
859
|
if (!volumeId) {
|
|
859
860
|
const actorEntries = this.getActors();
|
|
@@ -178,10 +178,10 @@ declare class StackViewport extends Viewport implements IStackViewport, IImagesL
|
|
|
178
178
|
private _getValidVOILUTFunction;
|
|
179
179
|
getCurrentImageIdIndex: () => number;
|
|
180
180
|
getSliceIndex: () => number;
|
|
181
|
-
isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean;
|
|
181
|
+
isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean | unknown;
|
|
182
182
|
getViewReference(viewRefSpecifier?: ViewReferenceSpecifier): ViewReference;
|
|
183
183
|
setViewReference(viewRef: ViewReference): void;
|
|
184
|
-
|
|
184
|
+
getViewReferenceId(specifier?: ViewReferenceSpecifier): string;
|
|
185
185
|
getTargetImageIdIndex: () => number;
|
|
186
186
|
getImageIds: () => Array<string>;
|
|
187
187
|
getCurrentImageId: () => string;
|
|
@@ -440,7 +440,10 @@ class StackViewport extends Viewport {
|
|
|
440
440
|
direction: vtkImageData.getDirection(),
|
|
441
441
|
scalarData: vtkImageData.getPointData().getScalars().getData(),
|
|
442
442
|
imageData: actor.getMapper().getInputData(),
|
|
443
|
-
metadata: {
|
|
443
|
+
metadata: {
|
|
444
|
+
Modality: this.modality,
|
|
445
|
+
FrameOfReferenceUID: this.getFrameOfReferenceUID(),
|
|
446
|
+
},
|
|
444
447
|
scaling: this.scaling,
|
|
445
448
|
hasPixelSpacing: this.hasPixelSpacing,
|
|
446
449
|
calibration: { ...this.csImage.calibration, ...this.calibration },
|
|
@@ -457,7 +460,10 @@ class StackViewport extends Viewport {
|
|
|
457
460
|
spacing,
|
|
458
461
|
origin: metadata.origin,
|
|
459
462
|
direction: metadata.direction,
|
|
460
|
-
metadata: {
|
|
463
|
+
metadata: {
|
|
464
|
+
Modality: this.modality,
|
|
465
|
+
FrameOfReferenceUID: this.getFrameOfReferenceUID(),
|
|
466
|
+
},
|
|
461
467
|
scaling: this.scaling,
|
|
462
468
|
imageData: {
|
|
463
469
|
getDirection: () => metadata.direction,
|
|
@@ -1352,7 +1358,8 @@ class StackViewport extends Viewport {
|
|
|
1352
1358
|
addImages(stackInputs) {
|
|
1353
1359
|
const actors = this.getActors();
|
|
1354
1360
|
stackInputs.forEach((stackInput) => {
|
|
1355
|
-
const
|
|
1361
|
+
const { imageId } = stackInput;
|
|
1362
|
+
const image = cache.getImage(imageId);
|
|
1356
1363
|
const { origin, dimensions, direction, spacing, numComps } = this.getImageDataMetadata(image);
|
|
1357
1364
|
const imagedata = this.createVTKImageData({
|
|
1358
1365
|
origin,
|
|
@@ -1364,7 +1371,11 @@ class StackViewport extends Viewport {
|
|
|
1364
1371
|
});
|
|
1365
1372
|
const imageActor = this.createActorMapper(imagedata);
|
|
1366
1373
|
if (imageActor) {
|
|
1367
|
-
actors.push({
|
|
1374
|
+
actors.push({
|
|
1375
|
+
uid: stackInput.actorUID,
|
|
1376
|
+
actor: imageActor,
|
|
1377
|
+
referencedId: imageId,
|
|
1378
|
+
});
|
|
1368
1379
|
if (stackInput.callback) {
|
|
1369
1380
|
stackInput.callback({ imageActor, imageId: stackInput.imageId });
|
|
1370
1381
|
}
|
|
@@ -1624,22 +1635,62 @@ class StackViewport extends Viewport {
|
|
|
1624
1635
|
if (!super.isReferenceViewable(viewRef, options)) {
|
|
1625
1636
|
return false;
|
|
1626
1637
|
}
|
|
1627
|
-
let { imageURI } = options;
|
|
1628
1638
|
const { referencedImageId, sliceIndex } = viewRef;
|
|
1629
1639
|
if (viewRef.volumeId && !referencedImageId) {
|
|
1630
1640
|
return options.asVolume === true;
|
|
1631
1641
|
}
|
|
1632
1642
|
let testIndex = this.getCurrentImageIdIndex();
|
|
1643
|
+
const currentImageId = this.imageIds[testIndex];
|
|
1633
1644
|
if (options.withNavigation && typeof sliceIndex === 'number') {
|
|
1634
1645
|
testIndex = sliceIndex;
|
|
1635
1646
|
}
|
|
1636
|
-
|
|
1637
|
-
if (!imageId) {
|
|
1647
|
+
if (!currentImageId) {
|
|
1638
1648
|
return false;
|
|
1639
1649
|
}
|
|
1650
|
+
if (options.asOverlay && referencedImageId) {
|
|
1651
|
+
const matchImagesForOverlay = (targetImageId) => {
|
|
1652
|
+
const referenceImagePlaneModule = metaData.get(MetadataModules.IMAGE_PLANE, referencedImageId);
|
|
1653
|
+
const currentImagePlaneModule = metaData.get(MetadataModules.IMAGE_PLANE, targetImageId);
|
|
1654
|
+
const referenceOrientation = referenceImagePlaneModule.imageOrientationPatient;
|
|
1655
|
+
const currentOrientation = currentImagePlaneModule.imageOrientationPatient;
|
|
1656
|
+
if (referenceOrientation && currentOrientation) {
|
|
1657
|
+
const closeEnough = isEqual(referenceImagePlaneModule.imageOrientationPatient, currentImagePlaneModule.imageOrientationPatient);
|
|
1658
|
+
if (closeEnough) {
|
|
1659
|
+
const referencePosition = referenceImagePlaneModule.imagePositionPatient;
|
|
1660
|
+
const currentPosition = currentImagePlaneModule.imagePositionPatient;
|
|
1661
|
+
if (referencePosition && currentPosition) {
|
|
1662
|
+
const vector = vec3.create();
|
|
1663
|
+
vec3.subtract(vector, currentPosition, referencePosition);
|
|
1664
|
+
const viewPlaneNormal = vec3.create();
|
|
1665
|
+
vec3.cross(viewPlaneNormal, currentOrientation.slice(0, 3), currentOrientation.slice(3, 6));
|
|
1666
|
+
const dotProduct = vec3.dot(vector, viewPlaneNormal);
|
|
1667
|
+
const isOrthogonal = Math.abs(dotProduct) < EPSILON;
|
|
1668
|
+
if (isOrthogonal) {
|
|
1669
|
+
return targetImageId;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
else {
|
|
1675
|
+
const referenceRows = referenceImagePlaneModule.rows;
|
|
1676
|
+
const referenceColumns = referenceImagePlaneModule.columns;
|
|
1677
|
+
const currentRows = currentImagePlaneModule.rows;
|
|
1678
|
+
const currentColumns = currentImagePlaneModule.columns;
|
|
1679
|
+
if (referenceRows === currentRows &&
|
|
1680
|
+
referenceColumns === currentColumns) {
|
|
1681
|
+
return targetImageId;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
const matchedImageId = matchImagesForOverlay(currentImageId);
|
|
1686
|
+
if (matchedImageId) {
|
|
1687
|
+
return matchedImageId;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
let { imageURI } = options;
|
|
1640
1691
|
if (!imageURI) {
|
|
1641
|
-
const colonIndex =
|
|
1642
|
-
imageURI =
|
|
1692
|
+
const colonIndex = currentImageId.indexOf(':');
|
|
1693
|
+
imageURI = currentImageId.substring(colonIndex + 1);
|
|
1643
1694
|
}
|
|
1644
1695
|
return referencedImageId?.endsWith(imageURI);
|
|
1645
1696
|
}
|
|
@@ -1680,7 +1731,7 @@ class StackViewport extends Viewport {
|
|
|
1680
1731
|
}
|
|
1681
1732
|
}
|
|
1682
1733
|
}
|
|
1683
|
-
|
|
1734
|
+
getViewReferenceId(specifier = {}) {
|
|
1684
1735
|
const { sliceIndex: sliceIndex = this.currentImageIdIndex } = specifier;
|
|
1685
1736
|
if (Array.isArray(sliceIndex)) {
|
|
1686
1737
|
throw new Error('Use of slice ranges for stacks not supported');
|
|
@@ -100,7 +100,7 @@ declare class VideoViewport extends Viewport implements IVideoViewport {
|
|
|
100
100
|
protected setColorTransform(): void;
|
|
101
101
|
setCamera(camera: ICamera): void;
|
|
102
102
|
getCurrentImageId(): string;
|
|
103
|
-
|
|
103
|
+
getViewReferenceId(specifier?: ViewReferenceSpecifier): string;
|
|
104
104
|
isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean;
|
|
105
105
|
getViewReference(viewRefSpecifier?: ViewReferenceSpecifier): ViewReference;
|
|
106
106
|
getFrameNumber(): number;
|
|
@@ -537,7 +537,7 @@ class VideoViewport extends Viewport {
|
|
|
537
537
|
: `/frames/${this.getFrameNumber()}`);
|
|
538
538
|
return current;
|
|
539
539
|
}
|
|
540
|
-
|
|
540
|
+
getViewReferenceId(specifier = {}) {
|
|
541
541
|
const { sliceIndex: sliceIndex } = specifier;
|
|
542
542
|
if (sliceIndex === undefined) {
|
|
543
543
|
return `videoId:${this.getCurrentImageId()}`;
|
|
@@ -589,7 +589,7 @@ class VideoViewport extends Viewport {
|
|
|
589
589
|
}
|
|
590
590
|
return {
|
|
591
591
|
...super.getViewReference(viewRefSpecifier),
|
|
592
|
-
referencedImageId: this.
|
|
592
|
+
referencedImageId: this.getViewReferenceId(viewRefSpecifier),
|
|
593
593
|
sliceIndex: sliceIndex,
|
|
594
594
|
};
|
|
595
595
|
}
|
|
@@ -84,7 +84,7 @@ declare class Viewport implements IViewport {
|
|
|
84
84
|
getPan(initialCamera?: ICamera): Point2;
|
|
85
85
|
getCurrentImageIdIndex(): number;
|
|
86
86
|
getSliceIndex(): number;
|
|
87
|
-
|
|
87
|
+
getViewReferenceId(_specifier?: ViewReferenceSpecifier): string;
|
|
88
88
|
setPan(pan: Point2, storeAsInitialCamera?: boolean): void;
|
|
89
89
|
getZoom(compareCamera?: ICamera): number;
|
|
90
90
|
setZoom(value: number, storeAsInitialCamera?: boolean): void;
|
|
@@ -101,7 +101,7 @@ declare class Viewport implements IViewport {
|
|
|
101
101
|
getClippingPlanesForActor(actorEntry?: ActorEntry): vtkPlane[];
|
|
102
102
|
private _getWorldDistanceViewUpAndViewRight;
|
|
103
103
|
getViewReference(viewRefSpecifier?: ViewReferenceSpecifier): ViewReference;
|
|
104
|
-
isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean;
|
|
104
|
+
isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean | unknown;
|
|
105
105
|
getViewPresentation(viewPresSel?: ViewPresentationSelector): ViewPresentation;
|
|
106
106
|
setViewReference(viewRef: ViewReference): void;
|
|
107
107
|
setViewPresentation(viewPres: ViewPresentation): void;
|
|
@@ -19,7 +19,9 @@ declare class Cache implements ICache {
|
|
|
19
19
|
purgeCache: () => void;
|
|
20
20
|
purgeVolumeCache: () => void;
|
|
21
21
|
decacheIfNecessaryUntilBytesAvailable(numBytes: number, volumeImageIds?: Array<string>): number | undefined;
|
|
22
|
+
private _putImageCommon;
|
|
22
23
|
putImageLoadObject(imageId: string, imageLoadObject: IImageLoadObject): Promise<any>;
|
|
24
|
+
putImageSync(imageId: string, image: IImage): void;
|
|
23
25
|
getImageLoadObject(imageId: string): IImageLoadObject;
|
|
24
26
|
isLoaded(imageId: string): boolean;
|
|
25
27
|
getVolumeContainingImageId(imageId: string): {
|
package/dist/esm/cache/cache.js
CHANGED
|
@@ -265,6 +265,31 @@ class Cache {
|
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
|
+
_putImageCommon(imageId, image, cachedImage) {
|
|
269
|
+
if (!this._imageCache.get(imageId)) {
|
|
270
|
+
console.warn('The image was purged from the cache before it completed loading.');
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (image.sizeInBytes === undefined || Number.isNaN(image.sizeInBytes)) {
|
|
274
|
+
throw new Error('_putImageCommon: image.sizeInBytes must not be undefined');
|
|
275
|
+
}
|
|
276
|
+
if (image.sizeInBytes.toFixed === undefined) {
|
|
277
|
+
throw new Error('_putImageCommon: image.sizeInBytes is not a number');
|
|
278
|
+
}
|
|
279
|
+
if (!this.isCacheable(image.sizeInBytes)) {
|
|
280
|
+
throw new Error(Events.CACHE_SIZE_EXCEEDED);
|
|
281
|
+
}
|
|
282
|
+
this.decacheIfNecessaryUntilBytesAvailable(image.sizeInBytes);
|
|
283
|
+
cachedImage.loaded = true;
|
|
284
|
+
cachedImage.image = image;
|
|
285
|
+
cachedImage.sizeInBytes = image.sizeInBytes;
|
|
286
|
+
this.incrementImageCacheSize(cachedImage.sizeInBytes);
|
|
287
|
+
const eventDetails = {
|
|
288
|
+
image: cachedImage,
|
|
289
|
+
};
|
|
290
|
+
triggerEvent(eventTarget, Events.IMAGE_CACHE_IMAGE_ADDED, eventDetails);
|
|
291
|
+
cachedImage.sharedCacheKey = image.sharedCacheKey;
|
|
292
|
+
}
|
|
268
293
|
putImageLoadObject(imageId, imageLoadObject) {
|
|
269
294
|
if (imageId === undefined) {
|
|
270
295
|
throw new Error('putImageLoadObject: imageId must not be undefined');
|
|
@@ -290,36 +315,39 @@ class Cache {
|
|
|
290
315
|
this._imageCache.set(imageId, cachedImage);
|
|
291
316
|
return imageLoadObject.promise
|
|
292
317
|
.then((image) => {
|
|
293
|
-
|
|
294
|
-
console.warn('The image was purged from the cache before it completed loading.');
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
if (image.sizeInBytes === undefined ||
|
|
298
|
-
Number.isNaN(image.sizeInBytes)) {
|
|
299
|
-
throw new Error('putImageLoadObject: image.sizeInBytes must not be undefined');
|
|
300
|
-
}
|
|
301
|
-
if (image.sizeInBytes.toFixed === undefined) {
|
|
302
|
-
throw new Error('putImageLoadObject: image.sizeInBytes is not a number');
|
|
303
|
-
}
|
|
304
|
-
if (!this.isCacheable(image.sizeInBytes)) {
|
|
305
|
-
throw new Error(Events.CACHE_SIZE_EXCEEDED);
|
|
306
|
-
}
|
|
307
|
-
this.decacheIfNecessaryUntilBytesAvailable(image.sizeInBytes);
|
|
308
|
-
cachedImage.loaded = true;
|
|
309
|
-
cachedImage.image = image;
|
|
310
|
-
cachedImage.sizeInBytes = image.sizeInBytes;
|
|
311
|
-
this.incrementImageCacheSize(cachedImage.sizeInBytes);
|
|
312
|
-
const eventDetails = {
|
|
313
|
-
image: cachedImage,
|
|
314
|
-
};
|
|
315
|
-
triggerEvent(eventTarget, Events.IMAGE_CACHE_IMAGE_ADDED, eventDetails);
|
|
316
|
-
cachedImage.sharedCacheKey = image.sharedCacheKey;
|
|
318
|
+
this._putImageCommon(imageId, image, cachedImage);
|
|
317
319
|
})
|
|
318
320
|
.catch((error) => {
|
|
319
321
|
this._imageCache.delete(imageId);
|
|
320
322
|
throw error;
|
|
321
323
|
});
|
|
322
324
|
}
|
|
325
|
+
putImageSync(imageId, image) {
|
|
326
|
+
if (imageId === undefined) {
|
|
327
|
+
throw new Error('putImageSync: imageId must not be undefined');
|
|
328
|
+
}
|
|
329
|
+
if (this._imageCache.has(imageId)) {
|
|
330
|
+
throw new Error('putImageSync: imageId already in cache');
|
|
331
|
+
}
|
|
332
|
+
const cachedImage = {
|
|
333
|
+
loaded: false,
|
|
334
|
+
imageId,
|
|
335
|
+
sharedCacheKey: undefined,
|
|
336
|
+
imageLoadObject: {
|
|
337
|
+
promise: Promise.resolve(image),
|
|
338
|
+
},
|
|
339
|
+
timeStamp: Date.now(),
|
|
340
|
+
sizeInBytes: 0,
|
|
341
|
+
};
|
|
342
|
+
this._imageCache.set(imageId, cachedImage);
|
|
343
|
+
try {
|
|
344
|
+
this._putImageCommon(imageId, image, cachedImage);
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
this._imageCache.delete(imageId);
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
323
351
|
getImageLoadObject(imageId) {
|
|
324
352
|
if (imageId === undefined) {
|
|
325
353
|
throw new Error('getImageLoadObject: imageId must not be undefined');
|
|
@@ -54,7 +54,6 @@ export declare class ImageVolume implements IImageVolume {
|
|
|
54
54
|
protected getScalarDataByImageIdIndex(imageIdIndex: number): PixelDataTypedArray;
|
|
55
55
|
getCornerstoneImage(imageId: string, imageIdIndex: number): IImage;
|
|
56
56
|
protected imageIdIndexToFrameIndex(imageIdIndex: number): number;
|
|
57
|
-
convertToCornerstoneImage(imageId: string, imageIdIndex: number): IImageLoadObject;
|
|
58
57
|
getCornerstoneImageLoadObject(imageId: string, imageIdIndex: number): IImageLoadObject;
|
|
59
58
|
getCornerstoneImages(): IImage[];
|
|
60
59
|
convertToImageSlicesAndCache(): string[];
|
|
@@ -120,6 +120,15 @@ export class ImageVolume {
|
|
|
120
120
|
}
|
|
121
121
|
else {
|
|
122
122
|
this.convertToImageSlicesAndCache();
|
|
123
|
+
const otherVolumes = cache.filterVolumesByReferenceId(this.volumeId);
|
|
124
|
+
if (otherVolumes.length) {
|
|
125
|
+
otherVolumes.forEach((volume) => {
|
|
126
|
+
volume.referencedImageIds = this.imageIds;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (completelyRemove) {
|
|
130
|
+
this.removeFromCache();
|
|
131
|
+
}
|
|
123
132
|
}
|
|
124
133
|
}
|
|
125
134
|
removeFromCache() {
|
|
@@ -207,7 +216,24 @@ export class ImageVolume {
|
|
|
207
216
|
const intercept = modalityLutModule.rescaleIntercept
|
|
208
217
|
? modalityLutModule.rescaleIntercept
|
|
209
218
|
: 0;
|
|
210
|
-
|
|
219
|
+
const imageOrientationPatient = [
|
|
220
|
+
this.direction[0],
|
|
221
|
+
this.direction[1],
|
|
222
|
+
this.direction[2],
|
|
223
|
+
this.direction[3],
|
|
224
|
+
this.direction[4],
|
|
225
|
+
this.direction[5],
|
|
226
|
+
];
|
|
227
|
+
const precision = 6;
|
|
228
|
+
const imagePositionPatient = [
|
|
229
|
+
parseFloat((this.origin[0] +
|
|
230
|
+
imageIdIndex * this.direction[6] * this.spacing[0]).toFixed(precision)),
|
|
231
|
+
parseFloat((this.origin[1] +
|
|
232
|
+
imageIdIndex * this.direction[7] * this.spacing[1]).toFixed(precision)),
|
|
233
|
+
parseFloat((this.origin[2] +
|
|
234
|
+
imageIdIndex * this.direction[8] * this.spacing[2]).toFixed(precision)),
|
|
235
|
+
];
|
|
236
|
+
const image = {
|
|
211
237
|
imageId,
|
|
212
238
|
intercept,
|
|
213
239
|
windowCenter,
|
|
@@ -233,13 +259,43 @@ export class ImageVolume {
|
|
|
233
259
|
invert,
|
|
234
260
|
photometricInterpretation,
|
|
235
261
|
};
|
|
262
|
+
const pixelData = image.getPixelData();
|
|
263
|
+
const bitsAllocated = pixelData.BYTES_PER_ELEMENT * 8;
|
|
264
|
+
const imagePixelModule = {
|
|
265
|
+
bitsAllocated,
|
|
266
|
+
photometricInterpretation: image.photometricInterpretation,
|
|
267
|
+
windowWidth: image.windowWidth,
|
|
268
|
+
windowCenter: image.windowCenter,
|
|
269
|
+
voiLUTFunction: image.voiLUTFunction,
|
|
270
|
+
};
|
|
271
|
+
const imagePlaneModule = {
|
|
272
|
+
rowCosines: [this.direction[0], this.direction[1], this.direction[2]],
|
|
273
|
+
columnCosines: [this.direction[3], this.direction[4], this.direction[5]],
|
|
274
|
+
pixelSpacing: [this.spacing[0], this.spacing[1]],
|
|
275
|
+
imageOrientationPatient: imageOrientationPatient,
|
|
276
|
+
imagePositionPatient: imagePositionPatient,
|
|
277
|
+
columnPixelSpacing: image.columnPixelSpacing,
|
|
278
|
+
rowPixelSpacing: image.rowPixelSpacing,
|
|
279
|
+
columns: image.columns,
|
|
280
|
+
rows: image.rows,
|
|
281
|
+
};
|
|
282
|
+
const generalSeriesModule = {};
|
|
283
|
+
const metadata = {
|
|
284
|
+
imagePixelModule,
|
|
285
|
+
imagePlaneModule,
|
|
286
|
+
generalSeriesModule,
|
|
287
|
+
};
|
|
288
|
+
['imagePixelModule', 'imagePlaneModule', 'generalSeriesModule'].forEach((type) => {
|
|
289
|
+
genericMetadataProvider.add(imageId, {
|
|
290
|
+
type,
|
|
291
|
+
metadata: metadata[type],
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
return image;
|
|
236
295
|
}
|
|
237
296
|
imageIdIndexToFrameIndex(imageIdIndex) {
|
|
238
297
|
return imageIdIndex % this.numFrames;
|
|
239
298
|
}
|
|
240
|
-
convertToCornerstoneImage(imageId, imageIdIndex) {
|
|
241
|
-
return this.getCornerstoneImageLoadObject(imageId, imageIdIndex);
|
|
242
|
-
}
|
|
243
299
|
getCornerstoneImageLoadObject(imageId, imageIdIndex) {
|
|
244
300
|
const image = this.getCornerstoneImage(imageId, imageIdIndex);
|
|
245
301
|
const imageLoadObject = {
|
|
@@ -276,78 +332,13 @@ export class ImageVolume {
|
|
|
276
332
|
const imageId = this.imageIds[imageIdIndex];
|
|
277
333
|
bytesRemaining = bytesRemaining - bytesPerImage;
|
|
278
334
|
const image = this.getCornerstoneImage(imageId, imageIdIndex);
|
|
279
|
-
const imageLoadObject = {
|
|
280
|
-
promise: Promise.resolve(image),
|
|
281
|
-
};
|
|
282
335
|
if (!cache.getImageLoadObject(imageId)) {
|
|
283
|
-
cache.
|
|
284
|
-
console.error(err);
|
|
285
|
-
});
|
|
336
|
+
cache.putImageSync(imageId, image);
|
|
286
337
|
}
|
|
287
338
|
if (bytesRemaining <= bytesPerImage) {
|
|
288
339
|
break;
|
|
289
340
|
}
|
|
290
|
-
const imageOrientationPatient = [
|
|
291
|
-
this.direction[0],
|
|
292
|
-
this.direction[1],
|
|
293
|
-
this.direction[2],
|
|
294
|
-
this.direction[3],
|
|
295
|
-
this.direction[4],
|
|
296
|
-
this.direction[5],
|
|
297
|
-
];
|
|
298
|
-
const precision = 6;
|
|
299
|
-
const imagePositionPatient = [
|
|
300
|
-
parseFloat((this.origin[0] +
|
|
301
|
-
imageIdIndex * this.direction[6] * this.spacing[0]).toFixed(precision)),
|
|
302
|
-
parseFloat((this.origin[1] +
|
|
303
|
-
imageIdIndex * this.direction[7] * this.spacing[1]).toFixed(precision)),
|
|
304
|
-
parseFloat((this.origin[2] +
|
|
305
|
-
imageIdIndex * this.direction[8] * this.spacing[2]).toFixed(precision)),
|
|
306
|
-
];
|
|
307
|
-
const pixelData = image.getPixelData();
|
|
308
|
-
const bitsAllocated = pixelData.BYTES_PER_ELEMENT * 8;
|
|
309
|
-
const imagePixelModule = {
|
|
310
|
-
bitsAllocated,
|
|
311
|
-
photometricInterpretation: image.photometricInterpretation,
|
|
312
|
-
windowWidth: image.windowWidth,
|
|
313
|
-
windowCenter: image.windowCenter,
|
|
314
|
-
voiLUTFunction: image.voiLUTFunction,
|
|
315
|
-
};
|
|
316
|
-
const imagePlaneModule = {
|
|
317
|
-
rowCosines: [this.direction[0], this.direction[1], this.direction[2]],
|
|
318
|
-
columnCosines: [
|
|
319
|
-
this.direction[3],
|
|
320
|
-
this.direction[4],
|
|
321
|
-
this.direction[5],
|
|
322
|
-
],
|
|
323
|
-
pixelSpacing: [this.spacing[0], this.spacing[1]],
|
|
324
|
-
imageOrientationPatient: imageOrientationPatient,
|
|
325
|
-
imagePositionPatient: imagePositionPatient,
|
|
326
|
-
columnPixelSpacing: image.columnPixelSpacing,
|
|
327
|
-
rowPixelSpacing: image.rowPixelSpacing,
|
|
328
|
-
columns: image.columns,
|
|
329
|
-
rows: image.rows,
|
|
330
|
-
};
|
|
331
|
-
const generalSeriesModule = {};
|
|
332
|
-
const metadata = {
|
|
333
|
-
imagePixelModule,
|
|
334
|
-
imagePlaneModule,
|
|
335
|
-
generalSeriesModule,
|
|
336
|
-
};
|
|
337
|
-
['imagePixelModule', 'imagePlaneModule', 'generalSeriesModule'].forEach((type) => {
|
|
338
|
-
genericMetadataProvider.add(imageId, {
|
|
339
|
-
type,
|
|
340
|
-
metadata: metadata[type],
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
const otherVolumes = cache.filterVolumesByReferenceId(this.volumeId);
|
|
345
|
-
if (otherVolumes.length) {
|
|
346
|
-
otherVolumes.forEach((volume) => {
|
|
347
|
-
volume.referencedImageIds = this.imageIds;
|
|
348
|
-
});
|
|
349
341
|
}
|
|
350
|
-
this.removeFromCache();
|
|
351
342
|
return this.imageIds;
|
|
352
343
|
}
|
|
353
344
|
}
|
|
@@ -41,7 +41,7 @@ function loadImageFromCacheOrVolume(imageId, options) {
|
|
|
41
41
|
if (cachedVolumeInfo?.volume?.loadStatus?.loaded) {
|
|
42
42
|
const { volume, imageIdIndex } = cachedVolumeInfo;
|
|
43
43
|
if (volume instanceof ImageVolume) {
|
|
44
|
-
imageLoadObject = volume.
|
|
44
|
+
imageLoadObject = volume.getCornerstoneImageLoadObject(imageId, imageIdIndex);
|
|
45
45
|
}
|
|
46
46
|
return imageLoadObject;
|
|
47
47
|
}
|
|
@@ -157,6 +157,7 @@ export function createAndCacheLocalImage(options, imageId, preventCache = false)
|
|
|
157
157
|
rgba: undefined,
|
|
158
158
|
columnPixelSpacing: imagePlaneModule.columnPixelSpacing,
|
|
159
159
|
rowPixelSpacing: imagePlaneModule.rowPixelSpacing,
|
|
160
|
+
FrameOfReferenceUID: imagePlaneModule.FrameOfReferenceUID,
|
|
160
161
|
invert: false,
|
|
161
162
|
};
|
|
162
163
|
if (options.scalarData) {
|
|
@@ -133,6 +133,13 @@ export async function createAndCacheDerivedVolume(referencedVolumeId, options) {
|
|
|
133
133
|
derivedImageData.setDirection(direction);
|
|
134
134
|
derivedImageData.setOrigin(origin);
|
|
135
135
|
derivedImageData.getPointData().setScalars(scalarArray);
|
|
136
|
+
const referencedImageIds = referencedVolume.imageIds ?? [];
|
|
137
|
+
let derivedVolumeImageIds = [];
|
|
138
|
+
if (referencedImageIds.length) {
|
|
139
|
+
derivedVolumeImageIds = referencedImageIds.map((imageId) => {
|
|
140
|
+
return `derived:${imageId}`;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
136
143
|
const derivedVolume = new ImageVolume({
|
|
137
144
|
volumeId,
|
|
138
145
|
metadata: structuredClone(metadata),
|
|
@@ -143,13 +150,15 @@ export async function createAndCacheDerivedVolume(referencedVolumeId, options) {
|
|
|
143
150
|
imageData: derivedImageData,
|
|
144
151
|
scalarData: volumeScalarData,
|
|
145
152
|
sizeInBytes: numBytes,
|
|
146
|
-
imageIds: [],
|
|
147
153
|
referencedVolumeId,
|
|
154
|
+
imageIds: derivedVolumeImageIds,
|
|
155
|
+
referencedImageIds: referencedVolume.imageIds ?? [],
|
|
148
156
|
});
|
|
149
157
|
const volumeLoadObject = {
|
|
150
158
|
promise: Promise.resolve(derivedVolume),
|
|
151
159
|
};
|
|
152
160
|
await cache.putVolumeLoadObject(volumeId, volumeLoadObject);
|
|
161
|
+
performCacheOptimizationForVolume(derivedVolume);
|
|
153
162
|
return derivedVolume;
|
|
154
163
|
}
|
|
155
164
|
export function createLocalVolume(options, volumeId, preventCache = false) {
|
|
@@ -212,6 +221,7 @@ export function createLocalVolume(options, volumeId, preventCache = false) {
|
|
|
212
221
|
promise: Promise.resolve(derivedVolume),
|
|
213
222
|
};
|
|
214
223
|
cache.putVolumeLoadObject(volumeId, volumeLoadObject);
|
|
224
|
+
performCacheOptimizationForVolume(derivedVolume);
|
|
215
225
|
return derivedVolume;
|
|
216
226
|
}
|
|
217
227
|
export async function createAndCacheVolumeFromImages(volumeId, imageIds, options = {}) {
|
|
@@ -18,9 +18,10 @@ export type ReferenceCompatibleOptions = {
|
|
|
18
18
|
asVolume?: boolean;
|
|
19
19
|
withOrientation?: boolean;
|
|
20
20
|
imageURI?: string;
|
|
21
|
+
asOverlay?: boolean;
|
|
21
22
|
};
|
|
22
23
|
export type ViewReference = {
|
|
23
|
-
FrameOfReferenceUID
|
|
24
|
+
FrameOfReferenceUID?: string;
|
|
24
25
|
referencedImageId?: string;
|
|
25
26
|
cameraFocalPoint?: Point3;
|
|
26
27
|
viewPlaneNormal?: Point3;
|
|
@@ -104,9 +105,9 @@ interface IViewport {
|
|
|
104
105
|
getNumberOfSlices(): number;
|
|
105
106
|
getCurrentImageIdIndex(): number;
|
|
106
107
|
getSliceIndex(): number;
|
|
107
|
-
|
|
108
|
+
getViewReferenceId(viewRefSpecifier?: ViewReferenceSpecifier): string;
|
|
108
109
|
getViewReference(viewRefSpecifier?: ViewReferenceSpecifier): ViewReference;
|
|
109
|
-
isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean;
|
|
110
|
+
isReferenceViewable(viewRef: ViewReference, options?: ReferenceCompatibleOptions): boolean | unknown;
|
|
110
111
|
getViewPresentation(viewPresSel?: ViewPresentationSelector): ViewPresentation;
|
|
111
112
|
setViewReference(viewRef: ViewReference): any;
|
|
112
113
|
setViewPresentation(viewPres: ViewPresentation): any;
|