Hi!
Our company has been making a piece of software with VTK for some time now and we’ve been using the vtkImageResliceMapper
class to display DICOM CT images ( vtkImageData
). I came across an issue when zooming into the vtkImageSlice
too far. If you zoom in too far, the image just disappears and the working maximum zoom also depends on how close the camera focal point is to the relevant bounds of the image. I made a small working example with the FullHead.mhd
image as input:
#include <vtkImageData.h>
#include <vtkImageResliceMapper.h>
#include <vtkImageSlice.h>
#include <vtkInteractorStyleImage.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkImageProperty.h>
#include <vtkOpenGLRenderer.h>
#include <vtkCamera.h>
#include <vtkTransform.h>
#include <vtkMetaImageReader.h>
#include <iostream>
namespace {
class vtkImageInteractionCallback1 : public vtkCommand
{
public:
static vtkImageInteractionCallback1* New()
{
return new vtkImageInteractionCallback1;
}
vtkImageInteractionCallback1()
{}
~vtkImageInteractionCallback1()
{}
void addCamera(vtkCamera* cam) {
camera = cam;
}
void addRenderWindow(vtkRenderWindow* window) {
renderWindow = window;
}
void addRenderer(vtkOpenGLRenderer* ren) {
renderer = ren;
}
void addImageSlice(vtkImageSlice* slice) {
imageSlice = slice;
}
virtual void Execute(vtkObject*, unsigned long event, void*)
{
if (event == vtkCommand::MouseWheelForwardEvent) {
if (auto transform = dynamic_cast<vtkTransform*>(imageSlice->GetUserTransform())) {
transform->Translate(0, 0, 1);
}
renderer->ResetCameraClippingRange();
camera->Modified();
renderWindow->Render();
}
else {
if (auto transform = dynamic_cast<vtkTransform*>(imageSlice->GetUserTransform())) {
transform->Translate(0, 0, -1);
}
renderer->ResetCameraClippingRange();
camera->Modified();
renderWindow->Render();
}
std::cout << "camera foc z " << camera->GetFocalPoint()[2] << "\n";
std::cout << "camera pos z " << camera->GetPosition()[2] << "\n";
std::cout << "foc pos diff " << sqrt(vtkMath::Distance2BetweenPoints(camera->GetFocalPoint(), camera->GetPosition())) << "\n";
std::cout << "image z bnds "
<< imageSlice->GetBounds()[4] << " "
<< imageSlice->GetBounds()[5] << "\n";
}
private:
vtkCamera* camera;
vtkRenderWindow* renderWindow;
vtkOpenGLRenderer* renderer;
vtkImageSlice* imageSlice;
};
} // namespace
int main(int, char*[])
{
vtkNew<vtkNamedColors> colors;
vtkNew<vtkMetaImageReader> reader;
reader->SetFileName("../FullHead.mhd");
vtkNew<vtkImageResliceMapper> imageResliceMapper;
imageResliceMapper->SetInputConnection(reader->GetOutputPort());
imageResliceMapper->ResampleToScreenPixelsOff();
imageResliceMapper->AutoAdjustImageQualityOn();
imageResliceMapper->SliceFacesCameraOn();
imageResliceMapper->SeparateWindowLevelOperationOff();
imageResliceMapper->SetSliceAtFocalPoint(true);
imageResliceMapper->JumpToNearestSliceOn();
vtkNew<vtkTransform> transform;
transform->Identity();
vtkNew<vtkImageSlice> imageSlice;
imageSlice->SetMapper(imageResliceMapper);
imageSlice->GetProperty()->SetInterpolationTypeToNearest();
imageSlice->SetUserTransform(transform);
// Setup renderers.
vtkNew<vtkOpenGLRenderer> renderer;
renderer->AddViewProp(imageSlice);
renderer->ResetCamera();
renderer->SetBackground(colors->GetColor3d("NavajoWhite").GetData());
// Setup render window.
vtkNew<vtkRenderWindow> renderWindow;
renderWindow->SetSize(1000, 1000);
renderWindow->AddRenderer(renderer);
// Setup render window interactor.
vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
vtkNew<vtkImageInteractionCallback1> callback;
callback->addCamera(renderer->GetActiveCamera());
callback->addRenderWindow(renderWindow);
callback->addRenderer(renderer);
callback->addImageSlice(imageSlice);
vtkNew<vtkInteractorStyleTrackballCamera> style;
style->AddObserver(vtkCommand::MouseWheelBackwardEvent, callback);
style->AddObserver(vtkCommand::MouseWheelForwardEvent, callback);
renderWindowInteractor->SetInteractorStyle(style);
// Render and start interaction.
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindow->Render();
renderWindowInteractor->Initialize();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}
You can scroll through the slices using the mouse wheel, zoom with right drag and move with middle drag.
If you zoom into the image such that the distance between the focal point and the position of the camera is around 2.0 or lower (in the command line output; also you should see roughly one image voxel on the screen), then certain slices don’t render, particularly when the focal point of the camera is close to the upper bound of the image.
I tried with both JumpToNearestSliceOn()
and JumpToNearestSliceOff()
. I also tested it with VTK 8.2.0 and VTK 9.2.6 to see if it was a version issue, but the problem is identical in all cases.
Is there a known reason why this happens, and possibly a way to fix it?
I understand it’s a bit of niche problem, but our software has certain requirements it has to meet and with our DICOM images it happens even when zooming to about 3-5 voxels on the screen, so if possible I’d like to fix this.
Thank you for the help!
Jakob