VTK 数据处理:特征边提取

VTK 数据处理:特征边提取

  • VTK 数据处理:特征边提取
    • 原理
    • 实例 1:边界边提取
    • 实例 2:模型特征边提取
    • 实例 3:利用 vtkFeatureEdges 提取的边界补洞
    • 实例 4:利用 vtkFillHolesFilter 补洞

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()
{}

运行结果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/644037.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[C语言]自定义类型详解:结构体、联合体、枚举

目录 &#x1f680;结构体 &#x1f525;结构体类型的声明 &#x1f525;结构的自引用 &#x1f525;结构体变量的定义和初始化 &#x1f525;结构体内存对齐 &#x1f525;结构体传参 &#x1f525;结构体实现位段&#xff08;位段的填充&可移植性&#xff09; &a…

企业异地网络组网:SD-WAN解决方案的优势

在当今全球化的商业环境中&#xff0c;企业的业务扩展已不再局限于本地或单一国家。随着分公司和子公司的不断增加&#xff0c;企业总部与这些分支机构之间的数据通信和资源共享变得尤为重要。然而&#xff0c;传统的网络访问方式&#xff0c;如点对点电路和多协议标签交换&…

17.7K星开源产品分析平台:Posthog

Posthog&#xff1a;开源洞察&#xff0c;产品优化的得力助手 - 精选真开源&#xff0c;释放新价值。 概览 PostHog是一个全面开源的平台&#xff0c;旨在帮助团队构建更好的产品。它提供了从产品分析到会话回放、功能标志和A/B测试等一系列工具&#xff0c;支持自托管&#x…

个人博客网站搭建笔记1

文章目录 前言要求自己的理解资源过程视频教程SpringBoot开发一个小而美的个人博客p1课程介绍p2需求和功能 前言 自己之前其实就想搭建一个属于自己的网站&#xff0c;但是不知道怎么操作&#xff0c;没找到合适的教程&#xff0c;&#xff08;手把手的那种&#xff09;&#…

Prometheus+Grafana监控服务器、mysql数据库并配置报警规则推送邮箱

文章目录 一、安装prometheus1.1下载1.2 安装1.3 开机启动1.4 验证 二、安装 Grafana2.1 下载2.2 安装2.3 启动2.4 验证 三、安装服务器监控 node_exporter3.1 下载3.2 安装3.3 设置 node_exporter 系统服务3.4 设置开机自动启动3.5 验证3.6配置Prometheus3.7 修改 Prometheus …

Tomcat部署项目的方式

目录 1、Tomcat发布项目的方式 方式1&#xff1a; 直接把项目发布到webapps目录下 方式2&#xff1a;项目发布到ROOT目录 方式3&#xff1a;虚拟路径方式发布项目 方式4&#xff1a;(推荐)虚拟路径&#xff0c;另外的方式&#xff01; 方式5&#xff1a;发布多个网站 1、…

[windows系统安装/重装系统][step-4][番外篇-2]N卡驱动重装 |解决:开机几小时后电脑卡顿 | 后台自动运行了上千个Rundll32进程问题

现象 开机几小时后&#xff0c;电脑变卡&#xff0c;打开后台管理器都卡&#xff0c;后台管理去转圈圈一小会儿后看到后台进程上千个&#xff0c;好多个Rundll32进程 重启下运行会稍快 重启后运行快&#xff0c;后台管理器反应也快 打开后台管理器不卡&#xff08;几小时后打…

Python语法(全)

前言&#xff1a; 下面是Python基本的语法&#xff0c;大家耐心观看&#xff01; 1.基础语法 1.1字面量 字面量&#xff1a;在代码中&#xff0c;被写下来的的固定的值&#xff0c;称之为字面 1.2字符串 字符串&#xff08;string&#xff09;&#xff0c;又称文本&#xff…

C语言数据结构栈的概念及结构、栈的实现、栈的初始化、销毁栈、入栈、出栈、检查是否为空、获取栈顶元素、获取有效元素个数等的介绍

文章目录 前言栈的概念及结构栈的实现一、 栈结构创建二、 初始化结构三、销毁栈四、入栈五、出栈六、检查是否为空七、获取栈顶元素八、获取有效元素的个数九、测试 1十、测试 2总结 前言 C语言数据结构栈的概念及结构、栈的实现、栈的初始化、销毁栈、入栈、出栈、检查是否为…

blender 布尔运算,切割模型。

1.创建一个立方体和球体。 2.选中立方体&#xff0c;在属性面板添加布尔修改器。点击物体属性右边的按钮选中球体。参数如下。 3.此时隐藏球体&#xff0c;就可以看到被切掉的效果了。

C结构详解

目录 1、结构模板 1. 建立结构声明 2. 定义结构变量 3. 访问结构成员 4. 初始化结构 声明结构数组 声明和初始化结构指针 1、结构模板 1. 建立结构声明 struct book{char title[MAXTITL];char author[MAXAUTL];float value; }&#xff1b; 该声明描述了一个又两个字符…

【漫画算法】哈希表:古代皇帝的秘密魔法书

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

三个有意思的链表面试题的完成

上一篇博客我们已经完成了链表的所有内容&#xff0c;那么这一篇博客我们来看一下三个特别有意思的链表题目。 **第一个题目如下&#xff1a;**相信不少朋友看到这题目就已经晕了&#xff0c;那就简单说明下这个题目&#xff0c;题目就是创建一个链表&#xff0c;其中每个节点…

软件构造复习1

一、软件构造的多维度视图&#xff1a; 共有三个维度&#xff1a;1.按阶段划分&#xff1a;构造时/运行时视图&#xff0c;2.按动态性划分&#xff1a;时刻/阶段视图&#xff0c;3.按构造对象层次划分&#xff1a;代码/构件视图 具体可如图所示&#xff08;图片来自PPT&#…

数据库-SQL性能分析

SQL执行频率 慢查询日志 慢查询日志记录了所有执行时间超过指定参数&#xff08;long_query_time&#xff0c;单位&#xff1a;秒&#xff0c;默认10秒&#xff09;的所有 SQL语句的日志。 MySQL的慢查询日志默认没有开启&#xff0c;我们可以查看一下系统变量 slow_query_l…

掩码生成蒸馏——知识蒸馏

摘要 https://arxiv.org/pdf/2205.01529 知识蒸馏已成功应用于各种任务。当前的蒸馏算法通常通过模仿教师的输出来提高学生的性能。本文表明&#xff0c;教师还可以通过指导学生的特征恢复来提高学生的表示能力。从这一观点出发&#xff0c;我们提出了掩码生成蒸馏&#xff08…

Redis常见基本类型(5)-List, Set

List 命令小结 命令及解释时间复杂度lpush/rpush key value[key value...](向右/左端插入元素)O(k), k是元素个数linsert key before | after pivot value(在某个坐标之前/右插入元素)O(n), n是pivot距离头尾的距离lrange start end(获取从start到end部分的元素)O(s n): s是…

与用户沟通获取需求的方法

1 访谈 访谈是最早开始使用的获取用户需求的技术&#xff0c;也是迄今为止仍然广泛使用的需求分析技术。 访谈有两种基本形式&#xff0c;分别是正式的和非正式的访谈。正式访谈时&#xff0c;系统分析员将提出一些事先准备好的具体问题&#xff0c;例如&#xff0…

Java使用apache.poi生成excel插入word中

加油&#xff0c;新时代打工人&#xff01; 工作需求&#xff0c;上个文章我们生成好的word&#xff0c;这次将生成好的excel表格数据&#xff0c;插入word中。需要准备好excle数据&#xff0c;然后插入到word中。 最后个需要&#xff0c;就是把这些生成好的word文档转成pdf进行…

基础技术-ELF系列(1)-ELF文件基础

成就更好的自己 本篇是基础技术系列中ELF相关技术的首篇文章。 尽管网上有许多关于ELF相关内容的文章&#xff0c;但总体而言&#xff0c;要么是一些非常基础且重复性强的内容&#xff0c;要么直接深入探讨相对高深的主题&#xff0c;缺乏系统化分析和解释。 接下来&#xf…