UNPKG

@cornerstonejs/core

Version:
244 lines (243 loc) • 9.59 kB
import '@kitware/vtk.js/Rendering/Profiles/Volume.js'; import { ImageVolume } from '../cache/classes/ImageVolume.js'; import cache from '../cache/cache.js'; import Events from '../enums/Events.js'; import eventTarget from '../eventTarget.js'; import triggerEvent from '../utilities/triggerEvent.js'; import { deepClone } from '../utilities/deepClone.js'; import uuidv4 from '../utilities/uuidv4.js'; import VoxelManager from '../utilities/VoxelManager.js'; import { createAndCacheLocalImage, createAndCacheDerivedImages, } from './imageLoader.js'; import { generateVolumePropsFromImageIds } from '../utilities/generateVolumePropsFromImageIds.js'; import { cornerstoneStreamingImageVolumeLoader } from './cornerstoneStreamingImageVolumeLoader.js'; const volumeLoaders = {}; let unknownVolumeLoader = cornerstoneStreamingImageVolumeLoader; function loadVolumeFromVolumeLoader(volumeId, options) { const colonIndex = volumeId.indexOf(':'); const scheme = volumeId.substring(0, colonIndex); let loader = volumeLoaders[scheme]; if (loader === undefined || loader === null) { if (unknownVolumeLoader == null || typeof unknownVolumeLoader !== 'function') { throw new Error(`No volume loader for scheme ${scheme} has been registered`); } loader = unknownVolumeLoader; } const volumeLoadObject = loader(volumeId, options); volumeLoadObject.promise.then(function (volume) { triggerEvent(eventTarget, Events.VOLUME_LOADED, { volume }); }, function (error) { const errorObject = { volumeId, error, }; triggerEvent(eventTarget, Events.VOLUME_LOADED_FAILED, errorObject); }); return volumeLoadObject; } export function loadVolume(volumeId, options = { imageIds: [] }) { if (volumeId === undefined) { throw new Error('loadVolume: parameter volumeId must not be undefined'); } let volumeLoadObject = cache.getVolumeLoadObject(volumeId); if (volumeLoadObject !== undefined) { return volumeLoadObject.promise; } volumeLoadObject = loadVolumeFromVolumeLoader(volumeId, options); return volumeLoadObject.promise.then((volume) => { return volume; }); } export async function createAndCacheVolume(volumeId, options) { if (volumeId === undefined) { throw new Error('createAndCacheVolume: parameter volumeId must not be undefined'); } let volumeLoadObject = cache.getVolumeLoadObject(volumeId); if (volumeLoadObject !== undefined) { return volumeLoadObject.promise; } volumeLoadObject = loadVolumeFromVolumeLoader(volumeId, options); cache.putVolumeLoadObject(volumeId, volumeLoadObject); return volumeLoadObject.promise; } export function createAndCacheDerivedVolume(referencedVolumeId, options) { const referencedVolume = cache.getVolume(referencedVolumeId); if (!referencedVolume) { throw new Error(`Cannot created derived volume: Referenced volume with id ${referencedVolumeId} does not exist.`); } let { volumeId } = options; const { voxelRepresentation } = options; if (volumeId === undefined) { volumeId = uuidv4(); } const { metadata, dimensions, spacing, origin, direction } = referencedVolume; const referencedImageIds = referencedVolume.isDynamicVolume() ? referencedVolume.getCurrentDimensionGroupImageIds() : (referencedVolume.imageIds ?? []); const derivedImages = createAndCacheDerivedImages(referencedImageIds, { targetBuffer: options.targetBuffer, voxelRepresentation, }); const dataType = derivedImages[0].dataType; const derivedVolumeImageIds = derivedImages.map((image) => image.imageId); const derivedVolume = new ImageVolume({ volumeId, dataType, metadata: deepClone(metadata), dimensions: [dimensions[0], dimensions[1], dimensions[2]], spacing, origin, direction, referencedVolumeId, imageIds: derivedVolumeImageIds, referencedImageIds: referencedVolume.imageIds ?? [], }); cache.putVolumeSync(volumeId, derivedVolume); return derivedVolume; } export async function createAndCacheVolumeFromImages(volumeId, imageIds) { if (imageIds === undefined) { throw new Error('createAndCacheVolumeFromImages: parameter imageIds must not be undefined'); } if (volumeId === undefined) { throw new Error('createAndCacheVolumeFromImages: parameter volumeId must not be undefined'); } const cachedVolume = cache.getVolume(volumeId); if (cachedVolume) { return cachedVolume; } const imageIdsToLoad = imageIds.filter((imageId) => !cache.getImage(imageId)); if (imageIdsToLoad.length === 0) { return createAndCacheVolumeFromImagesSync(volumeId, imageIds); } const volume = (await createAndCacheVolume(volumeId, { imageIds, })); return volume; } export function createAndCacheVolumeFromImagesSync(volumeId, imageIds) { if (imageIds === undefined) { throw new Error('createAndCacheVolumeFromImagesSync: parameter imageIds must not be undefined'); } if (volumeId === undefined) { throw new Error('createAndCacheVolumeFromImagesSync: parameter volumeId must not be undefined'); } const cachedVolume = cache.getVolume(volumeId); if (cachedVolume) { return cachedVolume; } const volumeProps = generateVolumePropsFromImageIds(imageIds, volumeId); const derivedVolume = new ImageVolume({ volumeId, dataType: volumeProps.dataType, metadata: deepClone(volumeProps.metadata), dimensions: volumeProps.dimensions, spacing: volumeProps.spacing, origin: volumeProps.origin, direction: volumeProps.direction, referencedVolumeId: volumeProps.referencedVolumeId, imageIds: volumeProps.imageIds, referencedImageIds: volumeProps.referencedImageIds, }); cache.putVolumeSync(volumeId, derivedVolume); return derivedVolume; } export function createLocalVolume(volumeId, options = {}) { const { metadata, dimensions, spacing, origin, direction, scalarData, referencedImageIds, referencedVolumeId, targetBuffer, preventCache = false, } = options; const cachedVolume = cache.getVolume(volumeId); if (cachedVolume) { return cachedVolume; } const sliceLength = dimensions[0] * dimensions[1]; const dataType = scalarData ? scalarData.constructor.name : (targetBuffer?.type ?? 'Float32Array'); const totalNumberOfVoxels = sliceLength * dimensions[2]; let byteLength; switch (dataType) { case 'Uint8Array': case 'Int8Array': byteLength = totalNumberOfVoxels; break; case 'Uint16Array': case 'Int16Array': byteLength = totalNumberOfVoxels * 2; break; case 'Float32Array': byteLength = totalNumberOfVoxels * 4; break; } const isCacheable = cache.isCacheable(byteLength); if (!isCacheable) { throw new Error(`Cannot created derived volume: Volume with id ${volumeId} is not cacheable.`); } const imageIds = []; const derivedImages = []; for (let i = 0; i < dimensions[2]; i++) { const imageId = `${volumeId}_slice_${i}`; imageIds.push(imageId); const sliceData = scalarData.subarray(i * sliceLength, (i + 1) * sliceLength); const derivedImage = createAndCacheLocalImage(imageId, { scalarData: sliceData, dimensions: [dimensions[0], dimensions[1]], spacing: [spacing[0], spacing[1]], origin, direction, referencedImageId: referencedImageIds?.[i], targetBuffer: { type: dataType }, }); derivedImages.push(derivedImage); } const imageVolume = new ImageVolume({ volumeId, metadata: deepClone(metadata), dimensions: [dimensions[0], dimensions[1], dimensions[2]], spacing, origin, direction, imageIds, dataType, referencedVolumeId, referencedImageIds, }); const voxelManager = VoxelManager.createImageVolumeVoxelManager({ imageIds, dimensions, numberOfComponents: 1, id: volumeId, }); imageVolume.voxelManager = voxelManager; if (!preventCache) { cache.putVolumeSync(volumeId, imageVolume); } return imageVolume; } export function registerVolumeLoader(scheme, volumeLoader) { volumeLoaders[scheme] = volumeLoader; } export function getVolumeLoaderSchemes() { return Object.keys(volumeLoaders); } export function registerUnknownVolumeLoader(volumeLoader) { const oldVolumeLoader = unknownVolumeLoader; unknownVolumeLoader = volumeLoader; return oldVolumeLoader; } export function getUnknownVolumeLoaderSchema() { return unknownVolumeLoader.name; } export function createAndCacheDerivedLabelmapVolume(referencedVolumeId, options = {}) { return createAndCacheDerivedVolume(referencedVolumeId, { ...options, targetBuffer: { type: 'Uint8Array', ...options?.targetBuffer, }, }); } export function createLocalLabelmapVolume(options, volumeId, preventCache = false) { if (!options.scalarData) { options.scalarData = new Uint8Array(options.dimensions[0] * options.dimensions[1] * options.dimensions[2]); } return createLocalVolume(volumeId, { ...options, preventCache }); }