vtk创建MPR四视图

VTKRenwin.h(vtk源码为QtVTKRenderWindows.h)VTKRenwin.cpp (vtk源码为QtVTKRenderWindows.h)前言:

MPR(multi-planner reformation)也称多平面重建,多重面重建是将扫描范围内所有的轴位图像叠加起来再对某些标线标定的重组线所指定的组织进行冠状、矢状位、任意角度斜位图像重组。

1、VTK7.x早期版本中,官方仅提供QVTKWidget,需要手动将QVTKWidgetPlugin.dll和QVTKWidgetPlugin.lib放到qt design下的plugin里。

2、在Qt版本为5.4之后时,在VTK8.2之前的版本,一般是使用QVTKOpenGLWidget;在VTK8.2及之后的版本里,引入了QVTKOpenGLNativeWidget;

3、VTK9.0版本删除了QVTKWidget,但是有QVTKOpenGLNativeWidget,QVTKOpenGLStereoWidget,QVTKOpenGLWindow。

该项目使用vtk9.0版本。(记得工程文件加入vtk lib库)

一.添加UI布局如下

       

 该布局包含了MPR视图移动或旋转的联动等基础功能。

二.代码实现

#include "mainwindow.h"
#include <QApplication>
#include "vtkOutputWindow.h"
#include "vtkAutoInit.h"


VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkRenderingContextOpenGL2);


int main(int argc, char *argv[])
{
    //    vtkOutputWindow::SetGlobalWarningDisplay(0);       //关闭错误输出


    QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H


#include <QMainWindow>
#include <QResizeEvent>
#include <QTabBar>
#include <QPaintEvent>
#include <QPixmap>
#include <QPainter>
#include <QLabel>
#include <QMouseEvent>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGridLayout>
#include <VTKRenwin.h>
namespace Ui {
class MainWindow;
}


class MainWindow : public QMainWindow
{
    Q_OBJECT


public:
    explicit MainWindow(QWidget *parent = 0);


    ~MainWindow();


private slots:
    void on_btn_close_clicked();


    void on_btn_fullscreen_clicked();


    void on_btn_select_clicked();


private:
    Ui::MainWindow *ui;
    QVBoxLayout *vlayout;
    QWidget *viewParent;
    QHBoxLayout *hlayout ;
    //图像管理
    VTKRenwin *mImageManage;
};


#endif // MAINWINDOW_H

 MainWindow.cpp(主界面)

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

static QPoint last(0,0);        //保存坐标
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowFlag(Qt::CustomizeWindowHint);
    ui->centralwidget->installEventFilter(this);


    vlayout = new QVBoxLayout(ui->widget_Main);
    vlayout->setMargin(0);
    vlayout->setSpacing(0);


    viewParent = new QWidget(ui->widget_Main);


    vlayout->addWidget(viewParent);
    hlayout = new QHBoxLayout(viewParent);
    hlayout->setMargin(0);
    hlayout->setMargin(0);


    mImageManage = new VTKRenwin(viewParent);
    hlayout->addWidget(mImageManage);
}


MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_btn_close_clicked()
{
    this->close();
}


void MainWindow::on_btn_fullscreen_clicked()
{
    if(this->isFullScreen())
    {
        this->showNormal();
    }
    else
    {
        this->showFullScreen();
    }


}
void MainWindow::on_btn_select_clicked()
{
    if(mImageManage)
        mImageManage->openFolder();
}

 VTKRenwin.h(vtk源码为QtVTKRenderWindows.h)

#ifndef VTKRENWIN_H
#define VTKRENWIN_H
#include <QWidget>
#include <vtkDICOMImageReader.h>
#include "vtkSmartPointer.h"
#include "vtkResliceImageViewer.h"
#include "vtkImagePlaneWidget.h"
#include "vtkDistanceWidget.h"
#include "vtkResliceImageViewerMeasurements.h"


#include "vtkTextActor.h"
#include "vtkTextProperty.h"
// Forward Qt class declarations
// class Ui_QtVTKRenderWindows;


namespace Ui {
class VTKRenwin;
}


class VTKRenwin : public QWidget
{
    Q_OBJECT


public:
    explicit VTKRenwin(QWidget *parent = nullptr);
    ~VTKRenwin();


    void openFolder();
    void slot_ReaderDICOMImage(const char *fn);
public slots:


  virtual void slotExit();
  virtual void resliceMode(int);
  virtual void thickMode(int);
  virtual void SetBlendModeToMaxIP();
  virtual void SetBlendModeToMinIP();
  virtual void SetBlendModeToMeanIP();
  virtual void SetBlendMode(int);
  virtual void ResetViews();
  virtual void Render();
  virtual void AddDistanceMeasurementToView1();
  virtual void AddDistanceMeasurementToView( int );


protected:
    vtkSmartPointer<vtkDICOMImageReader> reader = nullptr;       //读取DICOM文件
    int imageDims[3] = {0};
  vtkSmartPointer< vtkResliceImageViewer > riw[3];
  vtkSmartPointer< vtkImagePlaneWidget > planeWidget[3];
  vtkSmartPointer< vtkDistanceWidget > DistanceWidget[3];
  vtkSmartPointer< vtkResliceImageViewerMeasurements > ResliceMeasurements;


  vtkSmartPointer<vtkTextActor> textActor[4];                    //文本信息


private:


  // Designer form
    Ui::VTKRenwin *ui;
};


#endif // VTKRENWIN_H

 VTKRenwin.cpp (vtk源码为QtVTKRenderWindows.h)

#include "VTKRenwin.h"
#include "ui_VTKRenwin.h"

#include "vtkBoundedPlanePointPlacer.h"
#include "vtkCellPicker.h"
#include "vtkCommand.h"
#include "vtkDICOMImageReader.h"
#include "vtkDistanceRepresentation.h"
#include "vtkDistanceRepresentation2D.h"
#include "vtkDistanceWidget.h"
#include <vtkGenericOpenGLRenderWindow.h>
#include "vtkHandleRepresentation.h"
#include "vtkImageData.h"
#include "vtkImageMapToWindowLevelColors.h"
#include "vtkImageSlabReslice.h"
#include "vtkInteractorStyleImage.h"
#include "vtkLookupTable.h"
#include "vtkPlane.h"
#include "vtkPlaneSource.h"
#include "vtkPointHandleRepresentation2D.h"
#include "vtkPointHandleRepresentation3D.h"
#include "vtkProperty.h"
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <QFileDialog>
#include <vtkTextActor.h>
#include "vtkRenderWindowInteractor.h"
#include "vtkResliceImageViewer.h"
#include "vtkResliceCursorLineRepresentation.h"
#include "vtkResliceCursorThickLineRepresentation.h"
#include "vtkResliceCursorWidget.h"
#include "vtkResliceCursorActor.h"
#include "vtkResliceCursorPolyDataAlgorithm.h"
#include "vtkResliceCursor.h"
#include "vtkMetaImageReader.h"
#include "vtkResliceImageViewerMeasurements.h"
//----------------------------------------------------------------------------
class vtkResliceCursorCallback : public vtkCommand
{
public:
  static vtkResliceCursorCallback *New()
  { return new vtkResliceCursorCallback; }


  void Execute( vtkObject *caller, unsigned long ev,
                void *callData ) override
  {


    if (ev == vtkResliceCursorWidget::WindowLevelEvent ||
        ev == vtkCommand::WindowLevelEvent ||
        ev == vtkResliceCursorWidget::ResliceThicknessChangedEvent)
    {
      // Render everything
      for (int i = 0; i < 3; i++)
      {
        this->RCW[i]->Render();
      }
      this->IPW[0]->GetInteractor()->GetRenderWindow()->Render();
      return;
    }


    vtkImagePlaneWidget* ipw =
      dynamic_cast< vtkImagePlaneWidget* >( caller );
    if (ipw)
    {
      double* wl = static_cast<double*>( callData );


      if ( ipw == this->IPW[0] )
      {
        this->IPW[1]->SetWindowLevel(wl[0],wl[1],1);
        this->IPW[2]->SetWindowLevel(wl[0],wl[1],1);
      }
      else if( ipw == this->IPW[1] )
      {
        this->IPW[0]->SetWindowLevel(wl[0],wl[1],1);
        this->IPW[2]->SetWindowLevel(wl[0],wl[1],1);
      }
      else if (ipw == this->IPW[2])
      {
        this->IPW[0]->SetWindowLevel(wl[0],wl[1],1);
        this->IPW[1]->SetWindowLevel(wl[0],wl[1],1);
      }
    }


    vtkResliceCursorWidget *rcw = dynamic_cast<
      vtkResliceCursorWidget * >(caller);
    if (rcw)
    {
      vtkResliceCursorLineRepresentation *rep = dynamic_cast<
        vtkResliceCursorLineRepresentation * >(rcw->GetRepresentation());
      // Although the return value is not used, we keep the get calls
      // in case they had side-effects
      rep->GetResliceCursorActor()->GetCursorAlgorithm()->GetResliceCursor();
      for (int i = 0; i < 3; i++)
      {
        vtkPlaneSource *ps = static_cast< vtkPlaneSource * >(
            this->IPW[i]->GetPolyDataAlgorithm());
        ps->SetOrigin(this->RCW[i]->GetResliceCursorRepresentation()->
                                          GetPlaneSource()->GetOrigin());
        ps->SetPoint1(this->RCW[i]->GetResliceCursorRepresentation()->
                                          GetPlaneSource()->GetPoint1());
        ps->SetPoint2(this->RCW[i]->GetResliceCursorRepresentation()->
                                          GetPlaneSource()->GetPoint2());


        // If the reslice plane has modified, update it on the 3D widget
        this->IPW[i]->UpdatePlacement();
      }
    }


    // Render everything
    for (int i = 0; i < 3; i++)
    {
      this->RCW[i]->Render();
    }
    this->IPW[0]->GetInteractor()->GetRenderWindow()->Render();
  }


  vtkResliceCursorCallback() {}
  vtkImagePlaneWidget* IPW[3];
  vtkResliceCursorWidget *RCW[3];
};
VTKRenwin::~VTKRenwin()
{
    delete ui;
}
VTKRenwin::VTKRenwin(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::VTKRenwin)
{
    ui->setupUi(this);


  // Set up action signals and slots
//  connect(this->ui->actionExit, SIGNAL(triggered()), this, SLOT(slotExit()));
  connect(this->ui->resliceModeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(resliceMode(int)));
  connect(this->ui->thickModeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(thickMode(int)));
  this->ui->thickModeCheckBox->setEnabled(0);


  connect(this->ui->radioButton_Max, SIGNAL(pressed()), this, SLOT(SetBlendModeToMaxIP()));
  connect(this->ui->radioButton_Min, SIGNAL(pressed()), this, SLOT(SetBlendModeToMinIP()));
  connect(this->ui->radioButton_Mean, SIGNAL(pressed()), this, SLOT(SetBlendModeToMeanIP()));
  this->ui->blendModeGroupBox->setEnabled(0);


  connect(this->ui->resetButton, SIGNAL(pressed()), this, SLOT(ResetViews()));
  connect(this->ui->AddDistance1Button, SIGNAL(pressed()), this, SLOT(AddDistanceMeasurementToView1()));


  reader = vtkSmartPointer<vtkDICOMImageReader>::New();
  update();
};


void VTKRenwin::slotExit()
{
  qApp->exit();
}


void VTKRenwin::resliceMode(int mode)
{
  this->ui->thickModeCheckBox->setEnabled(mode ? 1 : 0);
  this->ui->blendModeGroupBox->setEnabled(mode ? 1 : 0);


  for (int i = 0; i < 3; i++)
  {
    riw[i]->SetResliceMode(mode ? 1 : 0);
    riw[i]->GetRenderer()->ResetCamera();
    riw[i]->Render();
  }
}


void VTKRenwin::thickMode(int mode)
{
  for (int i = 0; i < 3; i++)
  {
    riw[i]->SetThickMode(mode ? 1 : 0);
    riw[i]->Render();
  }
}


void VTKRenwin::SetBlendMode(int m)
{
  for (int i = 0; i < 3; i++)
  {
    vtkImageSlabReslice *thickSlabReslice = vtkImageSlabReslice::SafeDownCast(
        vtkResliceCursorThickLineRepresentation::SafeDownCast(
          riw[i]->GetResliceCursorWidget()->GetRepresentation())->GetReslice());
    thickSlabReslice->SetBlendMode(m);
    riw[i]->Render();
  }
}


void VTKRenwin::SetBlendModeToMaxIP()
{
  this->SetBlendMode(VTK_IMAGE_SLAB_MAX);
}


void VTKRenwin::SetBlendModeToMinIP()
{
  this->SetBlendMode(VTK_IMAGE_SLAB_MIN);
}


void VTKRenwin::SetBlendModeToMeanIP()
{
  this->SetBlendMode(VTK_IMAGE_SLAB_MEAN);
}


void VTKRenwin::ResetViews()
{
  // Reset the reslice image views
  for (int i = 0; i < 3; i++)
  {
    riw[i]->Reset();
  }


  // Also sync the Image plane widget on the 3D top right view with any
  // changes to the reslice cursor.
  for (int i = 0; i < 3; i++)
  {
    vtkPlaneSource *ps = static_cast< vtkPlaneSource * >(
        planeWidget[i]->GetPolyDataAlgorithm());
    ps->SetNormal(riw[0]->GetResliceCursor()->GetPlane(i)->GetNormal());
    ps->SetCenter(riw[0]->GetResliceCursor()->GetPlane(i)->GetOrigin());


    // If the reslice plane has modified, update it on the 3D widget
    this->planeWidget[i]->UpdatePlacement();
  }


  // Render in response to changes.
  this->Render();
}
void VTKRenwin::openFolder()
{
    QString dir = QFileDialog::getExistingDirectory(this, tr("打开影像文件夹"),"/home",QFileDialog::ShowDirsOnly| QFileDialog::DontResolveSymlinks);
    if(!dir.isEmpty())
    {
        this->slot_ReaderDICOMImage(dir.toLocal8Bit().data());
    }
}
void VTKRenwin::slot_ReaderDICOMImage(const char *fn)
{
    reader->SetDirectoryName(fn);
    reader->Update();
    reader->GetOutput()->GetDimensions(imageDims);              //获取维度


    for (auto i=0;i<4;i++)
    {
        textActor[i] = vtkSmartPointer<vtkTextActor>::New();
        textActor[i]->SetDisplayPosition(5, 5);
        textActor[i]->GetTextProperty()->SetFontSize(14);
    }


    textActor[0]->SetInput(QString::fromUtf8("Sagittal").toUtf8());
    textActor[0]->GetTextProperty()->SetColor(0, 1, 0);


    textActor[1]->SetInput(QString::fromUtf8("Coronal").toUtf8());
    textActor[1]->GetTextProperty()->SetColor(0, 0, 1);


    textActor[2]->SetInput(QString::fromUtf8("Axial").toUtf8());
    textActor[2]->GetTextProperty()->SetColor(1, 0, 0);


    textActor[3]->SetInput(QString::fromUtf8("3D").toUtf8());
    textActor[3]->GetTextProperty()->SetColor(1, 1, 0);


    for (int i = 0; i < 3; i++)
    {
      riw[i] = vtkSmartPointer< vtkResliceImageViewer >::New();
      riw[i]->GetRenderer()->AddActor(textActor[i]);
      vtkNew<vtkGenericOpenGLRenderWindow> renderWindow;
      riw[i]->SetRenderWindow(renderWindow);
    }


    ui->view1->SetRenderWindow(riw[0]->GetRenderWindow());
    riw[0]->SetupInteractor(ui->view1->GetRenderWindow()->GetInteractor());


    ui->view2->SetRenderWindow(riw[1]->GetRenderWindow());
    riw[1]->SetupInteractor(ui->view2->GetRenderWindow()->GetInteractor());


    ui->view3->SetRenderWindow(riw[2]->GetRenderWindow());
    riw[2]->SetupInteractor(ui->view3->GetRenderWindow()->GetInteractor());


    for (int i = 0; i < 3; i++)
    {
      // make them all share the same reslice cursor object.
      vtkResliceCursorLineRepresentation *rep = vtkResliceCursorLineRepresentation::SafeDownCast(riw[i]->GetResliceCursorWidget()->GetRepresentation());
      riw[i]->SetResliceCursor(riw[0]->GetResliceCursor());


      rep->GetResliceCursorActor()->GetCursorAlgorithm()->SetReslicePlaneNormal(i);


      riw[i]->SetInputData(reader->GetOutput());
      riw[i]->SetSliceOrientation(i);
      riw[i]->SetResliceModeToAxisAligned();
    }


    vtkSmartPointer<vtkCellPicker> picker = vtkSmartPointer<vtkCellPicker>::New();
    picker->SetTolerance(0.005);


    vtkSmartPointer<vtkProperty> ipwProp = vtkSmartPointer<vtkProperty>::New();


    vtkSmartPointer< vtkRenderer > ren = vtkSmartPointer< vtkRenderer >::New();


    ren->AddActor(textActor[3]);


    vtkNew<vtkGenericOpenGLRenderWindow> renderWindow;


    this->ui->view4->SetRenderWindow(renderWindow);
    this->ui->view4->GetRenderWindow()->AddRenderer(ren);
    vtkRenderWindowInteractor *iren = this->ui->view4->GetInteractor();


    for (int i = 0; i < 3; i++)
    {
      planeWidget[i] = vtkSmartPointer<vtkImagePlaneWidget>::New();
      planeWidget[i]->SetInteractor( iren );
      planeWidget[i]->SetPicker(picker);
      planeWidget[i]->RestrictPlaneToVolumeOn();
      double color[3] = {0, 0, 0};
      color[i] = 1;
      planeWidget[i]->GetPlaneProperty()->SetColor(color);


      color[0] /= 4.0;
      color[1] /= 4.0;
      color[2] /= 4.0;
      riw[i]->GetRenderer()->SetBackground( color );


      planeWidget[i]->SetTexturePlaneProperty(ipwProp);
      planeWidget[i]->TextureInterpolateOff();
      planeWidget[i]->SetResliceInterpolateToLinear();
      planeWidget[i]->SetInputConnection(reader->GetOutputPort());
      planeWidget[i]->SetPlaneOrientation(i);
      planeWidget[i]->SetSliceIndex(imageDims[i]/2);
      planeWidget[i]->DisplayTextOn();
      planeWidget[i]->SetDefaultRenderer(ren);
      planeWidget[i]->SetWindowLevel(1358, -27);
      planeWidget[i]->On();
      planeWidget[i]->InteractionOn();
    }


    vtkSmartPointer<vtkResliceCursorCallback> cbk = vtkSmartPointer<vtkResliceCursorCallback>::New();


    for (int i = 0; i < 3; i++)
    {
      cbk->IPW[i] = planeWidget[i];
      cbk->RCW[i] = riw[i]->GetResliceCursorWidget();
      riw[i]->GetResliceCursorWidget()->AddObserver(
          vtkResliceCursorWidget::ResliceAxesChangedEvent, cbk );
      riw[i]->GetResliceCursorWidget()->AddObserver(
          vtkResliceCursorWidget::WindowLevelEvent, cbk );
      riw[i]->GetResliceCursorWidget()->AddObserver(
          vtkResliceCursorWidget::ResliceThicknessChangedEvent, cbk );
      riw[i]->GetResliceCursorWidget()->AddObserver(
          vtkResliceCursorWidget::ResetCursorEvent, cbk );
      riw[i]->GetInteractorStyle()->AddObserver(
          vtkCommand::WindowLevelEvent, cbk );


      // Make them all share the same color map.
      riw[i]->SetLookupTable(riw[0]->GetLookupTable());
      planeWidget[i]->GetColorMap()->SetLookupTable(riw[0]->GetLookupTable());
      //planeWidget[i]->GetColorMap()->SetInput(riw[i]->GetResliceCursorWidget()->GetResliceCursorRepresentation()->GetColorMap()->GetInput());
      planeWidget[i]->SetColorMap(riw[i]->GetResliceCursorWidget()->GetResliceCursorRepresentation()->GetColorMap());


    }


//    this->ui->view1->show();
//    this->ui->view2->show();
//    this->ui->view3->show();
}
void VTKRenwin::Render()
{
  for (int i = 0; i < 3; i++)
  {
    riw[i]->Render();
  }
  this->ui->view3->GetRenderWindow()->Render();
}


void VTKRenwin::AddDistanceMeasurementToView1()
{
  this->AddDistanceMeasurementToView(1);
}


void VTKRenwin::AddDistanceMeasurementToView(int i)
{
  // remove existing widgets.
  if (this->DistanceWidget[i])
  {
    this->DistanceWidget[i]->SetEnabled(0);
    this->DistanceWidget[i] = nullptr;
  }


  // add new widget
  this->DistanceWidget[i] = vtkSmartPointer< vtkDistanceWidget >::New();
  this->DistanceWidget[i]->SetInteractor(
    this->riw[i]->GetResliceCursorWidget()->GetInteractor());


  // Set a priority higher than our reslice cursor widget
  this->DistanceWidget[i]->SetPriority(
    this->riw[i]->GetResliceCursorWidget()->GetPriority() + 0.01);


  vtkSmartPointer< vtkPointHandleRepresentation2D > handleRep =
    vtkSmartPointer< vtkPointHandleRepresentation2D >::New();
  vtkSmartPointer< vtkDistanceRepresentation2D > distanceRep =
    vtkSmartPointer< vtkDistanceRepresentation2D >::New();
  distanceRep->SetHandleRepresentation(handleRep);
  this->DistanceWidget[i]->SetRepresentation(distanceRep);
  distanceRep->InstantiateHandleRepresentation();
  distanceRep->GetPoint1Representation()->SetPointPlacer(riw[i]->GetPointPlacer());
  distanceRep->GetPoint2Representation()->SetPointPlacer(riw[i]->GetPointPlacer());


  // Add the distance to the list of widgets whose visibility is managed based
  // on the reslice plane by the ResliceImageViewerMeasurements class
  this->riw[i]->GetMeasurements()->AddItem(this->DistanceWidget[i]);


  this->DistanceWidget[i]->CreateDefaultRepresentation();
  this->DistanceWidget[i]->EnabledOn();
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值