All files / packages/tools/src/utilities/segmentation getSegmentAtLabelmapBorder.ts

0% Statements 0/48
0% Branches 0/22
0% Functions 0/7
0% Lines 0/48

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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181                                                                                                                                                                                                                                                                                                                                                                         
import { cache, utilities } from '@cornerstonejs/core';
import type { Types } from '@cornerstonejs/core';
import {
  getSegmentation,
  getSegmentationIdRepresentations,
} from '../../stateManagement/segmentation/segmentationState';
import {
  LabelmapSegmentationDataStack,
  LabelmapSegmentationDataVolume,
} from '../../types/LabelmapTypes';
import { isVolumeSegmentation } from '../../tools/segmentation/strategies/utils/stackVolumeCheck';
 
type Options = {
  viewport?: Types.IViewport;
  searchRadius?: number;
};
 
/**
 * Retrieves the segment index at the border of a labelmap in a segmentation.
 *
 * @param segmentationId - The ID of the segmentation.
 * @param worldPoint - The world coordinates of the point.
 * @param options - Additional options.
 * @param options.viewport - The viewport to use.
 * @param options.searchRadius - The search radius to use.
 * @returns The segment index at the labelmap border, or undefined if not found.
 */
export function getSegmentAtLabelmapBorder(
  segmentationId: string,
  worldPoint: Types.Point3,
  { viewport, searchRadius }: Options
): number {
  const segmentation = getSegmentation(segmentationId);
 
  const labelmapData = segmentation.representationData.LABELMAP;
 
  if (isVolumeSegmentation(labelmapData)) {
    const { volumeId } = labelmapData as LabelmapSegmentationDataVolume;
    const segmentationVolume = cache.getVolume(volumeId);
 
    if (!segmentationVolume) {
      return;
    }
 
    const imageData = segmentationVolume.imageData;
 
    const segmentIndex = imageData.getScalarValueFromWorld(worldPoint);
 
    const canvasPoint = viewport.worldToCanvas(worldPoint);
 
    const onEdge = isSegmentOnEdgeCanvas(
      canvasPoint as Types.Point2,
      segmentIndex,
      viewport,
      imageData,
      searchRadius
    );
 
    return onEdge ? segmentIndex : undefined;
  }
 
  // stack segmentation case
  const { imageIdReferenceMap } = labelmapData as LabelmapSegmentationDataStack;
 
  const currentImageId = (viewport as Types.IStackViewport).getCurrentImageId();
 
  const segmentationImageId = imageIdReferenceMap.get(currentImageId);
  const image = cache.getImage(segmentationImageId);
 
  if (!image) {
    return;
  }
 
  // find the first segmentationRepresentationUID for the segmentationId, since
  // that is what we use as actorUID in the viewport
 
  const segmentationRepresentations = getSegmentationIdRepresentations(
    segmentation.segmentationId
  );
 
  const { segmentationRepresentationUID } = segmentationRepresentations[0];
  const segmentationActor = viewport.getActor(segmentationRepresentationUID);
  const imageData = segmentationActor?.actor.getMapper().getInputData();
  const indexIJK = utilities.transformWorldToIndex(imageData, worldPoint);
 
  const dimensions = imageData.getDimensions();
  const voxelManager = (imageData.voxelManager ||
    utilities.VoxelManager.createVolumeVoxelManager(
      dimensions,
      imageData.getPointData().getScalars().getData()
    )) as utilities.VoxelManager<number>;
 
  const segmentIndex = voxelManager.getAtIJKPoint(indexIJK as Types.Point3);
 
  const onEdge = isSegmentOnEdgeIJK(
    indexIJK as Types.Point3,
    dimensions,
    voxelManager,
    segmentIndex
  );
 
  return onEdge ? segmentIndex : undefined;
}
 
/**
 * Checks if a segment is on the edge of a labelmap.
 * @param getNeighborIndex - A function that returns the neighbor index given the delta values.
 * @param segmentIndex - The index of the segment to check.
 * @param searchRadius - The radius within which to search for neighboring segments. Default is 1.
 * @returns A boolean indicating whether the segment is on the edge.
 */
function isSegmentOnEdge(
  getNeighborIndex: (
    deltaI: number,
    deltaJ: number,
    deltaK: number
  ) => number | undefined,
  segmentIndex: number,
  searchRadius = 1 // Default search radius
): boolean {
  const neighborRange = Array.from(
    { length: 2 * searchRadius + 1 },
    (_, i) => i - searchRadius
  );
 
  for (const deltaI of neighborRange) {
    for (const deltaJ of neighborRange) {
      for (const deltaK of neighborRange) {
        if (deltaI === 0 && deltaJ === 0 && deltaK === 0) {
          continue; // Skipping the central point
        }
 
        const neighborIndex = getNeighborIndex(deltaI, deltaJ, deltaK);
 
        if (neighborIndex !== undefined && segmentIndex !== neighborIndex) {
          return true; // On the edge
        }
      }
    }
  }
 
  return false; // No edge neighbors found
}
 
function isSegmentOnEdgeIJK(
  indexIJK: Types.Point3,
  dimensions: Types.Point3,
  voxelManager: any,
  segmentIndex: number,
  searchRadius?: number
): boolean {
  const getNeighborIndex = (deltaI: number, deltaJ: number, deltaK: number) => {
    const neighborIJK = [
      indexIJK[0] + deltaI,
      indexIJK[1] + deltaJ,
      indexIJK[2] + deltaK,
    ];
 
    return voxelManager.getAtIJK(...neighborIJK);
  };
 
  return isSegmentOnEdge(getNeighborIndex, segmentIndex, searchRadius);
}
 
function isSegmentOnEdgeCanvas(
  canvasPoint: Types.Point2,
  segmentIndex: number,
  viewport: Types.IViewport,
  imageData: any,
  searchRadius?: number
): boolean {
  const getNeighborIndex = (deltaI: number, deltaJ: number) => {
    const neighborCanvas = [canvasPoint[0] + deltaI, canvasPoint[1] + deltaJ];
 
    const worldPoint = viewport.canvasToWorld(neighborCanvas as Types.Point2);
    return imageData.getScalarValueFromWorld(worldPoint);
  };
 
  return isSegmentOnEdge(getNeighborIndex, segmentIndex, searchRadius);
}