tests/image/image.test.js

// namespace
var dwv = dwv || {};
dwv.test = dwv.test || {};

/**
 * Tests for the 'image/image.js' file.
 */
// Do not warn if these variables were not defined before.
/* global QUnit */

/**
 * Compare an image and a buffer.
 *
 * @param {object} image The input image.
 * @param {object} size The size of the input buffer.
 * @param {Array} buffer The input buffer.
 * @param {object} rsi The rescale slope of the input buffer.
 * @returns {object} Statistics of the value and rescaled value differences.
 */
dwv.test.compareImageAndBuffer = function (image, size, buffer, rsi) {
  var diffs = [];
  var diffsRescaled = [];

  // calculate differences
  var index = 0;
  for (var k = 0; k < size.get(2); ++k) {
    for (var j = 0; j < size.get(1); ++j) {
      for (var i = 0; i < size.get(0); ++i) {
        var diff = Math.abs(image.getValue(i, j, k) - buffer[index]);
        if (diff !== 0) {
          diffs.push(diff);
        }
        var diffRescaled = Math.abs(
          image.getRescaledValue(i, j, k) - rsi.apply(buffer[index]));
        if (diffRescaled !== 0) {
          diffsRescaled.push(diffRescaled);
        }
        ++index;
      }
    }
  }

  // calculate stats if necessary
  var statsDiff = new dwv.math.SimpleStats(0, 0, 0, 0);
  if (diffs.length !== 0) {
    statsDiff = dwv.math.getStats(diffs);
  }
  var statsDiffRescaled = new dwv.math.SimpleStats(0, 0, 0, 0);
  if (diffsRescaled.length !== 0) {
    statsDiffRescaled = dwv.math.SimpleStats(diffsRescaled);
  }

  // return stats
  return {
    valuesStats: statsDiff,
    rescaledStats: statsDiffRescaled
  };
};

/**
 * Tests for {@link dwv.image.Image} getValue.
 *
 * @function module:tests/image~getvalue
 */
QUnit.test('Test Image getValue.', function (assert) {
  var zeroStats = new dwv.math.SimpleStats(0, 0, 0, 0);

  // create a simple image
  var size0 = 4;
  var imgSize0 = new dwv.image.Size([size0, size0, 1]);
  var imgSpacing0 = new dwv.image.Spacing([1, 1, 1]);
  var imgOrigin0 = new dwv.math.Point3D(0, 0, 0);
  var imgGeometry0 = new dwv.image.Geometry(imgOrigin0, imgSize0, imgSpacing0);
  var buffer0 = [];
  for (var i = 0; i < size0 * size0; ++i) {
    buffer0[i] = i;
  }
  var image0 = new dwv.image.Image(imgGeometry0, buffer0);
  // test its geometry
  assert.equal(image0.getGeometry(), imgGeometry0, 'Image geometry');
  // test its values
  var rsi0 = new dwv.image.RescaleSlopeAndIntercept(1, 0);
  var res0 = dwv.test.compareImageAndBuffer(image0, imgSize0, buffer0, rsi0);
  assert.propEqual(
    res0.valuesStats.asObject(),
    zeroStats.asObject(),
    'Values should be equal');
  assert.propEqual(
    res0.rescaledStats.asObject(),
    zeroStats.asObject(),
    'Rescaled values should be equal');
  // outside value
  assert.equal(isNaN(image0.getValue(4, 3, 0)), true, 'Value outside is NaN');
  // TODO: wrong, should not be accessed
  assert.equal(image0.getValue(5, 0, 0), 1 * size0 + 1, 'Value at 5,0,0');
  // check range
  var theoRange0 = {min: 0, max: (size0 * size0) - 1};
  var imgRange00 = image0.getDataRange();
  assert.equal(imgRange00.max, theoRange0.max, 'Range max');
  assert.equal(imgRange00.min, theoRange0.min, 'Range min');
  var imgRange01 = image0.getRescaledDataRange();
  assert.equal(imgRange01.max, theoRange0.max, 'Rescaled range max');
  assert.equal(imgRange01.min, theoRange0.min, 'Rescaled range min');

  // image with rescale
  var image1 = new dwv.image.Image(imgGeometry0, buffer0);
  var slope1 = 2;
  var intercept1 = 10;
  var rsi1 = new dwv.image.RescaleSlopeAndIntercept(slope1, intercept1);
  image1.setRescaleSlopeAndIntercept(rsi1, new dwv.math.Index([0, 0, 0]));
  // test its geometry
  assert.equal(image1.getGeometry(), imgGeometry0, 'Image geometry');
  // test its values
  var res1 = dwv.test.compareImageAndBuffer(image1, imgSize0, buffer0, rsi1);
  assert.propEqual(
    res1.valuesStats.asObject(),
    zeroStats.asObject(),
    'Values should be equal');
  assert.propEqual(
    res1.rescaledStats.asObject(),
    zeroStats.asObject(),
    'Rescaled values should be equal');
  // check range
  var imgRange10 = image0.getDataRange();
  assert.equal(imgRange10.max, theoRange0.max, 'Range max');
  assert.equal(imgRange10.min, theoRange0.min, 'Range min');
  var theoRange1 = {
    min: theoRange0.min * slope1 + intercept1,
    max: theoRange0.max * slope1 + intercept1
  };
  var imgRange11 = image1.getRescaledDataRange();
  assert.equal(imgRange11.max, theoRange1.max, 'Rescaled range max');
  assert.equal(imgRange11.min, theoRange1.min, 'Rescaled range min');
});

/**
 * Tests for {@link dwv.image.Image} histogram.
 *
 * @function module:tests/image~histogram
 */
QUnit.test('Test Image histogram.', function (assert) {
  // create a simple image
  var size0 = 4;
  var imgSize0 = new dwv.image.Size([size0, size0, 1]);
  var imgSpacing0 = new dwv.image.Spacing([1, 1, 1]);
  var imgOrigin0 = new dwv.math.Point3D(0, 0, 0);
  var imgGeometry0 = new dwv.image.Geometry(imgOrigin0, imgSize0, imgSpacing0);
  var buffer0 = [];
  for (var i = 0; i < size0 * size0; ++i) {
    buffer0[i] = i;
  }
  var image0 = new dwv.image.Image(imgGeometry0, buffer0);

  // histogram
  var histogram = image0.getHistogram();
  assert.equal(histogram.length, size0 * size0, 'histogram size');
  var histoContentTest = true;
  for (var j = 0; j < size0 * size0; ++j) {
    if (histogram[j][0] !== j) {
      histoContentTest = false;
      break;
    }
    if (histogram[j][1] !== 1) {
      histoContentTest = false;
      break;
    }
  }
  assert.equal(histoContentTest, true, 'histogram content');
});

/**
 * Tests for {@link dwv.image.Image} append.
 *
 * @function module:tests/image~append
 */
QUnit.test('Test Image append slice.', function (assert) {
  /**
   * Compare two arrays of vectors.
   *
   * @param {Array} arr0 The first array.
   * @param {Array} arr1 The second array.
   * @returns {boolean} True if both arrays are equal.
   */
  function compareArrayOfVectors(arr0, arr1) {
    return arr0.every(function (element, index) {
      return element.equals(arr1[index]);
    });
  }

  var size = 4;
  var imgSize = new dwv.image.Size([size, size, 2]);
  var imgSizeMinusOne = new dwv.image.Size([size, size, 1]);
  var imgSpacing = new dwv.image.Spacing([1, 1, 1]);
  var imgOrigin = new dwv.math.Point3D(0, 0, 1);
  var imgGeometry0 = new dwv.image.Geometry(
    imgOrigin, imgSizeMinusOne, imgSpacing);
  imgGeometry0.appendOrigin(new dwv.math.Point3D(0, 0, 0), 1);

  // slice to append
  var sliceSize = new dwv.image.Size([size, size, 1]);
  var sliceBuffer = new Int16Array(sliceSize.getTotalSize());
  for (var i = 0; i < size * size; ++i) {
    sliceBuffer[i] = 2;
  }

  // image buffer
  var buffer = new Int16Array(imgSize.getTotalSize());
  for (var j = 0; j < size * size; ++j) {
    buffer[j] = 1;
  }
  for (var k = size * size; k < 2 * size * size; ++k) {
    buffer[k] = 0;
  }

  // image 0
  var image0 = new dwv.image.Image(imgGeometry0, buffer, ['0']);
  image0.setMeta({numberOfFiles: 3});
  // append null
  assert.throws(function () {
    image0.appendSlice(null);
  }, new Error('Cannot append null slice'), 'append null slice');
  // real slice
  var sliceOrigin = new dwv.math.Point3D(0, 0, 2);
  var sliceGeometry = new dwv.image.Geometry(
    sliceOrigin, sliceSize, imgSpacing);
  var slice0 = new dwv.image.Image(sliceGeometry, sliceBuffer, ['1']);
  slice0.setMeta({numberOfFiles: 3});
  // append slice before
  image0.appendSlice(slice0);
  // test its values
  assert.equal(image0.getValue(0, 0, 0), 2, 'Value at 0,0,0 (append before)');
  assert.equal(image0.getValue(3, 3, 0), 2, 'Value at 3,3,0 (append before)');
  assert.equal(image0.getValue(0, 0, 1), 1, 'Value at 0,0,1 (append before)');
  assert.equal(image0.getValue(3, 3, 1), 1, 'Value at 3,3,1 (append before)');
  assert.equal(image0.getValue(0, 0, 2), 0, 'Value at 0,0,2 (append before)');
  assert.equal(image0.getValue(3, 3, 2), 0, 'Value at 3,3,2 (append before)');
  // test its positions
  var sliceOrigins0 = [];
  sliceOrigins0[0] = new dwv.math.Point3D(0, 0, 2);
  sliceOrigins0[1] = new dwv.math.Point3D(0, 0, 1);
  sliceOrigins0[2] = new dwv.math.Point3D(0, 0, 0);
  assert.ok(
    compareArrayOfVectors(imgGeometry0.getOrigins(), sliceOrigins0),
    'Slice positions (append before)');

  // image 1
  var imgGeometry1 = new dwv.image.Geometry(
    imgOrigin, imgSizeMinusOne, imgSpacing);
  imgGeometry1.appendOrigin(new dwv.math.Point3D(0, 0, 0), 1);
  var image1 = new dwv.image.Image(imgGeometry1, buffer, ['0']);
  image1.setMeta({numberOfFiles: 3});
  var sliceOrigin1 = new dwv.math.Point3D(0, 0, -1);
  var sliceGeometry1 = new dwv.image.Geometry(
    sliceOrigin1, sliceSize, imgSpacing);
  var slice1 = new dwv.image.Image(sliceGeometry1, sliceBuffer, ['0']);
  slice1.setMeta({numberOfFiles: 3});
  // append slice before
  image1.appendSlice(slice1);
  // test its values
  assert.equal(image1.getValue(0, 0, 0), 1, 'Value at 0,0,0 (append after)');
  assert.equal(image1.getValue(3, 3, 0), 1, 'Value at 3,3,0 (append after)');
  assert.equal(image1.getValue(0, 0, 1), 0, 'Value at 0,0,1 (append after)');
  assert.equal(image1.getValue(3, 3, 1), 0, 'Value at 3,3,1 (append after)');
  assert.equal(image1.getValue(0, 0, 2), 2, 'Value at 0,0,2 (append after)');
  assert.equal(image1.getValue(3, 3, 2), 2, 'Value at 3,3,2 (append after)');
  // test its positions
  var sliceOrigins1 = [];
  sliceOrigins1[0] = new dwv.math.Point3D(0, 0, 1);
  sliceOrigins1[1] = new dwv.math.Point3D(0, 0, 0);
  sliceOrigins1[2] = new dwv.math.Point3D(0, 0, -1);
  assert.ok(
    compareArrayOfVectors(imgGeometry1.getOrigins(), sliceOrigins1),
    'Slice positions (append after)');

  // image 2
  var imgGeometry2 = new dwv.image.Geometry(
    imgOrigin, imgSizeMinusOne, imgSpacing);
  imgGeometry2.appendOrigin(new dwv.math.Point3D(0, 0, 0), 1);
  var image2 = new dwv.image.Image(imgGeometry2, buffer, ['0']);
  image2.setMeta({numberOfFiles: 3});
  var sliceOrigin2 = new dwv.math.Point3D(0, 0, 0.4);
  var sliceGeometry2 = new dwv.image.Geometry(
    sliceOrigin2, sliceSize, imgSpacing);
  var slice2 = new dwv.image.Image(sliceGeometry2, sliceBuffer, ['0']);
  slice2.setMeta({numberOfFiles: 3});
  // append slice before
  image2.appendSlice(slice2);
  // test its values
  assert.equal(image2.getValue(0, 0, 0), 1, 'Value at 0,0,0 (append between)');
  assert.equal(image2.getValue(3, 3, 0), 1, 'Value at 3,3,0 (append between)');
  assert.equal(image2.getValue(0, 0, 1), 2, 'Value at 0,0,1 (append between)');
  assert.equal(image2.getValue(3, 3, 1), 2, 'Value at 3,3,1 (append between)');
  assert.equal(image2.getValue(0, 0, 2), 0, 'Value at 0,0,2 (append between)');
  assert.equal(image2.getValue(3, 3, 2), 0, 'Value at 3,3,2 (append between)');
  // test its positions
  var sliceOrigins2 = [];
  sliceOrigins2[0] = new dwv.math.Point3D(0, 0, 1);
  sliceOrigins2[1] = new dwv.math.Point3D(0, 0, 0.4);
  sliceOrigins2[2] = new dwv.math.Point3D(0, 0, 0);
  assert.ok(
    compareArrayOfVectors(imgGeometry2.getOrigins(), sliceOrigins2),
    'Slice positions (append between)');
});

/**
 * Tests for {@link dwv.image.Image} convolute2D.
 *
 * @function module:tests/image~convolute2D
 */
QUnit.test('Test Image convolute2D.', function (assert) {
  // create a simple image
  var size0 = 3;
  var imgSize0 = new dwv.image.Size([size0, size0, 1]);
  var imgSpacing0 = new dwv.image.Spacing([1, 1, 1]);
  var imgOrigin0 = new dwv.math.Point3D(0, 0, 0);
  var imgGeometry0 = new dwv.image.Geometry(imgOrigin0, imgSize0, imgSpacing0);
  var buffer0 = [];
  for (var i = 0; i < size0 * size0; ++i) {
    buffer0[i] = i;
  }
  var image0 = new dwv.image.Image(imgGeometry0, buffer0);
  // id convolution
  var weights0 = [0, 0, 0, 0, 1, 0, 0, 0, 0];
  var resImage0 = image0.convolute2D(weights0);
  var testContent0 = true;
  for (i = 0; i < size0 * size0; ++i) {
    if (image0.getValueAtOffset(i, 0) !== resImage0.getValueAtOffset(i, 0)) {
      testContent0 = false;
      break;
    }
  }
  assert.equal(testContent0, true, 'convolute2D id');
  // blur convolution
  var weights1 = [1, 1, 1, 1, 1, 1, 1, 1, 1];
  var resImage1 = image0.convolute2D(weights1);
  var theoResImage1 = [12, 18, 24, 30, 36, 42, 48, 54, 60];
  var testContent1 = true;
  for (i = 0; i < size0 * size0; ++i) {
    if (theoResImage1[i] !== resImage1.getValueAtOffset(i, 0)) {
      testContent1 = false;
      break;
    }
  }
  assert.equal(testContent1, true, 'convolute2D blur');
});

/**
 * Tests for {@link dwv.image.Image} transform.
 *
 * @function module:tests/image~transform
 */
QUnit.test('Test Image transform.', function (assert) {
  // create a simple image
  var size0 = 3;
  var imgSize0 = new dwv.image.Size([size0, size0, 1]);
  var imgSpacing0 = new dwv.image.Spacing([1, 1, 1]);
  var imgOrigin0 = new dwv.math.Point3D(0, 0, 0);
  var imgGeometry0 = new dwv.image.Geometry(imgOrigin0, imgSize0, imgSpacing0);
  var buffer0 = [];
  for (var i = 0; i < size0 * size0; ++i) {
    buffer0[i] = i;
  }
  var image0 = new dwv.image.Image(imgGeometry0, buffer0);

  // treshold function
  var func0 = function (value) {
    if (value < 3 || value > 5) {
      return 0;
    } else {
      return value;
    }
  };
  var resImage0 = image0.transform(func0);
  var theoResImage0 = [0, 0, 0, 3, 4, 5, 0, 0, 0];
  var testContent0 = true;
  for (i = 0; i < size0 * size0; ++i) {
    if (theoResImage0[i] !== resImage0.getValueAtOffset(i, 0)) {
      testContent0 = false;
      break;
    }
  }
  assert.equal(testContent0, true, 'transform threshold');

  // new image
  image0 = new dwv.image.Image(imgGeometry0, buffer0);

  // multiply function
  var func1 = function (value) {
    return value * 2;
  };
  var resImage1 = image0.transform(func1);
  var theoResImage1 = [0, 2, 4, 6, 8, 10, 12, 14, 16];
  var testContent1 = true;
  for (i = 0; i < size0 * size0; ++i) {
    if (theoResImage1[i] !== resImage1.getValueAtOffset(i, 0)) {
      testContent1 = false;
      break;
    }
  }
  assert.equal(testContent1, true, 'transform multiply');
});

/**
 * Tests for {@link dwv.image.Image} compose.
 *
 * @function module:tests/image~compose
 */
QUnit.test('Test Image compose.', function (assert) {
  // create two simple images
  var size0 = 3;
  var imgSize0 = new dwv.image.Size([size0, size0, 1]);
  var imgSpacing0 = new dwv.image.Spacing([1, 1, 1]);
  var imgOrigin0 = new dwv.math.Point3D(0, 0, 0);
  var imgGeometry0 = new dwv.image.Geometry(imgOrigin0, imgSize0, imgSpacing0);
  var buffer0 = [];
  for (var i = 0; i < size0 * size0; ++i) {
    buffer0[i] = i;
  }
  var image0 = new dwv.image.Image(imgGeometry0, buffer0);
  var buffer1 = [];
  for (i = 0; i < size0 * size0; ++i) {
    buffer1[i] = i;
  }
  var image1 = new dwv.image.Image(imgGeometry0, buffer1);

  // addition function
  var func0 = function (a, b) {
    return a + b;
  };
  var resImage0 = image0.compose(image1, func0);
  var theoResImage0 = [0, 2, 4, 6, 8, 10, 12, 14, 16];
  var testContent0 = true;
  for (i = 0; i < size0 * size0; ++i) {
    if (theoResImage0[i] !== resImage0.getValueAtOffset(i, 0)) {
      testContent0 = false;
      break;
    }
  }
  assert.equal(testContent0, true, 'compose addition');
});

/**
 * Tests for {@link dwv.image.ImageFactory}.
 *
 * @function module:tests/image~ImageFactory
 */
QUnit.test('Test ImageFactory.', function (assert) {
  var zeroStats = new dwv.math.SimpleStats(0, 0, 0, 0);

  var size0 = 3;
  var imgSize0 = new dwv.image.Size([size0, size0, 1]);
  var imgSpacing0 = new dwv.image.Spacing([1, 1, 1]);
  var imgOrigin0 = new dwv.math.Point3D(0, 0, 0);
  var imgGeometry0 = new dwv.image.Geometry(imgOrigin0, imgSize0, imgSpacing0);
  var buffer0 = [];
  for (var i = 0; i < size0 * size0; ++i) {
    buffer0[i] = i;
  }
  var rsi0 = new dwv.image.RescaleSlopeAndIntercept(1, 0);

  var dicomElements0 = [];
  // columns
  dicomElements0.x00280011 = {value: imgSize0.get(0)};
  // rows
  dicomElements0.x00280010 = {value: imgSize0.get(1)};
  // spacing
  dicomElements0.x00280030 = {
    value: [imgSpacing0.get(1), imgSpacing0.get(2)]
  };
  // transfer syntax (explicit VR)
  dicomElements0.x00020010 = {value: '1.2.840.10008.1.2.1'};

  // wrap the dicom elements
  var wrappedDicomElements0 =
    new dwv.dicom.DicomElementsWrapper(dicomElements0);
  // create the image factory
  var factory0 = new dwv.image.ImageFactory();
  // create the image
  var image0 = factory0.create(wrappedDicomElements0, buffer0);

  // test its geometry
  assert.ok(image0.getGeometry().equals(imgGeometry0), 'Image geometry');
  // test its values
  var res0 = dwv.test.compareImageAndBuffer(image0, imgSize0, buffer0, rsi0);
  assert.propEqual(
    res0.valuesStats.asObject(),
    zeroStats.asObject(),
    'Values should be equal');
  assert.propEqual(
    res0.rescaledStats.asObject(),
    zeroStats.asObject(),
    'Rescaled values should be equal');
});