VTK 数据处理:特征边提取

VTK 数据处理:特征边提取

原理

VTK 的特征边提取只针对 PolyData,属于拓扑操作。

可以提取出 4 种类型的边:

  1. 边界边:只被 1 个网格使用的边;
  2. 流形边:被 2 个网格使用的边;
  3. 非流形边:被 2 个以上网格使用的边,一般不会在 PolyData 中出现;
  4. 特征边:属于流形边的一种,当一条流形边关联的两个面片的夹角大于特征角的话,它就是特征边。

实例 1:边界边提取

本实例创建了一个 vtkDiskSource 对象 disk,通过 vtkFeatureEdges 类提取 disk 的边界边,并设置不提取流形边、非流形边和特征边,并显示边界边的颜色以作区分。

VTKBoundaryEdge.h:

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_VTKFeatureEdge.h"

#include <QVTKOpenGLNativeWidget.h>

class VTKBoundaryEdge : public QMainWindow
{
	Q_OBJECT

public:
	VTKBoundaryEdge(QWidget* parent = nullptr);
	~VTKBoundaryEdge();

private:
	Ui::VTKFeatureEdgeClass ui;

	QVTKOpenGLNativeWidget* _pVTKWidget = nullptr;
};

VTKBoundaryEdge.cpp:

#include "VTKBoundaryEdge.h"

#include <vtkDiskSource.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkPolyData.h>
#include <vtkFeatureEdges.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>

VTKBoundaryEdge::VTKBoundaryEdge(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<vtkDiskSource> disk;
	disk->Update();

	vtkNew<vtkFeatureEdges> featureEdges;
	featureEdges->SetInputConnection(disk->GetOutputPort());
	featureEdges->BoundaryEdgesOn(); // 提取边界边设置打开
	featureEdges->ManifoldEdgesOff();
	featureEdges->NonManifoldEdgesOff();
	featureEdges->FeatureEdgesOff();
	featureEdges->ColoringOn(); // 显示颜色

	vtkNew<vtkPolyDataMapper> diskMapper;
	diskMapper->SetInputConnection(disk->GetOutputPort());
	vtkNew<vtkPolyDataMapper> edgeMapper;
	edgeMapper->SetInputConnection(featureEdges->GetOutputPort());

	vtkNew<vtkActor> diskActor;
	diskActor->SetMapper(diskMapper);
	vtkNew<vtkActor> edgeActor;
	edgeActor->SetMapper(edgeMapper);

	renderer->AddActor(diskActor);
	renderer->AddActor(edgeActor);
}

VTKBoundaryEdge::~VTKBoundaryEdge()
{
}

运行结果:

在这里插入图片描述

实例 2:模型特征边提取

前面提到特征边的识别取决于两个面片的夹角,如下图所示,指针指着的角就是夹角,VTK 默认将该角大于 30 度的都视为特征边。可以看出,连续性不是很好的边都被视为特征边。

在这里插入图片描述

本实例使用 vtkBYUReader 读取了一个牛的模型,并用 vtkFeatureEdges 类提取了模型的特征边,并设置不提取流形边、非流形边和边界边,并显示特征边的颜色以作区分。

VTKFeatureEdge.h:

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_VTKFeatureEdge.h"

#include <QVTKOpenGLNativeWidget.h>

class VTKFeatureEdge : public QMainWindow
{
	Q_OBJECT

public:
	VTKFeatureEdge(QWidget* parent = nullptr);
	~VTKFeatureEdge();

private:
	Ui::VTKFeatureEdgeClass ui;

	QVTKOpenGLNativeWidget* _pVTKWidget = nullptr;
};

VTKFeatureEdge.cpp:

#include "VTKFeatureEdge.h"

#include <vtkBYUReader.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkPolyData.h>
#include <vtkFeatureEdges.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>

VTKFeatureEdge::VTKFeatureEdge(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<vtkBYUReader> reader;
	reader->SetFileName("cow.g");
	reader->Update();

	vtkNew<vtkFeatureEdges> featureEdges;
	featureEdges->SetInputConnection(reader->GetOutputPort());
	featureEdges->BoundaryEdgesOff();
	featureEdges->ManifoldEdgesOn(); // 提取特征边设置打开
	featureEdges->NonManifoldEdgesOff();
	featureEdges->FeatureEdgesOff();
	featureEdges->ColoringOn(); // 显示颜色

	vtkNew<vtkPolyDataMapper> cowMapper;
	cowMapper->SetInputConnection(reader->GetOutputPort());
	vtkNew<vtkPolyDataMapper> edgeMapper;
	edgeMapper->SetInputConnection(featureEdges->GetOutputPort());

	vtkNew<vtkActor> cowActor;
	cowActor->SetMapper(cowMapper);
	vtkNew<vtkActor> edgeActor;
	edgeActor->SetMapper(edgeMapper);

	renderer->AddActor(cowActor);
	renderer->AddActor(edgeActor);
}

VTKFeatureEdge::~VTKFeatureEdge()
{}

实例 3:利用 vtkFeatureEdges 提取的边界补洞

建立一个 vtkPlane 类对象作为截面, 设置好 plane 的法向量。利用 vtkClipPolyData 类将模型作为输入,将 plane 设为 clipper 的截取函数。再用 vtkFeatureEdges 类提取截取后模型 clipper 的边界边。将边界边作为 vtkStripper 类对象 boundaryStrips 的输入,将 featureEdges 生成的三角片连接成三角带,再转换成多边形数据 boundaryPolyData。最后把 clipper、featureEdges、boundaryPolyData 全部显示出来。

#include "VTKFeatureEdge.h"

#include <vtkBYUReader.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkPolyData.h>
#include <vtkClipPolyData.h>
#include <vtkFeatureEdges.h>
#include <vtkStripper.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>

VTKFeatureEdge::VTKFeatureEdge(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<vtkBYUReader> reader;
	reader->SetFileName("cow.g");
	reader->Update();

	// 截面
	vtkNew<vtkPlane> plane;
	plane->SetOrigin(reader->GetOutput()->GetCenter());
	plane->SetNormal(1.0, -1.0, -1.0); // 设置截面的法向量

	// 截取模型
	vtkNew<vtkClipPolyData> clipper;
	clipper->SetInputData(reader->GetOutput());
	clipper->SetClipFunction(plane); // 将 plane 设为 clipper 的截取函数
	clipper->SetValue(0.0);

	vtkNew<vtkFeatureEdges> featureEdges;
	featureEdges->SetInputConnection(clipper->GetOutputPort());
	featureEdges->BoundaryEdgesOn();
	featureEdges->ManifoldEdgesOff(); // 提取特征边设置打开
	featureEdges->NonManifoldEdgesOff();
	featureEdges->FeatureEdgesOff();
	featureEdges->ColoringOn(); // 显示颜色

	// 建立三角带对象
	vtkNew<vtkStripper> boundaryStrips;
	boundaryStrips->SetInputConnection(featureEdges->GetOutputPort()); // 将 featureEdges 生成的三角片连接成三角带
	boundaryStrips->Update();

	vtkNew<vtkPolyData> boundaryPolyData;
	boundaryPolyData->SetPoints(boundaryStrips->GetOutput()->GetPoints());
	boundaryPolyData->SetPolys(boundaryStrips->GetOutput()->GetLines());

	vtkNew<vtkPolyDataMapper> clipperMapper;
	clipperMapper->SetInputConnection(clipper->GetOutputPort());
	vtkNew<vtkPolyDataMapper> edgeMapper;
	edgeMapper->SetInputConnection(featureEdges->GetOutputPort());
	vtkNew<vtkPolyDataMapper> boundaryMapper;
	boundaryMapper->SetInputData(boundaryPolyData);

	vtkNew<vtkActor> clipperActor;
	clipperActor->SetMapper(clipperMapper);
	vtkNew<vtkActor> edgeActor;
	edgeActor->SetMapper(edgeMapper);
	vtkNew<vtkActor> boundaryActor;
	boundaryActor->SetMapper(boundaryMapper);

	renderer->AddActor(clipperActor);
	renderer->AddActor(edgeActor);
	renderer->AddActor(boundaryActor);
}

VTKFeatureEdge::~VTKFeatureEdge()
{}

运行结果:

在这里插入图片描述

实例 4:利用 vtkFillHolesFilter 补洞

vtkFillHolesFilter 是 VTK 中用于自动填充三角网格模型中洞的过滤器。vtkFillHolesFilter 支持多种数据格式,包括 vtkPolyData、vtkUnstructuredGrid、vtkStructuredGrid、vtkImageData 等。它还支持多种洞填充算法,包括 Delaunay 三角剖分、路径填充、平面填充等。使用 vtkFillHolesFilter 进行填洞时,可以保持原始模型的拓扑结构,避免生成不良的三角形。

vtkFillHolesFilter 的内部执行过程是首先检测出网格中的所有边界边,然后找出这些边界边中的每一个闭合回路,最后将这些闭合回路进行三角化(即生成三角网格)以实现填补的目的。这个类也是非常简单的,只需要设置需要填补的网格数据即可。

需要注意的是,有些边界的闭合回路是不需要三角化的,例如一个平面网格,若填补其四周的边界边,则会与原网格产生覆盖。vtkFillHolesFilters 中的 SetHoleSize() 函数可用于控制需要修补的漏洞面积的最大值,大于该值的漏洞则不需要填补处理。

#include "VTKFeatureEdge.h"

#include <vtkBYUReader.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkPolyData.h>
#include <vtkClipPolyData.h>
#include <vtkFeatureEdges.h>
#include <vtkStripper.h>
#include <vtkFillHolesFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>

VTKFeatureEdge::VTKFeatureEdge(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<vtkBYUReader> reader;
	reader->SetFileName("cow.g");
	reader->Update();

	// 截面
	vtkNew<vtkPlane> plane;
	plane->SetOrigin(reader->GetOutput()->GetCenter());
	plane->SetNormal(1.0, -1.0, -1.0); // 设置截面的法向量

	// 截取模型
	vtkNew<vtkClipPolyData> clipper;
	clipper->SetInputData(reader->GetOutput());
	clipper->SetClipFunction(plane); // 将 plane 设为 clipper 的截取函数
	clipper->SetValue(0.0);

	/*
	vtkNew<vtkFeatureEdges> featureEdges;
	featureEdges->SetInputConnection(clipper->GetOutputPort());
	featureEdges->BoundaryEdgesOn();
	featureEdges->ManifoldEdgesOff(); // 提取特征边设置打开
	featureEdges->NonManifoldEdgesOff();
	featureEdges->FeatureEdgesOff();
	featureEdges->ColoringOn(); // 显示颜色

	// 建立三角带对象
	vtkNew<vtkStripper> boundaryStrips;
	boundaryStrips->SetInputConnection(featureEdges->GetOutputPort()); // 将 featureEdges 生成的三角片连接成三角带
	boundaryStrips->Update();

	vtkNew<vtkPolyData> boundaryPolyData;
	boundaryPolyData->SetPoints(boundaryStrips->GetOutput()->GetPoints());
	boundaryPolyData->SetPolys(boundaryStrips->GetOutput()->GetLines());

	vtkNew<vtkPolyDataMapper> clipperMapper;
	clipperMapper->SetInputConnection(clipper->GetOutputPort());
	vtkNew<vtkPolyDataMapper> edgeMapper;
	edgeMapper->SetInputConnection(featureEdges->GetOutputPort());
	vtkNew<vtkPolyDataMapper> boundaryMapper;
	boundaryMapper->SetInputData(boundaryPolyData);

	vtkNew<vtkActor> clipperActor;
	clipperActor->SetMapper(clipperMapper);
	vtkNew<vtkActor> edgeActor;
	edgeActor->SetMapper(edgeMapper);
	vtkNew<vtkActor> boundaryActor;
	boundaryActor->SetMapper(boundaryMapper);

	renderer->AddActor(clipperActor);
	renderer->AddActor(edgeActor);
	renderer->AddActor(boundaryActor);
	*/

	vtkNew<vtkFillHolesFilter> fillHolesFilter;
	fillHolesFilter->SetInputConnection(clipper->GetOutputPort());
	fillHolesFilter->SetHoleSize(1000.0); // 大于该值的漏洞则不需要填补处理

	vtkNew<vtkPolyDataMapper> fillHolesMapper;
	fillHolesMapper->SetInputConnection(fillHolesFilter->GetOutputPort());

	vtkNew<vtkActor> fillHolesActor;
	fillHolesActor->SetMapper(fillHolesMapper);

	renderer->AddActor(fillHolesActor);
}

VTKFeatureEdge::~VTKFeatureEdge()
{}

运行结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值