[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