All files / extensions/dicom-microscopy/src/utils loadSR.ts

0% Statements 0/84
0% Branches 0/28
0% Functions 0/16
0% Lines 0/82

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 184 185                                                                                                                                                                                                                                                                                                                                                                                 
import dcmjs from 'dcmjs';
 
import DCM_CODE_VALUES from './dcmCodeValues';
import toArray from './toArray';
 
const MeasurementReport = dcmjs.adapters.DICOMMicroscopyViewer.MeasurementReport;
 
// Define as async so that it returns a promise, expected by the ViewportGrid
export default async function loadSR(
  microscopyService,
  microscopySRDisplaySet,
  referencedDisplaySet
) {
  const naturalizedDataset = microscopySRDisplaySet.metadata;
 
  const { StudyInstanceUID, FrameOfReferenceUID } = referencedDisplaySet;
 
  const managedViewers = microscopyService.getManagedViewersForStudy(StudyInstanceUID);
 
  if (!managedViewers || !managedViewers.length) {
    return;
  }
 
  microscopySRDisplaySet.isLoaded = true;
 
  const { rois, labels } = await _getROIsFromToolState(microscopyService, naturalizedDataset, FrameOfReferenceUID);
 
  const managedViewer = managedViewers[0];
 
  for (let i = 0; i < rois.length; i++) {
    // NOTE: When saving Microscopy SR, we are attaching identifier property
    // to each ROI, and when read for display, it is coming in as "TEXT"
    // evaluation.
    // As the Dicom Microscopy Viewer will override styles for "Text" evaluations
    // to hide all other geometries, we are going to manually remove that
    // evaluation item.
    const roi = rois[i];
    const roiSymbols = Object.getOwnPropertySymbols(roi);
    const _properties = roiSymbols.find(s => s.description === 'properties');
    const properties = roi[_properties];
    properties['evaluations'] = [];
 
    managedViewer.addRoiGraphicWithLabel(roi, labels[i]);
  }
}
 
async function _getROIsFromToolState(microscopyService, naturalizedDataset, FrameOfReferenceUID) {
  const toolState = MeasurementReport.generateToolState(naturalizedDataset);
  const tools = Object.getOwnPropertyNames(toolState);
  // Does a dynamic import to prevent webpack from rebuilding the library
  const DICOMMicroscopyViewer = await microscopyService.importDicomMicroscopyViewer();
 
  const measurementGroupContentItems = _getMeasurementGroups(naturalizedDataset);
 
  const rois = [];
  const labels = [];
 
  tools.forEach(t => {
    const toolSpecificToolState = toolState[t];
    let scoord3d;
 
    const capsToolType = t.toUpperCase();
 
    const measurementGroupContentItemsForTool = measurementGroupContentItems.filter(mg => {
      const imageRegionContentItem = toArray(mg.ContentSequence).find(
        ci => ci.ConceptNameCodeSequence.CodeValue === DCM_CODE_VALUES.IMAGE_REGION
      );
 
      return imageRegionContentItem.GraphicType === capsToolType;
    });
 
    toolSpecificToolState.forEach((coordinates, index) => {
      const properties = {};
 
      const options = {
        coordinates,
        frameOfReferenceUID: FrameOfReferenceUID,
      };
 
      if (t === 'Polygon') {
        scoord3d = new DICOMMicroscopyViewer.scoord3d.Polygon(options);
      } else if (t === 'Polyline') {
        scoord3d = new DICOMMicroscopyViewer.scoord3d.Polyline(options);
      } else if (t === 'Point') {
        scoord3d = new DICOMMicroscopyViewer.scoord3d.Point(options);
      } else if (t === 'Ellipse') {
        scoord3d = new DICOMMicroscopyViewer.scoord3d.Ellipse(options);
      } else {
        throw new Error('Unsupported tool type');
      }
 
      const measurementGroup = measurementGroupContentItemsForTool[index];
      const findingGroup = toArray(measurementGroup.ContentSequence).find(
        ci => ci.ConceptNameCodeSequence.CodeValue === DCM_CODE_VALUES.FINDING
      );
 
      const trackingGroup = toArray(measurementGroup.ContentSequence).find(
        ci => ci.ConceptNameCodeSequence.CodeValue === DCM_CODE_VALUES.TRACKING_UNIQUE_IDENTIFIER
      );
 
      /**
       * Extract presentation state from tracking identifier.
       * Currently is stored in SR but should be stored in its tags.
       */
      if (trackingGroup) {
        const regExp = /\(([^)]+)\)/;
        const matches = regExp.exec(trackingGroup.TextValue);
        if (matches && matches[1]) {
          properties.presentationState = JSON.parse(matches[1]);
          properties.marker = properties.presentationState.marker;
        }
      }
 
      let measurements = toArray(measurementGroup.ContentSequence).filter(ci =>
        [
          DCM_CODE_VALUES.LENGTH,
          DCM_CODE_VALUES.AREA,
          DCM_CODE_VALUES.SHORT_AXIS,
          DCM_CODE_VALUES.LONG_AXIS,
          DCM_CODE_VALUES.ELLIPSE_AREA,
        ].includes(ci.ConceptNameCodeSequence.CodeValue)
      );
 
      let evaluations = toArray(measurementGroup.ContentSequence).filter(ci =>
        [DCM_CODE_VALUES.TRACKING_UNIQUE_IDENTIFIER].includes(ci.ConceptNameCodeSequence.CodeValue)
      );
 
      /**
       * TODO: Resolve bug in DCMJS.
       * ConceptNameCodeSequence should be a sequence with only one item.
       */
      evaluations = evaluations.map(evaluation => {
        const e = { ...evaluation };
        e.ConceptNameCodeSequence = toArray(e.ConceptNameCodeSequence);
        return e;
      });
 
      /**
       * TODO: Resolve bug in DCMJS.
       * ConceptNameCodeSequence should be a sequence with only one item.
       */
      measurements = measurements.map(measurement => {
        const m = { ...measurement };
        m.ConceptNameCodeSequence = toArray(m.ConceptNameCodeSequence);
        return m;
      });
 
      if (measurements && measurements.length) {
        properties.measurements = measurements;
        console.log('[SR] retrieving measurements...', measurements);
      }
 
      if (evaluations && evaluations.length) {
        properties.evaluations = evaluations;
        console.log('[SR] retrieving evaluations...', evaluations);
      }
 
      const roi = new DICOMMicroscopyViewer.roi.ROI({ scoord3d, properties });
      rois.push(roi);
 
      if (findingGroup) {
        labels.push(findingGroup.ConceptCodeSequence.CodeValue);
      } else {
        labels.push('');
      }
    });
  });
 
  return { rois, labels };
}
 
function _getMeasurementGroups(naturalizedDataset) {
  const { ContentSequence } = naturalizedDataset;
 
  const imagingMeasurementsContentItem = ContentSequence.find(
    ci => ci.ConceptNameCodeSequence.CodeValue === DCM_CODE_VALUES.IMAGING_MEASUREMENTS
  );
 
  const measurementGroupContentItems = toArray(
    imagingMeasurementsContentItem.ContentSequence
  ).filter(ci => ci.ConceptNameCodeSequence.CodeValue === DCM_CODE_VALUES.MEASUREMENT_GROUP);
 
  return measurementGroupContentItems;
}