VTK 的可视化方法:等值面
- VTK 的可视化方法:等值面
- VTK 中的数据表达
- VTK 等值面提取类
- 实例1:模型数据的等值面提取
- 空间函数数据的等值面提取
- 参考
VTK 的可视化方法:等值面
等值面是指一组具有相同标量值的点所构成的表面。
等值面(线)提取是一种常用的可视化技术,常应用于医学、地质、气象等领域。例如,在医学图像处理中,由于CT、MRI等图像分辨率越来越高,虽然体绘制技术可以清晰地对数据内部结构进行可视化,但是其计算量和效率却制约了其使用。此时可通过等值面提取技术,仅提取感兴趣的一个或者几个组织轮廓,并生成网格模型以供后续的处理和研究。
例子:
VTK 中的数据表达
- Image Data:最基本的网格数据,存储复杂度是 O(1)。
- Rectilinear Grid:放宽网格间距的限制,网格间距存储在 dx 和 dy 数组中,存储复杂度是 O(m+n)。
- Structured Grid:放宽行列之间正交关系的约束,需要每个网格的 [px, py] 坐标,存储复杂度为 O(mn)。
- Polygonal Data:网格拓扑结构的约束也不存在了,这时网格可以是离散的,需要更高的存储复杂度。
VTK 等值面提取类
根据数据类型的不同,VTK中提供了多个等值面提取类,其类图如图所示:
VTK中的等值面提取算法多基于MarchingCube算法来实现。MarchingCube是经典的移动立方体等值面提取算法。该算法是由W.E.Lorenson和H.E.Cline在1987年提出的。由于这一方法原理简单,易于实现,目前已经得到了较为广泛的应用,称为三维数据等值面生成的经典算法。
等值面提取类根据数据类型的不同而有所侧重:
-
vtkContourFilter:一个更加通用的等值面提取类,其可以接受任意的数据类型生成等值线或等值面。
-
vtkMarchingContourFilter:可以接受任何类型的数据,其内部根据数据不同生成不同的算法对象实现等值面/线的提取,具有较高的效率。
- vtkDiscreteMarchingCubes:继承自vtkMarchingCubes,主要针对Label图像,比如利用图像分割算法对医学图像进行分割后得到含有不同Label值得数据,每个Label对应一个组织,如果想要得到其中一个或者几个组织的模型,则可以考虑使用该类。
-
vtkMarchingCubes:主要针对规则体数据生成等值面。
-
vtkImageMarchingCubes:主要处理三维图像数据。
-
vtkMarchingSquares:针对二维规则网格数据生成等值线。
实例1:模型数据的等值面提取
#include "VTKContour.h"
#include <vtkConeSource.h>
#include <vtkSTLReader.h>
#include <vtkMultiBlockPLOT3DReader.h>
#include <vtkDataSet.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkDataArray.h>
#include <vtkPointData.h>
#include <vtkShrinkPolyData.h>
#include <vtkStructuredGridGeometryFilter.h>
#include <vtkStructuredGridOutlineFilter.h>
#include <vtkContourFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
VTKContour::VTKContour(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
_pVTKWidget = new QVTKOpenGLNativeWidget();
this->setCentralWidget(_pVTKWidget);
// this->showMaximized();
// 1. generate data
// vtkSmartPointer<vtkConeSource> cone = vtkSmartPointer<vtkConeSource>::New();
// or, read data
// vtkMultiBlockPLOT3DReader 是一个读取器对象,用于读取 PLOT3D 格式的文件并在输出时生成结构化网格
vtkSmartPointer<vtkMultiBlockPLOT3DReader> plot3dReader = vtkSmartPointer<vtkMultiBlockPLOT3DReader>::New();
plot3dReader->SetXYZFileName("combxyz.bin");
plot3dReader->SetQFileName("combq.bin");
plot3dReader->SetScalarFunctionNumber(100);
plot3dReader->SetVectorFunctionNumber(202);
qDebug() << plot3dReader->GetOutput()->GetNumberOfBlocks(); // 0
// 反向更新管线
plot3dReader->Update();
qDebug() << plot3dReader->GetOutput()->GetNumberOfBlocks(); // 1
vtkDataSet* plot3dOutput = (vtkDataSet*)(plot3dReader->GetOutput()->GetBlock(0));
// 获取标量数据
vtkDataArray* scalars = plot3dOutput->GetPointData()->GetScalars();
double* range = scalars->GetRange();
qDebug() << range[0] << ":" << range[1];
// 2. filter
// 产生结构化栅格边界的一个线轮廓
vtkSmartPointer<vtkStructuredGridOutlineFilter> outline = vtkSmartPointer<vtkStructuredGridOutlineFilter>::New();
outline->SetInputData(plot3dOutput);
// 等值面提取
vtkSmartPointer<vtkContourFilter> contour = vtkSmartPointer<vtkContourFilter>::New();
contour->SetInputData(plot3dOutput);
// 在 range[0] 到 range[1] 的范围内提取 5 个等值面
contour->GenerateValues(5, range[0], range[1]);
// 3. mapper
vtkSmartPointer<vtkPolyDataMapper> outlineMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
vtkSmartPointer<vtkPolyDataMapper> contourMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
// 4. actor
vtkSmartPointer<vtkActor> outlineActor = vtkSmartPointer<vtkActor>::New();
vtkSmartPointer<vtkActor> contourActor = vtkSmartPointer<vtkActor>::New();
// 5. renderer
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(0.3, 0.6, 0.3); // Background Color: Green
// 6. connect
outlineMapper->SetInputConnection(outline->GetOutputPort());
contourMapper->SetInputConnection(contour->GetOutputPort());
outlineActor->SetMapper(outlineMapper);
contourActor->SetMapper(contourMapper);
renderer->AddActor(outlineActor);
renderer->AddActor(contourActor);
this->_pVTKWidget->renderWindow()->AddRenderer(renderer);
this->_pVTKWidget->renderWindow()->Render();
}
VTKContour::~VTKContour()
{}
本程序首先获得了 plot3dOutput 的标量数据,从中得到了数据范围 range,使用 vtkContourFilter 等值面提取类,通过 contour->GenerateValues(5, range[0], range[1])
提取了在 range[0] 到 range[1] 的范围内提取 5 个等值面。
运行结果:
空间函数数据的等值面提取
在本实例中,我们将用到vtkQuadric、vtkSampleFunction、vtkContourFilter三个类,分别是二次曲面函数、函数曲面抽样和等高滤波。
vtkQuadric负责二次曲面基本参数的设置,vtkSampleFunction则是对二次曲面进行等间隔逐点采样,在本例中采样点数为303030。vtkContourFilter负责将采集到空间点,转成vtkPolyData型对象。
#include "VTKContour.h"
#include <vtkConeSource.h>
#include <vtkSTLReader.h>
#include <vtkMultiBlockPLOT3DReader.h>
#include <vtkDataSet.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkDataArray.h>
#include <vtkPointData.h>
#include <vtkShrinkPolyData.h>
#include <vtkStructuredGridGeometryFilter.h>
#include <vtkStructuredGridOutlineFilter.h>
#include <vtkContourFilter.h>
#include <vtkQuadric.h>
#include <vtkSampleFunction.h>
#include <vtkLookupTable.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
VTKContour::VTKContour(QWidget* parent)
: QMainWindow(parent)
{
ui.setupUi(this);
_pVTKWidget = new QVTKOpenGLNativeWidget();
this->setCentralWidget(_pVTKWidget);
// this->showMaximized();
// 1. generate data
// 二次曲面
vtkSmartPointer<vtkQuadric> quadric = vtkSmartPointer<vtkQuadric>::New();
quadric->SetCoefficients(0.5, 1, 0.2, 0, 0.1, 0, 0, 0.2, 0, 0);
// 对二次曲面进行等间隔逐点采样
vtkSmartPointer<vtkSampleFunction> sample = vtkSmartPointer<vtkSampleFunction>::New();
// 设置采样分辨率为 30*30*30
sample->SetSampleDimensions(30, 30, 30);
sample->SetImplicitFunction(quadric);
// 2. filter
// 等值面提取,将采集到的空间点,转成 vtkPolyData 型对象
vtkSmartPointer<vtkContourFilter> contour = vtkSmartPointer<vtkContourFilter>::New();
contour->SetInputConnection(sample->GetOutputPort());
// 在 [0, 1.2] 的范围内提取 5 个等值面
contour->GenerateValues(5, 0, 1.2);
// 3. mapper
vtkSmartPointer<vtkPolyDataMapper> contourMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
// 4. actor
vtkSmartPointer<vtkActor> contourActor = vtkSmartPointer<vtkActor>::New();
// 5. renderer
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(0.3, 0.6, 0.3); // Background Color: Green
// 6. connect
contourMapper->SetInputConnection(contour->GetOutputPort());
contourActor->SetMapper(contourMapper);
renderer->AddActor(contourActor);
this->_pVTKWidget->renderWindow()->AddRenderer(renderer);
this->_pVTKWidget->renderWindow()->Render();
}
VTKContour::~VTKContour()
{}
运行结果:
参考
- https://www.bilibili.com/video/BV11K411C7j9
- https://blog.csdn.net/m0_45306991/article/details/124710932
- https://www.cnblogs.com/phoenixdsg/p/6193904.html