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

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

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

ochampao
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
_______________________________________________
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
Reply | Threaded
Open this post in threaded view
|

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

Andras Lasso
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 <[hidden email]> On Behalf Of ochampao
Sent: Tuesday, December 18, 2018 1:41 PM
To: [hidden email]
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