All files / packages/tools/src/stateManagement/segmentation/polySeg/Labelmap labelmapComputationStrategies.ts

0% Statements 0/46
0% Branches 0/35
0% Functions 0/4
0% Lines 0/46

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 182 183                                                                                                                                                                                                                                                                                                                                                                             
import { VolumeViewport, volumeLoader, utilities } from '@cornerstonejs/core';
import type { Types } from '@cornerstonejs/core';
import { getUniqueSegmentIndices } from '../../../../utilities/segmentation';
import { getSegmentation } from '../../segmentationState';
import {
  LabelmapSegmentationDataStack,
  LabelmapSegmentationDataVolume,
} from '../../../../types/LabelmapTypes';
import {
  convertContourToStackLabelmap,
  convertContourToVolumeLabelmap,
} from './convertContourToLabelmap';
import { convertSurfaceToVolumeLabelmap } from './convertSurfaceToLabelmap';
import { computeStackSegmentationFromVolume } from '../../convertVolumeToStackSegmentation';
import { PolySegConversionOptions } from '../../../../types';
 
export type RawLabelmapData =
  | LabelmapSegmentationDataVolume
  | LabelmapSegmentationDataStack;
 
export async function computeLabelmapData(
  segmentationId: string,
  options: PolySegConversionOptions = {}
) {
  const segmentIndices = options.segmentIndices?.length
    ? options.segmentIndices
    : getUniqueSegmentIndices(segmentationId);
 
  let rawLabelmapData: RawLabelmapData;
  const segmentation = getSegmentation(segmentationId);
  const representationData = segmentation.representationData;
 
  try {
    if (representationData.CONTOUR) {
      rawLabelmapData = await computeLabelmapFromContourSegmentation(
        segmentationId,
        {
          segmentIndices,
          ...options,
        }
      );
    } else if (representationData.SURFACE) {
      rawLabelmapData = await computeLabelmapFromSurfaceSegmentation(
        segmentation.segmentationId,
        {
          segmentIndices,
          ...options,
        }
      );
    }
  } catch (error) {
    console.error(error);
    throw error;
  }
 
  if (!rawLabelmapData) {
    throw new Error(
      'Not enough data to convert to surface, currently only support converting volume labelmap to surface if available'
    );
  }
 
  return rawLabelmapData;
}
 
async function computeLabelmapFromContourSegmentation(
  segmentationId,
  options: PolySegConversionOptions = {}
): Promise<LabelmapSegmentationDataVolume | LabelmapSegmentationDataStack> {
  const isVolume = options.viewport instanceof VolumeViewport ?? true;
 
  if (isVolume && !options.viewport) {
    // Todo: we don't have support for volume viewport without providing the
    // viewport, since we need to get the referenced volumeId from the viewport
    // but we can alternatively provide the volumeId directly, or even better
    // the target metadata for the volume (spacing, origin, dimensions, etc.)
    // and then we can create the volume from that
    throw new Error(
      'Cannot compute labelmap from contour segmentation without providing the viewport'
    );
  }
 
  const segmentIndices = options.segmentIndices?.length
    ? options.segmentIndices
    : getUniqueSegmentIndices(segmentationId);
 
  const segmentation = getSegmentation(segmentationId);
  const representationData = segmentation.representationData.CONTOUR;
 
  const convertFunction = isVolume
    ? convertContourToVolumeLabelmap
    : convertContourToStackLabelmap;
 
  const result = await convertFunction(representationData, {
    segmentIndices,
    segmentationRepresentationUID: options.segmentationRepresentationUID,
    viewport: options.viewport,
  });
 
  return result;
}
 
async function computeLabelmapFromSurfaceSegmentation(
  segmentationId,
  options: PolySegConversionOptions = {}
): Promise<LabelmapSegmentationDataVolume | LabelmapSegmentationDataStack> {
  const isVolume = options.viewport instanceof VolumeViewport ?? true;
 
  const segmentIndices = options.segmentIndices?.length
    ? options.segmentIndices
    : getUniqueSegmentIndices(segmentationId);
 
  const segmentation = getSegmentation(segmentationId);
 
  const segmentsGeometryIds = new Map() as Map<number, string>;
  const representationData = segmentation.representationData.SURFACE;
  representationData.geometryIds.forEach((geometryId, segmentIndex) => {
    if (segmentIndices.includes(segmentIndex)) {
      segmentsGeometryIds.set(segmentIndex, geometryId);
    }
  });
 
  if (isVolume && !options.viewport) {
    // Todo: we don't have support for volume viewport without providing the
    // viewport, since we need to get the referenced volumeId from the viewport
    // but we can alternatively provide the volumeId directly, or even better
    // the target metadata for the volume (spacing, origin, dimensions, etc.)
    // and then we can create the volume from that
    throw new Error(
      'Cannot compute labelmap from surface segmentation without providing the viewport'
    );
  }
 
  let segmentationVolume;
  if (isVolume) {
    const defaultActor = options.viewport.getDefaultActor();
    const { uid: volumeId } = defaultActor;
    segmentationVolume =
      await volumeLoader.createAndCacheDerivedSegmentationVolume(volumeId);
  } else {
    // for stack we basically need to create a volume from the stack
    // imageIds and then create a segmentation volume from that and finally
    // convert the surface to a labelmap and later on convert the labelmap
    // to a stack labelmap
    const imageIds = (options.viewport as Types.IStackViewport).getImageIds();
    const volumeId = 'generatedSegmentationVolumeId';
    const volumeProps = utilities.generateVolumePropsFromImageIds(
      imageIds,
      volumeId
    );
 
    // we don't need the imageIds for the viewport (e.g., CT), but rather
    // want to use the imageIds as a reference
    delete volumeProps.imageIds;
 
    segmentationVolume = await volumeLoader.createLocalSegmentationVolume(
      {
        ...volumeProps,
        scalarData: volumeProps.scalarData as Types.PixelDataTypedArray,
        referencedImageIds: imageIds,
      },
      volumeId
    );
  }
 
  const result = await convertSurfaceToVolumeLabelmap(
    { geometryIds: segmentsGeometryIds },
    segmentationVolume
  );
 
  if (isVolume) {
    return result;
  }
 
  // we need to convert the volume labelmap to a stack labelmap
  const stackData = (await computeStackSegmentationFromVolume({
    volumeId: segmentationVolume.volumeId,
  })) as LabelmapSegmentationDataStack;
 
  return stackData;
}
 
export { computeLabelmapFromContourSegmentation };