ResetView and fitwindow for volume rendering.
This commit is contained in:
@@ -17,8 +17,23 @@
|
||||
#include <vtkPiecewiseFunction.h>
|
||||
#include <vtkColorTransferFunction.h>
|
||||
#include <vtkCornerAnnotation.h>
|
||||
#include <vtkPlane.h>
|
||||
#include <vtkBoundingBox.h>
|
||||
|
||||
|
||||
#include "Interaction/VolumeInteractorStyle.h"
|
||||
#include "IO/DICOM/ExtendMedicalImageProperties.h"
|
||||
|
||||
namespace {
|
||||
enum ViewDirection{
|
||||
Anterior,
|
||||
Posterior,
|
||||
Left,
|
||||
Right,
|
||||
Superior,
|
||||
Inferior
|
||||
};
|
||||
}
|
||||
|
||||
vtkStandardNewMacro(VolumeRenderingViewer);
|
||||
|
||||
@@ -30,7 +45,8 @@ VolumeRenderingViewer::VolumeRenderingViewer()
|
||||
, annotation(vtkCornerAnnotation::New())
|
||||
, VolumeMapper(vtkFixedPointVolumeRayCastMapper::New())
|
||||
, InteractorStyle(nullptr)
|
||||
, Interactor(nullptr){
|
||||
, Interactor(nullptr)
|
||||
, firstRender(true){
|
||||
if (gpuMode){
|
||||
auto mapper = vtkGPUVolumeRayCastMapper::New();
|
||||
mapper->SetUseJittering(1);
|
||||
@@ -42,19 +58,21 @@ VolumeRenderingViewer::VolumeRenderingViewer()
|
||||
//设置线程数为核心数的两倍,为了可能用到超线程技术
|
||||
mapper->SetNumberOfThreads(2*mapper->GetNumberOfThreads());
|
||||
mapper->SetAutoAdjustSampleDistances(1);
|
||||
mapper->SetSampleDistance(1.0);
|
||||
mapper->SetSampleDistance(0.25);
|
||||
mapper->SetMaximumImageSampleDistance(4.0);
|
||||
mapper->SetMinimumImageSampleDistance(1.0);
|
||||
}
|
||||
vtkNew<vtkColorTransferFunction> colorFun;
|
||||
colorFun->SetScaleToLinear();
|
||||
colorFun->AddRGBPoint(100.0,0.5,0.25,0.125);
|
||||
colorFun->AddRGBPoint(200.0,1.0,0.9,0.6);
|
||||
colorFun->AddRGBPoint(300.0,1.0,1.0,1.0);
|
||||
colorFun->AddRGBPoint(110.0,0.0,0.0,0.0);
|
||||
colorFun->AddRGBPoint(121.0,0.5,0.25,0.125);
|
||||
|
||||
colorFun->AddRGBPoint(453.0,1.0,1,1);
|
||||
vtkNew<vtkPiecewiseFunction> opacityFun;
|
||||
opacityFun->AddPoint(100.0, 0.0);
|
||||
opacityFun->AddPoint(125.0, 0.5);
|
||||
opacityFun->AddPoint(200.0, 1.0);
|
||||
opacityFun->AddPoint(120.5, 0.0);
|
||||
// opacityFun->AddPoint(121.0, 1.0);
|
||||
// opacityFun->AddPoint(287.0, 1.0);
|
||||
opacityFun->AddPoint(453.5, 1.0);
|
||||
|
||||
vtkNew<vtkVolumeProperty> volumeProperty;
|
||||
volumeProperty->ShadeOn();
|
||||
@@ -63,10 +81,10 @@ VolumeRenderingViewer::VolumeRenderingViewer()
|
||||
volumeProperty->SetScalarOpacity(opacityFun);
|
||||
volumeProperty->SetScalarOpacityUnitDistance(1.0);
|
||||
|
||||
volumeProperty->SetAmbient(0.2);
|
||||
volumeProperty->SetAmbient(0.1);
|
||||
volumeProperty->SetDiffuse(0.7);
|
||||
volumeProperty->SetSpecular(0.3);
|
||||
volumeProperty->SetSpecularPower(8.0);
|
||||
volumeProperty->SetSpecular(0.2);
|
||||
volumeProperty->SetSpecularPower(10.0);
|
||||
VolumeActor->SetProperty(volumeProperty);
|
||||
VolumeActor->SetMapper(VolumeMapper);
|
||||
}
|
||||
@@ -219,15 +237,16 @@ void VolumeRenderingViewer::SetRenderer(vtkRenderer *arg) {
|
||||
|
||||
void VolumeRenderingViewer::Render() {
|
||||
if (RenderWindow && Interactor){
|
||||
if (firstRender){
|
||||
firstRender = false;
|
||||
SetViewDirection(0);
|
||||
}
|
||||
Interactor->Render();
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeRenderingViewer::SetInputData(vtkImageData *in) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -251,3 +270,102 @@ void VolumeRenderingViewer::SetInteractorStyleMode(int 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();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#define OMEGAV_VOLUMERENDERINGVIEWER_H
|
||||
|
||||
#include "vtkObject.h"
|
||||
#include "vtkNew.h"
|
||||
#include "vtkMatrix4x4.h"
|
||||
|
||||
class vtkAlgorithm;
|
||||
|
||||
class vtkImageData;
|
||||
@@ -27,6 +30,8 @@ class vtkVolumeMapper;
|
||||
|
||||
class vtkCornerAnnotation;
|
||||
|
||||
class ExtendMedicalImageProperties;
|
||||
|
||||
class VolumeRenderingViewer:public vtkObject {
|
||||
public:
|
||||
static VolumeRenderingViewer *New();
|
||||
@@ -43,6 +48,8 @@ public:
|
||||
|
||||
virtual vtkImageData *GetInput();
|
||||
|
||||
void SetCoordsTransformMatrix(ExtendMedicalImageProperties *pSeries);
|
||||
|
||||
//@{
|
||||
/**
|
||||
* Get the internal render window, renderer, image actor, and
|
||||
@@ -70,15 +77,24 @@ public:
|
||||
|
||||
void SetInteractorStyleMode(int mode);
|
||||
|
||||
void SetViewDirection(int direction);
|
||||
|
||||
void ResetViewDirection(){
|
||||
SetViewDirection(0);
|
||||
}
|
||||
|
||||
void ResetView(){
|
||||
ResetViewDirection();
|
||||
ResetZoomFitWindow();
|
||||
}
|
||||
|
||||
void ResetZoomFitWindow();
|
||||
|
||||
protected:
|
||||
VolumeRenderingViewer();
|
||||
|
||||
~VolumeRenderingViewer() override;
|
||||
|
||||
// vtkAlgorithm *GetInputAlgorithm();
|
||||
//
|
||||
// vtkInformation *GetInputInformation();
|
||||
|
||||
virtual void InstallPipeline();
|
||||
|
||||
virtual void UnInstallPipeline();
|
||||
@@ -96,7 +112,9 @@ private:
|
||||
vtkInteractorStyle *InteractorStyle;
|
||||
vtkRenderWindowInteractor *Interactor;
|
||||
vtkCornerAnnotation* annotation;
|
||||
vtkNew<vtkMatrix4x4> OrientationMatrix;
|
||||
bool gpuMode = false;
|
||||
bool firstRender = true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -6,10 +6,15 @@
|
||||
|
||||
#include <QToolButton>
|
||||
#include <QButtonGroup>
|
||||
#include <QActionGroup>
|
||||
#include <QMenu>
|
||||
|
||||
VolumeRenderingToolBar::VolumeRenderingToolBar(QWidget *parent) : QToolBar(parent) {
|
||||
auto btnReset = new QToolButton(this);
|
||||
addButton(btnReset, "reset");
|
||||
connect(btnReset,&QToolButton::clicked,[=](){
|
||||
emit resetView();
|
||||
});
|
||||
|
||||
auto mBtnAnonymize = new QToolButton(this);
|
||||
addButton(mBtnAnonymize, "anonymize");
|
||||
@@ -21,6 +26,24 @@ VolumeRenderingToolBar::VolumeRenderingToolBar(QWidget *parent) : QToolBar(paren
|
||||
addButton(btnFreeRotate, "freeRotate");
|
||||
btnFreeRotate->setCheckable(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);
|
||||
addButton(btnRotate, "planeRotate");
|
||||
btnRotate->setCheckable(true);
|
||||
@@ -29,6 +52,15 @@ VolumeRenderingToolBar::VolumeRenderingToolBar(QWidget *parent) : QToolBar(paren
|
||||
btnPan->setCheckable(true);
|
||||
auto btnZoom = new QToolButton(this);
|
||||
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);
|
||||
auto btnWindow = new QToolButton(this);
|
||||
addButton(btnWindow, "window");
|
||||
|
||||
@@ -17,6 +17,9 @@ public:
|
||||
~VolumeRenderingToolBar() override;
|
||||
signals:
|
||||
void modeButtonClicked(int id);
|
||||
void viewDirectionChanged(int id);
|
||||
void resetView();
|
||||
void fitWindow();
|
||||
|
||||
private:
|
||||
QAction* addButton(QToolButton* button, const char* objectName);
|
||||
|
||||
@@ -37,19 +37,31 @@ VolumeRenderingWindow::VolumeRenderingWindow(QWidget *parent , Qt::WindowFlags f
|
||||
layout->addWidget(widget);
|
||||
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){
|
||||
printf("mode:%d \r\n", mode);
|
||||
mViewer->SetInteractorStyleMode(mode);
|
||||
});
|
||||
|
||||
connect(toolBar, &VolumeRenderingToolBar::viewDirectionChanged, [=](int direction){
|
||||
mViewer->SetViewDirection(direction);
|
||||
mViewer->Render();
|
||||
});
|
||||
}
|
||||
|
||||
VolumeRenderingWindow::~VolumeRenderingWindow() {
|
||||
mViewer->Delete();
|
||||
qDebug() << "delete VolumeRenderingWindow";
|
||||
}
|
||||
|
||||
void VolumeRenderingWindow::loadData(SeriesImageSet* series){
|
||||
mViewer->SetupInteractor(mRenderWin->GetInteractor());
|
||||
mViewer->SetInputData(series->GetData());
|
||||
mViewer->SetCoordsTransformMatrix(series->GetProperty());
|
||||
mViewer->Render();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user