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

Andras Lasso lasso at queensu.ca
Wed Dec 19 01:31:31 EST 2018


It is great that you are interested in medical imaging and has chosen to work with VTK. I would recommend to put your talent and effort into contributing to an existing open-source medical image viewer instead of spending time with redeveloping basic features. You could work on more interesting things, develop new features, and you would very quickly become a useful, valued member of a community.

If you are interested in answers to your question (and your future questions) you can have a look at source code of existing VTK-based open-source medical image viewers, such as 3D Slicer, MITK, medInria, Horos, Ibis, CustusX, CamiTK, Invesalius, etc.

Andras

-----Original Message-----
From: vtkusers <vtkusers-bounces at public.kitware.com> On Behalf Of ochampao
Sent: Tuesday, December 18, 2018 1:41 PM
To: vtkusers at vtk.org
Subject: [vtkusers] Problem with displaying cross-hairs on 2D views when using vtkImageResliceMapper -> vtkImageSlice -> vtkImageStack pipeline

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
<https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fvtk.1045678.n5.nabble.com%2Ffile%2Ft341857%2FBasicPipeline.cxx&data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&sdata=nT57nY7HqbSgYg8flXNrjvfhqXVDbvDHbyEOMCVE3V0%3D&reserved=0>
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: https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fvtk.1045678.n5.nabble.com%2FVTK-Users-f1224199.html&data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&sdata=y%2FPL7RWRxL9djen9OHHFpV8mML1l8tDDID8vwEPDzr4%3D&reserved=0
_______________________________________________
Powered by https://na01.safelinks.protection.outlook.com/?url=www.kitware.com&data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&sdata=a5wXXUd01fJsRRfeOlVc%2Bk6JcNbeRrCaawzEeOBEpIQ%3D&reserved=0

Visit other Kitware open-source projects at https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.kitware.com%2Fopensource%2Fopensource.html&data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&sdata=GTPQfYrm85nLVvMYqtzwxYrfNS%2BHK2U5E%2F8%2FXjTuyGQ%3D&reserved=0

Please keep messages on-topic and check the VTK FAQ at: https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.vtk.org%2FWiki%2FVTK_FAQ&data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&sdata=3OkV0h9SM82LHRbPbSKbfXkZuLWSqAjPyULJuBV%2FrGQ%3D&reserved=0

Search the list archives at: https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fmarkmail.org%2Fsearch%2F%3Fq%3Dvtkusers&data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&sdata=ydyX30Af9G0j8PwVigfaCkucOiFqv0SdNKwyi7vx56Y%3D&reserved=0

Follow this link to subscribe/unsubscribe:
https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpublic.kitware.com%2Fmailman%2Flistinfo%2Fvtkusers&data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&sdata=zYNgte5262YQQDT42ydJgSd1qTiDwQBW4DeWwgOlCzE%3D&reserved=0


More information about the vtkusers mailing list