QT传输函数控件设计5 显示体渲染效果
初期为了简单,我们就直接在open()函数下面直接继续写。
为了保证没有读到文件就不处理下面的显示,我们就在上面定义一个flag,只有在文件数量>20的时候,flag = 1; 然后接着写:
if (flag == 1) {
}
我们接下来的功能都是在这里面实现的。
注意先包含一堆头文件:
#include <vtkImageData.h>
#include <vtkDICOMImageReader.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVolumeProperty.h>
#include <vtkPiecewiseFunction.h>
#include <vtkColorTransferFunction.h>
#include <vtkGPUVolumeRayCastMapper.h>
#include <vtkPointData.h>
#include <vtkCamera.h>
#include <vtkNew.h>
#include <vtkFloatArray.h>
#include <vtkImageViewer2.h>
#include <vtkEventQtSlotConnect.h>
然后开始在 if 语句里面写:
vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
vtkSmartPointer<vtkPiecewiseFunction> gradientTF = vtkSmartPointer<vtkPiecewiseFunction>::New();
vtkSmartPointer<vtkPiecewiseFunction> scalarTF = vtkSmartPointer<vtkPiecewiseFunction>::New();
vtkSmartPointer<vtkColorTransferFunction> colorTF = vtkSmartPointer<vtkColorTransferFunction>::New();
vtkSmartPointer< vtkImageViewer2 > m_pImageViewer;
vtkSmartPointer< vtkRenderer > m_pRenderder;
m_pRenderder = vtkSmartPointer< vtkRenderer >::New();
// 设置m_QVTKWidget的渲染器
myQVTKWidget->GetRenderWindow()->AddRenderer(m_pRenderder);
vtkSmartPointer<vtkGPUVolumeRayCastMapper> mapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
就是首先定义了一堆对象(都用智能指针管理的)。
然后
std::string strFolder = DcmPathName.toStdString(); //put your directory path here
// read the dicom dir
reader->SetDirectoryName(strFolder.c_str());
reader->Update();
imageData->ShallowCopy(reader->GetOutput());
把我们的文件路径导入。记得要使用toStdString函数转换成标准字符串。不然VTK接收不了QString,然后再把下面这一坨东西都放上去:
// properties options
volumeProperty->ShadeOn();
volumeProperty->SetInterpolationType(VTK_LINEAR_INTERPOLATION);
// get the real range in hounsfield
vtkDataArray* arr = reader->GetOutput()->GetPointData()->GetScalars();
double range[2];
arr->GetRange(range);
// 1D transfer functions
colorTF->AddRGBPoint(-200, 0.0, 0.0, 0.0);
colorTF->AddRGBPoint(150, 0.1, 0.1, 0.7);
colorTF->AddRGBPoint(350, 0.2, 0.2, 0.4);
colorTF->AddRGBPoint(512, 1.0, 1.0, 1.0);
colorTF->AddRGBPoint(range[1], 0.9, 0.1, 0.1);
scalarTF->AddPoint(-200, 0.00);
scalarTF->AddPoint(150, 0.20);
scalarTF->AddPoint(350, 0.30);
scalarTF->AddPoint(512, 0.5);
scalarTF->AddPoint(range[1], 0.4);
gradientTF->AddPoint(-200, 0.0);
gradientTF->AddPoint(150, 0.1);
gradientTF->AddPoint(350, 0.1);
gradientTF->AddPoint(512, 0.6);
gradientTF->AddPoint(range[1] / 4.0, 1.0);
volumeProperty->SetScalarOpacity(scalarTF);
volumeProperty->SetGradientOpacity(gradientTF);
volumeProperty->SetColor(colorTF);
// setup rendering context
myQVTKWidget->GetRenderWindow()->SetSize(myQVTKWidget->width(), myQVTKWidget->height());
myQVTKWidget->GetRenderWindow()->SetMultiSamples(0);
// mapping data
mapper->SetInputConnection(reader->GetOutputPort());
mapper->SetBlendModeToComposite();
mapper->SetUseJittering(1);
// renderer and volume
myQVTKWidget->GetRenderWindow()->AddRenderer(m_pRenderder);
m_pRenderder->SetBackground(0.03, 0.33, 0.33);
volume->SetMapper(mapper);
volume->SetProperty(volumeProperty);
m_pRenderder->AddVolume(volume);
m_pRenderder->ResetCamera();
m_pRenderder->GetActiveCamera()->Zoom(1.3);
//interactor->SetRenderWindow(m_QVTKWidget->GetRenderWindow());
//interactor->SetInteractorStyle(style);
myQVTKWidget->GetRenderWindow()->Render();
我们先把里面的与interactor有关的交互器屏蔽掉。现在就能看到效果了:
这里面有它自己带的交互效果,我们现在尝试一下使用QT和VTKWidget进行交互。
在类声明里加入私有变量:
vtkEventQtSlotConnect* myQtVtkConnections;
以及私有槽函数:
void currentMousePosition(vtkObject* obj);
之后在构造函数中定义:
myQtVtkConnections = vtkEventQtSlotConnect::New();
myQtVtkConnections->Connect(myQVTKWidget->GetRenderWindow()->GetInteractor(),
vtkCommand::MouseMoveEvent,
this, SLOT(currentMousePosition(vtkObject*)));
通过VTK里面的链接函数链接VTK的信号和QT的槽。
在类声明中加入:
QStatusBar *myStatusBar;
构造函数中:
myStatusBar = new QStatusBar(this);
setStatusBar(myStatusBar);
然后就是事件函数:
void MyMainWindow::currentMousePosition(vtkObject * obj)
{
// 获取交互器
vtkRenderWindowInteractor* iren = vtkRenderWindowInteractor::SafeDownCast(obj);
// 获取鼠标的当前位置
int eventPos[2];
iren->GetEventPosition(eventPos);
QString str;
myStatusBar->showMessage("x = "+QString::number(eventPos[0])+ " y = " + QString::number(eventPos[1]));
}
获取场景的交互器,然后找到当前的鼠标点,并在状态栏上进行显示。
效果如下:(气死我了,移植代码的时候不知道怎么定义了两个状态栏,结果一直各种错误,调了一个小时才发现问题。大家如果也有报错什么访问冲突,估计也是给同一个菜单定义了多个状态栏)
现在,表面上的控件都搞完了,下节我们开始定义传输函数QDockWidget .