Re: Problem with displaying cross-hairs on 2D views when using vtkImageResliceMapper -> vtkImageSlice -> vtkImageStack pipeline

Andras Lasso <[email protected]>
Newsgroups gmane.comp.lib.vtk.user
Message-ID <YTXPR0101MB1870ABE4ABBF6595CEEDB4FDD8BE0@YTXPR0101MB1870.CANPRD01.PROD.OUTLOOK.COM>
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 <[email protected]> On Behalf Of ochampao
Sent: Tuesday, December 18, 2018 1:41 PM
To: [email protected]
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&amp;data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&amp;sdata=nT57nY7HqbSgYg8flXNrjvfhqXVDbvDHbyEOMCVE3V0%3D&amp;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&amp;data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&amp;sdata=y%2FPL7RWRxL9djen9OHHFpV8mML1l8tDDID8vwEPDzr4%3D&amp;reserved=0
_______________________________________________
Powered by https://na01.safelinks.protection.outlook.com/?url=www.kitware.com&amp;data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&amp;sdata=a5wXXUd01fJsRRfeOlVc%2Bk6JcNbeRrCaawzEeOBEpIQ%3D&amp;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&amp;data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&amp;sdata=GTPQfYrm85nLVvMYqtzwxYrfNS%2BHK2U5E%2F8%2FXjTuyGQ%3D&amp;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&amp;data=02%7C01%7Classo%40queensu.ca%7C80118413b7ba4ceb15d108d665185599%7Cd61ecb3b38b142d582c4efb2838b925c%7C1%7C1%7C636807552523602151&amp;sdata=3OkV0h9SM82LHRbPbSKbfXkZuLWSqAjPyULJuBV%2FrGQ%3D&amp;reserved=0

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

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

Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html

Please keep messages on-topic and check the VTK FAQ at: http://www.vtk.org/Wiki/VTK_FAQ

Search the list archives at: http://markmail.org/search/?q=vtkusers

Follow this link to subscribe/unsubscribe:
https://public.kitware.com/mailman/listinfo/vtkusers
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.