QT传输函数控件设计2 体渲染和VTK
程序我就先直接放这里:
#include<vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
#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 <string>
int main(int argc, char *argv[])
{
vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::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<vtkGPUVolumeRayCastMapper> mapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
std::string strFolder = "data"; //put your directory path here
// read the dicom dir
reader->SetDirectoryName(strFolder.c_str());
reader->Update();
imageData->ShallowCopy(reader->GetOutput());
// 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
renderWindow->SetSize(512, 512);
renderWindow->SetMultiSamples(0);
// mapping data
mapper->SetInputConnection(reader->GetOutputPort());
mapper->SetBlendModeToComposite();
mapper->SetUseJittering(1);
// renderer and volume
renderWindow->AddRenderer(renderer);
renderer->SetBackground(0.03, 0.33, 0.33);
volume->SetMapper(mapper);
volume->SetProperty(volumeProperty);
renderer->AddVolume(volume);
renderer->ResetCamera();
renderer->GetActiveCamera()->Zoom(1.3);
interactor->SetRenderWindow(renderWindow);
interactor->SetInteractorStyle(style);
renderWindow->Render();
interactor->Start();
return EXIT_SUCCESS;
}
显示效果如下:(注意首先你需要有一组CT影像组,需要各位去网上找,这里提供一组,因为百度网盘实在慢,本人又穷充不起会员,等这60多M的数据传完我再把链接附上来。如果我忘了记得在底下评论我能看到)
怎么装VTK大家自己百度,VTK里面的各种库的编译方法以及配置这里就不涉及了,CSDN博客有一大堆。
我们不需要都清楚细节,只需要知道这么几点:
1. std::string strFolder = "data"; 这个函数是用来记录存放医学影像文件组的文件夹的。
2. 这段程序
// 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);
这些函数是用来设置传输函数的,大家可以看到其实就是一堆断点,相当于之前我们设计的xml解析程序里面的各个Node。
大家注意到我们在这里故意设置每组传输函数(例如 colorTF 和 scalarTF 和 gradientTF)它们对应的第一个参数值都是相同的,所以我们可以用一个Node来同时记录这些数据,第一个参数是表示Intensity,程序里(还记得吗)我们设置的是NormalizedIntensity,即单位化的Intensity。
3. 把设置的断点加进来
volumeProperty->SetScalarOpacity(scalarTF);
volumeProperty->SetGradientOpacity(gradientTF);
volumeProperty->SetColor(colorTF);
这三句话是把属性设置到我们的渲染框架里
4.
后面关于interactor之类的函数是来设置交互方式的,这我们暂时先不用管,注意你们要配置的VTK库一定是要在CMAKE中选择支持QT,然后相关配置查其他博客就好了。弄好以后,会生成QVTKWidget,作为VTK渲染数据的显示区。
我们到时候要在窗口里放置一个QVTKWidget,然后直接用VTK自己带的交互功能就好了(旋转三维物体等)。
关于传输函数如果不会的话就看这篇博客:
VTK修炼之道 不透明度传输函数
其实以前我都是直接在GPU上做渲染引擎的,现在为了方便实现就不把那一大套东西都给搬过来了,直接用VTK实现,这样大家可以比较轻松地入手。下一节我们就开始设计界面了。