All files / extensions/default/src/utils registerNaturalizedDatasetForLocalWadouri.js

13.63% Statements 6/44
0% Branches 0/30
20% Functions 1/5
14.28% Lines 6/42

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                    182x   182x         182x                 12x   12x                                 12x                                                                                                                                                                                                          
import dicomImageLoader from '@cornerstonejs/dicom-image-loader';
import OHIF from '@ohif/core';
 
import {
  datasetToDicomBlob,
  makeExistingPropertiesNonEnumerable,
  setNonEnumerableInstanceProperty,
} from './dicomWriter';
import { appendFrameQueryToImageId } from './appendFrameQueryToImageId';
 
const metadataProvider = OHIF.classes.MetadataProvider;
 
const SEG_REGISTER_LOG_PREFIX = '[SEG register]';
 
// Every registration pins a full Part 10 Blob in the fileManager (and, once
// loaded, a parsed copy in the dataSetCacheManager) with no other owner, so
// track the imageIds and release them on mode exit.
const registeredImageIds = new Set();
 
/**
 * Releases everything previously registered via
 * {@link registerNaturalizedDatasetForLocalWadouri}: the Part 10 Blob held by the
 * wadouri fileManager and any parsed dataset the dataSetCacheManager retained.
 * Call on mode exit.
 */
export function releaseLocalWadouriRegistrations() {
  const { fileManager, dataSetCacheManager } = dicomImageLoader.wadouri;
 
  for (const imageId of registeredImageIds) {
    // 'dicomfile:<index>' — the index doubles as the fileManager slot and the
    // dataSetCacheManager uri (the scheme is stripped for cache keys).
    const uri = imageId.substring(imageId.indexOf(':') + 1);
    const fileIndex = Number(uri);
 
    Iif (Number.isInteger(fileIndex) && fileIndex >= 0) {
      fileManager.remove(fileIndex);
    }
 
    // unload() decrements a refcount shared with cornerstone's image decache;
    // nothing may outlive mode exit, so drive it to zero.
    for (let guard = 0; guard < 100000 && dataSetCacheManager.isLoaded(uri); guard++) {
      dataSetCacheManager.unload(uri);
    }
  }
 
  registeredImageIds.clear();
}
 
/**
 * Registers a naturalized DICOM dataset with the wadouri file manager so it can be
 * loaded like other locally stored instances (blob URL), instead of remote wadors URLs.
 *
 * Datasets without pixel data (SR, RTSTRUCT, ...) are skipped: they have no frames
 * to serve through the image loader, so registering them would only pin their
 * Part 10 Blob in memory for the rest of the session.
 *
 * @param {object} dataset - Naturalized DICOM instance (e.g. generated SEG).
 * @param {object} [options]
 * @param {string[]} [options.referencedImageIds] - Used for logging / frame count hints.
 * @returns {string|undefined} wadouri imageId assigned to dataset.url, or undefined when skipped
 */
export function registerNaturalizedDatasetForLocalWadouri(dataset, options = {}) {
  const { referencedImageIds = [] } = options;
 
  Iif (
    dataset.PixelData === undefined &&
    !Array.isArray(dataset.PerFrameFunctionalGroupsSequence)
  ) {
    OHIF.log.debug(
      SEG_REGISTER_LOG_PREFIX,
      'Skipping local wadouri registration (no pixel data)',
      { SOPClassUID: dataset.SOPClassUID, SOPInstanceUID: dataset.SOPInstanceUID }
    );
    return undefined;
  }
 
  const blob = datasetToDicomBlob(dataset);
  const imageId = dicomImageLoader.wadouri.fileManager.add(blob);
  registeredImageIds.add(imageId);
 
  setNonEnumerableInstanceProperty(dataset, 'url', imageId);
 
  const { StudyInstanceUID, SeriesInstanceUID } = dataset;
  const SOPInstanceUID = dataset.SOPInstanceUID || dataset.SopInstanceUID;
 
  const perFrameGroups = dataset.PerFrameFunctionalGroupsSequence;
  Iif (Array.isArray(perFrameGroups) && !dataset.NumberOfFrames) {
    dataset.NumberOfFrames = perFrameGroups.length;
  }
 
  // Local only — never written back to the dataset: single-frame IODs (SR,
  // RTSTRUCT) must not gain a NumberOfFrames element in their serialized form.
  const numberOfFrames = Math.max(
    Number(dataset.NumberOfFrames) || 0,
    Array.isArray(perFrameGroups) ? perFrameGroups.length : 0,
    1
  );
 
  const frameImageIds = [];
  Iif (StudyInstanceUID && SeriesInstanceUID && SOPInstanceUID) {
    const registerMapping = (id, frameNumber = 1) => {
      metadataProvider.addImageIdToUIDs(id, {
        StudyInstanceUID,
        SeriesInstanceUID,
        SOPInstanceUID,
        frameNumber,
      });
    };
 
    for (let frame = 1; frame <= numberOfFrames; frame++) {
      const frameImageId =
        numberOfFrames > 1 ? appendFrameQueryToImageId(imageId, frame) : imageId;
 
      frameImageIds.push(frameImageId);
      registerMapping(frameImageId, frame);
    }
  }
 
  makeExistingPropertiesNonEnumerable(dataset);
 
  OHIF.log.debug(SEG_REGISTER_LOG_PREFIX, 'Registered local wadouri instance', {
    StudyInstanceUID,
    SeriesInstanceUID,
    SOPInstanceUID,
    numberOfFrames,
    baseImageId: imageId,
    frameImageIds,
    blobSizeBytes: blob.size,
    referencedImageIdCount: referencedImageIds.length,
    SegmentationType: dataset.SegmentationType,
    SOPClassUID: dataset.SOPClassUID,
  });
 
  return imageId;
}
 
export function registerNaturalizedDatasetsForLocalWadouri(instances, options = {}) {
  const list = Array.isArray(instances) ? instances : [instances];
  const { referencedImageIds = [] } = options;
 
  list.forEach(instance =>
    registerNaturalizedDatasetForLocalWadouri(instance, { referencedImageIds })
  );
 
  return list;
}