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

42.37% Statements 25/59
42.85% Branches 12/28
25% Functions 4/16
43.1% Lines 25/58

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          1x                 1x 10x 10x 1x       1x               2x 2x 1x   1x                       2x 2x 1x     1x 1x             1x         1x 1x                 1x 1x                                                                                     1x 1x           1x 1x         1x                        
import { Types, cache } from '@cornerstonejs/core';
import { getSegmentation } from '../../stateManagement/segmentation/segmentationState';
import { isVolumeSegmentation } from '../../tools/segmentation/strategies/utils/stackVolumeCheck';
import { SegmentationRepresentations } from '../../enums';
 
const segmentIndicesCache = new Map<
  string,
  { indices: number[]; isDirty: boolean }
>();
 
/**
 * Sets the segmentation as dirty, indicating that it needs to be updated.
 * @param segmentationId - The ID of the segmentation.
 */
export const setSegmentationDirty = (segmentationId: string) => {
  const cached = segmentIndicesCache.get(segmentationId);
  if (cached) {
    cached.isDirty = true;
  }
};
 
export const setSegmentationClean = (segmentationId: string) => {
  const cached = segmentIndicesCache.get(segmentationId);
  if (cached) {
    cached.isDirty = false;
  }
};
 
function getCachedSegmentIndices(segmentationId) {
  const cached = segmentIndicesCache.get(segmentationId);
  if (cached && !cached.isDirty) {
    return cached.indices;
  }
  return null;
}
 
/**
 * Retrieves the unique segment indices from a given segmentation.
 *
 * @param segmentationId - The ID of the segmentation.
 * @returns An array of unique segment indices.
 * @throws If no geometryIds are found for the segmentationId.
 */
function getUniqueSegmentIndices(segmentationId) {
  // Attempt to fetch from cache first
  const cachedResult = getCachedSegmentIndices(segmentationId);
  if (cachedResult) {
    return cachedResult;
  }
 
  const segmentation = getSegmentation(segmentationId);
  Iif (!segmentation) {
    throw new Error(
      `No segmentation found for segmentationId ${segmentationId}`
    );
  }
 
  let indices;
  switch (segmentation.type) {
    case SegmentationRepresentations.Labelmap:
      indices = handleLabelmapSegmentation(segmentation, segmentationId);
      break;
    case SegmentationRepresentations.Contour:
      indices = handleContourSegmentation(segmentation);
      break;
    case SegmentationRepresentations.Surface:
      indices = handleSurfaceSegmentation(segmentation);
      break;
    default:
      throw new Error(`Unsupported segmentation type: ${segmentation.type}`);
  }
 
  // Update cache
  segmentIndicesCache.set(segmentationId, { indices, isDirty: false });
  return indices;
}
 
function handleLabelmapSegmentation(segmentation, segmentationId) {
  const labelmapData =
    segmentation.representationData[SegmentationRepresentations.Labelmap];
  const keySet = new Set();
 
  if (isVolumeSegmentation(labelmapData)) {
    addVolumeSegmentIndices(keySet, segmentationId);
  } else {
    addImageSegmentIndices(keySet, labelmapData.imageIdReferenceMap);
  }
 
  return Array.from(keySet)
    .map(Number)
    .sort((a, b) => a - b);
}
 
function addVolumeSegmentIndices(keySet, segmentationId) {
  const volume = cache.getVolume(segmentationId);
  const scalarData = volume.getScalarData();
  scalarData.forEach((segmentIndex) => {
    if (segmentIndex !== 0) {
      keySet.add(segmentIndex);
    }
  });
}
 
function addImageSegmentIndices(keySet, imageIdReferenceMap) {
  imageIdReferenceMap.forEach((segmentationImageId) => {
    const image = cache.getImage(segmentationImageId);
    const scalarData = image.getPixelData();
    scalarData.forEach((segmentIndex) => {
      if (segmentIndex !== 0) {
        keySet.add(segmentIndex);
      }
    });
  });
}
 
function handleContourSegmentation(segmentation) {
  const { annotationUIDsMap, geometryIds } =
    segmentation.representationData.CONTOUR || {};
  Iif (!geometryIds) {
    throw new Error(
      `No geometryIds found for segmentationId ${segmentation.segmentationId}`
    );
  }
 
  const indices = new Set([...annotationUIDsMap.keys()]);
  geometryIds.forEach((geometryId) => {
    const geometry = cache.getGeometry(geometryId);
    indices.add((geometry.data as Types.IContourSet).getSegmentIndex());
  });
 
  return Array.from(indices).sort((a, b) => a - b);
}
 
function handleSurfaceSegmentation(segmentation) {
  const geometryIds =
    segmentation.representationData.SURFACE?.geometryIds ?? [];
  return Array.from(geometryIds.keys())
    .map(Number)
    .sort((a, b) => a - b);
}
 
export { getUniqueSegmentIndices };