I’m running into some issues trying to smooth the ImageData coming from a Niftii file.
When I render the ImageData without the ImageMarchingCubes and WindowedSyncPolyDataFilter, everything works as expected but the ImageData is not smooth (stair-stepping occurs). That’s where MarchingCubes and WindowedSyncPolyDataFilter should come in. Unfortunately, applying ImageMarchingCubes and WindowedSyncPolyDataFilter throws the following error:
Uncaught (in promise) TypeError: WebGL2RenderingContext.bindTexture: Argument 2 is not an object.
I’ve been stuck on this issue for the past few days now and I can’t figure out what’s happening or how to fix this. I’ve tried replacing RenderWindows with OpenGLRenderWindows, I’ve tried adding an OpenGLRenderWindow as view, I’ve tried adding PolyDataNormals, but the error keeps appearing, either immediately on render (after applying ImageMarchingCubes and WindowedSyncPolyDataNormals) or when I interact with the window (with no visualisation happening). Can anyone help me out?
Thanks in advance!
My code (it’s possible there are some syntax issues or missing imports since I’ve edited my actual code to be more readable for this forum):
import { useEffect } from 'react';
// MUI imports
import {
Container,
Grid,
} from '@mui/material';
// VTK imports.
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
import '@kitware/vtk.js/Rendering/Profiles/Volume';
import vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume';
import vtkVolumeMapper from '@kitware/vtk.js/Rendering/Core/VolumeMapper';
import vtkGenericRenderWindow from '@kitware/vtk.js/Rendering/Misc/GenericRenderWindow';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps';
import vtkImageMarchingCubes from '@kitware/vtk.js/Filters/General/ImageMarchingCubes';
import vtkWindowedSincPolyDataFilter from '@kitware/vtk.js/Filters/General/WindowedSincPolyDataFilter';
import vtkITKHelper from '@kitware/vtk.js/Common/DataModel/ITKHelper';
import vtkLiteHttpDataAccessHelper from '@kitware/vtk.js/IO/Core/DataAccessHelper/LiteHttpDataAccessHelper';
// ITK imports.
import { readImageArrayBuffer } from 'itk-wasm';
const App = () => {
useEffect(() => {
createWindow();
}, []);
const readFile = async (file) => {
const arrayBuffer = await vtkLiteHttpDataAccessHelper.fetchBinary(file.path);
const { image: itkImage, webWorker } = await readImageArrayBuffer(
null,
arrayBuffer,
file.name,
);
webWorker.terminate();
return vtkITKHelper.convertItkToVtkImage(itkImage);
};
const update = async (actor, renderer, renderWindow, imageData) => {
// Set up color lookup table and opacity piecewise function.
const lookupTable = vtkColorTransferFunction.newInstance();
const piecewiseFun = vtkPiecewiseFunction.newInstance();
// Set up color transfer function.
lookupTable.applyColorMap(vtkColorMaps.getPresetByName('bone_Matlab'));
// Initial mapping range.
const range = imageData.getPointData().getScalars().getRange();
lookupTable.setMappingRange(...range);
lookupTable.updateRange();
// Set up simple linear opacity function.
// This assumes a data range of 0 -> 256.
for (let i = 0; i <= 8; i++) {
piecewiseFun.addPoint(i * 32, i / 8);
}
// Set the actor properties.
actor.getProperty().setRGBTransferFunction(0, lookupTable);
actor.getProperty().setScalarOpacity(0, piecewiseFun);
actor.getProperty().setInterpolationTypeToLinear();
// Add volume actor to scene.
renderer.addVolume(actor);
// Update lookup table mapping range based on input dataset.
lookupTable.setMappingRange(...range);
lookupTable.updateRange();
// Reset camera and render scene.
renderer.resetCamera();
renderer.updateLightsGeometryToFollowCamera();
renderWindow.render();
};
const createWindow = async () => {
const container = document.querySelector('.container');
const genericRenderWindow = vtkGenericRenderWindow.newInstance({
background: [0, 0, 0],
});
genericRenderWindow.setContainer(container);
genericRenderWindow.resize();
const renderer = genericRenderWindow.getRenderer();
const renderWindow = genericRenderWindow.getRenderWindow();
const actor = vtkVolume.newInstance();
const mapper = vtkVolumeMapper.newInstance();
actor.setMapper(mapper);
const file = {
name: 'test.nii',
path: 'data/test.nii',
};
const imageData = await readFile(file);
// This is where the issues happen.
const mCubes = vtkImageMarchingCubes.newInstance({
contourValue: 0.5,
computeNormals: true,
mergePoints: true,
});
mCubes.setInputData(imageData);
const smoothFilter = vtkWindowedSincPolyDataFilter.newInstance();
smoothFilter.setInputData(mCubes.getOutputData());
// mapper.setInputData(imageData); --> This works.
mapper.setInputData(smoothFilter.getOutputData()); // --> This throws an error.
update(actor, renderer, renderWindow, imageData);
};
return (
<Container>
<Grid container>
<Grid item>
<div
className="container"
style={{ width: '400px', height: '400px' }}
></div>
</Grid>
</Grid>
</Container>
);
};
export default App;