[vtkusers] Problem with displaying cross-hairs on 2D views when using vtkImageResliceMapper -> vtkImageSlice -> vtkImageStack pipeline

ochampao ochampao at hotmail.com
Tue Dec 18 13:40:46 EST 2018


Hi vtkUsers, 

I am developing a four-pane viewer for displaying medical images. The 3
views display slices of the volumes in the 3 standard anatomical views
(axial, coronal, sagittal) whereas the 4th one displays some 3D surface
rendering of the volume. For now, I will only focus on the 2D views.

The 2D views use the following pipeline: vtkImageResliceMapper ->
vtkImageSlice -> vtkImageStack. This allows the application to overlay
slices from multiple volumes simultaneously. The attached code is a minimal
example of the 2D pipeline I use in my application. The slices displayed are
determined by the focal point of the camera in each 2D view.

What I would like to do is display a set of cross-hairs on each of the 2D
views, but I don't know how to achieve this using the pipeline I am
currently using.

I am aware of the classes: 
vtkResliceCursorWidget, 
vtkResliceCursor, 
vtkResliceCursorLineRepresentation, 
vtkImagePlaneWidget, 
vtkResliceImageViewer

I have tried using these classes (see commented code in attached source
code), but they don't seem compatible with my current pipeline. For example,
when using vtkResliceCursorWidget the cross-hairs are visible and I can
interact with them but slicing stops working. I also see some weird
artefacts around the border of the slice. Also, to use these classes I need
to specify the volume which the reslice cursor will be slicing
(vtkResliceCursor->SetImage(imageData)), but in my case I have multiple
volumes.

I am also aware of and tried using vtkCursor2D/vtkCursor3D. Although they
are close to what I have in mind, they allow very limited customization of
their look.

Essentially what I would like to implement is something that looks like the
cursor of vtkResliceCursorWidget/vtkImagePlaneWidget/vtkResliceImageViewer
but does not handle slicing.

Can someone recommend how can I achieve this, or point me to some
classes/examples? Is their a way of using
vtkResliceCursorWidget/vtkImagePlaneWidget/vtkResliceImageViewer with my
current pipeline, or would I need to change it?

Thanks a lot for your help.
Panos

========================================================
Basic Pipeline Minimal Example
========================================================
#include <vtkCamera.h>
#include <vtkDICOMImageReader.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageData.h>
#include <vtkImageProperty.h>
#include <vtkImageResliceMapper.h>
#include <vtkImageSlice.h>
#include <vtkImageStack.h>
#include <vtkInteractorStyleImage.h>
#include <vtkMath.h>
#include <vtkNIFTIImageReader.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>

#include <vtkResliceCursor.h>
#include <vtkResliceCursorActor.h>
#include <vtkResliceCursorLineRepresentation.h>
#include <vtkResliceCursorPolyDataAlgorithm.h>
#include <vtkResliceCursorWidget.h>

void setupCamera(vtkCamera* camera);
vtkSmartPointer<vtkImageData> loadDicom(const char* filename);
vtkSmartPointer<vtkImageData> loadNifti(const char* filename);

int main(int, char*[])
{
	// Setup renderer
	vtkNew<vtkRenderer> renderer;
	renderer->SetBackground(0.0, 0.0, 0.0);	
	renderer->GetActiveCamera()->ParallelProjectionOn();
	renderer->ResetCameraClippingRange();
	renderer->ResetCamera();

	// Setup renderWindow
	vtkNew<vtkRenderWindow> renderWindow;
	renderWindow->AddRenderer(renderer);

	// Setup interaction style
	vtkNew<vtkInteractorStyleImage> interactorStyle;
	interactorStyle->SetInteractionModeToImageSlicing();

	// Setup window interactor
	vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
	renderWindowInteractor->SetRenderWindow(renderWindow);
	renderWindowInteractor->SetInteractorStyle(interactorStyle);

	// Setup camera
	setupCamera(renderer->GetActiveCamera());

	// Load data from DICOM series
//	vtkSmartPointer<vtkImageData> imageData = 
//		loadDicom("<path_to_DICOM_series>"");

	// Load data from Nifti file
	vtkSmartPointer<vtkImageData> imageData =
		loadNifti("<path_to_nii_file>");

	// setup slice mapper
	vtkNew<vtkImageResliceMapper> resliceMapper;
	resliceMapper->SetInputData(imageData);
	resliceMapper->SliceFacesCameraOn();
	resliceMapper->SliceAtFocalPointOn();
	resliceMapper->JumpToNearestSliceOn();
	resliceMapper->BorderOff();

	// Set to full window and centered level:
	double window = imageData->GetScalarRange()[1] -
imageData->GetScalarRange()[0];
	double level = imageData->GetScalarRange()[0] + window / 2.0;

	// Setup prop holding the slice
	vtkNew<vtkImageSlice> imageSlice;
	imageSlice->SetMapper(resliceMapper);
	imageSlice->GetProperty()->SetColorWindow(window);
	imageSlice->GetProperty()->SetColorLevel(level);
	imageSlice->GetProperty()->SetLayerNumber(0);
	imageSlice->GetProperty()->SetInterpolationTypeToNearest();
	
	// Setup prop holding the multiple slices
	vtkNew<vtkImageStack> imageStack;
	imageStack->SetActiveLayer(imageSlice->GetProperty()->GetLayerNumber());
	imageStack->AddImage(imageSlice); // add slice

	vtkNew<vtkResliceCursor> resliceCursor;
	resliceCursor->SetCenter(imageData->GetCenter());
	resliceCursor->SetThickMode(0);
	resliceCursor->SetImage(imageData);

	vtkNew<vtkResliceCursorLineRepresentation> cursorRepresentation;
	cursorRepresentation->GetResliceCursorActor()->
           GetCursorAlgorithm()->SetResliceCursor(resliceCursor);
	cursorRepresentation->GetResliceCursorActor()->
            BasicPipeline.cxx
<http://vtk.1045678.n5.nabble.com/file/t341857/BasicPipeline.cxx> 
GetCursorAlgorithm()->SetReslicePlaneNormal(2);

	vtkNew<vtkResliceCursorWidget> resliceCursorWidget;
	resliceCursorWidget->SetInteractor(renderWindowInteractor);
	resliceCursorWidget->SetDefaultRenderer(renderer);
	resliceCursorWidget->SetRepresentation(cursorRepresentation);
	resliceCursorWidget->SetManageWindowLevel(false);
	resliceCursorWidget->EnabledOn();
	//resliceCursorWidget->ProcessEventsOff();

	// Add actors to renderer
	renderer->AddViewProp(imageStack);
	renderer->ResetCamera(); 

	// Start interaction
	renderWindowInteractor->Initialize();
	renderWindow->Render();
	renderWindowInteractor->Start();

	return EXIT_SUCCESS;
}

void setupCamera(vtkCamera* camera)
{
	double viewUp[3] = { 0, 1, 0 };
	double leftToRight[3] = { 1, 0, 0 };

	// compute the view plane normal
	double normal[3];
	vtkMath::Cross(leftToRight, viewUp, normal);

	// get the camera focus
	double focus[3];
	camera->GetFocalPoint(focus);

	// get the camera distance from the focus
	double d = camera->GetDistance();

	// position the camera on view plane normal keeping the focus and the
distance from it fixed
	camera->SetPosition(
		focus[0] + d*normal[0],
		focus[1] + d*normal[1],
		focus[2] + d*normal[2]);

	// make sure focus is the same
	camera->SetFocalPoint(focus);

	// setup view up vector
	camera->SetViewUp(viewUp);
	camera->OrthogonalizeViewUp();
}

vtkSmartPointer<vtkImageData> loadDicom(const char* filename)
{
	vtkNew<vtkDICOMImageReader> reader;
	reader->FileLowerLeftOn();
	reader->SetDirectoryName(filename);
	reader->UpdateInformation();

	vtkNew<vtkImageChangeInformation> imageInfo;
	imageInfo->SetOutputOrigin(0.0, 0.0, 0.0);
	imageInfo->SetOutputSpacing(reader->GetPixelSpacing());
	imageInfo->SetInputConnection(reader->GetOutputPort());
	imageInfo->Update();

	return imageInfo->GetOutput();
}

vtkSmartPointer<vtkImageData> loadNifti(const char* filename)
{
	vtkNew<vtkNIFTIImageReader> niftiiReader;
	niftiiReader->SetFileName(filename);

	vtkNew<vtkImageChangeInformation> imageInfo;
	imageInfo->SetOutputOrigin(0.0, 0.0, 0.0);
	imageInfo->SetInputConnection(niftiiReader->GetOutputPort());
	imageInfo->Update();

	return imageInfo->GetOutput();//data;
}





--
Sent from: http://vtk.1045678.n5.nabble.com/VTK-Users-f1224199.html


More information about the vtkusers mailing list