vtkImageCropFilter - performance in vtk.js

Good morning forum!

Firstly, thank you vtk community for providing such an awesome framework! I have a question on vtkImageCropFilter performance. We are using vtkImageCropFilter to crop and render 3-D volume, The widget works perfectly - however, as we crop the volume to a smaller portion, the rendered image blurs while rotating or when you click the left mouse button anywhere in the screen.

We are using vtkGenericRenderWindow for rendering. I am attaching the reactjs code for your review.

Any insight to resolve this will be greatly appreciated!
App.js (4.7 KB)

In case the attachment cannot be viewed, pasting the code here.

import { useRef, useEffect, useState } from 'react';
import '@kitware/vtk.js/Rendering/Profiles/All';
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
import vtkColorMaps from "@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps";

import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow';
import vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume';
import vtkVolumeMapper from '@kitware/vtk.js/Rendering/Core/VolumeMapper';
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';

import vtkImageCroppingWidget from '@kitware/vtk.js/Widgets/Widgets3D/ImageCroppingWidget';
import vtkImageCropFilter from '@kitware/vtk.js/Filters/General/ImageCropFilter';
import vtkWidgetManager from '@kitware/vtk.js/Widgets/Core/WidgetManager';


const MAX = 350;
function App() {
  const vtkContainerRef = useRef(null);
  const context = useRef(null);
  let fullScreenRenderer = null;
  
  

  useEffect(() => {
    if (!context.current) {
      
      fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
        background: [.1, .2, 1],
        rootContainer: vtkContainerRef.current
      });
      
      

      const actor = vtkVolume.newInstance();    
      const mapper = vtkVolumeMapper.newInstance();

      actor.setMapper(mapper);
      const lookupTable = vtkColorTransferFunction.newInstance();
      const piecewiseFun = vtkPiecewiseFunction.newInstance();
      lookupTable.applyColorMap(vtkColorMaps.getPresetByName('Cool to Warm'))
      

      lookupTable.setMappingRange(0, 256);
      lookupTable.updateRange();
      for (let i = 0; i <= 8; i++) {
        piecewiseFun.addPoint(i * 32, i / 8);
      }


      actor.getProperty().setRGBTransferFunction(0, lookupTable);
      actor.getProperty().setScalarOpacity(0, piecewiseFun);


      const float32 = new Float32Array(MAX * MAX * MAX);
      let idx = 0;
      let density=200;

      for (let i = 0; i < MAX; i++) {
        for (let j = 0; j < MAX; j++) {
          for (let k = 0; k < MAX; k++) {
            float32[idx++] = i *10 %256;        
          }
        }
      }

      const imageData = vtkImageData.newInstance({
        origin: [0, 0, 0],
        spacing: [1, 1, 1],
        direction: [1, 0, 0, 0, 1, 0, 0, 0, 1]
      })


      const dataArray = vtkDataArray.newInstance({
        name: 'Data',
        values: float32
      });

      imageData.getPointData().setScalars(dataArray);
      imageData.setDimensions([MAX, MAX, MAX]);
      
      const renderer = fullScreenRenderer.getRenderer();
      const renderWindow = fullScreenRenderer.getRenderWindow();
      renderer.addVolume(actor);
      
      //Cropping code
      const widgetManager = vtkWidgetManager.newInstance();
      widgetManager.setRenderer(renderer);
      const widget = vtkImageCroppingWidget.newInstance();
      widgetManager.addWidget(widget);
      const cropFilter = vtkImageCropFilter.newInstance();
      const cropState = widget.getWidgetState().getCroppingPlanes();
      cropState.onModified(() =>
        cropFilter.setCroppingPlanes(cropState.getPlanes())
      );
      cropFilter.setInputData(imageData);
      mapper.setInputConnection(cropFilter.getOutputPort());
      widget.copyImageDataDescription(imageData);
      widgetManager.enablePicking();     


      const range = imageData.getPointData().getScalars().getRange();
      lookupTable.setMappingRange(...range);
      lookupTable.updateRange();

      renderer.resetCamera();
      global.renderer = renderer;
      global.renderWindow = renderWindow;
      renderWindow.render();
      context.current = {
        fullScreenRenderer,
        renderWindow,
        renderer,
        actor,
        mapper,
        imageData,
        widget
      };
    }


    return () => {
      if (context.current) {
        const { fullScreenRenderer, actor, mapper, imageData } = context.current;
        actor.delete();
        mapper.delete();
        fullScreenRenderer.delete();
        imageData.delete();
        context.current = null;
      }
    };
  }, [vtkContainerRef]);



  return (

    <div>
      <div ref={vtkContainerRef} />
      <table
      style={{
          position: 'absolute',
          top: '25px',
          left: '25px',
          background: 'white',
          padding: '12px',
        }}>
        <tr>
          <td>
            
          </td>
        </tr>
      </table>
    </div>
  );
}

export default App;

The blur thing is usually related to the interactive rate but I will let someone using volume rendering comment out on it.

Is the interactive rate set anywhere?

cc @Sebastien_Jourdain

That’s what I found vtk-js/index.js at master · Kitware/vtk-js · GitHub