VTK 图形基本操作进阶_网格平滑(点云的曲面重建技术)

1.网格平滑

现代扫描技术的发展使得获取点云数据不再困难,通过曲线重建技术可以获取表面网格来表示各种复杂的实体。但是点云数据中往往存在噪声,这样得到的重建网格通常都需要进行平滑处理。
拉普拉斯平滑是一种常用的网格平滑算法。该方法的原理比较简单,如下图所示:
将每个点用其邻域点的中心来代替。通过不断地迭代,可以得到较为光滑的网格。
VTK中,VTKSmoothPolyDataFilter类实现了网格的拉普拉斯平滑算法,使用方法如下:
 1 #include <vtkAutoInit.h>
 2 VTK_MODULE_INIT(vtkRenderingOpenGL);
 3 VTK_MODULE_INIT(vtkInteractionStyle);
 4 VTK_MODULE_INIT(vtkRenderingFreeType);
 5  
 6 #include <vtkSmartPointer.h>
 7 #include <vtkPolyDataReader.h>
 8 #include <vtkSmoothPolyDataFilter.h>
 9 #include <vtkPolyDataMapper.h>
10 #include <vtkActor.h>
11 #include <vtkRenderer.h>
12 #include <vtkRenderWindow.h>
13 #include <vtkRenderWindowInteractor.h>
14 int main()
15 {
16     vtkSmartPointer<vtkPolyDataReader> reader =
17         vtkSmartPointer<vtkPolyDataReader>::New();
18     reader->SetFileName("fran_cut.vtk");
19     reader->Update();
20  
21     vtkSmartPointer<vtkSmoothPolyDataFilter> smoothFilter =
22         vtkSmartPointer<vtkSmoothPolyDataFilter>::New();
23     smoothFilter->SetInputConnection(reader->GetOutputPort());
24     smoothFilter->SetNumberOfIterations(100);
25     smoothFilter->Update();
26     /
27     vtkSmartPointer<vtkPolyDataMapper> inputMapper =
28         vtkSmartPointer<vtkPolyDataMapper>::New();
29     inputMapper->SetInputConnection(reader->GetOutputPort());
30     vtkSmartPointer<vtkActor> inputActor =
31         vtkSmartPointer<vtkActor>::New();
32     inputActor->SetMapper(inputMapper);
33  
34     vtkSmartPointer<vtkPolyDataMapper> smoothedMapper =
35         vtkSmartPointer<vtkPolyDataMapper>::New();
36     smoothedMapper->SetInputConnection(smoothFilter->GetOutputPort());
37     vtkSmartPointer<vtkActor> smoothedActor =
38         vtkSmartPointer<vtkActor>::New();
39     smoothedActor->SetMapper(smoothedMapper);
40     /
41     double leftViewport[4] = { 0.0, 0.0, 0.5, 1.0 };
42     double rightViewport[4] = { 0.5, 0.0, 1.0, 1.0 };
43  
44     vtkSmartPointer<vtkRenderer> leftRenderer =
45         vtkSmartPointer<vtkRenderer>::New();
46     leftRenderer->SetViewport(leftViewport);
47     leftRenderer->AddActor(inputActor);
48     leftRenderer->SetBackground(1, 0, 0);
49     leftRenderer->ResetCamera();
50  
51     vtkSmartPointer<vtkRenderer> rightRenderer =
52         vtkSmartPointer<vtkRenderer>::New();
53     rightRenderer->SetViewport(rightViewport);
54     rightRenderer->AddActor(smoothedActor);
55     rightRenderer->SetBackground(0, 0, 0);
56     rightRenderer->ResetCamera();
57     /
58     vtkSmartPointer<vtkRenderWindow> rw =
59         vtkSmartPointer<vtkRenderWindow>::New();
60     rw->AddRenderer(leftRenderer);
61     rw->AddRenderer(rightRenderer);
62     rw->SetSize(640, 320);
63     rw->SetWindowName("PolyData Grid Smooth By LapLasian");
64  
65     vtkSmartPointer<vtkRenderWindowInteractor> rwi =
66         vtkSmartPointer<vtkRenderWindowInteractor>::New();
67     rwi->SetRenderWindow(rw);
68     rwi->Initialize();
69     rwi->Start();
70     return 0;
71 }
vtkSmoothPolyDataFilter::SetNumberOfIterations()控制平滑次数,次数越多平滑的越厉害。200次的平滑效果如下图所示:
左图为原始模型,右图为平滑后模型。从图中可以看出,经过200次Laplace平滑后,模型变得相当的光滑,但是在平滑的同事也损失了一些细节信息。
其实在该类中还有多个变量来控制平滑过程,利用这些变量在一定程度上可以控制细节的损失。
BoundarySmoothing:控制是否对边界点平滑。这里需要理解边界点的概念,在一个网格模型中,一条边只被一个单元包含那么这条边就是边界边,而边界边上的点则为边界点。如果一个模型中含有边界边,则说明该模型不是封闭的,正如图中的一样。
FeatureEdgeSmoothing:控制是否对特征边上的点进行平滑。如果一条边被两个邻近的多边形共用,若两个多边形法向量的夹角(特征角)大于定义的阈值,则说明该边为一条特征边。因此,FeatureEdgeSmoothing设置开始时,需要调用SetFeatureAngle()函数设置特征角的阈值。具体如图所示:
特征角越大,说明改边越尖锐。特征边/角往往表达模型的细节,平滑过程中最好不要进行处理,以保护细节不受损伤。
虽然通过特征边平滑设置可以降低一部分细节损失,但并不能完全避免,且随着laplace平滑的不断迭代,模型会逐渐向网格的中心收缩。所以,vtkWindowSincPolyDataFilter是一种更好的选择,该算法采用窗口Sinc函数实现网格平滑,能够最小程度地避免收缩,vtkWindowSincPolyDataFilter的用法和VTKSmoothPolyDataFilter用法完全一致。
1 vtkSmartPointer<vtkWindowedSincPolyDataFilter> wndSincSmoothFilter =
2         vtkSmartPointer<vtkWindowedSincPolyDataFilter>::New();
3     wndSincSmoothFilter->SetInputConnection(reader->GetOutputPort());
4     wndSincSmoothFilter->SetNumberOfIterations(10);
5     wndSincSmoothFilter->Update();
算法的输出结果如下所示:
原文地址:https://www.cnblogs.com/ybqjymy/p/14241824.html