All files / core/src/utilities windowLevel.ts

41.66% Statements 5/12
28.57% Branches 2/7
100% Functions 2/2
41.66% Lines 5/12

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                                        384x 384x   384x                                                                         728x   728x                                                  
import VOILUTFunctionType from '../enums/VOILUTFunctionType';
import { logit } from './logit';
 
/**
 * Given a low and high window level, return the window width and window center
 * Formulas from note 4 in
 * https://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.11.2.1.2.1
 * extended to allow for low/high swapping
 * @param low - The low window level.
 * @param high - The high window level.
 * @returns a JavaScript object with two properties: windowWidth and windowCenter.
 */
function toWindowLevel(
  low: number,
  high: number
): {
  windowWidth: number;
  windowCenter: number;
} {
  // Allow for swapping high/low
  const windowWidth = Math.abs(high - low) + 1;
  const windowCenter = (low + high + 1) / 2;
 
  return { windowWidth, windowCenter };
}
 
/**
 * Given a window width and center, return the lower and upper bounds of the window.
 * The calculation depends on the VOI LUT Function:
 *
 * LINEAR (default):
 * - Uses the DICOM standard formula from C.11.2.1.2.1:
 *   if x {'<='} c - 0.5 - (w-1)/2 {'=>'} lower bound
 *   if x {'>'} c - 0.5 + (w-1)/2 {'=>'} upper bound
 *
 * LINEAR_EXACT (C.11.2.1.3.2):
 * - Uses:
 *   lower = c - w/2
 *   upper = c + w/2
 *
 * SIGMOID (C.11.2.1.3.1):
 * - The sigmoid does not define linear "bounds" in the same way. It's asymptotic.
 * - We define approximate bounds by choosing output thresholds (e.g., 1% and 99%)
 *   and solving for input x:
 *   y = 1/(1 + exp(-4*(x - c)/w))
 *   For y=0.01 and y=0.99, solve for x.
 *
 * @param windowWidth - The width of the window
 * @param windowCenter - The center of the window
 * @param voiLUTFunction - 'LINEAR' | 'LINEAR_EXACT' | 'SIGMOID'
 * @returns An object containing the lower and upper bounds of the window
 */
function toLowHighRange(
  windowWidth: number,
  windowCenter: number,
  voiLUTFunction: VOILUTFunctionType = VOILUTFunctionType.LINEAR
): {
  lower: number;
  upper: number;
} {
  if (voiLUTFunction === VOILUTFunctionType.LINEAR) {
    // From C.11.2.1.2.1 (linear function)
    return {
      lower: windowCenter - 0.5 - (windowWidth - 1) / 2,
      upper: windowCenter - 0.5 + (windowWidth - 1) / 2,
    };
  } else Eif (voiLUTFunction === VOILUTFunctionType.LINEAR_EXACT) {
    // From C.11.2.1.3.2 (linear exact function)
    return {
      lower: windowCenter - windowWidth / 2,
      upper: windowCenter + windowWidth / 2,
    };
  } else if (voiLUTFunction === VOILUTFunctionType.SAMPLED_SIGMOID) {
    // From C.11.2.1.3.1 (sigmoid function)
    // Sigmoid: y = 1 / (1 + exp(-4*(x - c)/w))
    const xLower = logit(0.01, windowCenter, windowWidth);
    const xUpper = logit(0.99, windowCenter, windowWidth);
    return {
      lower: xLower,
      upper: xUpper,
    };
  } else {
    throw new Error('Invalid VOI LUT function');
  }
}
 
export { toWindowLevel, toLowHighRange };