All files / packages/core/src/RenderingEngine/helpers/cpuFallback/rendering getVOILut.ts

29.41% Statements 5/17
16.66% Branches 1/6
60% Functions 3/5
29.41% Lines 5/17

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                                                          24x   6144x   6144x                                                                                     24x       24x    
/* eslint no-bitwise: 0 */
 
/**
 * Volume of Interest Lookup Table Function
 *
 * @typedef {Function} VOILUTFunction
 *
 * @param {Number} modalityLutValue
 * @returns {Number} transformed value
 * @memberof Objects
 */
 
/**
 * @module: VOILUT
 */
 
/**
 * Generates the linear VOI LUT function.
 * From the DICOM standard:
 * https://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.11.2.1.2.1
 * ((x - (c - 0.5)) / (w-1) + 0.5) * (ymax- ymin) + ymin
 * clipped to the ymin...ymax range
 *
 * @param {Number} windowWidth Window Width
 * @param {Number} windowCenter Window Center
 * @returns {VOILUTFunction} VOI LUT mapping function
 * @memberof VOILUT
 */
function generateLinearVOILUT(windowWidth: number, windowCenter: number) {
  return function (modalityLutValue) {
    const value =
      ((modalityLutValue - (windowCenter - 0.5)) / (windowWidth - 1) + 0.5) *
      255.0;
    return Math.min(Math.max(value, 0), 255);
  };
}
 
/**
 * Generate a non-linear volume of interest lookup table
 *
 * @param {LUT} voiLUT Volume of Interest Lookup Table Object
 *
 * @returns {VOILUTFunction} VOI LUT mapping function
 * @memberof VOILUT
 */
function generateNonLinearVOILUT(voiLUT) {
  // We don't trust the voiLUT.numBitsPerEntry, mainly thanks to Agfa!
  const bitsPerEntry = Math.max(...voiLUT.lut).toString(2).length;
  const shift = bitsPerEntry - 8;
  const minValue = voiLUT.lut[0] >> shift;
  const maxValue = voiLUT.lut[voiLUT.lut.length - 1] >> shift;
  const maxValueMapped = voiLUT.firstValueMapped + voiLUT.lut.length - 1;
 
  return function (modalityLutValue) {
    if (modalityLutValue < voiLUT.firstValueMapped) {
      return minValue;
    } else if (modalityLutValue >= maxValueMapped) {
      return maxValue;
    }
 
    return voiLUT.lut[modalityLutValue - voiLUT.firstValueMapped] >> shift;
  };
}
 
/**
 * Retrieve a VOI LUT mapping function given the current windowing settings
 * and the VOI LUT for the image
 *
 * @param {Number} windowWidth Window Width
 * @param {Number} windowCenter Window Center
 * @param {LUT} [voiLUT] Volume of Interest Lookup Table Object
 *
 * @return {VOILUTFunction} VOI LUT mapping function
 * @memberof VOILUT
 */
export default function (windowWidth: number, windowCenter: number, voiLUT) {
  Iif (voiLUT) {
    return generateNonLinearVOILUT(voiLUT);
  }
 
  return generateLinearVOILUT(windowWidth, windowCenter);
}