Unable to display an image slice using VTK.js from a VTI image file in React

Hi there,

I am trying to load a VTI image file in my React/NextJS/VTK.js app, but I am struggling to display a simple slice of the image.

I have added an actor to display the outline of the image that works well.

This the code of my component:

'use client';

import { useRef, useEffect } from 'react';

import '@kitware/vtk.js/Rendering/Profiles/Geometry';

import vtkGenericRenderWindow from '@kitware/vtk.js/Rendering/Misc/GenericRenderWindow';
import vtkImageMapper from '@kitware/vtk.js/Rendering/Core/ImageMapper';
import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice';

import vtkXMLImageDataReader from '@kitware/vtk.js/IO/XML/XMLImageDataReader';

import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';

import vtkOutlineFilter from '@kitware/vtk.js/Filters/General/OutlineFilter';
import { SlicingMode } from '@kitware/vtk.js/Rendering/Core/ImageMapper/Constants';


export default function ImageComponent() {

    const vtkContainerRef = useRef(null);
    const context = useRef(null);

    const loadScene = async () => {
        if (context.current) {
            return;
        }

        const genericRenderWindow = vtkGenericRenderWindow.newInstance();
        genericRenderWindow.setContainer(vtkContainerRef.current);
        genericRenderWindow.resize();

        const renderer = genericRenderWindow.getRenderer();
        const renderWindow = genericRenderWindow.getRenderWindow();

        renderer.setBackground(0.5, 0.5, 0.5);
        renderWindow.render();

        const response = await fetch("/vase.vti");
        const content = await response.arrayBuffer();

        const reader = vtkXMLImageDataReader.newInstance();
        reader.parseAsArrayBuffer(content);

        const data = reader.getOutputData(0);


        // IMAGE
        const mapper = vtkImageMapper.newInstance();
        const actor = vtkImageSlice.newInstance();

        actor.setMapper(mapper);

        mapper.setInputData(data);
        mapper.setSlicingMode(SlicingMode.Z);
        mapper.setSlice(0);

        actor.getProperty().setColorWindow(255);
        actor.getProperty().setColorLevel(127.5);

        renderer.addActor(actor);


        // OUTLINE
        const outline = vtkOutlineFilter.newInstance();
        outline.setInputData(data);

        const outlineMapper = vtkMapper.newInstance();
        outlineMapper.setInputConnection(outline.getOutputPort());

        const outlineActor = vtkActor.newInstance();
        outlineActor.setMapper(outlineMapper);
        outlineActor.getProperty().setColor(1, 1, 0);
        outlineActor.getProperty().setLineWidth(2);
        renderer.addActor(outlineActor);

        renderer.resetCamera();
        renderer.resetCameraClippingRange();

        renderWindow.render();

        context.current = {
            genericRenderWindow,
            actor,
            outlineActor
        };

    };

    useEffect(() => {

        loadScene()

        return () => {
            if (context.current) {
                const { genericRenderWindow, actor, outlineActor } = context.current;

                actor?.getMapper().delete();
                actor?.delete();

                outlineActor?.getMapper().delete();
                outlineActor?.delete();

                genericRenderWindow.delete();

                if (vtkContainerRef.current) {
                    vtkContainerRef.current.innerHTML = '';
                }

                context.current = null;
            }
        };

    }, [vtkContainerRef]);


    return (
        <div ref={vtkContainerRef} className="h-[calc(100vh-64px)]" />
    )
}

This is the result:

The image comes from the vtk examples:
https://gitlab.kitware.com/vtk/vtk-examples/-/blob/master/src/Testing/Data/vase.vti

Does anyone have any clue on what I might be doing wrong ?

Thanks a lot,

Clément

You might need to import the Volume profile to have access of image/volume rendering mappers:

import '@kitware/vtk.js/Rendering/Profiles/Volume';

That did the trick, thanks a lot !

Clément