VTK 数据处理:属性操作
回顾
VTK 中数据类型的继承关系:
数据结构:
- 几何结构:由 points 定义
- 拓扑结构:由 cells 定义
数据属性:主要有标量、矢量、法向量、张量、纹理坐标等。
VTK 的四种数据处理方法
- 几何操作
- 拓扑操作:比如等值面,流线、流管、流面、流带,Cutting&Probing等。
- 属性操作
- 混合操作:上面三种操作的综合使用。
实例 1:高程属性过滤器、颜色条、坐标系
构建一个球体,使用高程属性过滤器按高度对球体做颜色映射。使用 vtkScalarBarActor 类制作一个颜色条并显示,使用 vtkCameraOrientationWidget 类制作一个坐标轴并显示。
#include "VTKGenerateAttribute.h"
#include <vtkSphereSource.h>
#include <vtkNamedColors.h>
#include <vtkElevationFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkScalarBarActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
VTKGenerateAttribute::VTKGenerateAttribute(QWidget* parent)
: QMainWindow(parent)
{
ui.setupUi(this);
_pVTKWidget = new QVTKOpenGLNativeWidget();
this->setCentralWidget(_pVTKWidget);
vtkNew<vtkRenderer> renderer;
this->_pVTKWidget->renderWindow()->AddRenderer(renderer);
this->_pVTKWidget->renderWindow()->Render();
vtkNew<vtkNamedColors> colors;
renderer->SetBackground(colors->GetColor3d("DarkSlateGray").GetData());
vtkNew<vtkSphereSource> sphere;
sphere->SetThetaResolution(51);
sphere->SetPhiResolution(17);
sphere->SetRadius(0.5);
// vtkElevationFilter:高程属性过滤器
// 由用户设置一条线,将数据集里的所有点投影到这条线上,根据投影结果确定每个点的属性数据
// 简单地说,就是进行颜色映射,让 RGB 的值从 LowPoint 到 HighPoint 进行变化
vtkNew<vtkElevationFilter> elevation;
elevation->SetInputConnection(sphere->GetOutputPort());
elevation->SetLowPoint(0, 0, -0.5);
elevation->SetHighPoint(0, 0, 0.5);
double range[] = { -1, 1 };
elevation->SetScalarRange(range);
elevation->Update();
elevation->GetOutput()->Print(std::cout);
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(elevation->GetOutputPort());
mapper->SetScalarRange(range);
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
// 颜色条
vtkNew<vtkScalarBarActor> colorBarActor;
colorBarActor->SetLookupTable(mapper->GetLookupTable());
colorBarActor->SetMaximumHeightInPixels(500);
renderer->AddActor(actor);
renderer->AddActor2D(colorBarActor);
// 打开坐标系
cameraOrientationWidget->SetParentRenderer(renderer);
cameraOrientationWidget->On();
}
VTKGenerateAttribute::~VTKGenerateAttribute()
{}
运行结果:
elevation->GetOutput()->Print(std::cout)
打印的多边形信息如下所示:
vtkPolyData (00000165F9340AE0)
Debug: Off
Modified Time: 1468
Reference Count: 1
Registered Events: (none)
Information: 00000165FB8C9750
Data Released: False
Global Release Data: Off
UpdateTime: 1469
Field Data:
Debug: Off
Modified Time: 1442
Reference Count: 1
Registered Events: (none)
Number Of Arrays: 0
Number Of Components: 0
Number Of Tuples: 0
Number Of Points: 767
Number Of Cells: 1530
Cell Data:
Debug: Off
Modified Time: 1450
Reference Count: 1
Registered Events:
Registered Observers:
vtkObserver (00000165FB8B9860)
Event: 33
EventName: ModifiedEvent
Command: 00000165FB8715E0
Priority: 0
Tag: 1
Number Of Arrays: 0
Number Of Components: 0
Number Of Tuples: 0
Copy Tuple Flags: ( 1 1 1 1 1 0 1 1 1 1 1 1 )
Interpolate Flags: ( 1 1 1 1 1 0 0 1 1 1 1 0 )
Pass Through Flags: ( 1 1 1 1 1 1 1 1 1 1 1 1 )
Scalars: (none)
Vectors: (none)
Normals: (none)
TCoords: (none)
Tensors: (none)
GlobalIds: (none)
PedigreeIds: (none)
EdgeFlag: (none)
Tangents: (none)
RationalWeights: (none)
HigherOrderDegrees: (none)
ProcessIds: (none)
Point Data:
Debug: Off
Modified Time: 1468
Reference Count: 1
Registered Events:
Registered Observers:
vtkObserver (00000165FB8BA3A0)
Event: 33
EventName: ModifiedEvent
Command: 00000165FB8715E0
Priority: 0
Tag: 1
Number Of Arrays: 2
Array 0 name = Normals
Array 1 name = Elevation
Number Of Components: 4
Number Of Tuples: 767
Copy Tuple Flags: ( 1 1 1 1 1 0 1 1 1 1 1 1 )
Interpolate Flags: ( 1 1 1 1 1 0 0 1 1 1 1 0 )
Pass Through Flags: ( 1 1 1 1 1 1 1 1 1 1 1 1 )
Scalars:
Debug: Off
Modified Time: 1465
Reference Count: 1
Registered Events: (none)
Name: Elevation
Data type: float
Size: 767
MaxId: 766
NumberOfComponents: 1
Information: 0000000000000000
Name: Elevation
Number Of Components: 1
Number Of Tuples: 767
Size: 767
MaxId: 766
LookupTable: (none)
Vectors: (none)
Normals:
Debug: Off
Modified Time: 1412
Reference Count: 2
Registered Events: (none)
Name: Normals
Data type: float
Size: 2301
MaxId: 2300
NumberOfComponents: 3
Information: 0000000000000000
Name: Normals
Number Of Components: 3
Number Of Tuples: 767
Size: 2301
MaxId: 2300
LookupTable: (none)
TCoords: (none)
Tensors: (none)
GlobalIds: (none)
PedigreeIds: (none)
EdgeFlag: (none)
Tangents: (none)
RationalWeights: (none)
HigherOrderDegrees: (none)
ProcessIds: (none)
Bounds:
Xmin,Xmax: (-0.499052, 0.5)
Ymin,Ymax: (-0.499763, 0.499763)
Zmin,Zmax: (-0.5, 0.5)
Compute Time: 1481
Editable: false
Number Of Points: 767
Point Coordinates: 00000165FB8B0FF0
PointLocator: 0000000000000000
CellLocator: 0000000000000000
Number Of Vertices: 0
Number Of Lines: 0
Number Of Polygons: 1530
Number Of Triangle Strips: 0
Number Of Pieces: 1
Piece: 0
Ghost Level: 0
CellsBounds:
Xmin,Xmax: (-0.499052, 0.5)
Ymin,Ymax: (-0.499763, 0.499763)
Zmin,Zmax: (-0.5, 0.5)
CellsBounds Time: 1482
实例 2:把 PointIds 存储为属性、获取连通分量、实现框选操作
本实例首先创建了一个球体对象 sphere。
vtkIdFilter 是 Id 过滤器,能够从数据集中提取 Point 或 Cell 的 Id 作为数据集属性数据。我们利用它设置 sphere 的 PointIdsArrayName 为 OriginalIds。再利用 vtkDataSetSurfaceFilter 提取 sphere 的外部(多边形)表面。
vtkInteractorStyleRubberBandPick 是一个交互器。它定义:用户可以通过 R 键切换交互模式和框选模式。如果在框选模式下,用户可以通过鼠标左键画一个矩形。当鼠标释放的时候,附加的拾取器可以操作所选矩形中间的像素值。如果拾取器是 vtkAreaPicker,可以操作整个所选的矩形。
我们自定义 ExtrudeInteractorStyle 类继承自 vtkInteractorStyleRubberBandPick,重写其 OnLeftButtonUp() 方法,利用观察者模式监听鼠标左键来实现自定义功能:使用 vtkConnectivityFilter 将输入数据集中连接的单元提取到一个非结构化的网络,将选取面片网格(vtkConnectivityFilter 的输出)的点的坐标都增大为原来的 1.2 倍,可视化效果就是向外突出一块。
#include "VTKAttributeOperation.h"
#include <vtkSphereSource.h>
#include <vtkPlanes.h>
#include <vtkNamedColors.h>
#include <vtkPointData.h>
#include <vtkPointSet.h>
#include <vtkPolyData.h>
#include <vtkImplicitFunction.h>
#include <vtkIdTypeArray.h>
#include <vtkIdFilter.h>
#include <vtkConnectivityFilter.h>
#include <vtkDataSetSurfaceFilter.h>
#include <vtkInteractorStyleRubberBandPick.h>
#include <vtkAreaPicker.h>
#include <vtkExtractPolyDataGeometry.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
/* vtkInteractorStyleRubberBandPick:
This interactor style allows the user to draw a rectangle in the render window by hitting 'r' and then using the left mouse button.
When the mouse button is released, the attached picker operates on the pixel in the center of the selection rectangle.
If the picker happens to be a vtkAreaPicker it will operate on the entire selection rectangle.
When the 'p' key is hit the above pick operation occurs on a 1x1 rectangle.
In other respects it behaves the same as its parent class.
*/
// vtkInteractorStyleRubberBandPick 是一个交互器
// 用户可以通过 R 键切换交互模式和框选模式
// 如果在框选模式下,用户可以通过鼠标左键画一个矩形。当鼠标释放的时候,附加的拾取器可以操作所选矩形中间的像素值
// 如果拾取器是 vtkAreaPicker,可以操作整个所选的矩形
class ExtrudeInteractorStyle : public vtkInteractorStyleRubberBandPick
{
private:
vtkSmartPointer<vtkPolyData> m_pPolyData; // 需要处理的几何数据
vtkSmartPointer<vtkPolyDataMapper> m_pMapper; // 用于显示框选面片的 mapper
public:
static ExtrudeInteractorStyle* New();
vtkTypeMacro(ExtrudeInteractorStyle, vtkInteractorStyleRubberBandPick);
ExtrudeInteractorStyle()
{
m_pPolyData = vtkSmartPointer<vtkPolyData>::New();
m_pMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
}
void setPolyData(vtkSmartPointer<vtkPolyData> polyData)
{
m_pPolyData = polyData;
}
void setPolyDataMapper(vtkSmartPointer<vtkPolyDataMapper> polyDataMapper)
{
m_pMapper = polyDataMapper;
}
// 利用观察者模式监听鼠标左键来实现自定义功能
virtual void OnLeftButtonUp() override
{
vtkInteractorStyleRubberBandPick::OnLeftButtonUp();
if (this->CurrentMode == 1)
{
vtkPlanes* frustum = static_cast<vtkAreaPicker*>(this->GetInteractor()->GetPicker())->GetFrustum();
vtkNew<vtkExtractPolyDataGeometry> extractPolyDataGeometry;
extractPolyDataGeometry->SetInputData(m_pPolyData);
extractPolyDataGeometry->SetImplicitFunction(frustum);
extractPolyDataGeometry->Update();
// vtkConnectivityFilter 将输入数据集中连接的单元提取到一个非结构化的网络
vtkNew<vtkConnectivityFilter> connectivityFilter;
connectivityFilter->SetInputConnection(extractPolyDataGeometry->GetOutputPort());
connectivityFilter->SetExtractionModeToClosestPointRegion();
connectivityFilter->SetClosestPoint(extractPolyDataGeometry->GetOutput()->GetPoint(0));
connectivityFilter->Update();
vtkSmartPointer<vtkPointSet> selected = connectivityFilter->GetOutput();
// std::cout << "Selected " << selected->GetNumberOfPoints() << " points." << std::endl;
// std::cout << "Selected " << selected->GetNumberOfCells() << " cells." << std::endl;
vtkIdTypeArray* ids = dynamic_cast<vtkIdTypeArray*>(selected->GetPointData()->GetArray("OriginalIds"));
vtkNew<vtkPolyData> polyNew;
polyNew->DeepCopy(m_pPolyData);
// 将选取面片的点的坐标都增大为原来的 1.2 倍
for (int i = 0; i < selected->GetNumberOfPoints(); i++)
{
vtkIdType id = ids->GetTuple1(i);
double pt[3];
polyNew->GetPoint(id, pt);
pt[0] *= 1.2;
pt[1] *= 1.2;
pt[2] *= 1.2;
polyNew->GetPoints()->SetPoint(id, pt);
}
m_pMapper->SetInputData(polyNew);
this->GetInteractor()->GetRenderWindow()->Render();
}
}
};
vtkStandardNewMacro(ExtrudeInteractorStyle);
VTKAttributeOperation::VTKAttributeOperation(QWidget* parent)
: QMainWindow(parent)
{
ui.setupUi(this);
_pVTKWidget = new QVTKOpenGLNativeWidget();
this->setCentralWidget(_pVTKWidget);
vtkNew<vtkRenderer> renderer;
this->_pVTKWidget->renderWindow()->AddRenderer(renderer);
this->_pVTKWidget->renderWindow()->Render();
vtkNew<vtkSphereSource> sphere;
sphere->SetPhiResolution(21);
sphere->SetThetaResolution(40);
// Id 过滤器,能够从数据集中提取 Point 或 Cell 的 Id 作为数据集属性数据
vtkNew<vtkIdFilter> idFilter;
idFilter->SetInputConnection(sphere->GetOutputPort());
idFilter->SetPointIdsArrayName("OriginalIds");
// 提取外部(多边形)表面
vtkNew<vtkDataSetSurfaceFilter> surfaceFilter;
surfaceFilter->SetInputConnection(idFilter->GetOutputPort());
surfaceFilter->Update();
surfaceFilter->GetOutput()->Print(std::cout);
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(surfaceFilter->GetOutputPort());
mapper->ScalarVisibilityOff();
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
renderer->AddActor(actor);
// 基于硬件的拾取器,在用户指定的区域内选择对象
vtkNew<vtkAreaPicker> areaPicker;
this->_pVTKWidget->renderWindow()->GetInteractor()->SetPicker(areaPicker);
// 创建交互器
vtkNew<ExtrudeInteractorStyle> style;
style->setPolyData(surfaceFilter->GetOutput());
style->setPolyDataMapper(mapper);
// 使用时,加入到 vtkRenderWinderInteractor 的对象中
this->_pVTKWidget->renderWindow()->GetInteractor()->SetInteractorStyle(style);
}
VTKAttributeOperation::~VTKAttributeOperation()
{}
运行结果:
输出:
vtkPolyData (0000016B63085770)
Debug: Off
Modified Time: 1489
Reference Count: 1
Registered Events: (none)
Information: 0000016B6325CBF0
Data Released: False
Global Release Data: Off
UpdateTime: 1490
Field Data:
Debug: Off
Modified Time: 1461
Reference Count: 1
Registered Events: (none)
Number Of Arrays: 0
Number Of Components: 0
Number Of Tuples: 0
Number Of Points: 762
Number Of Cells: 1520
Cell Data:
Debug: Off
Modified Time: 1482
Reference Count: 1
Registered Events:
Registered Observers:
vtkObserver (0000016B6323A1B0)
Event: 33
EventName: ModifiedEvent
Command: 0000016B6327F900
Priority: 0
Tag: 1
Number Of Arrays: 1
Array 0 name = vtkIdFilter_Ids
Number Of Components: 1
Number Of Tuples: 1520
Copy Tuple Flags: ( 0 1 1 1 1 0 1 1 1 1 1 1 )
Interpolate Flags: ( 0 1 1 1 1 0 0 1 1 1 1 0 )
Pass Through Flags: ( 0 1 1 1 1 1 1 1 1 1 1 1 )
Scalars:
Debug: Off
Modified Time: 1435
Reference Count: 2
Registered Events: (none)
Name: vtkIdFilter_Ids
Data type: idtype
Size: 1520
MaxId: 1519
NumberOfComponents: 1
Information: 0000000000000000
Name: vtkIdFilter_Ids
Number Of Components: 1
Number Of Tuples: 1520
Size: 1520
MaxId: 1519
LookupTable: (none)
Vectors: (none)
Normals: (none)
TCoords: (none)
Tensors: (none)
GlobalIds: (none)
PedigreeIds: (none)
EdgeFlag: (none)
Tangents: (none)
RationalWeights: (none)
HigherOrderDegrees: (none)
ProcessIds: (none)
Point Data:
Debug: Off
Modified Time: 1487
Reference Count: 1
Registered Events:
Registered Observers:
vtkObserver (0000016B63239C70)
Event: 33
EventName: ModifiedEvent
Command: 0000016B6327F900
Priority: 0
Tag: 1
Number Of Arrays: 2
Array 0 name = OriginalIds
Array 1 name = Normals
Number Of Components: 4
Number Of Tuples: 762
Copy Tuple Flags: ( 0 1 1 1 1 0 1 1 1 1 1 1 )
Interpolate Flags: ( 0 1 1 1 1 0 0 1 1 1 1 0 )
Pass Through Flags: ( 0 1 1 1 1 1 1 1 1 1 1 1 )
Scalars:
Debug: Off
Modified Time: 1426
Reference Count: 2
Registered Events: (none)
Name: OriginalIds
Data type: idtype
Size: 762
MaxId: 761
NumberOfComponents: 1
Information: 0000000000000000
Name: OriginalIds
Number Of Components: 1
Number Of Tuples: 762
Size: 762
MaxId: 761
LookupTable: (none)
Vectors: (none)
Normals:
Debug: Off
Modified Time: 1376
Reference Count: 3
Registered Events: (none)
Name: Normals
Data type: float
Size: 2286
MaxId: 2285
NumberOfComponents: 3
Information: 0000000000000000
Name: Normals
Number Of Components: 3
Number Of Tuples: 762
Size: 2286
MaxId: 2285
LookupTable: (none)
TCoords: (none)
Tensors: (none)
GlobalIds: (none)
PedigreeIds: (none)
EdgeFlag: (none)
Tangents: (none)
RationalWeights: (none)
HigherOrderDegrees: (none)
ProcessIds: (none)
Bounds:
Xmin,Xmax: (-0.5, 0.5)
Ymin,Ymax: (-0.5, 0.5)
Zmin,Zmax: (-0.5, 0.5)
Compute Time: 1502
Editable: false
Number Of Points: 762
Point Coordinates: 0000016B63277F80
PointLocator: 0000000000000000
CellLocator: 0000000000000000
Number Of Vertices: 0
Number Of Lines: 0
Number Of Polygons: 1520
Number Of Triangle Strips: 0
Number Of Pieces: 1
Piece: 0
Ghost Level: 0
CellsBounds:
Xmin,Xmax: (-0.5, 0.5)
Ymin,Ymax: (-0.5, 0.5)
Zmin,Zmax: (-0.5, 0.5)
CellsBounds Time: 1503