ResetView and fitwindow for volume rendering.

This commit is contained in:
Krad
2022-08-26 10:05:09 +08:00
parent ea04c5e9a0
commit b909435490
5 changed files with 203 additions and 20 deletions

View File

@@ -17,8 +17,23 @@
#include <vtkPiecewiseFunction.h> #include <vtkPiecewiseFunction.h>
#include <vtkColorTransferFunction.h> #include <vtkColorTransferFunction.h>
#include <vtkCornerAnnotation.h> #include <vtkCornerAnnotation.h>
#include <vtkPlane.h>
#include <vtkBoundingBox.h>
#include "Interaction/VolumeInteractorStyle.h" #include "Interaction/VolumeInteractorStyle.h"
#include "IO/DICOM/ExtendMedicalImageProperties.h"
namespace {
enum ViewDirection{
Anterior,
Posterior,
Left,
Right,
Superior,
Inferior
};
}
vtkStandardNewMacro(VolumeRenderingViewer); vtkStandardNewMacro(VolumeRenderingViewer);
@@ -30,7 +45,8 @@ VolumeRenderingViewer::VolumeRenderingViewer()
, annotation(vtkCornerAnnotation::New()) , annotation(vtkCornerAnnotation::New())
, VolumeMapper(vtkFixedPointVolumeRayCastMapper::New()) , VolumeMapper(vtkFixedPointVolumeRayCastMapper::New())
, InteractorStyle(nullptr) , InteractorStyle(nullptr)
, Interactor(nullptr){ , Interactor(nullptr)
, firstRender(true){
if (gpuMode){ if (gpuMode){
auto mapper = vtkGPUVolumeRayCastMapper::New(); auto mapper = vtkGPUVolumeRayCastMapper::New();
mapper->SetUseJittering(1); mapper->SetUseJittering(1);
@@ -42,19 +58,21 @@ VolumeRenderingViewer::VolumeRenderingViewer()
//设置线程数为核心数的两倍,为了可能用到超线程技术 //设置线程数为核心数的两倍,为了可能用到超线程技术
mapper->SetNumberOfThreads(2*mapper->GetNumberOfThreads()); mapper->SetNumberOfThreads(2*mapper->GetNumberOfThreads());
mapper->SetAutoAdjustSampleDistances(1); mapper->SetAutoAdjustSampleDistances(1);
mapper->SetSampleDistance(1.0); mapper->SetSampleDistance(0.25);
mapper->SetMaximumImageSampleDistance(4.0); mapper->SetMaximumImageSampleDistance(4.0);
mapper->SetMinimumImageSampleDistance(1.0); mapper->SetMinimumImageSampleDistance(1.0);
} }
vtkNew<vtkColorTransferFunction> colorFun; vtkNew<vtkColorTransferFunction> colorFun;
colorFun->SetScaleToLinear(); colorFun->SetScaleToLinear();
colorFun->AddRGBPoint(100.0,0.5,0.25,0.125); colorFun->AddRGBPoint(110.0,0.0,0.0,0.0);
colorFun->AddRGBPoint(200.0,1.0,0.9,0.6); colorFun->AddRGBPoint(121.0,0.5,0.25,0.125);
colorFun->AddRGBPoint(300.0,1.0,1.0,1.0);
colorFun->AddRGBPoint(453.0,1.0,1,1);
vtkNew<vtkPiecewiseFunction> opacityFun; vtkNew<vtkPiecewiseFunction> opacityFun;
opacityFun->AddPoint(100.0, 0.0); opacityFun->AddPoint(120.5, 0.0);
opacityFun->AddPoint(125.0, 0.5); // opacityFun->AddPoint(121.0, 1.0);
opacityFun->AddPoint(200.0, 1.0); // opacityFun->AddPoint(287.0, 1.0);
opacityFun->AddPoint(453.5, 1.0);
vtkNew<vtkVolumeProperty> volumeProperty; vtkNew<vtkVolumeProperty> volumeProperty;
volumeProperty->ShadeOn(); volumeProperty->ShadeOn();
@@ -63,10 +81,10 @@ VolumeRenderingViewer::VolumeRenderingViewer()
volumeProperty->SetScalarOpacity(opacityFun); volumeProperty->SetScalarOpacity(opacityFun);
volumeProperty->SetScalarOpacityUnitDistance(1.0); volumeProperty->SetScalarOpacityUnitDistance(1.0);
volumeProperty->SetAmbient(0.2); volumeProperty->SetAmbient(0.1);
volumeProperty->SetDiffuse(0.7); volumeProperty->SetDiffuse(0.7);
volumeProperty->SetSpecular(0.3); volumeProperty->SetSpecular(0.2);
volumeProperty->SetSpecularPower(8.0); volumeProperty->SetSpecularPower(10.0);
VolumeActor->SetProperty(volumeProperty); VolumeActor->SetProperty(volumeProperty);
VolumeActor->SetMapper(VolumeMapper); VolumeActor->SetMapper(VolumeMapper);
} }
@@ -219,15 +237,16 @@ void VolumeRenderingViewer::SetRenderer(vtkRenderer *arg) {
void VolumeRenderingViewer::Render() { void VolumeRenderingViewer::Render() {
if (RenderWindow && Interactor){ if (RenderWindow && Interactor){
if (firstRender){
firstRender = false;
SetViewDirection(0);
}
Interactor->Render(); Interactor->Render();
} }
} }
void VolumeRenderingViewer::SetInputData(vtkImageData *in) { void VolumeRenderingViewer::SetInputData(vtkImageData *in) {
if (in && in != this->VolumeMapper->GetInput()){ if (in && in != this->VolumeMapper->GetInput()){
double spacing[3]={0.0, 0.0, 0.0};
in->GetSpacing(spacing);
printf("set input, spacing: %f, %f, %f\r\n", spacing[0], spacing[1], spacing[2]);
this->VolumeMapper->SetInputData(in); this->VolumeMapper->SetInputData(in);
} }
} }
@@ -251,3 +270,102 @@ void VolumeRenderingViewer::SetInteractorStyleMode(int mode) {
style->SetInteractionMode(mode); style->SetInteractionMode(mode);
} }
} }
void VolumeRenderingViewer::SetCoordsTransformMatrix(ExtendMedicalImageProperties *pSeries) {
OrientationMatrix->DeepCopy(pSeries->GetOrientationMatrix());
//change to WToM
OrientationMatrix->Invert();
}
void VolumeRenderingViewer::SetViewDirection(int direction) {
double position[4] = {.0, .0, .0, 1.};
double ViewUp[4] = {.0, .0, .0, 1.};
switch (direction){
case Anterior:{
position[1] = -1.0;
ViewUp[2] = 1.0;
break;
}
case Posterior:{
position[1] = 1.0;
ViewUp[2] = 1.0;
break;
}
case Left:{
position[0] = 1.0;
ViewUp[2] = 1.0;
break;
}
case Right:{
position[0] = -1.0;
ViewUp[2] = 1.0;
break;
}
case Superior:{
position[2] = 1.0;
ViewUp[1] = 1.0;
break;
}
case Inferior:{
position[2] = -1.0;
ViewUp[1] = -1.0;
break;
}
}
OrientationMatrix->MultiplyPoint(position,position);
OrientationMatrix->MultiplyPoint(ViewUp,ViewUp);
vtkCamera* camera = Renderer->GetActiveCamera();
camera->SetFocalPoint(.0, .0, .0);
camera->SetPosition(position);
camera->SetViewUp(ViewUp);
Renderer->ResetCamera();
}
void VolumeRenderingViewer::ResetZoomFitWindow() {
auto data = VolumeMapper->GetInput();
if (!data) return;
double* bounds = data->GetBounds();
vtkBoundingBox boundingBox;
boundingBox.SetBounds(bounds);
double center[3]={0};
boundingBox.GetCenter(center);
auto camera = Renderer->GetActiveCamera();
double projNorm[3] = {.0, .0, .0};
camera->ComputeViewPlaneNormal();
camera->GetViewPlaneNormal(projNorm);
//move view projection to pass center, without projection normal change
double distance = camera->GetDistance();
camera->SetFocalPoint(center);
camera->SetPosition(center[0]+projNorm[0]*distance,
center[1]+projNorm[1]*distance,
center[2]+projNorm[2]*distance);
//use display point to cal dolly factor
double halfDisX = 0.0;
double halfDisY = 0.0;
double projOnPlane[3] ={.0};
for (int i = 0; i < 8; ++i) {
double corner[3] = {0};
boundingBox.GetCorner(i, corner);
Renderer->SetWorldPoint(corner);
Renderer->WorldToView();
Renderer->GetViewPoint(corner);
if (fabs(corner[0]) > halfDisX){
halfDisX = fabs(corner[0]);
}
if (fabs(corner[1]) > halfDisY){
halfDisY = fabs(corner[1]);
}
}
double *aspect = Renderer->GetAspect();
double volumeAspect = halfDisX/halfDisY;
if (volumeAspect>aspect[0]){
camera->Dolly(0.99/halfDisX);
}
else{
//use 1.97 to avoid border out of screen
camera->Dolly(0.99/halfDisY);
}
Renderer->ResetCameraClippingRange();
}

View File

@@ -6,6 +6,9 @@
#define OMEGAV_VOLUMERENDERINGVIEWER_H #define OMEGAV_VOLUMERENDERINGVIEWER_H
#include "vtkObject.h" #include "vtkObject.h"
#include "vtkNew.h"
#include "vtkMatrix4x4.h"
class vtkAlgorithm; class vtkAlgorithm;
class vtkImageData; class vtkImageData;
@@ -27,6 +30,8 @@ class vtkVolumeMapper;
class vtkCornerAnnotation; class vtkCornerAnnotation;
class ExtendMedicalImageProperties;
class VolumeRenderingViewer:public vtkObject { class VolumeRenderingViewer:public vtkObject {
public: public:
static VolumeRenderingViewer *New(); static VolumeRenderingViewer *New();
@@ -43,6 +48,8 @@ public:
virtual vtkImageData *GetInput(); virtual vtkImageData *GetInput();
void SetCoordsTransformMatrix(ExtendMedicalImageProperties *pSeries);
//@{ //@{
/** /**
* Get the internal render window, renderer, image actor, and * Get the internal render window, renderer, image actor, and
@@ -70,15 +77,24 @@ public:
void SetInteractorStyleMode(int mode); void SetInteractorStyleMode(int mode);
void SetViewDirection(int direction);
void ResetViewDirection(){
SetViewDirection(0);
}
void ResetView(){
ResetViewDirection();
ResetZoomFitWindow();
}
void ResetZoomFitWindow();
protected: protected:
VolumeRenderingViewer(); VolumeRenderingViewer();
~VolumeRenderingViewer() override; ~VolumeRenderingViewer() override;
// vtkAlgorithm *GetInputAlgorithm();
//
// vtkInformation *GetInputInformation();
virtual void InstallPipeline(); virtual void InstallPipeline();
virtual void UnInstallPipeline(); virtual void UnInstallPipeline();
@@ -96,7 +112,9 @@ private:
vtkInteractorStyle *InteractorStyle; vtkInteractorStyle *InteractorStyle;
vtkRenderWindowInteractor *Interactor; vtkRenderWindowInteractor *Interactor;
vtkCornerAnnotation* annotation; vtkCornerAnnotation* annotation;
vtkNew<vtkMatrix4x4> OrientationMatrix;
bool gpuMode = false; bool gpuMode = false;
bool firstRender = true;
}; };

View File

@@ -6,10 +6,15 @@
#include <QToolButton> #include <QToolButton>
#include <QButtonGroup> #include <QButtonGroup>
#include <QActionGroup>
#include <QMenu>
VolumeRenderingToolBar::VolumeRenderingToolBar(QWidget *parent) : QToolBar(parent) { VolumeRenderingToolBar::VolumeRenderingToolBar(QWidget *parent) : QToolBar(parent) {
auto btnReset = new QToolButton(this); auto btnReset = new QToolButton(this);
addButton(btnReset, "reset"); addButton(btnReset, "reset");
connect(btnReset,&QToolButton::clicked,[=](){
emit resetView();
});
auto mBtnAnonymize = new QToolButton(this); auto mBtnAnonymize = new QToolButton(this);
addButton(mBtnAnonymize, "anonymize"); addButton(mBtnAnonymize, "anonymize");
@@ -21,6 +26,24 @@ VolumeRenderingToolBar::VolumeRenderingToolBar(QWidget *parent) : QToolBar(paren
addButton(btnFreeRotate, "freeRotate"); addButton(btnFreeRotate, "freeRotate");
btnFreeRotate->setCheckable(true); btnFreeRotate->setCheckable(true);
btnFreeRotate->setChecked(true); btnFreeRotate->setChecked(true);
auto menu = new QMenu(this);
auto actionGroup = new QActionGroup(this);
actionGroup->addAction(menu->addAction(tr("Anterior")));
actionGroup->addAction(menu->addAction(tr("Posterior")));
actionGroup->addAction(menu->addAction(tr("Left")));
actionGroup->addAction(menu->addAction(tr("Right")));
actionGroup->addAction(menu->addAction(tr("Superior")));
actionGroup->addAction(menu->addAction(tr("Inferior")));
connect(actionGroup, &QActionGroup::triggered,[=](QAction* action){
int idx = actionGroup->actions().indexOf(action);
emit viewDirectionChanged(idx);
});
btnFreeRotate->setMenu(menu);
btnFreeRotate->setPopupMode(QToolButton::MenuButtonPopup);
btnFreeRotate->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
btnFreeRotate->setToolTip(tr("3D Rotate"));
auto btnRotate = new QToolButton(this); auto btnRotate = new QToolButton(this);
addButton(btnRotate, "planeRotate"); addButton(btnRotate, "planeRotate");
btnRotate->setCheckable(true); btnRotate->setCheckable(true);
@@ -29,6 +52,15 @@ VolumeRenderingToolBar::VolumeRenderingToolBar(QWidget *parent) : QToolBar(paren
btnPan->setCheckable(true); btnPan->setCheckable(true);
auto btnZoom = new QToolButton(this); auto btnZoom = new QToolButton(this);
addButton(btnZoom, "zoom"); addButton(btnZoom, "zoom");
menu = new QMenu(this);
btnZoom->setMenu(menu);
btnZoom->setPopupMode(QToolButton::MenuButtonPopup);
btnZoom->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
btnZoom->setToolTip(tr("Zoom"));
menu->addAction(tr("Fit window"),[=](){
emit fitWindow();
});
btnZoom->setCheckable(true); btnZoom->setCheckable(true);
auto btnWindow = new QToolButton(this); auto btnWindow = new QToolButton(this);
addButton(btnWindow, "window"); addButton(btnWindow, "window");

View File

@@ -17,6 +17,9 @@ public:
~VolumeRenderingToolBar() override; ~VolumeRenderingToolBar() override;
signals: signals:
void modeButtonClicked(int id); void modeButtonClicked(int id);
void viewDirectionChanged(int id);
void resetView();
void fitWindow();
private: private:
QAction* addButton(QToolButton* button, const char* objectName); QAction* addButton(QToolButton* button, const char* objectName);

View File

@@ -37,19 +37,31 @@ VolumeRenderingWindow::VolumeRenderingWindow(QWidget *parent , Qt::WindowFlags f
layout->addWidget(widget); layout->addWidget(widget);
setMinimumSize(680,500); setMinimumSize(680,500);
connect(toolBar, &VolumeRenderingToolBar::resetView,[=](){
mViewer->ResetView();
mViewer->Render();
});
connect(toolBar, &VolumeRenderingToolBar::fitWindow,[=](){
mViewer->ResetZoomFitWindow();
mViewer->Render();
});
connect(toolBar, &VolumeRenderingToolBar::modeButtonClicked,[=](int mode){ connect(toolBar, &VolumeRenderingToolBar::modeButtonClicked,[=](int mode){
printf("mode:%d \r\n", mode);
mViewer->SetInteractorStyleMode(mode); mViewer->SetInteractorStyleMode(mode);
}); });
connect(toolBar, &VolumeRenderingToolBar::viewDirectionChanged, [=](int direction){
mViewer->SetViewDirection(direction);
mViewer->Render();
});
} }
VolumeRenderingWindow::~VolumeRenderingWindow() { VolumeRenderingWindow::~VolumeRenderingWindow() {
mViewer->Delete(); mViewer->Delete();
qDebug() << "delete VolumeRenderingWindow";
} }
void VolumeRenderingWindow::loadData(SeriesImageSet* series){ void VolumeRenderingWindow::loadData(SeriesImageSet* series){
mViewer->SetupInteractor(mRenderWin->GetInteractor()); mViewer->SetupInteractor(mRenderWin->GetInteractor());
mViewer->SetInputData(series->GetData()); mViewer->SetInputData(series->GetData());
mViewer->SetCoordsTransformMatrix(series->GetProperty());
mViewer->Render(); mViewer->Render();
} }