Skip to main content

Streaming of Volume data

With the addition of Volumes to Cornerstone3D, we are adding and maintaining Streaming-volume-image-loader which is a progressive loader for volumes. This loader is designed to accept imageIds and load them into a Volume.

Creating Volumes From Images

Since 3D Volume is composed of 2D images (in StreamingImageVolume), its volume metadata is derived from the metadata of the 2D images. Therefore, an initial call to fetch images metadata is required for this loader. This way, not only we can pre-allocate and cache a Volume in memory, but we also can render the volume as the 2D images are being loaded (progressive loading).

By pre-fetching the metadata from all images (imageIds), we don't need to create the Image object for each imageId. Instead, we can just insert the pixelData of the image is directly inserted into the volume at the correct location. This guarantees speed and memory efficiency (but comes at minimal cost of pre-fetching the metadata).

Converting volumes from/to images

StreamingImageVolume loads a volume based on a series of fetched images (2D), a Volume can implement functions to convert its 3D pixel data to 2D images without re-requesting them over the network. For instance, using convertToCornerstoneImage, StreamingImageVolume instance takes an imageId and its imageId index and return a Cornerstone Image object (ImageId Index is required since we want to locate the imageId pixelData in the 3D array and copy it over the Cornerstone Image).

This is a process that can be reverted; Cornerstone3D can create a volume from a set of imageIds if they have properties of a volume (Same FromOfReference, origin, dimension, direction and pixelSpacing).

Usage

As mentioned before, a pre-cache volume should be created before hand from the image metadata. This can be done by calling the createAndCacheVolume.

const ctVolumeId = 'cornerstoneStreamingImageVolume:CT_VOLUME';
const ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, {
imageIds: ctImageIds,
});

Then the volume can call its load method to actually load the pixel data of the images.

await ctVolume.load();

imageLoader

Since the volume loader does not need to create the Image object for each imageId in the StreamingImageVolume, it will use the skipCreateImage option internally to skip the creation of the image object. Otherwise, the volume's image loader is the same as wadors image loader written in cornerstone-wado-image-loader.

// using sharedArrayBufferImageLoader to load the images
const imageIds = ['wadors:imageId1', 'wadors:imageId2'];

const ctVolumeId = 'cornerstoneStreamingImageVolume:CT_VOLUME';

const ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, {
imageIds: ctImageIds,
});

await ctVolume.load();

Alternative implementations to consider

Although we believe our pre-fetching method for volumes ensures that the volume is loaded as fast as possible, There can be other implementations of volume loaders that don't rely on this prefetching.

Creating Volumes without pre-fetching metadata

In this scenario, each image needs to be created separately, which means each image needs to be loaded and a Cornerstone Image should be created. This is a costly operation as all the image objects are loaded in memory and a separate creation of a Volume is required from those images.

Advantages:

  • Not need for a separate metadata call to fetch the image metadata.

Disadvantages:

  • Performance cost
  • Cannot progressively load the image data, as it requires creating a new volume for each image change