vtkResliceCursorWidget, flickering, Java wrapper, interpolation, rendering, numeric conversion

Dear VTK Java-wrapper experts,

I am facing some strange rendering/numeric conversion/interpolation problem when using the vtkResliceCursorWidget. When moving the reslice cursor, the displayed image flickers.

Why does the image change, even though no reslicing is necessary (reslice axes do not change)?
Even if it reslices again, why is there such a visible difference?

I attached a minimal example below (unfortunatly I am not allowed to upload the file) that can be started right from the scratch. I am using VTK 8.2.0 and JAVA 1.8.201.

I am glad for any hint.

Best,
Tom



import vtk.vtkCamera;
import vtk.vtkCanvas;
import vtk.vtkImageData;
import vtk.vtkImageReslice;
import vtk.vtkInteractorStyleImage;
import vtk.vtkNativeLibrary;
import vtk.vtkResliceCursor;
import vtk.vtkResliceCursorLineRepresentation;
import vtk.vtkResliceCursorWidget;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class TestResliceCursor extends JFrame {

private static final long serialVersionUID = 1L;
private static int VTK_UNSIGNED_CHAR  =  3;

static {
	if (!vtkNativeLibrary.LoadAllNativeLibraries()) {
		for (vtkNativeLibrary lib : vtkNativeLibrary.values()) {
			if (!lib.IsLoaded()) {
				System.out.println("Failed to load: " + lib.GetLibraryName());
			}
		}
	}
}
		
public TestResliceCursor(vtkImageData imageData) {
	
	vtkCanvas canvas = new vtkCanvas();		
	
	final double imageDataCenter[] = imageData.GetCenter();
	final double imageDataScalarRange[] = imageData.GetScalarRange();
	
	final double window = imageDataScalarRange[1]-imageDataScalarRange[0];
	final double level =  (imageDataScalarRange[0]+imageDataScalarRange[1])/2.0;
			
	vtkResliceCursor resliceCursor = new vtkResliceCursor();
	resliceCursor.SetImage(imageData);
	resliceCursor.SetCenter(imageDataCenter);
	resliceCursor.SetThickMode(0);

	canvas.setInteractorStyle(new vtkInteractorStyleImage());
	canvas.GetRenderer().SetBackground(0,0,0);

	vtkResliceCursorLineRepresentation lineRep = new vtkResliceCursorLineRepresentation();
	lineRep.SetRestrictPlaneToVolume(1);
	lineRep.GetResliceCursorActor().GetCursorAlgorithm().SetResliceCursor(resliceCursor);
	lineRep.GetResliceCursorActor().GetCursorAlgorithm().SetReslicePlaneNormal(2);
	lineRep.SetWindowLevel(window, level, 0);
	lineRep.DisplayTextOff();

	vtkImageReslice imageReslice = (vtkImageReslice) lineRep.GetReslice();
	if(imageReslice!=null) {
		imageReslice.TransformInputSamplingOff();
		imageReslice.SetBackgroundColor(imageDataScalarRange[0], imageDataScalarRange[0], imageDataScalarRange[0], imageDataScalarRange[0]);
	}
				
	vtkResliceCursorWidget resliceCursorWidget = new vtkResliceCursorWidget();
	resliceCursorWidget.SetInteractor(canvas.getRenderWindowInteractor());
	resliceCursorWidget.SetRepresentation(lineRep);
	resliceCursorWidget.SetEnabled(1);
				
	vtkCamera camera = canvas.GetRenderer().GetActiveCamera();
	camera.SetFocalPoint(imageDataCenter);
	camera.SetViewUp(0,-1,0);
 	camera.SetParallelProjection(1);		
	
 	canvas.GetRenderer().ResetCamera();
 	canvas.GetRenderer().ResetCameraClippingRange();
			
	this.getContentPane().setLayout(new BorderLayout());
	this.getContentPane().add(canvas, BorderLayout.CENTER);
	this.getContentPane().setBackground(Color.BLACK);
	this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	this.setResizable(true);
	this.setLocationRelativeTo(null);
	this.pack();
}
		
public static void main(String[] args) {
			
	try{
		SwingUtilities.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				final vtkImageData imageSeriesData = CreateImageData();
				TestResliceCursor test = new TestResliceCursor(imageSeriesData);
				test.setSize(new Dimension(600, 600));
				test.setVisible(true);
			}
		});	
	}
	catch (Exception e)	{
		e.printStackTrace();
	}
}

public static vtkImageData CreateImageData() {
 	
	vtkImageData imageData = new vtkImageData();
	imageData.SetDimensions(130, 130, 10);
	imageData.SetOrigin(0,0,0);
	imageData.SetSpacing(1,1,1);
	imageData.AllocateScalars(VTK_UNSIGNED_CHAR, 1);
	
	final int[] dims = imageData.GetDimensions();
	
	for (int z=0; z<dims[2]; z++) {
	    for (int y=0; y<dims[1]; y++) {
	      for (int x=0; x<dims[0]; x++) {
	    	 imageData.SetScalarComponentFromDouble(x, y, z, 0, 10000 * Math.sin(0.05*x) * Math.sin(0.05*y) );
	      }
	    }
	}
	return imageData;
}

}

Does this issue occur in a pure cpp framework as well? If not it might be related to vtkPanel or vtkCanvas.
Or did I miss something in my approach?

Thanks in advance.
Tom

So I setup a cpp framework close to the Java code (see below).
Unfortunatly the issue occurs as well in a plain vtkRenderWindow. It really looks like a numeric issue to me.

Any advise?


#include <vtkImageData.h>
#include <vtkImageReslice.h>
#include <vtkInteractorStyleImage.h>
#include <vtkLookupTable.h>
#include <vtkPlaneSource.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkResliceCursor.h>
#include <vtkResliceCursorActor.h>
#include <vtkResliceCursorLineRepresentation.h>
#include <vtkResliceCursorPolyDataAlgorithm.h>
#include <vtkResliceCursorWidget.h>

static vtkSmartPointer GenerateTestPattern() {

vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
imageData->SetDimensions(130, 130, 10);
imageData->SetOrigin(0, 0, 0);
imageData->SetSpacing(1, 1, 1);
imageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1);

int* dims = imageData->GetDimensions();

for (int z = 0; z < dims[2]; z++) {
    for (int y = 0; y < dims[1]; y++) {
        for (int x = 0; x < dims[0]; x++) {
            imageData->SetScalarComponentFromDouble(x, y, z, 0, 255 * sin(0.05 * x) * sin(0.05 * y));
        }
    }
}
return imageData;

}

int main() {

vtkSmartPointer<vtkImageData> imageData = GenerateTestPattern();

vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New();
style->SetInteractionModeToImage2D();

vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(0, 0, 0);

vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
interactor->SetInteractorStyle(style);

vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
//renderWindow->SetMultiSamples(0);
renderWindow->AddRenderer(renderer);
renderWindow->SetInteractor(interactor);
renderWindow->SetSize(600, 600);

double* imageDataCenter = imageData->GetCenter();
double* imageDataScalarRange = imageData->GetScalarRange();
double windowWidth = imageDataScalarRange[1] - imageDataScalarRange[0];
double windowLevel = .5*(imageDataScalarRange[0] + imageDataScalarRange[1]);
double minVal = imageDataScalarRange[0];

vtkSmartPointer<vtkResliceCursor> resliceCursor = vtkSmartPointer<vtkResliceCursor>::New();
resliceCursor->SetCenter(imageDataCenter);
resliceCursor->SetImage(imageData);
resliceCursor->SetThickMode(0);
//resliceCursor->SetThickness(1, 1, 1);

vtkSmartPointer<vtkLookupTable> lookupTable = vtkSmartPointer<vtkLookupTable>::New();
lookupTable->SetRampToLinear();
lookupTable->SetNumberOfColors(256);
lookupTable->SetHueRange(0, 0);
lookupTable->SetSaturationRange(0, 0);
lookupTable->SetValueRange(0, 1);
lookupTable->SetAlphaRange(1, 1);
lookupTable->SetRange(imageDataScalarRange[0], imageDataScalarRange[1]);
lookupTable->Build();

vtkSmartPointer<vtkResliceCursorLineRepresentation> resliceCursorLineRep = vtkSmartPointer<vtkResliceCursorLineRepresentation>::New();
resliceCursorLineRep->GetPlaneSource()->SetOutputPointsPrecision(vtkAlgorithm::DOUBLE_PRECISION);
resliceCursorLineRep->GetResliceCursorActor()->GetCursorAlgorithm()->SetResliceCursor(resliceCursor);
resliceCursorLineRep->GetResliceCursorActor()->GetCursorAlgorithm()->SetReslicePlaneNormal(2);
resliceCursorLineRep->SetLookupTable(lookupTable);
resliceCursorLineRep->SetRenderer(renderer);
//resliceCursorLineRep->SetRestrictPlaneToVolume(true);
//resliceCursorLineRep->SetUseImageActor(false);
resliceCursorLineRep->SetWindowLevel(windowWidth, windowLevel, 0);
resliceCursorLineRep->DisplayTextOff();

if (vtkImageReslice* reslice = vtkImageReslice::SafeDownCast(resliceCursorLineRep->GetReslice())) {
    //reslice->TransformInputSamplingOff();
    reslice->SetBackgroundColor(minVal, minVal, minVal, minVal);
}

vtkSmartPointer<vtkResliceCursorWidget> resliceCursorWidget = vtkSmartPointer<vtkResliceCursorWidget>::New();
resliceCursorWidget->SetInteractor(interactor);
resliceCursorWidget->SetDefaultRenderer(renderer);
resliceCursorWidget->SetRepresentation(resliceCursorLineRep);
resliceCursorWidget->SetEnabled(1);

renderer->ResetCamera();
renderer->ResetCameraClippingRange();

interactor->Initialize();
interactor->Start();

}