@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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { IStackViewport, IVolumeViewport, Point3 } from '../types';
|
|
2
|
+
import { setVolumesForViewports } from '../RenderingEngine/helpers';
|
|
3
|
+
import {
|
|
4
|
+
createAndCacheVolume,
|
|
5
|
+
getUnknownVolumeLoaderSchema,
|
|
6
|
+
} from '../loaders/volumeLoader';
|
|
7
|
+
import { Events, OrientationAxis, ViewportType } from '../enums';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Converts a stack viewport to a volume viewport.
|
|
11
|
+
*
|
|
12
|
+
* @param params - The parameters for the conversion.
|
|
13
|
+
* @param params.viewport - The stack viewport to convert.
|
|
14
|
+
* @param params.options - The options for the conversion.
|
|
15
|
+
* @param [params.options.volumeId] - The volumeId that will get generated, it should have the volume loader schema defined if not we will use the default one.
|
|
16
|
+
* @param [params.options.viewportId] - The viewportId that will get used for new viewport. If not provided, the stack viewport id will be used.
|
|
17
|
+
* @param [params.options.background] - The background color of the volume viewport.
|
|
18
|
+
* @returns The converted volume viewport.
|
|
19
|
+
*/
|
|
20
|
+
async function convertStackToVolumeViewport({
|
|
21
|
+
viewport,
|
|
22
|
+
options,
|
|
23
|
+
}: {
|
|
24
|
+
viewport: IStackViewport;
|
|
25
|
+
options: {
|
|
26
|
+
volumeId: string;
|
|
27
|
+
viewportId?: string;
|
|
28
|
+
background?: Point3;
|
|
29
|
+
orientation?: OrientationAxis;
|
|
30
|
+
};
|
|
31
|
+
}): Promise<IVolumeViewport> {
|
|
32
|
+
const renderingEngine = viewport.getRenderingEngine();
|
|
33
|
+
|
|
34
|
+
let { volumeId } = options;
|
|
35
|
+
|
|
36
|
+
// if there is no loader schema for the volume Id, we will use the default one
|
|
37
|
+
// which we can get from the volume loader
|
|
38
|
+
if (volumeId.split(':').length === 1) {
|
|
39
|
+
const schema = getUnknownVolumeLoaderSchema();
|
|
40
|
+
volumeId = `${schema}:${volumeId}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { id, element } = viewport;
|
|
44
|
+
const viewportId = options.viewportId || id;
|
|
45
|
+
|
|
46
|
+
const imageIds = viewport.getImageIds();
|
|
47
|
+
|
|
48
|
+
// It is important to keep the camera before enabling the viewport
|
|
49
|
+
const prevCamera = viewport.getCamera();
|
|
50
|
+
|
|
51
|
+
// this will disable the stack viewport and remove it from the toolGroup
|
|
52
|
+
renderingEngine.enableElement({
|
|
53
|
+
viewportId,
|
|
54
|
+
type: ViewportType.ORTHOGRAPHIC,
|
|
55
|
+
element,
|
|
56
|
+
defaultOptions: {
|
|
57
|
+
background: options.background,
|
|
58
|
+
orientation: options.orientation,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Ideally here we should be able to just create a local volume and not use the
|
|
63
|
+
// volume louder but we don't know if the stack is already pre-cached for all its
|
|
64
|
+
// imageIds or not so we just let the loader handle it and we have cache
|
|
65
|
+
// optimizations in place to avoid fetching the same imageId if it is already
|
|
66
|
+
// cached
|
|
67
|
+
const volume = await createAndCacheVolume(volumeId, {
|
|
68
|
+
imageIds,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
volume.load();
|
|
72
|
+
|
|
73
|
+
// we should get the new viewport from the renderingEngine since the stack viewport
|
|
74
|
+
// was disabled and replaced with a volume viewport of the same id
|
|
75
|
+
const volumeViewport = <IVolumeViewport>(
|
|
76
|
+
renderingEngine.getViewport(viewportId)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
setVolumesForViewports(
|
|
80
|
+
renderingEngine,
|
|
81
|
+
[
|
|
82
|
+
{
|
|
83
|
+
volumeId,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
[viewportId]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const volumeViewportNewVolumeHandler = () => {
|
|
90
|
+
volumeViewport.setCamera({
|
|
91
|
+
...prevCamera,
|
|
92
|
+
});
|
|
93
|
+
volumeViewport.render();
|
|
94
|
+
|
|
95
|
+
element.removeEventListener(
|
|
96
|
+
Events.VOLUME_VIEWPORT_NEW_VOLUME,
|
|
97
|
+
volumeViewportNewVolumeHandler
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const addVolumeViewportNewVolumeListener = () => {
|
|
102
|
+
element.addEventListener(
|
|
103
|
+
Events.VOLUME_VIEWPORT_NEW_VOLUME,
|
|
104
|
+
volumeViewportNewVolumeHandler
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
addVolumeViewportNewVolumeListener();
|
|
109
|
+
|
|
110
|
+
volumeViewport.render();
|
|
111
|
+
|
|
112
|
+
return volumeViewport;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { convertStackToVolumeViewport };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as Types from '../types';
|
|
2
|
+
import cache, { ImageVolume } from '../cache';
|
|
3
|
+
import { ViewportType } from '../enums';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Converts a volume viewport to a stack viewport.
|
|
7
|
+
*
|
|
8
|
+
* @param params - The parameters for the conversion.
|
|
9
|
+
* @param params.viewport - The volume viewport to convert.
|
|
10
|
+
* @param params.options - The conversion options.
|
|
11
|
+
* @param [params.options.viewportId] - The new stackViewportId, If not provided, the volume viewport id will be used.
|
|
12
|
+
* @param [params.options.background] - The background color of the stack viewport.
|
|
13
|
+
* @param [params.options.decache] - Whether to decache the volume. Defaults to false.
|
|
14
|
+
*
|
|
15
|
+
* @returns The converted stack viewport.
|
|
16
|
+
*/
|
|
17
|
+
async function convertVolumeToStackViewport({
|
|
18
|
+
viewport,
|
|
19
|
+
options,
|
|
20
|
+
}: {
|
|
21
|
+
viewport: Types.IVolumeViewport;
|
|
22
|
+
options: {
|
|
23
|
+
viewportId?: string;
|
|
24
|
+
background?: Types.Point3;
|
|
25
|
+
};
|
|
26
|
+
}): Promise<Types.IStackViewport> {
|
|
27
|
+
const volumeViewport = viewport;
|
|
28
|
+
const { id, element } = volumeViewport;
|
|
29
|
+
const renderingEngine = viewport.getRenderingEngine();
|
|
30
|
+
const imageIdIndex = viewport.getCurrentImageIdIndex();
|
|
31
|
+
|
|
32
|
+
const { background } = options;
|
|
33
|
+
const viewportId = options.viewportId || id;
|
|
34
|
+
|
|
35
|
+
const actorEntry = volumeViewport.getDefaultActor();
|
|
36
|
+
const { uid: volumeId } = actorEntry;
|
|
37
|
+
const volume = cache.getVolume(volumeId) as Types.IImageVolume;
|
|
38
|
+
|
|
39
|
+
if (!(volume instanceof ImageVolume)) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'Currently, you cannot decache a volume that is not an ImageVolume. So, unfortunately, volumes such as nifti (which are basic Volume, without imageIds) cannot be decached.'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const viewportInput = {
|
|
46
|
+
viewportId,
|
|
47
|
+
type: ViewportType.STACK,
|
|
48
|
+
element,
|
|
49
|
+
defaultOptions: {
|
|
50
|
+
background,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
renderingEngine.enableElement(viewportInput);
|
|
55
|
+
|
|
56
|
+
// Get the stack viewport that was created
|
|
57
|
+
const stackViewport = <Types.IStackViewport>(
|
|
58
|
+
renderingEngine.getViewport(viewportId)
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// So here we have two scenarios that we need to handle:
|
|
62
|
+
// 1. the volume was derived from a stack and we need to decache it, this is easy
|
|
63
|
+
// since we just need purge the volume from the cache and those images will get
|
|
64
|
+
// their copy of the image back
|
|
65
|
+
// 2. It was actually a native volume and we need to decache it, this is a bit more
|
|
66
|
+
// complicated since then we need to decide on the imageIds for it to get
|
|
67
|
+
// decached to
|
|
68
|
+
const hasCachedImages = volume.imageCacheOffsetMap.size > 0;
|
|
69
|
+
// Initialize the variable to hold the final result
|
|
70
|
+
let isAllImagesCached = false;
|
|
71
|
+
|
|
72
|
+
if (hasCachedImages) {
|
|
73
|
+
// Check if every imageId in the volume is in the _imageCache
|
|
74
|
+
isAllImagesCached = volume.imageIds.every((imageId) =>
|
|
75
|
+
cache.getImage(imageId)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const volumeUsedInOtherViewports = renderingEngine
|
|
80
|
+
.getVolumeViewports()
|
|
81
|
+
.find((vp) => vp.hasVolumeId(volumeId));
|
|
82
|
+
|
|
83
|
+
volume.decache(!volumeUsedInOtherViewports && isAllImagesCached);
|
|
84
|
+
|
|
85
|
+
const stack = [...volume.imageIds].reverse();
|
|
86
|
+
|
|
87
|
+
let imageIdIndexToJump = Math.max(
|
|
88
|
+
volume.imageIds.length - imageIdIndex - 1,
|
|
89
|
+
0
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Check to see if the image is already cached or not. If it's not, we will use another
|
|
93
|
+
// nearby imageId for the first image to jump to. There seem to be a lot of side effects
|
|
94
|
+
// if we jump to an image that is not cached in stack viewport while we convert
|
|
95
|
+
// from a volume viewport. For example, if we switch back and forth between stack and volume,
|
|
96
|
+
// and then try to jump to an image that is not cached, the image will not render at
|
|
97
|
+
// all when the full volume is filled. I'm not sure why yet.
|
|
98
|
+
const imageToJump = cache.getImage(stack[imageIdIndexToJump]);
|
|
99
|
+
if (!imageToJump) {
|
|
100
|
+
let minDistance = Infinity;
|
|
101
|
+
let minDistanceIndex = null;
|
|
102
|
+
|
|
103
|
+
stack.forEach((imageId, index) => {
|
|
104
|
+
const image = cache.getImage(imageId);
|
|
105
|
+
if (image) {
|
|
106
|
+
const distance = Math.abs(imageIdIndexToJump - index);
|
|
107
|
+
if (distance < minDistance) {
|
|
108
|
+
minDistance = distance;
|
|
109
|
+
minDistanceIndex = index;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
imageIdIndexToJump = minDistanceIndex;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await stackViewport.setStack(stack, imageIdIndexToJump);
|
|
118
|
+
|
|
119
|
+
// Render the image
|
|
120
|
+
stackViewport.render();
|
|
121
|
+
|
|
122
|
+
return stackViewport;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { convertVolumeToStackViewport };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
|
|
2
2
|
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
|
|
3
3
|
import { VOIRange } from '../types';
|
|
4
|
-
import
|
|
4
|
+
import * as windowLevelUtil from './windowLevel';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A utility that can be used to generate an Sigmoid RgbTransferFunction.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { vec3 } from 'gl-matrix';
|
|
2
|
+
import { getConfiguration, getShouldUseSharedArrayBuffer } from '../init';
|
|
3
|
+
import createFloat32SharedArray from './createFloat32SharedArray';
|
|
4
|
+
import createInt16SharedArray from './createInt16SharedArray';
|
|
5
|
+
import createUint16SharedArray from './createUInt16SharedArray';
|
|
6
|
+
import createUint8SharedArray from './createUint8SharedArray';
|
|
7
|
+
import getScalingParameters from './getScalingParameters';
|
|
8
|
+
import makeVolumeMetadata from './makeVolumeMetadata';
|
|
9
|
+
import sortImageIdsAndGetSpacing from './sortImageIdsAndGetSpacing';
|
|
10
|
+
import { ImageVolumeProps, Mat3, Point3 } from '../types';
|
|
11
|
+
import cache from '../cache';
|
|
12
|
+
import { Events } from '../enums';
|
|
13
|
+
|
|
14
|
+
function generateVolumePropsFromImageIds(
|
|
15
|
+
imageIds: string[],
|
|
16
|
+
volumeId: string
|
|
17
|
+
): ImageVolumeProps {
|
|
18
|
+
const { useNorm16Texture, preferSizeOverAccuracy } =
|
|
19
|
+
getConfiguration().rendering;
|
|
20
|
+
|
|
21
|
+
const use16BitDataType = useNorm16Texture || preferSizeOverAccuracy;
|
|
22
|
+
|
|
23
|
+
const volumeMetadata = makeVolumeMetadata(imageIds);
|
|
24
|
+
|
|
25
|
+
// For a streaming volume, the data type cannot rely on cswil to load
|
|
26
|
+
// the proper array buffer type. This is because the target buffer container
|
|
27
|
+
// must be decided ahead of time.
|
|
28
|
+
// TODO: move this logic into CSWIL to avoid logic duplication.
|
|
29
|
+
// We check if scaling parameters are negative we choose Int16 instead of
|
|
30
|
+
// Uint16 for cases where BitsAllocated is 16.
|
|
31
|
+
const imageIdIndex = Math.floor(imageIds.length / 2);
|
|
32
|
+
const imageId = imageIds[imageIdIndex];
|
|
33
|
+
const scalingParameters = getScalingParameters(imageId);
|
|
34
|
+
const hasNegativeRescale =
|
|
35
|
+
scalingParameters.rescaleIntercept < 0 ||
|
|
36
|
+
scalingParameters.rescaleSlope < 0;
|
|
37
|
+
|
|
38
|
+
// The prescale is ALWAYS used with modality LUT, so we can assume that
|
|
39
|
+
// if the rescale slope is not an integer, we need to use Float32
|
|
40
|
+
const hasFloatRescale =
|
|
41
|
+
scalingParameters.rescaleIntercept % 1 !== 0 ||
|
|
42
|
+
scalingParameters.rescaleSlope % 1 !== 0;
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
BitsAllocated,
|
|
46
|
+
PixelRepresentation,
|
|
47
|
+
PhotometricInterpretation,
|
|
48
|
+
ImageOrientationPatient,
|
|
49
|
+
PixelSpacing,
|
|
50
|
+
Columns,
|
|
51
|
+
Rows,
|
|
52
|
+
} = volumeMetadata;
|
|
53
|
+
|
|
54
|
+
const rowCosineVec = vec3.fromValues(
|
|
55
|
+
ImageOrientationPatient[0],
|
|
56
|
+
ImageOrientationPatient[1],
|
|
57
|
+
ImageOrientationPatient[2]
|
|
58
|
+
);
|
|
59
|
+
const colCosineVec = vec3.fromValues(
|
|
60
|
+
ImageOrientationPatient[3],
|
|
61
|
+
ImageOrientationPatient[4],
|
|
62
|
+
ImageOrientationPatient[5]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const scanAxisNormal = vec3.create();
|
|
66
|
+
|
|
67
|
+
vec3.cross(scanAxisNormal, rowCosineVec, colCosineVec);
|
|
68
|
+
|
|
69
|
+
const { zSpacing, origin, sortedImageIds } = sortImageIdsAndGetSpacing(
|
|
70
|
+
imageIds,
|
|
71
|
+
scanAxisNormal
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const numFrames = imageIds.length;
|
|
75
|
+
|
|
76
|
+
// Spacing goes [1] then [0], as [1] is column spacing (x) and [0] is row spacing (y)
|
|
77
|
+
const spacing = <Point3>[PixelSpacing[1], PixelSpacing[0], zSpacing];
|
|
78
|
+
const dimensions = <Point3>[Columns, Rows, numFrames];
|
|
79
|
+
const direction = [
|
|
80
|
+
...rowCosineVec,
|
|
81
|
+
...colCosineVec,
|
|
82
|
+
...scanAxisNormal,
|
|
83
|
+
] as Mat3;
|
|
84
|
+
const signed = PixelRepresentation === 1;
|
|
85
|
+
const numComponents = PhotometricInterpretation === 'RGB' ? 3 : 1;
|
|
86
|
+
const useSharedArrayBuffer = getShouldUseSharedArrayBuffer();
|
|
87
|
+
const length = dimensions[0] * dimensions[1] * dimensions[2];
|
|
88
|
+
const handleCache = (sizeInBytes) => {
|
|
89
|
+
if (!cache.isCacheable(sizeInBytes)) {
|
|
90
|
+
throw new Error(Events.CACHE_SIZE_EXCEEDED);
|
|
91
|
+
}
|
|
92
|
+
cache.decacheIfNecessaryUntilBytesAvailable(sizeInBytes);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
let scalarData, sizeInBytes;
|
|
96
|
+
switch (BitsAllocated) {
|
|
97
|
+
case 8:
|
|
98
|
+
if (signed) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
'8 Bit signed images are not yet supported by this plugin.'
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
sizeInBytes = length * numComponents;
|
|
104
|
+
handleCache(sizeInBytes);
|
|
105
|
+
scalarData = useSharedArrayBuffer
|
|
106
|
+
? createUint8SharedArray(length * numComponents)
|
|
107
|
+
: new Uint8Array(length * numComponents);
|
|
108
|
+
break;
|
|
109
|
+
|
|
110
|
+
case 16:
|
|
111
|
+
// Temporary fix for 16 bit images to use Float32
|
|
112
|
+
// until the new dicom image loader handler the conversion
|
|
113
|
+
// correctly
|
|
114
|
+
if (!use16BitDataType || hasFloatRescale) {
|
|
115
|
+
sizeInBytes = length * 4;
|
|
116
|
+
scalarData = useSharedArrayBuffer
|
|
117
|
+
? createFloat32SharedArray(length)
|
|
118
|
+
: new Float32Array(length);
|
|
119
|
+
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
sizeInBytes = length * 2;
|
|
124
|
+
if (signed || hasNegativeRescale) {
|
|
125
|
+
handleCache(sizeInBytes);
|
|
126
|
+
scalarData = useSharedArrayBuffer
|
|
127
|
+
? createInt16SharedArray(length)
|
|
128
|
+
: new Int16Array(length);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!signed && !hasNegativeRescale) {
|
|
133
|
+
handleCache(sizeInBytes);
|
|
134
|
+
scalarData = useSharedArrayBuffer
|
|
135
|
+
? createUint16SharedArray(length)
|
|
136
|
+
: new Uint16Array(length);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Default to Float32 again
|
|
141
|
+
sizeInBytes = length * 4;
|
|
142
|
+
handleCache(sizeInBytes);
|
|
143
|
+
scalarData = useSharedArrayBuffer
|
|
144
|
+
? createFloat32SharedArray(length)
|
|
145
|
+
: new Float32Array(length);
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case 24:
|
|
149
|
+
sizeInBytes = length * numComponents;
|
|
150
|
+
handleCache(sizeInBytes);
|
|
151
|
+
|
|
152
|
+
// hacky because we don't support alpha channel in dicom
|
|
153
|
+
scalarData = useSharedArrayBuffer
|
|
154
|
+
? createUint8SharedArray(length * numComponents)
|
|
155
|
+
: new Uint8Array(length * numComponents);
|
|
156
|
+
break;
|
|
157
|
+
case 32:
|
|
158
|
+
sizeInBytes = length * 4;
|
|
159
|
+
handleCache(sizeInBytes);
|
|
160
|
+
scalarData = useSharedArrayBuffer
|
|
161
|
+
? createFloat32SharedArray(length)
|
|
162
|
+
: new Float32Array(length);
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Bits allocated of ${BitsAllocated} is not defined to generate scalarData for the volume.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
dimensions,
|
|
172
|
+
spacing,
|
|
173
|
+
origin,
|
|
174
|
+
direction,
|
|
175
|
+
scalarData,
|
|
176
|
+
sizeInBytes,
|
|
177
|
+
metadata: volumeMetadata,
|
|
178
|
+
imageIds: sortedImageIds,
|
|
179
|
+
volumeId,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { generateVolumePropsFromImageIds };
|
package/src/utilities/index.ts
CHANGED
|
@@ -61,13 +61,18 @@ import decimate from './decimate';
|
|
|
61
61
|
import imageRetrieveMetadataProvider from './imageRetrieveMetadataProvider';
|
|
62
62
|
import isVideoTransferSyntax from './isVideoTransferSyntax';
|
|
63
63
|
import { getBufferConfiguration } from './getBufferConfiguration';
|
|
64
|
+
import { generateVolumePropsFromImageIds } from './generateVolumePropsFromImageIds';
|
|
65
|
+
import { convertStackToVolumeViewport } from './convertStackToVolumeViewport';
|
|
66
|
+
import { convertVolumeToStackViewport } from './convertVolumeToStackViewport';
|
|
64
67
|
import VoxelManager from './VoxelManager';
|
|
68
|
+
import roundNumber, { roundToPrecision } from './roundNumber';
|
|
65
69
|
|
|
66
70
|
// name spaces
|
|
67
71
|
import * as planar from './planar';
|
|
68
72
|
import * as windowLevel from './windowLevel';
|
|
69
73
|
import * as colormap from './colormap';
|
|
70
74
|
import * as transferFunctionUtils from './transferFunctionUtils';
|
|
75
|
+
import * as cacheUtils from './cacheUtils';
|
|
71
76
|
|
|
72
77
|
export {
|
|
73
78
|
eventListener,
|
|
@@ -139,4 +144,10 @@ export {
|
|
|
139
144
|
isVideoTransferSyntax,
|
|
140
145
|
getBufferConfiguration,
|
|
141
146
|
VoxelManager,
|
|
147
|
+
generateVolumePropsFromImageIds,
|
|
148
|
+
convertStackToVolumeViewport,
|
|
149
|
+
convertVolumeToStackViewport,
|
|
150
|
+
cacheUtils,
|
|
151
|
+
roundNumber,
|
|
152
|
+
roundToPrecision,
|
|
142
153
|
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { EPSILON } from '../constants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Truncates decimal points to that there is at least 1+precision significant
|
|
5
|
+
* digits.
|
|
6
|
+
*
|
|
7
|
+
* For example, with the default precision 2 (3 significant digits)
|
|
8
|
+
* * Values larger than 100 show no information after the decimal point
|
|
9
|
+
* * Values between 10 and 99 show 1 decimal point
|
|
10
|
+
* * Values between 1 and 9 show 2 decimal points
|
|
11
|
+
*
|
|
12
|
+
* @param value - to return a fixed measurement value from
|
|
13
|
+
* @param precision - defining how many digits after 1..9 are desired
|
|
14
|
+
*/
|
|
15
|
+
function roundNumber(
|
|
16
|
+
value: string | number | (string | number)[],
|
|
17
|
+
precision = 2
|
|
18
|
+
): string {
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return value.map((v) => roundNumber(v, precision)).join(', ');
|
|
21
|
+
}
|
|
22
|
+
if (value === undefined || value === null || value === '') {
|
|
23
|
+
return 'NaN';
|
|
24
|
+
}
|
|
25
|
+
value = Number(value);
|
|
26
|
+
if (value < 0.0001) {
|
|
27
|
+
return `${value}`;
|
|
28
|
+
}
|
|
29
|
+
const fixedPrecision =
|
|
30
|
+
value >= 100
|
|
31
|
+
? precision - 2
|
|
32
|
+
: value >= 10
|
|
33
|
+
? precision - 1
|
|
34
|
+
: value >= 1
|
|
35
|
+
? precision
|
|
36
|
+
: value >= 0.1
|
|
37
|
+
? precision + 1
|
|
38
|
+
: value >= 0.01
|
|
39
|
+
? precision + 2
|
|
40
|
+
: value >= 0.001
|
|
41
|
+
? precision + 3
|
|
42
|
+
: precision + 4;
|
|
43
|
+
return value.toFixed(fixedPrecision);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Rounds a number to the nearest multiple of EPSILON.
|
|
48
|
+
* @param value - The number to round.
|
|
49
|
+
* @returns The rounded number.
|
|
50
|
+
*/
|
|
51
|
+
function roundToPrecision(value) {
|
|
52
|
+
return Math.round(value / EPSILON) * EPSILON;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { roundToPrecision };
|
|
56
|
+
export default roundNumber;
|