All files / packages/tools/src/utilities/dynamicVolume getDataInTime.ts

0% Statements 0/44
0% Branches 0/16
0% Functions 0/8
0% Lines 0/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111                                                                                                                                                                                                                             
import { utilities, cache, Types } from '@cornerstonejs/core';
 
/**
 * Gets the scalar data for a series of time points for either a single
 * coordinate or a segmentation mask, it will return the an array of scalar
 * data for a single coordinate or an array of arrays for a segmentation.
 *
 * @param dynamicVolume: 4D volume to compute time point data from
 * @param options: frameNumbers: which frames to use as timepoints, if left
 * blank, gets data timepoints over all frames
 * maskVolumeId: segmentationId to get timepoint data of
 * imageCoordinate: world coordinate to get timepoint data of
 * @returns
 */
function getDataInTime(
  dynamicVolume: Types.IDynamicImageVolume,
  options: {
    frameNumbers?;
    maskVolumeId?;
    imageCoordinate?;
  }
): number[] | number[][] {
  let dataInTime;
 
  // if frameNumbers is not provided, all frames are selected
  const frames = options.frameNumbers || [
    ...Array(dynamicVolume.numTimePoints).keys(),
  ];
 
  // You only need to provide either maskVolumeId OR imageCoordinate.
  // Throws error if neither maskVolumeId or imageCoordinate is given,
  // throws error if BOTH maskVolumeId and imageCoordinate is given
  if (!options.maskVolumeId && !options.imageCoordinate) {
    throw new Error('No ROI provided');
  }
 
  if (options.maskVolumeId && options.imageCoordinate) {
    throw new Error('Please provide only one ROI');
  }
 
  if (options.maskVolumeId) {
    const segmentationVolume = cache.getVolume(options.maskVolumeId);
 
    // Get the index of every non-zero voxel in mask by mapping indexes to
    // new array, then using the array to filter
    const indexArray = segmentationVolume
      .getScalarData()
      .map((_, i) => i)
      .filter((i) => segmentationVolume.getScalarData()[i] !== 0);
    const dataInTime = _getTimePointDataMask(frames, indexArray, dynamicVolume);
 
    return dataInTime;
  }
 
  if (options.imageCoordinate) {
    const dataInTime = _getTimePointDataCoordinate(
      frames,
      options.imageCoordinate,
      dynamicVolume
    );
 
    return dataInTime;
  }
 
  return dataInTime;
}
 
function _getTimePointDataCoordinate(frames, coordinate, volume) {
  const { dimensions, imageData } = volume;
  const index = imageData.worldToIndex(coordinate);
 
  index[0] = Math.floor(index[0]);
  index[1] = Math.floor(index[1]);
  index[2] = Math.floor(index[2]);
 
  if (!utilities.indexWithinDimensions(index, dimensions)) {
    throw new Error('outside bounds');
  }
 
  // calculate offset for index
  const yMultiple = dimensions[0];
  const zMultiple = dimensions[0] * dimensions[1];
  const allScalarData = volume.getScalarDataArrays();
  const value = [];
 
  frames.forEach((frame) => {
    const activeScalarData = allScalarData[frame];
    const scalarIndex = index[2] * zMultiple + index[1] * yMultiple + index[0];
    value.push(activeScalarData[scalarIndex]);
  });
 
  return value;
}
 
function _getTimePointDataMask(frames, indexArray, volume) {
  const allScalarData = volume.getScalarDataArrays();
  const value = [];
 
  for (let i = 0; i < indexArray.length; i++) {
    const indexValues = [];
    frames.forEach((frame) => {
      const activeScalarData = allScalarData[frame];
      indexValues.push(activeScalarData[indexArray[i]]);
    });
    value.push(indexValues);
  }
  return value;
}
 
export default getDataInTime;