@cornerstonejs/core 1.32.2 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/RenderingEngine/StackViewport.d.ts +24 -3
- package/dist/cjs/RenderingEngine/StackViewport.js +47 -31
- package/dist/cjs/RenderingEngine/StackViewport.js.map +1 -1
- package/dist/cjs/RenderingEngine/helpers/addImageSlicesToViewports.d.ts +3 -0
- package/dist/cjs/RenderingEngine/helpers/addImageSlicesToViewports.js +33 -0
- package/dist/cjs/RenderingEngine/helpers/addImageSlicesToViewports.js.map +1 -0
- package/dist/cjs/RenderingEngine/helpers/index.d.ts +2 -1
- package/dist/cjs/RenderingEngine/helpers/index.js +3 -1
- package/dist/cjs/RenderingEngine/helpers/index.js.map +1 -1
- package/dist/cjs/cache/cache.d.ts +2 -1
- package/dist/cjs/cache/cache.js +11 -0
- package/dist/cjs/cache/cache.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/loaders/imageLoader.d.ts +21 -1
- package/dist/cjs/loaders/imageLoader.js +97 -1
- package/dist/cjs/loaders/imageLoader.js.map +1 -1
- package/dist/cjs/loaders/volumeLoader.d.ts +2 -2
- package/dist/cjs/loaders/volumeLoader.js +6 -28
- package/dist/cjs/loaders/volumeLoader.js.map +1 -1
- package/dist/cjs/types/IImage.d.ts +1 -0
- package/dist/cjs/types/IStackInput.d.ts +12 -0
- package/dist/cjs/types/IStackInput.js +3 -0
- package/dist/cjs/types/IStackInput.js.map +1 -0
- package/dist/cjs/types/IStackViewport.d.ts +3 -0
- package/dist/cjs/types/PixelDataTypedArray.d.ts +1 -0
- package/dist/cjs/types/index.d.ts +3 -2
- package/dist/cjs/utilities/genericMetadataProvider.d.ts +6 -0
- package/dist/cjs/utilities/genericMetadataProvider.js +23 -0
- package/dist/cjs/utilities/genericMetadataProvider.js.map +1 -0
- package/dist/cjs/utilities/getBufferConfiguration.d.ts +9 -0
- package/dist/cjs/utilities/getBufferConfiguration.js +47 -0
- package/dist/cjs/utilities/getBufferConfiguration.js.map +1 -0
- package/dist/cjs/utilities/getTargetVolumeAndSpacingInNormalDir.d.ts +1 -1
- package/dist/cjs/utilities/getTargetVolumeAndSpacingInNormalDir.js +4 -4
- package/dist/cjs/utilities/getTargetVolumeAndSpacingInNormalDir.js.map +1 -1
- package/dist/cjs/utilities/getVolumeSliceRangeInfo.d.ts +1 -1
- package/dist/cjs/utilities/getVolumeSliceRangeInfo.js +2 -2
- package/dist/cjs/utilities/getVolumeSliceRangeInfo.js.map +1 -1
- package/dist/cjs/utilities/getVolumeViewportScrollInfo.d.ts +1 -1
- package/dist/cjs/utilities/getVolumeViewportScrollInfo.js +2 -2
- package/dist/cjs/utilities/getVolumeViewportScrollInfo.js.map +1 -1
- package/dist/cjs/utilities/index.d.ts +7 -1
- package/dist/cjs/utilities/index.js +13 -1
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/isValidVolume.d.ts +2 -0
- package/dist/cjs/utilities/isValidVolume.js +35 -0
- package/dist/cjs/utilities/isValidVolume.js.map +1 -0
- package/dist/cjs/utilities/makeVolumeMetadata.d.ts +2 -0
- package/dist/cjs/utilities/makeVolumeMetadata.js +55 -0
- package/dist/cjs/utilities/makeVolumeMetadata.js.map +1 -0
- package/dist/cjs/utilities/sortImageIdsAndGetSpacing.d.ts +9 -0
- package/dist/cjs/utilities/sortImageIdsAndGetSpacing.js +82 -0
- package/dist/cjs/utilities/sortImageIdsAndGetSpacing.js.map +1 -0
- package/dist/cjs/utilities/updateVTKImageDataWithCornerstoneImage.d.ts +4 -0
- package/dist/cjs/utilities/updateVTKImageDataWithCornerstoneImage.js +27 -0
- package/dist/cjs/utilities/updateVTKImageDataWithCornerstoneImage.js.map +1 -0
- package/dist/esm/RenderingEngine/StackViewport.js +46 -32
- package/dist/esm/RenderingEngine/StackViewport.js.map +1 -1
- package/dist/esm/RenderingEngine/helpers/addImageSlicesToViewports.js +20 -0
- package/dist/esm/RenderingEngine/helpers/addImageSlicesToViewports.js.map +1 -0
- package/dist/esm/RenderingEngine/helpers/index.js +2 -1
- package/dist/esm/RenderingEngine/helpers/index.js.map +1 -1
- package/dist/esm/cache/cache.js +11 -0
- package/dist/esm/cache/cache.js.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/loaders/imageLoader.js +94 -1
- package/dist/esm/loaders/imageLoader.js.map +1 -1
- package/dist/esm/loaders/volumeLoader.js +7 -29
- package/dist/esm/loaders/volumeLoader.js.map +1 -1
- package/dist/esm/types/IStackInput.js +2 -0
- package/dist/esm/types/IStackInput.js.map +1 -0
- package/dist/esm/utilities/genericMetadataProvider.js +20 -0
- package/dist/esm/utilities/genericMetadataProvider.js.map +1 -0
- package/dist/esm/utilities/getBufferConfiguration.js +44 -0
- package/dist/esm/utilities/getBufferConfiguration.js.map +1 -0
- package/dist/esm/utilities/getTargetVolumeAndSpacingInNormalDir.js +4 -4
- package/dist/esm/utilities/getTargetVolumeAndSpacingInNormalDir.js.map +1 -1
- package/dist/esm/utilities/getVolumeSliceRangeInfo.js +2 -2
- package/dist/esm/utilities/getVolumeSliceRangeInfo.js.map +1 -1
- package/dist/esm/utilities/getVolumeViewportScrollInfo.js +2 -2
- package/dist/esm/utilities/getVolumeViewportScrollInfo.js.map +1 -1
- package/dist/esm/utilities/index.js +7 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/isValidVolume.js +29 -0
- package/dist/esm/utilities/isValidVolume.js.map +1 -0
- package/dist/esm/utilities/makeVolumeMetadata.js +52 -0
- package/dist/esm/utilities/makeVolumeMetadata.js.map +1 -0
- package/dist/esm/utilities/sortImageIdsAndGetSpacing.js +79 -0
- package/dist/esm/utilities/sortImageIdsAndGetSpacing.js.map +1 -0
- package/dist/esm/utilities/updateVTKImageDataWithCornerstoneImage.js +24 -0
- package/dist/esm/utilities/updateVTKImageDataWithCornerstoneImage.js.map +1 -0
- package/dist/types/RenderingEngine/StackViewport.d.ts +24 -3
- package/dist/types/RenderingEngine/StackViewport.d.ts.map +1 -1
- package/dist/types/RenderingEngine/helpers/addImageSlicesToViewports.d.ts +4 -0
- package/dist/types/RenderingEngine/helpers/addImageSlicesToViewports.d.ts.map +1 -0
- package/dist/types/RenderingEngine/helpers/index.d.ts +2 -1
- package/dist/types/RenderingEngine/helpers/index.d.ts.map +1 -1
- package/dist/types/cache/cache.d.ts +2 -1
- package/dist/types/cache/cache.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/loaders/imageLoader.d.ts +21 -1
- package/dist/types/loaders/imageLoader.d.ts.map +1 -1
- package/dist/types/loaders/volumeLoader.d.ts +2 -2
- package/dist/types/loaders/volumeLoader.d.ts.map +1 -1
- package/dist/types/types/IImage.d.ts +1 -0
- package/dist/types/types/IImage.d.ts.map +1 -1
- package/dist/types/types/IStackInput.d.ts +13 -0
- package/dist/types/types/IStackInput.d.ts.map +1 -0
- package/dist/types/types/IStackViewport.d.ts +3 -0
- package/dist/types/types/IStackViewport.d.ts.map +1 -1
- package/dist/types/types/PixelDataTypedArray.d.ts +1 -0
- package/dist/types/types/PixelDataTypedArray.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +3 -2
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/utilities/genericMetadataProvider.d.ts +7 -0
- package/dist/types/utilities/genericMetadataProvider.d.ts.map +1 -0
- package/dist/types/utilities/getBufferConfiguration.d.ts +10 -0
- package/dist/types/utilities/getBufferConfiguration.d.ts.map +1 -0
- package/dist/types/utilities/getTargetVolumeAndSpacingInNormalDir.d.ts +1 -1
- package/dist/types/utilities/getTargetVolumeAndSpacingInNormalDir.d.ts.map +1 -1
- package/dist/types/utilities/getVolumeSliceRangeInfo.d.ts +1 -1
- package/dist/types/utilities/getVolumeSliceRangeInfo.d.ts.map +1 -1
- package/dist/types/utilities/getVolumeViewportScrollInfo.d.ts +1 -1
- package/dist/types/utilities/getVolumeViewportScrollInfo.d.ts.map +1 -1
- package/dist/types/utilities/index.d.ts +7 -1
- package/dist/types/utilities/index.d.ts.map +1 -1
- package/dist/types/utilities/isValidVolume.d.ts +3 -0
- package/dist/types/utilities/isValidVolume.d.ts.map +1 -0
- package/dist/types/utilities/makeVolumeMetadata.d.ts +3 -0
- package/dist/types/utilities/makeVolumeMetadata.d.ts.map +1 -0
- package/dist/types/utilities/sortImageIdsAndGetSpacing.d.ts +10 -0
- package/dist/types/utilities/sortImageIdsAndGetSpacing.d.ts.map +1 -0
- package/dist/types/utilities/updateVTKImageDataWithCornerstoneImage.d.ts +5 -0
- package/dist/types/utilities/updateVTKImageDataWithCornerstoneImage.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/StackViewport.ts +77 -52
- package/src/RenderingEngine/helpers/addImageSlicesToViewports.ts +54 -0
- package/src/RenderingEngine/helpers/index.ts +2 -0
- package/src/cache/cache.ts +22 -0
- package/src/index.ts +2 -0
- package/src/loaders/imageLoader.ts +202 -2
- package/src/loaders/volumeLoader.ts +18 -27
- package/src/types/IImage.ts +2 -0
- package/src/types/IStackInput.ts +30 -0
- package/src/types/IStackViewport.ts +10 -3
- package/src/types/PixelDataTypedArray.ts +8 -0
- package/src/types/index.ts +8 -1
- package/src/utilities/genericMetadataProvider.ts +43 -0
- package/src/utilities/getBufferConfiguration.ts +69 -0
- package/src/utilities/getTargetVolumeAndSpacingInNormalDir.ts +9 -5
- package/src/utilities/getVolumeSliceRangeInfo.ts +10 -2
- package/src/utilities/getVolumeViewportScrollInfo.ts +5 -2
- package/src/utilities/index.ts +12 -0
- package/src/utilities/isValidVolume.ts +59 -0
- package/src/utilities/makeVolumeMetadata.ts +87 -0
- package/src/utilities/sortImageIdsAndGetSpacing.ts +174 -0
- package/src/utilities/updateVTKImageDataWithCornerstoneImage.ts +38 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import CPUFallbackColormapData from './CPUFallbackColormapData';
|
|
2
1
|
import CPUIImageData from './CPUIImageData';
|
|
3
2
|
import ICamera from './ICamera';
|
|
4
3
|
import IImageData from './IImageData';
|
|
@@ -7,9 +6,8 @@ import Point2 from './Point2';
|
|
|
7
6
|
import Point3 from './Point3';
|
|
8
7
|
import { Scaling } from './ScalingParameters';
|
|
9
8
|
import StackViewportProperties from './StackViewportProperties';
|
|
10
|
-
import { ColormapPublic } from './Colormap';
|
|
11
9
|
import type IImage from './IImage';
|
|
12
|
-
|
|
10
|
+
import { IStackInput } from './IStackInput';
|
|
13
11
|
/**
|
|
14
12
|
* Interface for Stack Viewport
|
|
15
13
|
*/
|
|
@@ -93,7 +91,16 @@ export default interface IStackViewport extends IViewport {
|
|
|
93
91
|
* Returns the currently rendered imageId
|
|
94
92
|
*/
|
|
95
93
|
getCurrentImageId: () => string;
|
|
94
|
+
/**
|
|
95
|
+
* Add Image Slices actors to the viewport
|
|
96
|
+
*/
|
|
97
|
+
addImages(
|
|
98
|
+
stackInputs: Array<IStackInput>,
|
|
99
|
+
immediateRender: boolean,
|
|
100
|
+
suppressEvents: boolean
|
|
101
|
+
);
|
|
96
102
|
|
|
103
|
+
getImageDataMetadata(image: IImage): any;
|
|
97
104
|
/**
|
|
98
105
|
* Custom rendering pipeline for the rendering for the CPU fallback
|
|
99
106
|
*/
|
package/src/types/index.ts
CHANGED
|
@@ -62,6 +62,7 @@ import type CPUFallbackLookupTable from './CPUFallbackLookupTable';
|
|
|
62
62
|
import type CPUFallbackLUT from './CPUFallbackLUT';
|
|
63
63
|
import type CPUFallbackRenderingTools from './CPUFallbackRenderingTools';
|
|
64
64
|
import type { IVolumeInput, VolumeInputCallback } from './IVolumeInput';
|
|
65
|
+
import type { IStackInput, StackInputCallback } from './IStackInput';
|
|
65
66
|
import type * as EventTypes from './EventTypes';
|
|
66
67
|
import type IRenderingEngine from './IRenderingEngine';
|
|
67
68
|
import type ActorSliceRange from './ActorSliceRange';
|
|
@@ -79,7 +80,10 @@ import type { IContour } from './IContour';
|
|
|
79
80
|
import type RGB from './RGB';
|
|
80
81
|
import { ColormapPublic, ColormapRegistration } from './Colormap';
|
|
81
82
|
import type { ViewportProperties } from './ViewportProperties';
|
|
82
|
-
import type {
|
|
83
|
+
import type {
|
|
84
|
+
PixelDataTypedArray,
|
|
85
|
+
PixelDataTypedArrayString,
|
|
86
|
+
} from './PixelDataTypedArray';
|
|
83
87
|
import type { ImagePixelModule } from './ImagePixelModule';
|
|
84
88
|
import type { ImagePlaneModule } from './ImagePlaneModule';
|
|
85
89
|
import type { AffineMatrix } from './AffineMatrix';
|
|
@@ -143,6 +147,8 @@ export type {
|
|
|
143
147
|
IVolumeLoadObject,
|
|
144
148
|
IVolumeInput,
|
|
145
149
|
VolumeInputCallback,
|
|
150
|
+
IStackInput,
|
|
151
|
+
StackInputCallback,
|
|
146
152
|
ViewportPreset,
|
|
147
153
|
//
|
|
148
154
|
Metadata,
|
|
@@ -195,6 +201,7 @@ export type {
|
|
|
195
201
|
ColormapRegistration,
|
|
196
202
|
// PixelData
|
|
197
203
|
PixelDataTypedArray,
|
|
204
|
+
PixelDataTypedArrayString,
|
|
198
205
|
ImagePixelModule,
|
|
199
206
|
ImagePlaneModule,
|
|
200
207
|
AffineMatrix,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { metaData } from '..';
|
|
2
|
+
|
|
3
|
+
let state: Record<string, any> = {}; // Calibrated pixel spacing per imageId
|
|
4
|
+
/**
|
|
5
|
+
* Simple metadata provider that stores some sort of meta data for each imageId.
|
|
6
|
+
*/
|
|
7
|
+
const metadataProvider = {
|
|
8
|
+
/**
|
|
9
|
+
* Adds metadata for an imageId.
|
|
10
|
+
* @param imageId - the imageId for the metadata to store
|
|
11
|
+
* @param payload - the payload
|
|
12
|
+
*/
|
|
13
|
+
add: (imageId: string, payload: any): void => {
|
|
14
|
+
const type = payload.type;
|
|
15
|
+
|
|
16
|
+
if (!state[imageId]) {
|
|
17
|
+
state[imageId] = {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Create a deep copy of payload.metadata
|
|
21
|
+
state[imageId][type] = JSON.parse(JSON.stringify(payload.metadata));
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns the metadata for an imageId if it exists.
|
|
26
|
+
* @param type - the type of metadata to enquire about
|
|
27
|
+
* @param imageId - the imageId to enquire about
|
|
28
|
+
*/
|
|
29
|
+
get: (type: string, imageId: string): any => {
|
|
30
|
+
return state[imageId]?.[type];
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Clears all metadata.
|
|
35
|
+
*/
|
|
36
|
+
clear: (): void => {
|
|
37
|
+
state = {};
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
metaData.addProvider(metadataProvider.get);
|
|
42
|
+
|
|
43
|
+
export default metadataProvider;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { PixelDataTypedArray, PixelDataTypedArrayString } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a target buffer based on the provided options.
|
|
5
|
+
*
|
|
6
|
+
* @param targetBufferType - The type of the target buffer.
|
|
7
|
+
* @param length - The length of the target buffer.
|
|
8
|
+
* @param options - Options for the target buffer. Currently supports
|
|
9
|
+
* `use16BitTexture` and `isVolumeBuffer`.
|
|
10
|
+
* @returns Returns an object containing the number of bytes and the type array
|
|
11
|
+
* constructor of the target buffer, which you then use to create the target buffer
|
|
12
|
+
* with new TypedArrayConstructor(length).
|
|
13
|
+
*/
|
|
14
|
+
function getBufferConfiguration(
|
|
15
|
+
targetBufferType: PixelDataTypedArrayString,
|
|
16
|
+
length: number,
|
|
17
|
+
options: { use16BitTexture?: boolean; isVolumeBuffer?: boolean } = {}
|
|
18
|
+
): {
|
|
19
|
+
numBytes: number;
|
|
20
|
+
TypedArrayConstructor: new (
|
|
21
|
+
length: number | SharedArrayBuffer
|
|
22
|
+
) => PixelDataTypedArray;
|
|
23
|
+
} {
|
|
24
|
+
const { use16BitTexture = false, isVolumeBuffer = false } = options;
|
|
25
|
+
|
|
26
|
+
switch (targetBufferType) {
|
|
27
|
+
case 'Float32Array':
|
|
28
|
+
return { numBytes: length * 4, TypedArrayConstructor: Float32Array };
|
|
29
|
+
case 'Uint8Array':
|
|
30
|
+
return { numBytes: length, TypedArrayConstructor: Uint8Array };
|
|
31
|
+
case 'Uint16Array':
|
|
32
|
+
if (!isVolumeBuffer) {
|
|
33
|
+
return { numBytes: length * 2, TypedArrayConstructor: Uint16Array };
|
|
34
|
+
} else {
|
|
35
|
+
if (use16BitTexture) {
|
|
36
|
+
return { numBytes: length * 2, TypedArrayConstructor: Uint16Array };
|
|
37
|
+
} else {
|
|
38
|
+
console.warn(
|
|
39
|
+
'Uint16Array is not supported for volume rendering, switching back to Float32Array'
|
|
40
|
+
);
|
|
41
|
+
return { numBytes: length * 4, TypedArrayConstructor: Float32Array };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
case 'Int16Array':
|
|
45
|
+
if (!isVolumeBuffer) {
|
|
46
|
+
return { numBytes: length * 2, TypedArrayConstructor: Int16Array };
|
|
47
|
+
} else {
|
|
48
|
+
if (use16BitTexture) {
|
|
49
|
+
return { numBytes: length * 2, TypedArrayConstructor: Int16Array };
|
|
50
|
+
} else {
|
|
51
|
+
console.warn(
|
|
52
|
+
'Int16Array is not supported for volume rendering, switching back to Float32Array'
|
|
53
|
+
);
|
|
54
|
+
return { numBytes: length * 4, TypedArrayConstructor: Float32Array };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
default:
|
|
58
|
+
if (targetBufferType) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
'TargetBuffer should be Float32Array, Uint8Array, Uint16Array, or Int16Array'
|
|
61
|
+
);
|
|
62
|
+
} else {
|
|
63
|
+
// Use Float32Array if no targetBuffer is provided
|
|
64
|
+
return { numBytes: length * 4, TypedArrayConstructor: Float32Array };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { getBufferConfiguration };
|
|
@@ -30,14 +30,16 @@ const isPrimaryVolume = (volume): boolean =>
|
|
|
30
30
|
* @param camera - current camera
|
|
31
31
|
* @param targetVolumeId - If a target volumeId is given that volume
|
|
32
32
|
* is forced to be used.
|
|
33
|
-
*
|
|
33
|
+
* @param useSlabThickness - If true, the number of steps will be calculated
|
|
34
|
+
* based on the slab thickness instead of the spacing in the normal direction
|
|
34
35
|
* @returns An object containing the imageVolume and spacingInNormalDirection.
|
|
35
36
|
*
|
|
36
37
|
*/
|
|
37
38
|
export default function getTargetVolumeAndSpacingInNormalDir(
|
|
38
39
|
viewport: IVolumeViewport,
|
|
39
40
|
camera: ICamera,
|
|
40
|
-
targetVolumeId?: string
|
|
41
|
+
targetVolumeId?: string,
|
|
42
|
+
useSlabThickness = false
|
|
41
43
|
): {
|
|
42
44
|
imageVolume: IImageVolume;
|
|
43
45
|
spacingInNormalDirection: number;
|
|
@@ -75,7 +77,8 @@ export default function getTargetVolumeAndSpacingInNormalDir(
|
|
|
75
77
|
const spacingInNormalDirection = getSpacingInNormal(
|
|
76
78
|
imageVolume,
|
|
77
79
|
viewPlaneNormal,
|
|
78
|
-
viewport
|
|
80
|
+
viewport,
|
|
81
|
+
useSlabThickness
|
|
79
82
|
);
|
|
80
83
|
|
|
81
84
|
return { imageVolume, spacingInNormalDirection, actorUID };
|
|
@@ -131,11 +134,12 @@ export default function getTargetVolumeAndSpacingInNormalDir(
|
|
|
131
134
|
function getSpacingInNormal(
|
|
132
135
|
imageVolume: IImageVolume,
|
|
133
136
|
viewPlaneNormal: Point3,
|
|
134
|
-
viewport: IVolumeViewport
|
|
137
|
+
viewport: IVolumeViewport,
|
|
138
|
+
useSlabThickness = false
|
|
135
139
|
): number {
|
|
136
140
|
const { slabThickness } = viewport.getProperties();
|
|
137
141
|
let spacingInNormalDirection = slabThickness;
|
|
138
|
-
if (!slabThickness) {
|
|
142
|
+
if (!slabThickness || useSlabThickness === false) {
|
|
139
143
|
spacingInNormalDirection = getSpacingInNormalDirection(
|
|
140
144
|
imageVolume,
|
|
141
145
|
viewPlaneNormal
|
|
@@ -11,11 +11,14 @@ import {
|
|
|
11
11
|
* Calculates the slice range for the given volume based on its orientation
|
|
12
12
|
* @param viewport - Volume viewport
|
|
13
13
|
* @param volumeId - Id of one of the volumes loaded on the given viewport
|
|
14
|
+
* @param useSlabThickness - If true, the slice range will be calculated
|
|
15
|
+
* based on the slab thickness instead of the spacing in the normal direction
|
|
14
16
|
* @returns slice range information
|
|
15
17
|
*/
|
|
16
18
|
function getVolumeSliceRangeInfo(
|
|
17
19
|
viewport: IVolumeViewport,
|
|
18
|
-
volumeId: string
|
|
20
|
+
volumeId: string,
|
|
21
|
+
useSlabThickness = false
|
|
19
22
|
): {
|
|
20
23
|
sliceRange: ActorSliceRange;
|
|
21
24
|
spacingInNormalDirection: number;
|
|
@@ -24,7 +27,12 @@ function getVolumeSliceRangeInfo(
|
|
|
24
27
|
const camera = viewport.getCamera();
|
|
25
28
|
const { focalPoint, viewPlaneNormal } = camera;
|
|
26
29
|
const { spacingInNormalDirection, actorUID } =
|
|
27
|
-
getTargetVolumeAndSpacingInNormalDir(
|
|
30
|
+
getTargetVolumeAndSpacingInNormalDir(
|
|
31
|
+
viewport,
|
|
32
|
+
camera,
|
|
33
|
+
volumeId,
|
|
34
|
+
useSlabThickness
|
|
35
|
+
);
|
|
28
36
|
|
|
29
37
|
if (!actorUID) {
|
|
30
38
|
throw new Error(
|
|
@@ -5,14 +5,17 @@ import getVolumeSliceRangeInfo from './getVolumeSliceRangeInfo';
|
|
|
5
5
|
* Calculates the number os steps the volume can scroll based on its orientation
|
|
6
6
|
* @param viewport - Volume viewport
|
|
7
7
|
* @param volumeId - Id of one of the volumes loaded on the given viewport
|
|
8
|
+
* @param useSlabThickness - If true, the number of steps will be calculated
|
|
9
|
+
* based on the slab thickness instead of the spacing in the normal direction
|
|
8
10
|
* @returns number of steps the volume can scroll and its current position
|
|
9
11
|
*/
|
|
10
12
|
function getVolumeViewportScrollInfo(
|
|
11
13
|
viewport: IVolumeViewport,
|
|
12
|
-
volumeId: string
|
|
14
|
+
volumeId: string,
|
|
15
|
+
useSlabThickness = false
|
|
13
16
|
) {
|
|
14
17
|
const { sliceRange, spacingInNormalDirection, camera } =
|
|
15
|
-
getVolumeSliceRangeInfo(viewport, volumeId);
|
|
18
|
+
getVolumeSliceRangeInfo(viewport, volumeId, useSlabThickness);
|
|
16
19
|
|
|
17
20
|
const { min, max, current } = sliceRange;
|
|
18
21
|
|
package/src/utilities/index.ts
CHANGED
|
@@ -49,10 +49,16 @@ import getScalingParameters from './getScalingParameters';
|
|
|
49
49
|
import getScalarDataType from './getScalarDataType';
|
|
50
50
|
import isPTPrescaledWithSUV from './isPTPrescaledWithSUV';
|
|
51
51
|
import getImageLegacy from './getImageLegacy';
|
|
52
|
+
import sortImageIdsAndGetSpacing from './sortImageIdsAndGetSpacing';
|
|
53
|
+
import makeVolumeMetadata from './makeVolumeMetadata';
|
|
54
|
+
import genericMetadataProvider from './genericMetadataProvider';
|
|
55
|
+
import { isValidVolume } from './isValidVolume';
|
|
56
|
+
import { updateVTKImageDataWithCornerstoneImage } from './updateVTKImageDataWithCornerstoneImage';
|
|
52
57
|
import ProgressiveIterator from './ProgressiveIterator';
|
|
53
58
|
import decimate from './decimate';
|
|
54
59
|
import imageRetrieveMetadataProvider from './imageRetrieveMetadataProvider';
|
|
55
60
|
import isVideoTransferSyntax from './isVideoTransferSyntax';
|
|
61
|
+
import { getBufferConfiguration } from './getBufferConfiguration';
|
|
56
62
|
|
|
57
63
|
// name spaces
|
|
58
64
|
import * as planar from './planar';
|
|
@@ -120,5 +126,11 @@ export {
|
|
|
120
126
|
decimate,
|
|
121
127
|
imageRetrieveMetadataProvider,
|
|
122
128
|
transferFunctionUtils,
|
|
129
|
+
updateVTKImageDataWithCornerstoneImage,
|
|
130
|
+
sortImageIdsAndGetSpacing,
|
|
131
|
+
makeVolumeMetadata,
|
|
132
|
+
isValidVolume,
|
|
133
|
+
genericMetadataProvider,
|
|
123
134
|
isVideoTransferSyntax,
|
|
135
|
+
getBufferConfiguration,
|
|
124
136
|
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { metaData } from '..';
|
|
2
|
+
import isEqual from './isEqual';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Checks if the given imageIds form a valid volume. A volume is considered valid if all imageIds
|
|
6
|
+
* have the same series instance UID, modality, columns, rows, image orientation patient, and pixel
|
|
7
|
+
* spacing.
|
|
8
|
+
*
|
|
9
|
+
* @param imageIds - The imageIds to check.
|
|
10
|
+
* @returns true if the imageIds form a valid volume, false otherwise.
|
|
11
|
+
*/
|
|
12
|
+
function isValidVolume(imageIds: string[]): boolean {
|
|
13
|
+
const imageId0 = imageIds[0];
|
|
14
|
+
|
|
15
|
+
const { modality, seriesInstanceUID } = metaData.get(
|
|
16
|
+
'generalSeriesModule',
|
|
17
|
+
imageId0
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
imageOrientationPatient,
|
|
22
|
+
pixelSpacing,
|
|
23
|
+
frameOfReferenceUID,
|
|
24
|
+
columns,
|
|
25
|
+
rows,
|
|
26
|
+
} = metaData.get('imagePlaneModule', imageId0);
|
|
27
|
+
|
|
28
|
+
const baseMetadata = {
|
|
29
|
+
modality,
|
|
30
|
+
imageOrientationPatient,
|
|
31
|
+
pixelSpacing,
|
|
32
|
+
frameOfReferenceUID,
|
|
33
|
+
columns,
|
|
34
|
+
rows,
|
|
35
|
+
seriesInstanceUID,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const validVolume = imageIds.every((imageId) => {
|
|
39
|
+
const { modality, seriesInstanceUID } = metaData.get(
|
|
40
|
+
'generalSeriesModule',
|
|
41
|
+
imageId
|
|
42
|
+
);
|
|
43
|
+
const { imageOrientationPatient, pixelSpacing, columns, rows } =
|
|
44
|
+
metaData.get('imagePlaneModule', imageId);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
seriesInstanceUID === baseMetadata.seriesInstanceUID &&
|
|
48
|
+
modality === baseMetadata.modality &&
|
|
49
|
+
columns === baseMetadata.columns &&
|
|
50
|
+
rows === baseMetadata.rows &&
|
|
51
|
+
isEqual(imageOrientationPatient, baseMetadata.imageOrientationPatient) &&
|
|
52
|
+
isEqual(pixelSpacing, baseMetadata.pixelSpacing)
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return validVolume;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { isValidVolume };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { metaData } from '../';
|
|
2
|
+
import { Metadata } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* It creates a metadata object for a volume given the imageIds that compose it.
|
|
6
|
+
* It uses the first imageId to get the metadata.
|
|
7
|
+
*
|
|
8
|
+
* @param imageIds - array of imageIds
|
|
9
|
+
* @returns The volume metadata
|
|
10
|
+
*/
|
|
11
|
+
export default function makeVolumeMetadata(imageIds: Array<string>): Metadata {
|
|
12
|
+
const imageId0 = imageIds[0];
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
pixelRepresentation,
|
|
16
|
+
bitsAllocated,
|
|
17
|
+
bitsStored,
|
|
18
|
+
highBit,
|
|
19
|
+
photometricInterpretation,
|
|
20
|
+
samplesPerPixel,
|
|
21
|
+
} = metaData.get('imagePixelModule', imageId0);
|
|
22
|
+
|
|
23
|
+
// Add list of VOIs stored on the DICOM.
|
|
24
|
+
const voiLut = [];
|
|
25
|
+
|
|
26
|
+
const voiLutModule = metaData.get('voiLutModule', imageId0);
|
|
27
|
+
|
|
28
|
+
// voiLutModule is not always present
|
|
29
|
+
let voiLUTFunction;
|
|
30
|
+
if (voiLutModule) {
|
|
31
|
+
const { windowWidth, windowCenter } = voiLutModule;
|
|
32
|
+
voiLUTFunction = voiLutModule?.voiLUTFunction;
|
|
33
|
+
|
|
34
|
+
if (Array.isArray(windowWidth)) {
|
|
35
|
+
for (let i = 0; i < windowWidth.length; i++) {
|
|
36
|
+
voiLut.push({
|
|
37
|
+
windowWidth: windowWidth[i],
|
|
38
|
+
windowCenter: windowCenter[i],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
voiLut.push({
|
|
43
|
+
windowWidth: windowWidth,
|
|
44
|
+
windowCenter: windowCenter,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
voiLut.push({
|
|
49
|
+
windowWidth: undefined,
|
|
50
|
+
windowCenter: undefined,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { modality, seriesInstanceUID } = metaData.get(
|
|
55
|
+
'generalSeriesModule',
|
|
56
|
+
imageId0
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const {
|
|
60
|
+
imageOrientationPatient,
|
|
61
|
+
pixelSpacing,
|
|
62
|
+
frameOfReferenceUID,
|
|
63
|
+
columns,
|
|
64
|
+
rows,
|
|
65
|
+
} = metaData.get('imagePlaneModule', imageId0);
|
|
66
|
+
|
|
67
|
+
// Map to dcmjs-style keywords. This is becoming the standard and makes it
|
|
68
|
+
// Easier to swap out cornerstoneDICOMImageLoader at a later date.
|
|
69
|
+
return {
|
|
70
|
+
BitsAllocated: bitsAllocated,
|
|
71
|
+
BitsStored: bitsStored,
|
|
72
|
+
SamplesPerPixel: samplesPerPixel,
|
|
73
|
+
HighBit: highBit,
|
|
74
|
+
PhotometricInterpretation: photometricInterpretation,
|
|
75
|
+
PixelRepresentation: pixelRepresentation,
|
|
76
|
+
Modality: modality,
|
|
77
|
+
ImageOrientationPatient: imageOrientationPatient,
|
|
78
|
+
PixelSpacing: pixelSpacing,
|
|
79
|
+
FrameOfReferenceUID: frameOfReferenceUID,
|
|
80
|
+
Columns: columns,
|
|
81
|
+
Rows: rows,
|
|
82
|
+
// This is a reshaped object and not a dicom tag:
|
|
83
|
+
voiLut,
|
|
84
|
+
VOILUTFunction: voiLUTFunction,
|
|
85
|
+
SeriesInstanceUID: seriesInstanceUID,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { vec3 } from 'gl-matrix';
|
|
2
|
+
import { metaData, getConfiguration } from '../';
|
|
3
|
+
import { Point3 } from '../types';
|
|
4
|
+
|
|
5
|
+
type SortedImageIdsItem = {
|
|
6
|
+
zSpacing: number;
|
|
7
|
+
origin: Point3;
|
|
8
|
+
sortedImageIds: Array<string>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Given an array of imageIds, sort them based on their imagePositionPatient, and
|
|
12
|
+
* also returns the spacing between images and the origin of the reference image
|
|
13
|
+
*
|
|
14
|
+
* @param imageIds - array of imageIds
|
|
15
|
+
* @param scanAxisNormal - [x, y, z] array or gl-matrix vec3
|
|
16
|
+
*
|
|
17
|
+
* @returns The sortedImageIds, zSpacing, and origin of the first image in the series.
|
|
18
|
+
*/
|
|
19
|
+
export default function sortImageIdsAndGetSpacing(
|
|
20
|
+
imageIds: Array<string>,
|
|
21
|
+
scanAxisNormal?: vec3
|
|
22
|
+
): SortedImageIdsItem {
|
|
23
|
+
const {
|
|
24
|
+
imagePositionPatient: referenceImagePositionPatient,
|
|
25
|
+
imageOrientationPatient,
|
|
26
|
+
} = metaData.get('imagePlaneModule', imageIds[0]);
|
|
27
|
+
|
|
28
|
+
if (!scanAxisNormal) {
|
|
29
|
+
const rowCosineVec = vec3.fromValues(
|
|
30
|
+
imageOrientationPatient[0],
|
|
31
|
+
imageOrientationPatient[1],
|
|
32
|
+
imageOrientationPatient[2]
|
|
33
|
+
);
|
|
34
|
+
const colCosineVec = vec3.fromValues(
|
|
35
|
+
imageOrientationPatient[3],
|
|
36
|
+
imageOrientationPatient[4],
|
|
37
|
+
imageOrientationPatient[5]
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
scanAxisNormal = vec3.create();
|
|
41
|
+
vec3.cross(scanAxisNormal, rowCosineVec, colCosineVec);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const refIppVec = vec3.create();
|
|
45
|
+
|
|
46
|
+
// Check if we are using wadouri scheme
|
|
47
|
+
const usingWadoUri = imageIds[0].split(':')[0] === 'wadouri';
|
|
48
|
+
|
|
49
|
+
vec3.set(
|
|
50
|
+
refIppVec,
|
|
51
|
+
referenceImagePositionPatient[0],
|
|
52
|
+
referenceImagePositionPatient[1],
|
|
53
|
+
referenceImagePositionPatient[2]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
let sortedImageIds: string[];
|
|
57
|
+
let zSpacing: number;
|
|
58
|
+
|
|
59
|
+
function getDistance(imageId: string) {
|
|
60
|
+
const { imagePositionPatient } = metaData.get('imagePlaneModule', imageId);
|
|
61
|
+
|
|
62
|
+
const positionVector = vec3.create();
|
|
63
|
+
|
|
64
|
+
vec3.sub(
|
|
65
|
+
positionVector,
|
|
66
|
+
referenceImagePositionPatient,
|
|
67
|
+
imagePositionPatient
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return vec3.dot(positionVector, scanAxisNormal);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* If we are using wadors and so have all image metadata cached ahead of time,
|
|
75
|
+
* then sort by image position in 3D space, and calculate average slice
|
|
76
|
+
* spacing from the entire volume. If not, then use the sampled images (1st
|
|
77
|
+
* and middle) to calculate slice spacing, and use the provided imageId order.
|
|
78
|
+
* Correct sorting must be done ahead of time.
|
|
79
|
+
*/
|
|
80
|
+
if (!usingWadoUri) {
|
|
81
|
+
const distanceImagePairs = imageIds.map((imageId) => {
|
|
82
|
+
const distance = getDistance(imageId);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
distance,
|
|
86
|
+
imageId,
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
distanceImagePairs.sort((a, b) => b.distance - a.distance);
|
|
91
|
+
|
|
92
|
+
sortedImageIds = distanceImagePairs.map((a) => a.imageId);
|
|
93
|
+
const numImages = distanceImagePairs.length;
|
|
94
|
+
|
|
95
|
+
// Calculated average spacing.
|
|
96
|
+
// We would need to resample if these are not similar.
|
|
97
|
+
// It should be up to the host app to do this if it needed to.
|
|
98
|
+
zSpacing =
|
|
99
|
+
Math.abs(
|
|
100
|
+
distanceImagePairs[numImages - 1].distance -
|
|
101
|
+
distanceImagePairs[0].distance
|
|
102
|
+
) /
|
|
103
|
+
(numImages - 1);
|
|
104
|
+
} else {
|
|
105
|
+
// Using wadouri, so we have only prefetched the first, middle, and last
|
|
106
|
+
// images for metadata. Assume initial imageId array order is pre-sorted,
|
|
107
|
+
// but check orientation.
|
|
108
|
+
const prefetchedImageIds = [
|
|
109
|
+
imageIds[0],
|
|
110
|
+
imageIds[Math.floor(imageIds.length / 2)],
|
|
111
|
+
];
|
|
112
|
+
sortedImageIds = imageIds;
|
|
113
|
+
const firstImageDistance = getDistance(prefetchedImageIds[0]);
|
|
114
|
+
const middleImageDistance = getDistance(prefetchedImageIds[1]);
|
|
115
|
+
if (firstImageDistance - middleImageDistance < 0) {
|
|
116
|
+
sortedImageIds.reverse();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Calculate average spacing between the first and middle prefetched images,
|
|
120
|
+
// otherwise fall back to DICOM `spacingBetweenSlices`
|
|
121
|
+
const metadataForMiddleImage = metaData.get(
|
|
122
|
+
'imagePlaneModule',
|
|
123
|
+
prefetchedImageIds[1]
|
|
124
|
+
);
|
|
125
|
+
if (!metadataForMiddleImage) {
|
|
126
|
+
throw new Error('Incomplete metadata required for volume construction.');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const positionVector = vec3.create();
|
|
130
|
+
|
|
131
|
+
vec3.sub(
|
|
132
|
+
positionVector,
|
|
133
|
+
referenceImagePositionPatient,
|
|
134
|
+
metadataForMiddleImage.imagePositionPatient
|
|
135
|
+
);
|
|
136
|
+
const distanceBetweenFirstAndMiddleImages = vec3.dot(
|
|
137
|
+
positionVector,
|
|
138
|
+
scanAxisNormal
|
|
139
|
+
);
|
|
140
|
+
zSpacing =
|
|
141
|
+
Math.abs(distanceBetweenFirstAndMiddleImages) /
|
|
142
|
+
Math.floor(imageIds.length / 2);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const { imagePositionPatient: origin, sliceThickness } = metaData.get(
|
|
146
|
+
'imagePlaneModule',
|
|
147
|
+
sortedImageIds[0]
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const { strictZSpacingForVolumeViewport } = getConfiguration().rendering;
|
|
151
|
+
|
|
152
|
+
// We implemented these lines for multiframe dicom files that does not have
|
|
153
|
+
// position for each frame, leading to incorrect calculation of zSpacing = 0
|
|
154
|
+
// If possible, we use the sliceThickness, but we warn about this dicom file
|
|
155
|
+
// weirdness. If sliceThickness is not available, we set to 1 just to render
|
|
156
|
+
if (zSpacing === 0 && !strictZSpacingForVolumeViewport) {
|
|
157
|
+
if (sliceThickness) {
|
|
158
|
+
console.log('Could not calculate zSpacing. Using sliceThickness');
|
|
159
|
+
zSpacing = sliceThickness;
|
|
160
|
+
} else {
|
|
161
|
+
console.log(
|
|
162
|
+
'Could not calculate zSpacing. The VolumeViewport visualization is compromised. Setting zSpacing to 1 to render'
|
|
163
|
+
);
|
|
164
|
+
zSpacing = 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const result: SortedImageIdsItem = {
|
|
168
|
+
zSpacing,
|
|
169
|
+
origin,
|
|
170
|
+
sortedImageIds,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
2
|
+
import { IImage, PixelDataTypedArray } from '../types';
|
|
3
|
+
|
|
4
|
+
function updateVTKImageDataWithCornerstoneImage(
|
|
5
|
+
sourceImageData: vtkImageData,
|
|
6
|
+
image: IImage
|
|
7
|
+
) {
|
|
8
|
+
const pixelData = image.getPixelData();
|
|
9
|
+
const scalarData = sourceImageData
|
|
10
|
+
.getPointData()
|
|
11
|
+
.getScalars()
|
|
12
|
+
.getData() as PixelDataTypedArray;
|
|
13
|
+
|
|
14
|
+
// if the color image is loaded with CPU previously, it loads it
|
|
15
|
+
// with RGBA, and here we need to remove the A channel from the
|
|
16
|
+
// pixel data.
|
|
17
|
+
if (image.color && image.rgba) {
|
|
18
|
+
const newPixelData = new Uint8Array(image.columns * image.rows * 3);
|
|
19
|
+
for (let i = 0; i < image.columns * image.rows; i++) {
|
|
20
|
+
newPixelData[i * 3] = pixelData[i * 4];
|
|
21
|
+
newPixelData[i * 3 + 1] = pixelData[i * 4 + 1];
|
|
22
|
+
newPixelData[i * 3 + 2] = pixelData[i * 4 + 2];
|
|
23
|
+
}
|
|
24
|
+
// modify the image object to have the correct pixel data for later
|
|
25
|
+
// use.
|
|
26
|
+
image.rgba = false;
|
|
27
|
+
image.getPixelData = () => newPixelData;
|
|
28
|
+
scalarData.set(newPixelData);
|
|
29
|
+
} else {
|
|
30
|
+
scalarData.set(pixelData);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Trigger modified on the VTK Object so the texture is updated
|
|
34
|
+
// TODO: evaluate directly changing things with texSubImage3D later
|
|
35
|
+
sourceImageData.modified();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { updateVTKImageDataWithCornerstoneImage };
|