1.表面重建
通过三维扫描仪所获取的实际物体的空间点云数据仅仅表示物体的几何形状,而无法表达其内部的拓扑结构。拓扑结构对于实际图形处理以及可视化具有更重要的意义。因此,这就需要利用表面重建技术奖点云数据转换成面模型,通常为三角网格模型。除此之外,基于图像数据的面绘制技术也是一种应用非常广泛的表面重建技术。
2.VTK中实现三角剖分技术
三角剖分技术是一种应用非常广泛的面重建技术。三角剖分将一些散乱的点云数据剖分为一系列的三角形网格。最常用的三角剖分技术为Delaunay三角剖分。Delaunay三角剖分具有许多优良的性质,如最大化最小角特性,即在所有可能的三角剖分中,其所生成的的三角形的最小角的角度最大。所以,Delaunay三角剖分无论从哪个区域开始构建,最终生成的三角网格还是唯一的。VTK的vtkDelaunay2D类实现了二维三角剖分。该类的输入数据为一个vtkPointSet或其子类表示的三维空间点集,其输出为一个三角网格vtkPolyData数据。虽然输入的是三维数据,但是算法仅适用XY平面数据进行平面三角剖分,而忽略Z方向数据。当然,也可以为vtkDelaunay2D设置一个投影变换从而在新的投影平面上进行三角剖分。需要注意的是,再不添加任何限制条件下,该类生成的平面三角网格为一个凸包。下例演示如何使用vtkDelaunay2D,将其生成的数据用于模型地形数据:1 #include <vtkAutoInit.h> 2 VTK_MODULE_INIT(vtkRenderingOpenGL); 3 VTK_MODULE_INIT(vtkRenderingFreeType); 4 VTK_MODULE_INIT(vtkInteractionStyle); 5 6 #include <vtkSmartPointer.h> 7 #include <vtkPoints.h> 8 #include <vtkPolyData.h> 9 #include <vtkPointData.h> 10 #include <vtkDelaunay2D.h> 11 #include <vtkMath.h> 12 #include <vtkVertexGlyphFilter.h> 13 #include <vtkPolyDataMapper.h> 14 #include <vtkProperty.h> 15 #include <vtkActor.h> 16 #include <vtkRenderWindow.h> 17 #include <vtkRenderer.h> 18 #include <vtkRenderWindowInteractor.h> 19 20 int main() 21 { 22 unsigned int gridSize = 10; 23 vtkSmartPointer<vtkPoints> points = 24 vtkSmartPointer<vtkPoints>::New(); 25 for (unsigned int x = 0; x < gridSize; x++) 26 { 27 for (unsigned int y = 0; y < gridSize; y++) 28 { 29 points->InsertNextPoint(x, y, vtkMath::Random(0.0, 3.0)); 30 } 31 } 32 33 vtkSmartPointer<vtkPolyData> polydata = 34 vtkSmartPointer<vtkPolyData>::New(); 35 polydata->SetPoints(points); 36 37 vtkSmartPointer<vtkDelaunay2D> delaunay = 38 vtkSmartPointer<vtkDelaunay2D>::New(); 39 delaunay->SetInputData(polydata); 40 delaunay->Update(); 41 42 vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = 43 vtkSmartPointer<vtkVertexGlyphFilter>::New(); 44 glyphFilter->SetInputData(polydata); 45 glyphFilter->Update(); 46 47 vtkSmartPointer<vtkPolyDataMapper> pointsMapper = 48 vtkSmartPointer<vtkPolyDataMapper>::New(); 49 pointsMapper->SetInputData(glyphFilter->GetOutput()); 50 vtkSmartPointer<vtkActor> pointsActor = 51 vtkSmartPointer<vtkActor>::New(); 52 pointsActor->SetMapper(pointsMapper); 53 pointsActor->GetProperty()->SetPointSize(3); 54 pointsActor->GetProperty()->SetColor(1, 0, 0); 55 56 vtkSmartPointer<vtkPolyDataMapper> triangulatedMapper = 57 vtkSmartPointer<vtkPolyDataMapper>::New(); 58 triangulatedMapper->SetInputData(delaunay->GetOutput()); 59 vtkSmartPointer<vtkActor> triangulatedActor = 60 vtkSmartPointer<vtkActor>::New(); 61 triangulatedActor->SetMapper(triangulatedMapper); 62 63 vtkSmartPointer<vtkRenderer> renderer = 64 vtkSmartPointer<vtkRenderer>::New(); 65 renderer->AddActor(pointsActor); 66 renderer->AddActor(triangulatedActor); 67 renderer->SetBackground(0, 0, 0); 68 69 vtkSmartPointer<vtkRenderWindow> renderWindow = 70 vtkSmartPointer<vtkRenderWindow>::New(); 71 renderWindow->AddRenderer(renderer); 72 renderWindow->SetSize(640, 640); 73 renderWindow->Render(); 74 renderWindow->SetWindowName("PolyData Delaunay2D"); 75 76 vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = 77 vtkSmartPointer<vtkRenderWindowInteractor>::New(); 78 renderWindowInteractor->SetRenderWindow(renderWindow); 79 renderWindowInteractor->Start(); 80 81 return 0; 82 }
该例先定义了一个vtkPolyData数据,并为其生成一个10*10的地面网格点集points以及每个点生成了一个随机数,表示每个点的海拔值;然后将该数据做为vtkDelaunay2D对象的输入实现三角剖分,即可得到一个地面的网格数据。输出结果如下所示:
3.局部数据三角剖分(带约束的三角平分)
vtkDelaunay2D还支持加入边界限制。用户需要设置另外一个vtkPolyData数据,其内部的线段、闭合或者非闭合的线段集合将作为边界条件控制三角剖分的过程。其中,组成这些边界的点的索引必须与原始点集数据一致。加入边界条件后,最后的剖分结果可能不在满足Delaunay准则。下面的例子,是在上例的基础上,加入一个多变形边界来限制三角剖分:1 #include <vtkAutoInit.h> 2 VTK_MODULE_INIT(vtkRenderingOpenGL); 3 VTK_MODULE_INIT(vtkRenderingFreeType); 4 VTK_MODULE_INIT(vtkInteractionStyle); 5 6 #include <vtkSmartPointer.h> 7 #include <vtkPoints.h> 8 #include <vtkMath.h> 9 #include <vtkPointData.h> 10 #include <vtkPolyData.h> 11 #include <vtkVertexGlyphFilter.h> 12 #include <vtkProperty.h> 13 #include <vtkPolygon.h> 14 #include <vtkCellArray.h> 15 #include <vtkDelaunay2D.h> 16 #include <vtkPolyDataMapper.h> 17 #include <vtkActor.h> 18 #include <vtkRenderer.h> 19 #include <vtkRenderWindow.h> 20 #include <vtkRenderWindowInteractor.h> 21 22 int main() 23 { 24 vtkSmartPointer<vtkPoints> points = 25 vtkSmartPointer<vtkPoints>::New(); 26 27 unsigned int gridSize = 10; 28 for (unsigned int x = 0; x < gridSize; x++) 29 { 30 for (unsigned int y = 0; y < gridSize; y++) 31 { 32 points->InsertNextPoint(x, y, vtkMath::Random(0.0, 3.0)); 33 } 34 } 35 36 vtkSmartPointer<vtkPolyData> polydata = 37 vtkSmartPointer<vtkPolyData>::New(); 38 polydata->SetPoints(points); 39 //多边形 40 vtkSmartPointer<vtkPolygon> poly = 41 vtkSmartPointer<vtkPolygon>::New(); 42 poly->GetPointIds()->InsertNextId(32); 43 poly->GetPointIds()->InsertNextId(42); 44 poly->GetPointIds()->InsertNextId(43); 45 poly->GetPointIds()->InsertNextId(44); 46 poly->GetPointIds()->InsertNextId(45); 47 poly->GetPointIds()->InsertNextId(35); 48 poly->GetPointIds()->InsertNextId(25); 49 poly->GetPointIds()->InsertNextId(24); 50 poly->GetPointIds()->InsertNextId(23); 51 poly->GetPointIds()->InsertNextId(22); 52 53 vtkSmartPointer<vtkCellArray> cell = 54 vtkSmartPointer<vtkCellArray>::New(); 55 cell->InsertNextCell(poly); //设计拓扑结构 56 //边界约束 57 vtkSmartPointer<vtkPolyData> boundary = 58 vtkSmartPointer<vtkPolyData>::New(); 59 boundary->SetPoints(points); 60 boundary->SetPolys(cell); //只显示具有拓扑结构部分 61 62 vtkSmartPointer<vtkDelaunay2D> delaunay = 63 vtkSmartPointer<vtkDelaunay2D>::New(); 64 delaunay->SetInputData(polydata); 65 delaunay->SetSourceData(boundary); //约束源 66 delaunay->Update(); 67 /// 68 vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = 69 vtkSmartPointer<vtkVertexGlyphFilter>::New(); 70 glyphFilter->SetInputData(polydata); 71 glyphFilter->Update(); 72 73 vtkSmartPointer<vtkPolyDataMapper> pointsMapper = 74 vtkSmartPointer<vtkPolyDataMapper>::New(); 75 pointsMapper->SetInputData(glyphFilter->GetOutput()); 76 vtkSmartPointer<vtkActor> pointsActor = 77 vtkSmartPointer<vtkActor>::New(); 78 pointsActor->SetMapper(pointsMapper); 79 pointsActor->GetProperty()->SetPointSize(8); 80 pointsActor->GetProperty()->SetColor(1, 0, 0); 81 82 vtkSmartPointer<vtkPolyDataMapper> triangulatedMapper = 83 vtkSmartPointer<vtkPolyDataMapper>::New(); 84 triangulatedMapper->SetInputData(delaunay->GetOutput()); 85 vtkSmartPointer<vtkActor> triangulatedActor = 86 vtkSmartPointer<vtkActor>::New(); 87 triangulatedActor->SetMapper(triangulatedMapper); 88 // 89 vtkSmartPointer<vtkRenderer> renderer = 90 vtkSmartPointer<vtkRenderer>::New(); 91 renderer->AddActor(pointsActor); 92 renderer->AddActor(triangulatedActor); 93 renderer->SetBackground(0, 0, 0); 94 95 vtkSmartPointer<vtkRenderWindow> rw = 96 vtkSmartPointer<vtkRenderWindow>::New(); 97 rw->AddRenderer(renderer); 98 rw->SetSize(640, 480); 99 rw->SetWindowName("PolyData By ConstrainedDelaunay2D"); 100 101 vtkSmartPointer<vtkRenderWindowInteractor> rwi = 102 vtkSmartPointer<vtkRenderWindowInteractor>::New(); 103 rwi->SetRenderWindow(rw); 104 rwi->Start(); 105 106 return 0; 107 }
这里定义一个vtkPolyData类型的数据boundary,其点数据与上例中的points一致。其单元数据为一个多边形。通过vtkDelaunay2D的SetSourceData()函数设置边界数据,运行结果如下所示:该边界多边形内部的数据并未进行三角剖分。对于边界多边形限制数据的内部或者外部,与多边形点的顺序有关。这里采用右手坐标系,从Z轴向下看去,如果多边形的点顺序为逆时针,则仅对多边形内部数据进行剖分;而如果为顺时针方向,则对多边形外部数据进行剖分,此时该边界多边形可以看成一个孔洞。
4.vtkelaunay3D实现三维三角剖分
VTK的vtkDelaunay3D类可实现三维三角剖分。该类的使用方法与vtkDelaunay2D基本一致,不同的是,三维三角剖分得到的结果并非三角网格,而是四面体网络。因此,其输出数据的类型为vtkUnstructuredGrid,在未加入边界条件下的三维三角剖分通常也为一个凸包。
5.简述类vtkVertexGlyphFilter
This filter throws away all of the cells in the input and replaces them with a vertex on each point. The intended use of this filter is roughly equivalent to the vtkGlyph3D filter, except this filter is specifically for
data that has many vertices, making the rendered result faster and less cluttered than the glyph filter. This filter may take a graph or point set as input.