tests_dicom_mprPixGenerator.js

// Do not warn if these variables were not defined before.
/* global dwv */

// namespace
// eslint-disable-next-line no-var
var test = test || {};

/**
 * MPRPixGenerator
 * Generates pixel data from file with an input per orientation.
 *
 * @param {object} options The generator options.
 * @class
 */
const MPRPixGenerator = function (options) {

  const self = this;

  const numberOfColumns = options.numberOfColumns;
  const numberOfRows = options.numberOfRows;
  const isRGB = options.photometricInterpretation === 'RGB';

  if (isRGB) {
    throw new Error('The MPRPixGenerator does not support RGB data.');
  }

  const halfNCols = numberOfColumns * 0.5;
  const halfNRows = numberOfRows * 0.5;

  this.images = [];
  this.buffers = [];

  let numberOfSlices = 0;
  let halfNSlices = 0;
  this.setNumberOfSlices = function (num) {
    numberOfSlices = num;
    halfNSlices = num * 0.5;
  };

  this.setImages = function (imgs) {
    // check sizes
    let img;
    for (let i = 0; i < imgs.length; ++i) {
      img = imgs[i];
      if (img.width !== halfNCols) {
        throw new Error('Image width mismatch: ' +
          img.width + '!=' + halfNCols);
      }
      if (img.height !== halfNRows) {
        throw new Error('Image height mismatch: ' +
          img.height + '!=' + halfNRows);
      }
    }
    // store
    this.images = imgs;
    // store buffers
    this.buffers = [];
    for (let i0 = 0; i0 < imgs.length; ++i0) {
      this.buffers.push(test.getImageDataData(this.images[i0]));
    }
  };

  this.generate = function (pixelBuffer, sliceNumber) {
    if (sliceNumber > numberOfSlices) {
      throw new Error('Cannot generate slice, number is above size: ' +
        sliceNumber + ', ' + numberOfSlices);
    }
    const orientationName =
      dwv.getOrientationName(options.imageOrientationPatient);
    if (orientationName === dwv.Orientation.Axial) {
      this.generateAsAxial(pixelBuffer, sliceNumber);
    } else if (orientationName === dwv.Orientation.Coronal) {
      this.generateAsCoronal(pixelBuffer, sliceNumber);
    } else if (orientationName === dwv.Orientation.Sagittal) {
      this.generateAsSagittal(pixelBuffer, sliceNumber);
    }
  };

  this.generateAsAxial = function (pixelBuffer, sliceNumber) {
    // axial
    let offset = 0;
    for (let j0 = 0; j0 < halfNRows; ++j0) {
      for (let i0 = 0; i0 < halfNCols; ++i0) {
        pixelBuffer[offset] = getFunc(dwv.Orientation.Axial, i0, j0);
        ++offset;
      }
      offset += halfNCols;
    }
    if (sliceNumber < halfNSlices) {
      // coronal
      offset = halfNCols;
      for (let j1 = 0; j1 < numberOfRows; ++j1) {
        for (let i1 = 0; i1 < halfNCols; ++i1) {
          pixelBuffer[offset] = getFunc(
            dwv.Orientation.Coronal, i1, (halfNSlices - 1 - sliceNumber));
          ++offset;
        }
        offset += halfNCols;
      }
    } else {
      // sagittal
      offset = numberOfColumns * halfNRows;
      for (let j2 = 0; j2 < halfNRows; ++j2) {
        for (let i2 = 0; i2 < numberOfColumns; ++i2) {
          pixelBuffer[offset] = getFunc(
            dwv.Orientation.Sagittal, j2, (numberOfSlices - 1 - sliceNumber));
          ++offset;
        }
      }
    }
  };

  this.generateAsCoronal = function (pixelBuffer, sliceNumber) {
    // coronal
    let offset = numberOfColumns * halfNRows + halfNCols;
    for (let j0 = 0; j0 < halfNRows; ++j0) {
      for (let i0 = 0; i0 < halfNCols; ++i0) {
        pixelBuffer[offset] = getFunc(dwv.Orientation.Coronal, i0, j0);
        ++offset;
      }
      offset += halfNCols;
    }
    if (sliceNumber < halfNSlices) {
      // axial
      offset = 0;
      for (let j1 = 0; j1 < numberOfRows; ++j1) {
        for (let i1 = 0; i1 < halfNCols; ++i1) {
          pixelBuffer[offset] = getFunc(
            dwv.Orientation.Axial, i1, sliceNumber);
          ++offset;
        }
        offset += halfNCols;
      }

    } else {
      // sagittal
      offset = 0;
      for (let j2 = 0; j2 < halfNRows; ++j2) {
        for (let i2 = 0; i2 < numberOfColumns; ++i2) {
          pixelBuffer[offset] = getFunc(
            dwv.Orientation.Sagittal, sliceNumber, j2 - 1);
          ++offset;
        }
      }
    }
  };

  this.generateAsSagittal = function (pixelBuffer, sliceNumber) {
    // sagittal
    let offset = halfNCols;
    for (let j0 = 0; j0 < halfNRows; ++j0) {
      for (let i0 = 0; i0 < halfNCols; ++i0) {
        pixelBuffer[offset] = getFunc(dwv.Orientation.Sagittal, i0, j0);
        ++offset;
      }
      offset += halfNCols;
    }
    if (sliceNumber < halfNSlices) {
      // axial
      offset = 0;
      for (let j1 = 0; j1 < numberOfRows; ++j1) {
        for (let i1 = 0; i1 < halfNCols; ++i1) {
          pixelBuffer[offset] = getFunc(
            dwv.Orientation.Axial, sliceNumber, i1);
          ++offset;
        }
        offset += halfNCols;
      }

    } else {
      // coronal
      offset = numberOfColumns * halfNRows;
      for (let j2 = 0; j2 < halfNRows; ++j2) {
        for (let i2 = 0; i2 < numberOfColumns; ++i2) {
          pixelBuffer[offset] = getFunc(
            dwv.Orientation.Coronal, sliceNumber, j2 - 1);
          ++offset;
        }
      }
    }
  };

  /**
   * @param {number} i The column index.
   * @param {number} j The row index.
   * @returns {number} The offset for the given position.
   */
  function getOffset(i, j) {
    return i + j * halfNCols;
  }

  /**
   * @param {string} name The image orientation.
   * @param {number} i The column index.
   * @param {number} j The row index.
   * @returns {number} The value at the given position.
   */
  function getFunc(name, i, j) {
    let imgIdx = 0;
    if (name === dwv.Orientation.Axial) {
      imgIdx = 0;
    } else if (name === dwv.Orientation.Coronal) {
      imgIdx = 1;
    } else if (name === dwv.Orientation.Sagittal) {
      imgIdx = 2;
    }
    return self.buffers[imgIdx][getOffset(i, j) * 4];
  }
};

/**
 * Check tags are coherent with image size.
 *
 * @param {object} tags The tags to check.
 * @param {object} image The associated image.
 * @returns {boolean} True if the tags are ok.
 */
function mprCheckTags(tags, image) {
  /**
   * @param {number} value The value to check.
   * @returns {number} The expected value.
   */
  function getExpectedSize(value) {
    return 2 * value;
  }

  let needUpdate = false;
  if (tags.Columns !== getExpectedSize(image.width)) {
    tags.Columns = getExpectedSize(image.width);
    needUpdate = true;
  }
  if (tags.Rows !== getExpectedSize(image.height)) {
    tags.Rows = getExpectedSize(image.height);
    needUpdate = true;
  }
  return needUpdate;
}

test.pixelGenerators = test.pixelGenerators || {};
test.pixelGenerators.mpr = {
  generator: MPRPixGenerator,
  checkTags: mprCheckTags
};