VTK_Learning_图形基本操作进阶_表面重建技术(三角剖分)

1.表面重建

通过三维扫描仪所获取的实际物体的空间点云数据仅仅表示物体的几何形状,而无法表达其内部的拓扑结构。拓扑结构对于实际图形处理以及可视化具有更重要的意义。因此,这就需要利用表面重建技术奖点云数据转换成面模型,通常为三角网格模型。除此之外,基于图像数据的面绘制技术也是一种应用非常广泛的表面重建技术。


2.VTK中实现三角剖分技术

三角剖分技术是一种应用非常广泛的面重建技术。三角剖分将一些散乱的点云数据剖分为一系列的三角形网格。最常用的三角剖分技术为Delaunay三角剖分。Delaunay三角剖分具有许多优良的性质,如最大化最小角特性,即在所有可能的三角剖分中,其所生成的的三角形的最小角的角度最大。所以,Delaunay三角剖分无论从哪个区域开始构建,最终生成的三角网格还是唯一的。
VTK的vtkDelaunay2D类实现了二维三角剖分。该类的输入数据为一个vtkPointSet或其子类表示的三维空间点集,其输出为一个三角网格vtkPolyData数据。虽然输入的是三维数据,但是算法仅适用XY平面数据进行平面三角剖分,而忽略Z方向数据。当然,也可以为vtkDelaunay2D设置一个投影变换从而在新的投影平面上进行三角剖分。需要注意的是,再不添加任何限制条件下,该类生成的平面三角网格为一个凸包。
下例演示如何使用vtkDelaunay2D,将其生成的数据用于模型地形数据:
 

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkInteractionStyle);
 
#include <vtkSmartPointer.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPointData.h>
#include <vtkDelaunay2D.h>
#include <vtkMath.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkActor.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
 
int main()
{
	unsigned int gridSize = 10;
	vtkSmartPointer<vtkPoints> points =
		vtkSmartPointer<vtkPoints>::New();
	for (unsigned int x = 0; x < gridSize; x++)
	{
		for (unsigned int y = 0; y < gridSize; y++)
		{
			points->InsertNextPoint(x, y, vtkMath::Random(0.0, 3.0));
		}
	}
 
	vtkSmartPointer<vtkPolyData> polydata =
		vtkSmartPointer<vtkPolyData>::New();
	polydata->SetPoints(points);
 
	vtkSmartPointer<vtkDelaunay2D> delaunay =
		vtkSmartPointer<vtkDelaunay2D>::New();
	delaunay->SetInputData(polydata);
	delaunay->Update();
 
	vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter =
		vtkSmartPointer<vtkVertexGlyphFilter>::New();
	glyphFilter->SetInputData(polydata);
	glyphFilter->Update();
 
	vtkSmartPointer<vtkPolyDataMapper> pointsMapper =
		vtkSmartPointer<vtkPolyDataMapper>::New();
	pointsMapper->SetInputData(glyphFilter->GetOutput());
	vtkSmartPointer<vtkActor> pointsActor =
		vtkSmartPointer<vtkActor>::New();
	pointsActor->SetMapper(pointsMapper);
	pointsActor->GetProperty()->SetPointSize(3);
	pointsActor->GetProperty()->SetColor(1, 0, 0);
 
	vtkSmartPointer<vtkPolyDataMapper> triangulatedMapper =
		vtkSmartPointer<vtkPolyDataMapper>::New();
	triangulatedMapper->SetInputData(delaunay->GetOutput());
	vtkSmartPointer<vtkActor> triangulatedActor =
		vtkSmartPointer<vtkActor>::New();
	triangulatedActor->SetMapper(triangulatedMapper);
 
	vtkSmartPointer<vtkRenderer> renderer =
		vtkSmartPointer<vtkRenderer>::New();
	renderer->AddActor(pointsActor);
	renderer->AddActor(triangulatedActor);
	renderer->SetBackground(0, 0, 0);
 
	vtkSmartPointer<vtkRenderWindow> renderWindow =
		vtkSmartPointer<vtkRenderWindow>::New();
	renderWindow->AddRenderer(renderer);
	renderWindow->SetSize(640, 640);
	renderWindow->Render();
	renderWindow->SetWindowName("PolyData Delaunay2D");
 
	vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
		vtkSmartPointer<vtkRenderWindowInteractor>::New();
	renderWindowInteractor->SetRenderWindow(renderWindow);
	renderWindowInteractor->Start();
 
	return 0;
}

该例先定义了一个vtkPolyData数据,并为其生成一个10*10的地面网格点集points以及每个点生成了一个随机数,表示每个点的海拔值;然后将该数据做为vtkDelaunay2D对象的输入实现三角剖分,即可得到一个地面的网格数据。

输出结果如下所示:

3.局部数据三角剖分(带约束的三角平分)

vtkDelaunay2D还支持加入边界限制。用户需要设置另外一个vtkPolyData数据,其内部的线段、闭合或者非闭合的线段集合将作为边界条件控制三角剖分的过程。其中,组成这些边界的点的索引必须与原始点集数据一致。加入边界条件后,最后的剖分结果可能不在满足Delaunay准则。
下面的例子,是在上例的基础上,加入一个多变形边界来限制三角剖分:
 

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkInteractionStyle);
 
#include <vtkSmartPointer.h>
#include <vtkPoints.h>
#include <vtkMath.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkProperty.h>
#include <vtkPolygon.h>
#include <vtkCellArray.h>
#include <vtkDelaunay2D.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
 
int main()
{
	vtkSmartPointer<vtkPoints> points =
		vtkSmartPointer<vtkPoints>::New();
 
	unsigned int gridSize = 10;
	for (unsigned int x = 0; x < gridSize; x++)
	{
		for (unsigned int y = 0; y < gridSize; y++)
		{
			points->InsertNextPoint(x, y, vtkMath::Random(0.0, 3.0));
		}
	}
 
	vtkSmartPointer<vtkPolyData> polydata =
		vtkSmartPointer<vtkPolyData>::New();
	polydata->SetPoints(points);
	//多边形
	vtkSmartPointer<vtkPolygon> poly =
		vtkSmartPointer<vtkPolygon>::New();
	poly->GetPointIds()->InsertNextId(32);
	poly->GetPointIds()->InsertNextId(42);
	poly->GetPointIds()->InsertNextId(43);
	poly->GetPointIds()->InsertNextId(44);
	poly->GetPointIds()->InsertNextId(45);
	poly->GetPointIds()->InsertNextId(35);
	poly->GetPointIds()->InsertNextId(25);
	poly->GetPointIds()->InsertNextId(24);
	poly->GetPointIds()->InsertNextId(23);
	poly->GetPointIds()->InsertNextId(22);

	vtkSmartPointer<vtkCellArray> cell =
		vtkSmartPointer<vtkCellArray>::New();
	cell->InsertNextCell(poly); //设计拓扑结构
	//边界约束
	vtkSmartPointer<vtkPolyData> boundary =
		vtkSmartPointer<vtkPolyData>::New();
	boundary->SetPoints(points);
	boundary->SetPolys(cell); //只显示具有拓扑结构部分
 
	vtkSmartPointer<vtkDelaunay2D> delaunay =
		vtkSmartPointer<vtkDelaunay2D>::New();
	delaunay->SetInputData(polydata);
	delaunay->SetSourceData(boundary); //约束源
	delaunay->Update();
///
	vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter =
		vtkSmartPointer<vtkVertexGlyphFilter>::New();
	glyphFilter->SetInputData(polydata);
	glyphFilter->Update();

	vtkSmartPointer<vtkPolyDataMapper> pointsMapper =
		vtkSmartPointer<vtkPolyDataMapper>::New();
	pointsMapper->SetInputData(glyphFilter->GetOutput());
	vtkSmartPointer<vtkActor> pointsActor =
		vtkSmartPointer<vtkActor>::New();
	pointsActor->SetMapper(pointsMapper);
	pointsActor->GetProperty()->SetPointSize(8);
	pointsActor->GetProperty()->SetColor(1, 0, 0);
 
	vtkSmartPointer<vtkPolyDataMapper> triangulatedMapper =
		vtkSmartPointer<vtkPolyDataMapper>::New();
	triangulatedMapper->SetInputData(delaunay->GetOutput());
	vtkSmartPointer<vtkActor> triangulatedActor =
		vtkSmartPointer<vtkActor>::New();
	triangulatedActor->SetMapper(triangulatedMapper);
//
	vtkSmartPointer<vtkRenderer> renderer =
		vtkSmartPointer<vtkRenderer>::New();
	renderer->AddActor(pointsActor);
	renderer->AddActor(triangulatedActor);
	renderer->SetBackground(0, 0, 0);
 
	vtkSmartPointer<vtkRenderWindow> rw =
		vtkSmartPointer<vtkRenderWindow>::New();
	rw->AddRenderer(renderer);
	rw->SetSize(640, 480);
	rw->SetWindowName("PolyData By ConstrainedDelaunay2D");
 
	vtkSmartPointer<vtkRenderWindowInteractor> rwi =
		vtkSmartPointer<vtkRenderWindowInteractor>::New();
	rwi->SetRenderWindow(rw);
	rwi->Start();
 
	return 0;
}

这里定义一个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.
 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VTK_CURSOR_CUSTOM是VTK(Visualization Toolkit)中提供的一种自定义光标的方式。使用VTK_CURSOR_CUSTOM可以让用户在VTK应用程序中使用自己定义的光标图像,从而增强应用程序的个性化和用户体验。 要使用VTK_CURSOR_CUSTOM,需要按照以下步骤进行操作: 1. 创建自定义光标图像。可以使用任何绘图工具创建PNG、JPEG或BMP格式的图像。 2. 将光标图像加载到VTK中。可以使用vtkImageData或vtkTexture等VTK类来加载图像数据。 3. 创建vtkCursor2D对象并设置自定义光标。vtkCursor2D是VTK中的一个2D光标类,可以在视图中显示自定义光标。 4. 将vtkCursor2D对象添加到vtkRenderWindow中。vtkRenderWindow是VTK中的一个窗口类,可以显示3D场景和2D元素。 5. 最后,将vtkRenderWindow显示出来,用户就可以看到自定义光标了。 下面是一个使用VTK_CURSOR_CUSTOM创建自定义光标的示例代码: ```python import vtk # 创建自定义光标图像 cursor_image = vtk.vtkPNGReader() cursor_image.SetFileName("custom_cursor.png") # 创建vtkCursor2D对象并设置自定义光标 cursor = vtk.vtkCursor2D() cursor.SetImageData(cursor_image.GetOutput()) # 创建vtkRenderWindow对象并添加vtkCursor2D对象 ren = vtk.vtkRenderer() renWin = vtk.vtkRenderWindow() renWin.AddRenderer(ren) iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(renWin) iren.SetInteractorStyle(vtk.vtkInteractorStyleImage()) iren.Initialize() cursor.SetRenderer(ren) cursor.SetDisplayPosition(100, 100) cursor.Update() # 显示vtkRenderWindow renWin.Render() iren.Start() ``` 这个示例代码会创建一个自定义的光标图像custom_cursor.png,并将它加载到vtkCursor2D对象中。然后将vtkCursor2D对象添加到vtkRenderWindow中,并显示出来。用户可以在vtkRenderWindow中看到自定义光标,并在应用程序中使用它。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值