All files / labelmap-interpolation/src/workers interpolationWorker.js

0% Statements 0/71
0% Branches 0/13
0% Functions 0/3
0% Lines 0/70

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                                                                                                                                                                                                                                                                                                                                                             
import { expose } from 'comlink';
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
 
/**
 * Dynamically imports ITK WASM modules needed for labelmap interpolation
 * @param moduleId - The module ID to import ('itk-wasm' or '@itk-wasm/morphological-contour-interpolation')
 * @returns Promise that resolves to the imported module
 */
async function peerImport(moduleId) {
  try {
    switch (moduleId) {
      case 'itk-wasm':
        return import('itk-wasm');
      case '@itk-wasm/morphological-contour-interpolation':
        return import('@itk-wasm/morphological-contour-interpolation');
      default:
        throw new Error(`Unknown module ID: ${moduleId}`);
    }
  } catch (error) {
    console.warn(`Error importing ${moduleId}:`, error);
    return null;
  }
}
 
const computeWorker = {
  getITKImage: async (args) => {
    const { imageData, options } = args;
 
    const { imageName, scalarData } = options;
 
    let Image, ImageType, IntTypes, FloatTypes, PixelTypes;
 
    try {
      const itkModule = await peerImport('itk-wasm');
      if (!itkModule) {
        throw new Error('Module not found');
      }
      ({ Image, ImageType, IntTypes, FloatTypes, PixelTypes } = itkModule);
    } catch (error) {
      console.warn(
        "Warning: 'itk-wasm' module not found. Please install it separately."
      );
      return null;
    }
 
    const dataTypesMap = {
      Int8: IntTypes.Int8,
      UInt8: IntTypes.UInt8,
      Int16: IntTypes.Int16,
      UInt16: IntTypes.UInt16,
      Int32: IntTypes.Int32,
      UInt32: IntTypes.UInt32,
      Int64: IntTypes.Int64,
      UInt64: IntTypes.UInt64,
      Float32: FloatTypes.Float32,
      Float64: FloatTypes.Float64,
    };
 
    const { numberOfComponents } = imageData.get('numberOfComponents');
 
    const dimensions = imageData.getDimensions();
    const origin = imageData.getOrigin();
    const spacing = imageData.getSpacing();
    const directionArray = imageData.getDirection();
    const direction = new Float64Array(directionArray);
    const dataType = scalarData.constructor.name
      .replace(/^Ui/, 'UI')
      .replace(/Array$/, '');
    const metadata = undefined;
 
    const imageType = new ImageType(
      dimensions.length,
      dataTypesMap[dataType],
      PixelTypes.Scalar,
      numberOfComponents
    );
 
    const image = new Image(imageType);
    image.name = imageName;
    image.origin = origin;
    image.spacing = spacing;
    image.direction = direction;
    image.size = dimensions;
    image.metadata = metadata;
    image.data = scalarData;
 
    return image;
  },
  interpolateLabelmap: async (args) => {
    const { segmentationInfo, configuration } = args;
    const { scalarData, dimensions, spacing, origin, direction } =
      segmentationInfo;
 
    let itkModule;
    try {
      itkModule = await peerImport(
        '@itk-wasm/morphological-contour-interpolation'
      );
      if (!itkModule) {
        throw new Error('Module not found');
      }
    } catch (error) {
      console.warn(
        "Warning: '@itk-wasm/morphological-contour-interpolation' module not found. Please install it separately."
      );
      return { data: scalarData };
    }
 
    const imageData = vtkImageData.newInstance();
    imageData.setDimensions(dimensions);
    imageData.setOrigin(origin);
    imageData.setDirection(direction || [1, 0, 0, 0, 1, 0, 0, 0, 1]);
    imageData.setSpacing(spacing);
 
    const scalarArray = vtkDataArray.newInstance({
      name: 'Pixels',
      numberOfComponents: 1,
      values: scalarData,
    });
 
    imageData.getPointData().setScalars(scalarArray);
    imageData.modified();
 
    try {
      const inputImage = await computeWorker.getITKImage({
        imageData,
        options: {
          imageName: 'interpolation',
          scalarData: scalarData,
        },
      });
 
      if (!inputImage) {
        throw new Error('Failed to get ITK image');
      }
 
      const { outputImage } = await itkModule.morphologicalContourInterpolation(
        inputImage,
        {
          ...configuration,
          // since we already have a web worker, we don't need to create another one
          webWorker: false,
        }
      );
 
      const outputScalarData = outputImage.data;
      const modifiedScalarData = new Uint16Array(scalarData.length);
 
      // Copy the original data first
      modifiedScalarData.set(scalarData);
 
      // Only update values that are different
      for (let i = 0; i < outputScalarData.length; i++) {
        const newValue = outputScalarData[i];
        const originalValue = scalarData[i];
 
        if (newValue !== originalValue) {
          modifiedScalarData[i] = newValue;
        }
      }
 
      return { data: modifiedScalarData };
    } catch (error) {
      console.error(error);
      console.warn(
        'Warning: Failed to perform morphological contour interpolation'
      );
      return { data: scalarData };
    }
  },
};
 
expose(computeWorker);