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
- “vtkColorTransferFunction.getTable” function is called with a fixed size of 1024 in “buildBufferObjects”(Rendering/OpenGL/ImageMapper.js)
- “vtkPiecewiseFunction.getTable” function is called with a fixed size of 1024 in “buildBufferObjects”(Rendering/OpenGL/ImageMapper.js)
- Point index mismatch between “getTable” and “addRGBPoint(Long)” in “vtkColorTransferFunction”
- Point index mismatch between “getTable” and “addPoint” in “vtkPiecewiseFunction”
Questions
- Why is the fixed size 1024 in “ImageMapper.buildBufferObjects”?
- 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)