VTK—三维图像重建和剖切
一、获取三维图像数据
1.vtkMetaImageReader 可以读取*.mhd、*.mha格式的图像文件
#include <vtkMetaImageReader.h>int main()
{vtkNew<vtkMetaImageReader>reader;reader->SetFileName("head.mhd");reader->Update();
}
2.vtkJPEGReader、vtkPNGReader可以读取一系列普通2d图像文件,然后使用沿着Z轴方向堆叠起来,从而获取3D图像数据
#include <vtkJPEGReader.h>
#include <vtkImageAppend.h>int main()
{//读取普通JPEG图像vtkNew<vtkJPEGReader>reader;//准备把多个普通2D图像沿着Z轴堆叠成3D图像vtkNew<vtkImageAppend>image;image->SetAppendAxis(2);//临时文件名字char name[64] = {};//读取100张普通2D图像,文件名字head001.jpg,head002.jpg...for (int i = 0; i < 100; i++){sprintf_s(name, 64, "head%03d.jpg", i + 1);reader->SetFileName(name);image->AddInputConnection(reader->GetOutputPort());}}
3.vtkImageData可以允许自己定义一个3d图像
//简化一下构造对象的写法
#define CREATE(type,name) vtkNew<type> name//自定义一个三维图像,维度128*128*128,像素值自己生成const int dim[3] = { 128,128,128 };//使用一个数据组存储每个点的像素值CREATE(vtkUnsignedCharArray,scalar);scalar->SetName("Scalar");for (int i = 0; i < dim[2]; i++)for (int j = 0; j < dim[1]; j++)for (int k = 0; k < dim[0]; k++){unsigned char value = 1.0 * (k+j+i)/dim[0]/3 * 255;scalar->InsertNextTuple1(value);}//定义图像维度等基本参数CREATE(vtkImageData,data);data->SetDimensions(dim[0],dim[1],dim[2]);data->SetOrigin(0, 0, 0);data->SetSpacing(1, 1, 1);
//上面生成的像素值作为点数据的标量data->GetPointData()->SetScalars(scalar);
二、体素绘制的常规步骤
1.获取图像数据(vtkImageData)
2.选择合适的映射器(vtkVolumeMapper)
3.定义属性数据(vtkVolumeProperty)
3.1设置颜色传递函数(vtkColorTransferFunction)—— 定义像素值和渲染颜色的对应关系
3.2设置不透明度传递函数(vtkPiecewiseFunction)—— 定义像素值和不透明度的对应关系
3.3设置环境亮度(SetAmbient(double))
3.4设置漫反射亮度(SetDiffuse(double))
3.5设置镜反射亮度(SetSpecular(double))
4.创建渲染对象(vtkVolume)
4.1设置映射器(SetMapper)
4.2设置属性(SetProperty)
5.常规的渲染管线
5.1 vtkRenderer
5.2 vtkRenderWindow
5.3 vtkRenderWindowInteractor
//选则常用的映射器CREATE(vtkFixedPointVolumeRayCastMapper,mapper);mapper->SetInputData(data); //颜色传递函数,像素值与颜色的对应关系CREATE(vtkColorTransferFunction,color);color->AddRGBPoint(0,10,10,0 );color->AddRGBPoint(127,255,255,0);color->AddRGBPoint(255,10,10,0);//不透明度传递函数,像素值与不透明度的对应关系CREATE(vtkPiecewiseFunction,opacity);opacity->AddPoint(10, 0.05);opacity->AddPoint(127, 0.9);opacity->AddPoint(250, 0.051);//体属性CREATE(vtkVolumeProperty,property);property->SetColor(color); //颜色传递函数property->SetScalarOpacity(opacity); //不透明度传递函数property->SetAmbient(0.3); //环境亮度 property->SetDiffuse(0.5); //漫反射亮度property->SetSpecular(0.2); //镜面反射亮度//可显示的体素CREATE(vtkVolume,volume);volume->SetMapper(mapper);volume->SetProperty(property);//下面是常规的可视化管线CREATE(vtkRenderer, render);CREATE(vtkRenderWindow, window);window->SetSize(800, 600);CREATE(vtkRenderWindowInteractor, inter);CREATE(vtkInteractorStyleSwitch, style);style->SetCurrentStyleToTrackballCamera();render->AddVolume(volume); window->AddRenderer(render);inter->SetRenderWindow(window);inter->SetInteractorStyle(style);
三、交互剖切3D图像
1.在窗口中添加一个vtkPlaneWidget交互对象
2.给vtkPlaneWidget添加一个回调对象
3.交互拖动vtkPlaneWidget过程中不断获取当前平面,使用该平面剖切图像
//实例化一个回调对象,实现3D图像剖切CREATE(myCallback,callback);callback->m_mapper = mapper;//添加一个平面小部件,改变剖切面CREATE(vtkPlaneWidget,planeWidget);planeWidget->SetInteractor(inter);planeWidget->SetCurrentRenderer(render);planeWidget->SetOrigin(0, 0, -100);planeWidget->SetPoint1(200, 0, -100);planeWidget->SetPoint2(0, 200, -100);planeWidget->SetRepresentationToWireframe();planeWidget->AddObserver(vtkCommand::InteractionEvent, callback);planeWidget->On();
四、完整的代码
#include <cassert>//一些初始化工作,否则会报错
#include <vtkAutoInit.h>VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2)
VTK_MODULE_INIT(vtkRenderingContextOpenGL2)//常用和可视化管线头文件
#include <vtkNew.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkInteractorStyleSwitch.h>
#include <vtkRenderWindowInteractor.h>#include <vtkPlane.h>
#include <vtkPlanes.h>
#include <vtkVolume.h>
#include <vtkPoints.h>
#include <vtkCommand.h>
#include <vtkPointData.h>
#include <vtkImageData.h>
#include <vtkAxesActor.h>
#include <vtkPlaneWidget.h>
#include <vtkDoubleArray.h>
#include <vtkVolumeProperty.h>
#include <vtkPiecewiseFunction.h>
#include <vtkUnsignedCharArray.h>
#include <vtkColorTransferFunction.h>
#include <vtkFixedPointVolumeRayCastMapper.h>//简化一下构造对象的写法
#define CREATE(type,name) vtkNew<type> name//回调对象的写法
class myCallback :public vtkCommand
{
public://不实现这个函数,无法使用vtkNewstatic myCallback* New(){return new myCallback;}//真正执行工作的函数,必须要实现void Execute(vtkObject* caller, unsigned long eventId, void* callData)override{assert(m_mapper);vtkPlaneWidget* widget = vtkPlaneWidget::SafeDownCast(caller);assert(widget);//获取交互小部件平面CREATE(vtkPlane,plane);widget->GetPlane(plane);//获取交互小部件的平面原点CREATE(vtkPoints,points);points->InsertNextPoint(plane->GetOrigin());//获取交互小部件的平面法向量CREATE(vtkDoubleArray, normals);;normals->SetNumberOfComponents(3);normals->InsertNextTuple3(plane->GetNormal()[0], plane->GetNormal()[1], plane->GetNormal()[2]);//使用一组平面剖切,组内可以只有一个平面CREATE(vtkPlanes,planes);planes->SetPoints(points);planes->SetNormals(normals);//使用平面剖切三维对象,支持平面组m_mapper->SetClippingPlanes(planes);}//三维重建的映射器vtkFixedPointVolumeRayCastMapper* m_mapper = nullptr;
};int main()
{//自定义一个三维图像,维度128*128*128,像素值自己生成const int dim[3] = { 128,128,128 };CREATE(vtkUnsignedCharArray,scalar);scalar->SetName("Scalar");for (int i = 0; i < dim[2]; i++)for (int j = 0; j < dim[1]; j++)for (int k = 0; k < dim[0]; k++){unsigned char value = 1.0 * (k+j+i)/dim[0]/3 * 255;scalar->InsertNextTuple1(value);}CREATE(vtkImageData,data);data->SetDimensions(dim[0],dim[1],dim[2]);data->SetOrigin(0, 0, 0);data->SetSpacing(1, 1, 1);data->GetPointData()->SetScalars(scalar);//选则常用的映射器CREATE(vtkFixedPointVolumeRayCastMapper,mapper);mapper->SetInputData(data); //颜色传递函数,像素值与颜色的对应关系CREATE(vtkColorTransferFunction,color);color->AddRGBPoint(0,10,10,0 );color->AddRGBPoint(127,255,255,0);color->AddRGBPoint(255,10,10,0);//不透明度传递函数,像素值与不透明度的对应关系CREATE(vtkPiecewiseFunction,opacity);opacity->AddPoint(10, 0.05);opacity->AddPoint(127, 0.9);opacity->AddPoint(250, 0.051);//体属性CREATE(vtkVolumeProperty,property);property->SetColor(color); //颜色传递函数property->SetScalarOpacity(opacity); //不透明度传递函数property->SetAmbient(0.3); //环境亮度 property->SetDiffuse(0.5); //漫反射亮度property->SetSpecular(0.2); //镜面反射亮度//可显示的体素CREATE(vtkVolume,volume);volume->SetMapper(mapper);volume->SetProperty(property);//下面是常规的可视化管线CREATE(vtkRenderer, render);CREATE(vtkRenderWindow, window);window->SetSize(800, 600);CREATE(vtkRenderWindowInteractor, inter);CREATE(vtkInteractorStyleSwitch, style);style->SetCurrentStyleToTrackballCamera();render->AddVolume(volume); window->AddRenderer(render);inter->SetRenderWindow(window);inter->SetInteractorStyle(style);//添加一个坐标轴,指示方向CREATE(vtkAxesActor,axes);axes->SetTotalLength(50, 50, 50);render->AddActor(axes);//实例化一个回调对象,实现3D图像剖切CREATE(myCallback,callback);callback->m_mapper = mapper;//添加一个平面小部件,改变剖切面CREATE(vtkPlaneWidget,planeWidget);planeWidget->SetInteractor(inter);planeWidget->SetCurrentRenderer(render);planeWidget->SetOrigin(0, 0, -100);planeWidget->SetPoint1(200, 0, -100);planeWidget->SetPoint2(0, 200, -100);planeWidget->SetRepresentationToWireframe();planeWidget->AddObserver(vtkCommand::InteractionEvent, callback);planeWidget->On();//开始交互inter->Start();return 0;
}