@cornerstonejs/core
Version:
Cornerstone3D Core
244 lines (243 loc) • 9.59 kB
JavaScript
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 });
}