src_image_windowLut.js

// doc imports
/* eslint-disable no-unused-vars */
import {ModalityLut} from './modalityLut';
import {VoiLut} from './voiLut';
/* eslint-enable no-unused-vars */

/**
 * Window LUT class: combines a modality LUT and a VOI LUT.
 */
export class WindowLut {

  /**
   * The modality LUT.
   *
   * @type {ModalityLut}
   */
  #modalityLut;

  /**
   * The VOI LUT.
   *
   * @type {VoiLut}
   */
  #voiLut;

  /**
   * The internal LUT array: Uint8ClampedArray clamps between 0 and 255.
   *
   * @type {Uint8ClampedArray}
   */
  #lut;

  /**
   * Shift for signed data.
   *
   * @type {number}
   */
  #signedShift = 0;

  /**
   * Is the RSI discrete.
   *
   * @type {boolean}
   */
  #isDiscrete = true;

  /**
   * Construct a window LUT object, VOI LUT is set with
   *   the 'setVoiLut' method.
   *
   * @param {ModalityLut} modalityLut The associated rescale LUT.
   * @param {boolean} isSigned Flag to know if the data is signed or not.
   * @param {boolean} isDiscrete Flag to know if the input data is discrete.
   */
  constructor(modalityLut, isSigned, isDiscrete) {
    this.#modalityLut = modalityLut;

    if (isSigned) {
      const size = this.#modalityLut.getLength();
      this.#signedShift = size / 2;
    } else {
      this.#signedShift = 0;
    }

    this.#isDiscrete = isDiscrete;
  }

  /**
   * Get the VOI LUT.
   *
   * @returns {VoiLut} The VOI LUT.
   */
  getVoiLut() {
    return this.#voiLut;
  }

  /**
   * Get the modality LUT.
   *
   * @returns {ModalityLut} The modality LUT.
   */
  getModalityLut() {
    return this.#modalityLut;
  }

  /**
   * Set the VOI LUT.
   *
   * @param {VoiLut} lut The VOI LUT.
   */
  setVoiLut(lut) {
    // store the window values
    this.#voiLut = lut;

    // possible signed shift (LUT indices are positive)
    this.#voiLut.setSignedOffset(
      this.#modalityLut.getRSI().getSlope() * this.#signedShift);

    // create lut if not continous
    if (this.#isDiscrete) {
      const size = this.#modalityLut.getLength();
      // use clamped array (polyfilled in env.js)
      this.#lut = new Uint8ClampedArray(size);
      // by default WindowLevel returns a value in the [0,255] range
      // this is ok with regular Arrays and ClampedArray.
      for (let i = 0; i < size; ++i) {
        this.#lut[i] = this.#voiLut.apply(this.#modalityLut.getValue(i));
      }
    }
  }

  /**
   * Get the value of the LUT at the given offset.
   *
   * @param {number} offset The input offset in [0,2^bitsStored] range
   *   for discrete data or full range for non discrete.
   * @returns {number} The integer value (default [0,255]) of the LUT
   *   at the given offset.
   */
  getValue(offset) {
    if (this.#isDiscrete) {
      return this.#lut[offset + this.#signedShift];
    } else {
      return Math.floor(this.#voiLut.apply(offset + this.#signedShift));
    }
  }

} // class WindowLut