Why is RGB point size fixed at 1024 and point index mismatch between “getTable” and “addRGBPoint(Long)” in Rendering/OpenGL/ImageMapper.js?

Dear VTK team,

We are a beginner in vtkjs and image processing.
We have one problem and find the cause of the problem.
We want an answer to that.

Problem
In the picture below, the mask image color difference between cornerstonejs and vtkjs.


(left - cornerstonejs / right - vtkjs)

Our source code

  const getSliceColorActor = (sliceAxis: string) => {
    const mapper = vtkImageMapper.newInstance();
    mapper.setInputData(colorLayerData);
    const actor = vtkImageSlice.newInstance();

    switch (sliceAxis) {
      case 'I':
        mapper.setISlice(iSlice);
        break;
      case 'J':
        mapper.setJSlice(coronalImageStackSize - kSlice);
        break;
      case 'K':
        mapper.setKSlice(axialImageStackSize - jSlice);
        break;
      case 'X':
        mapper.setXSlice(iSlice);
        break;
      case 'Y':
        mapper.setYSlice(coronalImageStackSize - kSlice);
        break;
      case 'Z':
        mapper.setZSlice(axialImageStackSize - jSlice);
    }

    const cfun = vtkColorTransferFunction.newInstance();
    const ofun = vtkPiecewiseFunction.newInstance();
    for (let i = 0; i < ATROPHY_ROI_COLOR_LUT.length; i++) {
      const item = ATROPHY_ROI_COLOR_LUT[i];
      const x = item[0] as number;
      const rgb = item[1];
      if (Array.isArray(rgb)) {
        if (x === 0) {
          ofun.addPoint(x, 0);
        } else {
          ofun.addPoint(x, maskOpacity);
        }
        cfun.addRGBPointLong(x, rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, 0, 1);
      }
    }

    actor.getProperty().setPiecewiseFunction(0, ofun);
    actor.getProperty().setRGBTransferFunction(0, cfun);
    actor.getProperty().setInterpolationTypeToNearest();
    actor.getProperty().setUseLookupTableScalarRange(true);
    actor.getProperty().setOpacity(1);

    actor.setMapper(mapper);
    return actor;
  };

Guessed cause

  1. “vtkColorTransferFunction.getTable” function is called with a fixed size of 1024 in “buildBufferObjects”(Rendering/OpenGL/ImageMapper.js)
  2. “vtkPiecewiseFunction.getTable” function is called with a fixed size of 1024 in “buildBufferObjects”(Rendering/OpenGL/ImageMapper.js)
  3. Point index mismatch between “getTable” and “addRGBPoint(Long)” in “vtkColorTransferFunction”
  4. Point index mismatch between “getTable” and “addPoint” in “vtkPiecewiseFunction”

Questions

  1. Why is the fixed size 1024 in “ImageMapper.buildBufferObjects”?
  2. How to point to index match between “addRGBPoint” and “getTable” in vtkColorTransferFunction or vtkPiecewiseFunction?

Cause detail

1. “getTable” function is called with a fixed size of 1024 in “getRGBTransferFunction”(Rendering/OpenGL/ImageMapper.js)

  • cWidth fixed 1024 (maximum our scalar data index is 2035)
    if (model.colorTextureString !== cfunToString) {
      const cWidth = 1024;
      const cSize = cWidth * textureHeight * 3;
      const cTable = new Uint8Array(cSize);
      let cfun = actorProperty.getRGBTransferFunction();
      if (cfun) {
        const tmpTable = new Float32Array(cWidth * 3);

        for (let c = 0; c < numIComps; c++) {
          cfun = actorProperty.getRGBTransferFunction(c);
          const cRange = cfun.getRange();
          cfun.getTable(cRange[0], cRange[1], cWidth, tmpTable, 1);
          if (iComps) {
            for (let i = 0; i < cWidth * 3; i++) {
              cTable[c * cWidth * 6 + i] = 255.0 * tmpTable[i];
              cTable[c * cWidth * 6 + i + cWidth * 3] = 255.0 * tmpTable[i];
            }
          } else {
            for (let i = 0; i < cWidth * 3; i++) {
              cTable[c * cWidth * 6 + i] = 255.0 * tmpTable[i];
            }
          }
        }

(“Rendering/OpenGL/ImageMapper.js > buildBufferObjects” Function)

2. “vtkPiecewiseFunction.getTable” function is called with a fixed size of 1024 in “buildBufferObjects”(Rendering/OpenGL/ImageMapper.js)

  • pwfWidth fixed 1024 (maximum our scalar data index is 2035)
    if (model.pwfTextureString !== pwfunToString) {
      const pwfWidth = 1024;
      const pwfSize = pwfWidth * textureHeight;
      const pwfTable = new Uint8Array(pwfSize);
      let pwfun = actorProperty.getPiecewiseFunction();
      // support case where pwfun is added/removed
      model.pwfTexture.resetFormatAndType();
      if (pwfun) {
        const pwfFloatTable = new Float32Array(pwfSize);
        const tmpTable = new Float32Array(pwfWidth);

        for (let c = 0; c < numIComps; ++c) {
          pwfun = actorProperty.getPiecewiseFunction(c);
          if (pwfun === null) {
            // Piecewise constant max if no function supplied for this component
            pwfFloatTable.fill(1.0);
          } else {
            const pwfRange = pwfun.getRange();
            pwfun.getTable(pwfRange[0], pwfRange[1], pwfWidth, tmpTable, 1);
            // adjust for sample distance etc

(“Rendering/OpenGL/ImageMapper.js > buildBufferObjects” Function)

3. Point index mismatch between “getTable” and “addRGBPoint(Long)” in "vtkColorTransferFunction"

  • “idx” of “model.nodes[idx]” is not “x” index in “addRGBPoint” function.
    // Create the new node
    const node = { x, r, g, b, midpoint, sharpness };

    // Add it, then sort to get everything in order
    model.nodes.push(node);

(“Common/Core/ColorTransferFunction/index.js > addRGBPointLong” Function)

      // Do we need to move to the next node?
      while (idx < numNodes && x > model.nodes[idx].x) {
        idx++;
        // If we are at a valid point index, fill in
        // the value at this node, and the one before (the
        // two that surround our current sample location)
        // idx cannot be 0 since we just incremented it.
        if (idx < numNodes) {
          x1 = model.nodes[idx - 1].x;
          x2 = model.nodes[idx].x;
          if (usingLogScale) {
            x1 = Math.log10(x1);
            x2 = Math.log10(x2);
          }

          rgb1[0] = model.nodes[idx - 1].r;
          rgb2[0] = model.nodes[idx].r;

          rgb1[1] = model.nodes[idx - 1].g;
          rgb2[1] = model.nodes[idx].g;

          rgb1[2] = model.nodes[idx - 1].b;
          rgb2[2] = model.nodes[idx].b;

          // We only need the previous midpoint and sharpness
          // since these control this region
          midpoint = model.nodes[idx - 1].midpoint;
          sharpness = model.nodes[idx - 1].sharpness;

          // Move midpoint away from extreme ends of range to avoid
          // degenerate math
          if (midpoint < 0.00001) {
            midpoint = 0.00001;
          }

          if (midpoint > 0.99999) {
            midpoint = 0.99999;
          }
        }
      }

(“Common/Core/ColorTransferFunction/index.js > getTable” Function)

4. Point index mismatch between “getTable” and “addPoint” in "vtkPiecewiseFunction"

  • “idx” of “model.nodes[idx]” is not “x” index in “addPoint” function.
    var node = {
      x: x,
      y: y,
      midpoint: midpoint,
      sharpness: sharpness
    }; // Add it, then sort to get everything in order

    model.nodes.push(node);
    publicAPI.sortAndUpdateRange(); // Now find this node so we can return the index

("Common/DataModel/PiecewiseFunction/index.js > addPointLong " Function)

      // Do we need to move to the next node?
      while (idx < numNodes && x > model.nodes[idx].x) {
        idx++;
        // If we are at a valid point index, fill in
        // the value at this node, and the one before (the
        // two that surround our current sample location)
        // idx cannot be 0 since we just incremented it.
        if (idx < numNodes) {
          x1 = model.nodes[idx - 1].x;
          x2 = model.nodes[idx].x;

          y1 = model.nodes[idx - 1].y;
          y2 = model.nodes[idx].y;

          // We only need the previous midpoint and sharpness
          // since these control this region
          midpoint = model.nodes[idx - 1].midpoint;
          sharpness = model.nodes[idx - 1].sharpness;

          // Move midpoint away from extreme ends of range to avoid
          // degenerate math
          if (midpoint < 0.00001) {
            midpoint = 0.00001;
          }

          if (midpoint > 0.99999) {
            midpoint = 0.99999;
          }
        }
      }

(“Common/DataModel/PiecewiseFunction/index.js > getTable” Function)