All files / tools/src/utilities/segmentation thresholdSegmentationByRange.ts

0% Statements 0/39
0% Branches 0/20
0% Functions 0/5
0% Lines 0/37

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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150                                                                                                                                                                                                                                                                                                           
import type { Types } from '@cornerstonejs/core';
import { triggerSegmentationDataModified } from '../../stateManagement/segmentation/triggerSegmentationEvents';
import type { ThresholdInformation, VolumeInfo } from './utilities';
import { getVoxelOverlap, processVolumes } from './utilities';
 
/**
 * It thresholds a segmentation volume based on a set of threshold values with
 * respect to a list of volumes and respective threshold ranges.
 * @param segmentationVolume - the segmentation volume to be modified
 * @param segmentationIndex - the index of the segmentation to modify
 * @param thresholdVolumeInformation - array of objects containing volume data
 * and a range (lower and upper values) to threshold
 * @param overlapType - indicates if the user requires all voxels pass
 * (overlapType = 1) or any voxel pass (overlapType = 0)
 * @returns
 */
function thresholdSegmentationByRange(
  segmentationVolume: Types.IImageVolume,
  segmentationIndex: number,
  thresholdVolumeInformation: ThresholdInformation[],
  overlapType: number,
  segmentationId: string
): Types.IImageVolume {
  if (!segmentationId) {
    throw new Error(
      'Segmentation ID is required to be passed inside thresholdSegmentationByRange'
    );
  }
  // prepare a list of volume information objects for callback functions
  const { baseVolumeIdx, volumeInfoList } = processVolumes(
    segmentationVolume,
    thresholdVolumeInformation
  );
 
  const { voxelManager } = volumeInfoList[baseVolumeIdx];
  const refVoxelManager = voxelManager;
 
  const scalarDataLength =
    segmentationVolume.voxelManager.getScalarDataLength();
 
  const segVoxelManager = segmentationVolume.voxelManager;
 
  /**
   * This function will test all overlaps between a voxel in base volume
   * (the reference for segmentation volume creation) and voxels in other
   * volumes.
   * If the segmentation volume and the image volume are the same size,
   * checks if the scalar data at each point is within the threshold values.
   * If the segmentation volume and the image volume are different sizes,
   * checks the voxel overlap
   */
  volumeInfoList.forEach((volumeInfo) => {
    const { volumeSize } = volumeInfo;
 
    if (volumeSize === scalarDataLength) {
      _handleSameSizeVolume(
        segVoxelManager,
        refVoxelManager,
        segmentationIndex,
        volumeInfo
      );
    } else {
      _handleDifferentSizeVolume(
        segVoxelManager,
        refVoxelManager,
        segmentationIndex,
        volumeInfo,
        volumeInfoList,
        baseVolumeIdx,
        overlapType
      );
    }
  });
 
  triggerSegmentationDataModified(segmentationId);
 
  return segmentationVolume;
}
 
function _handleDifferentSizeVolume(
  segVoxelManager,
  refVoxelManager,
  segmentationIndex: number,
  volumeInfo: VolumeInfo,
  volumeInfoList: VolumeInfo[],
  baseVolumeIdx: number,
  overlapType: number
) {
  const { imageData, lower, upper, dimensions } = volumeInfo;
 
  let total, overlaps, range;
 
  const segScalarDataLength = segVoxelManager.getScalarDataLength();
 
  for (let i = 0; i < segScalarDataLength; i++) {
    if (segScalarDataLength.getAtIndex(i) === segmentationIndex) {
      const overlapBounds = getVoxelOverlap(
        imageData,
        dimensions,
        volumeInfoList[baseVolumeIdx].spacing,
        volumeInfoList[baseVolumeIdx].imageData.getPoint(i)
      );
 
      const callbackOverlap = ({ value }) => {
        total = total + 1;
        if (value >= range.lower && value <= range.upper) {
          overlaps = overlaps + 1;
        }
      };
 
      total = 0;
      overlaps = 0;
      range = { lower, upper };
      let overlapTest = false;
 
      // check all voxel overlaps
      segVoxelManager.forEach(callbackOverlap, {
        imageData,
        boundsIJK: overlapBounds,
      });
 
      overlapTest = overlapType === 0 ? overlaps > 0 : overlaps === total;
      segVoxelManager.setAtIndex(i, overlapTest ? segmentationIndex : 0);
    }
  }
  return { total, range, overlaps };
}
 
function _handleSameSizeVolume(
  segVoxelManager,
  refVoxelManager,
  segmentationIndex: number,
  volumeInfo: VolumeInfo
) {
  const { lower, upper } = volumeInfo;
  const scalarDataLength = segVoxelManager.getScalarDataLength();
 
  for (let i = 0; i < scalarDataLength; i++) {
    if (segVoxelManager.getAtIndex[i] === segmentationIndex) {
      const value = refVoxelManager.getAtIndex(i);
      segVoxelManager.setAtIndex(
        i,
        value >= lower && value <= upper ? segmentationIndex : 0
      );
    }
  }
}
 
export default thresholdSegmentationByRange;