13. VTK采集点法向量标记、平面切割

今天依旧是在摸索医学图像可视化的一天呢。这个笔记主要介绍了VTK上做法向量标记以及做切割平面的方法。

1. 将多边形数据的采集点法向量标记成锥形符号

在读取和使用.stl文件过程中,我们经常要用到法向量。这个例子展示了我们应该如何计算多边形数据的法向量并用vtkGlyph3D绘制圆锥型状将其标记出来。

  1. 读取stl文件构建多边形数据集
  2. 计算多边形数据的点法向量
  3. 使用vtkMaskPoints类采样部分数据,保留输入数据中的点数据及其属性
  4. 构建vtkGlyph3D对象,绘制形状设置为圆锥,数据源为采样点数据
  5. 绘制法向量数据和符号数据
#include "vtkSmartPointer.h"
#include "vtkPolyDataReader.h"
#include "vtkPolyDataNormals.h"
#include "vtkPolyDataMapper.h"
#include "vtkMaskPoints.h"
#include "vtkConeSource.h"
#include "vtkGlyph3D.h"
#include "vtkActor.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSTLReader.h"
#include "vtkProperty.h"

#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);

int main()
{
	//读取模型文件
	vtkSmartPointer<vtkSTLReader> obj = vtkSmartPointer<vtkSTLReader>::New();
	obj->SetFileName("D:\\ct\\20201102113826651_3d\\pelvis.stl");

	// 计算模型每个面的法矢量,创建一个法向量对象
	vtkSmartPointer<vtkPolyDataNormals> normals = vtkSmartPointer<vtkPolyDataNormals>::New();
	//计算单元法向量时,要保持单元法向量一致才能得到合理的法向量。SetConsistency()可以自动调整单元点的顺序;SetAutoOrientNormals()可以自动调整法向量方向。
	//类vtkPolyDataNormals自动开启对锐边缘处理,如果检测到锐边缘,会将其分裂,使图形更加平滑,可通过SetSplitting()函数关闭该功能。
	//三维平面的法向量是指垂直该平面的向量。某点的法向量为垂直该点切平面的法向量。

	// 将多边形数据集作为法向量对象的数据输入
	normals->SetInputConnection(obj->GetOutputPort());
	// 指定锐边角度为30度
	normals->SetFeatureAngle(30);
	// 关闭单元法向量计算(单元法向量可以通过组成每个单元的任意两条边的叉乘向量归并化来表示)
	normals->SetComputeCellNormals(0);
	// 开启点法向量计算(点的法向量则是由使用该点的单元单元法向量的平均值表示)
	normals->SetComputePointNormals(1);
	// 开启正常方向的全局翻转
	normals->SetFlipNormals(1);
	// 如果检测到锐边缘,会将其分裂,使图形更加平滑
	normals->SetSplitting(1);
	vtkSmartPointer<vtkPolyDataMapper> objMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
	objMapper->SetInputConnection(normals->GetOutputPort());
	//由于读入的模型数据比较大,点比较多,因此使用vtkMaskPoints类采样部分数据,该类保留输入数据中的点数据及其属性,并支持点数据的采样
	vtkSmartPointer<vtkMaskPoints> ptMask = vtkSmartPointer<vtkMaskPoints>::New();
	ptMask->SetInputConnection(normals->GetOutputPort());

	//创建符号——圆锥形
	vtkSmartPointer<vtkConeSource> cone = vtkSmartPointer<vtkConeSource>::New();
	cone->SetAngle(26.5651);
	cone->SetHeight(1);
	cone->SetRadius(0.5);
	cone->SetResolution(6);
	cone->SetCapping(1);
	vtkSmartPointer<vtkGlyph3D> glyph = vtkSmartPointer<vtkGlyph3D>::New();
	//设置被标识的点
	glyph->SetInputConnection(normals->GetOutputPort());
	//设置符号
	glyph->SetSourceConnection(cone->GetOutputPort());

	vtkSmartPointer<vtkPolyDataMapper> glyphMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
	glyphMapper->SetInputConnection(glyph->GetOutputPort());

	vtkSmartPointer<vtkActor> glyphActor = vtkSmartPointer<vtkActor>::New();
	glyphActor->SetMapper(glyphMapper);
	glyphActor->GetProperty()->SetColor(1, 0, 0);

	vtkSmartPointer<vtkActor> normalActor = vtkSmartPointer<vtkActor>::New();
	normalActor->SetMapper(objMapper);

	// Setup render window, renderer, and interactor
	vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
	renderer->AddActor(normalActor);
	renderer->AddActor(glyphActor);

	vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
	renderWindow->AddRenderer(renderer);
	vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
	renderWindowInteractor->SetRenderWindow(renderWindow);

	renderWindow->Render();
	renderWindowInteractor->Start();


	return 0;
}

运行结果:

 

备注: 

这里要特别注意.stl文件的读取问题。我一开始的写法是:

	obj->SetFileName("D:\ct\20201102113826651_3d\femur.stl");

 

就会出现以下问题:


 需要将代码里的地址改为双斜杠,即可正常读取文件。

obj->SetFileName("D:\\ct\\20201102113826651_3d\\pelvis.stl");

2. VTK构建切割面

在VTK中,如果我们需要构建一个切割面,需要做的步骤有:

1. 指定切割面中心SetOrigin()及切割面的法向量SetNormal()。

2. 设定切割面的个数及位置SetValue()GenerateValue()。

3. 设置切割过滤器对象vtkCutter, 指定被切割的数据集,及切割面隐函数vtkPlane。

4. 计算数据集在指定点的位置的属性vtkProbeFilter,指定切割对象的数据集及被切割的数据集。

5. 绘制vtkProbeFilter对象计算后的数据集(绘制的是切割对象的轮廓)。

#include "vtkPlane.h"
#include "vtkSTLReader.h"
#include "vtkPlane.h"
#include "vtkPolyData.h"
#include "vtkProbeFilter.h"
#include "vtkDataSetMapper.h"
#include "vtkPointData.h"
#include "vtkSmartPointer.h"
#include "vtkCutter.h"
#include "vtkActor.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"

#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);

int main()
{
	//读取模型文件,被切割的数据
	vtkSmartPointer<vtkSTLReader> obj = vtkSmartPointer<vtkSTLReader>::New();
	obj->SetFileName("D:\\ct\\20201102113826651_3d\\femur.stl");
	obj->Update();

	//创建切割平面
	vtkSmartPointer<vtkPlane> plane = vtkSmartPointer<vtkPlane>::New();
	//设定剪切平面在被剪切的数据中心位置
	vtkSmartPointer<vtkPolyData> polyData = obj->GetOutput();
	plane->SetOrigin(polyData->GetCenter());

	plane->SetNormal(-0.287, 0, 0.9579);

	// Print the plane parameters
	std::cout << "Plane Origin: " << plane->GetOrigin() << std::endl;
	std::cout << "Plane Normal: " << plane->GetNormal() << std::endl;

	//过滤器类,利用隐函数对数据进行剪切
	vtkSmartPointer<vtkCutter> planeCut = vtkSmartPointer<vtkCutter>::New();
	// 设置被裁剪的数据集
	planeCut->SetInputConnection(obj->GetOutputPort());
	//设定隐函数
	planeCut->SetCutFunction(plane);
	planeCut->SetValue(0, 50);
	planeCut->GenerateValues(10, 0, 500);
	//计算数据集在指定点的位置的属性
	vtkSmartPointer<vtkProbeFilter> probe = vtkSmartPointer<vtkProbeFilter>::New();
	//计算平面在数据集通过点的属性
	probe->SetInputConnection(planeCut->GetOutputPort());
	//被计算的数据集
	probe->SetSourceConnection(obj->GetOutputPort());
	vtkSmartPointer<vtkDataSetMapper> cutMapper = vtkSmartPointer<vtkDataSetMapper>::New();
	cutMapper->SetInputConnection(probe->GetOutputPort());
	//得到数据点集属性值的范围
	vtkSmartPointer<vtkPointData> pData = vtkSmartPointer<vtkPointData>::New();
	pData = polyData->GetPointData();

	//设定属性值范围
	//cutMapper->SetScalarRange(pData->GetScalars()->GetRange());
	cutMapper->ScalarVisibilityOn();

	vtkSmartPointer<vtkActor> cutActor = vtkSmartPointer<vtkActor>::New();
	cutActor->SetMapper(cutMapper);

	// 可视化
	vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
	renderer->SetBackground(.0, .0, .0);
	vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
	renderWindow->AddRenderer(renderer);
	vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
	renderWindowInteractor->SetRenderWindow(renderWindow);

	renderer->AddActor(cutActor);

	renderWindow->Render();
	renderWindowInteractor->Start();

	return 0;
}


 

运行结果:

如果想修改切面数量或者其他属性,修改这几行的参数即可:

	planeCut->SetCutFunction(plane);
	planeCut->SetValue(0, 50);
	planeCut->GenerateValues(50, 0, 500);

运行后,

备注:

一开始我运行代码的时候,出来的总是一片黑乎乎的,就很头疼。改了很久,发现是管道没有及时更新。这里要特别注意的是:在VTK的某些版本中,可能需要手动更新管道。在渲染之前,我们可能需要调用Update方法来确保所有的过滤器和读取器都处理了数据。

果然我就加上了这一行,就可以正常出来结果了:

	vtkSmartPointer<vtkSTLReader> obj = vtkSmartPointer<vtkSTLReader>::New();
	obj->SetFileName("D:\\ct\\20201102113826651_3d\\femur.stl");
	obj->Update();// 更新!!!确保STL文件被读取

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

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

相关文章

8.Gateway服务网关

3.Gateway服务网关 Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式…

银行常用操作指引:浦发

文章目录 引言浦发2.1 设置查询密码2.2 微信公众号绑定2.3 查询卡转账额度II 其他银行常用操作see also引言 浦发 2.1 设置查询密码 2.2 微信公众号绑定 入口:点击菜单的微信通知 用途:查询余额和明细 口令:解除绑定 2.3 查询卡转账额度 II 其他银行常用操作

基于SpringBoot Vue求职招聘系统

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

解锁加密货币增长的秘密:通过 Token Explorer 解读市场信号

解读市场信号&#xff0c;就像医生通过观察患者的体征来判断健康状况一样&#xff0c;可以帮助我们评估加密货币的采用速度。 Token Explorer 这个工具&#xff0c;就像是我们医生的听诊器&#xff0c;它追踪了一些核心的采用指标&#xff1a; ● 市值&#xff1a;通过比较主…

一文搞清楚Java中的包、类、接口

写在开头 包、类、接口、方法、变量、参数、代码块&#xff0c;这些都是构成Java程序的核心部分&#xff0c;即便最简单的一段代码里都至少要包含里面的三四个内容&#xff0c;这两天花点时间梳理了一下&#xff0c;理解又深刻了几分。 Java中的包 Java 定义了一种名字空间&…

Python __repr__()方法:显示属性

先看下面程序&#xff1a; class Item:def __init__ (self, name, price):self.name nameself.price price # 创建一个Item对象&#xff0c;将之赋给im变量 im Item(鼠标, 29.8) # 打印im所引用的Item对象 print(im) 上面程序创建了一个 Item 对象&#xff0c;然后使用 prin…

数据库原理及应用期末复习汇总(附某高校期末真题试卷)

文章目录 《数据库原理及应用》试题1一、选择题二、填空三、简答题四、T-SQL综合题五、综合应用题 《数据库原理及应用》试题2一、选择题二、填空三、简答题四、T-SQL综合题五、综合应用题 《数据库原理及应用》试题3一、选择题二、填空三、简答题四、T&#xff0d;SQL语言编程…

性能优化-高通的Hexagon DSP和NPU

原文来自【 Qualcomm’s Hexagon DSP, and now, NPU 】 本文主要介绍Qualcomm Hexagon DSP和NPU&#xff0c;这些为处理简单大量运算而设计的硬件。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xf…

如何在阿里云提交使用工单

有时候大家在使用阿里云的服务时候&#xff0c;可能会遇到一些问题&#xff0c;或许是云服务器如何升级了如何改套餐啊之类的&#xff0c;亦或者是域名ICP备案啊看进度啊等等问题&#xff0c;遇到问题怎么办不要慌。我们可以使用阿里云的工单系统&#xff0c;阿里云工单系统可以…

【UEFI基础】EDK网络框架(MTFTP4)

MTFTP4 在TCP/IP网络协议族中有FTP协议&#xff0c;但是UEFI下的MTFTP4并不是对FTP协议的实现&#xff0c;两者虽然功能上差不多&#xff0c;但是实现却是不同的。FTP下层使用TCP来连接&#xff1a; 而MTFTP4下层却是UDP4。 MTFTP4代码综述 MTFTP4的实现在NetworkPkg\Mtftp4…

AssertionError: Torch not compiled with CUDA enabled

目录 报错查看已安装的torch的版本卸载安装GPU版本的torch查看cuda版本手工安装通过pip命令手工安装。 结果更新cuda到12.1大功告成 报错 经查阅&#xff0c;这个问题是因为conda默认安装的是CPU版本的torch&#xff0c;应该使用GPU版本的。 查看已安装的torch的版本 pip li…

UI设计师主要是做什么的?

由于用户体验越来越受到重视&#xff0c;UI 设计师也成为现代互联网行业不可或缺的职业。他们通过美化和优化软件界面&#xff0c;为用户带来舒适的使用体验&#xff0c;目标是提高用户满意度。本文将对 UI 进行深入分析设计人员的工作职责和 UI 设计人员的工作内容&#xff0c…

云架构师学习------腾讯云通识-网络与安全

云架构师学习------腾讯云通识-存储与数据库 云架构师学习------腾讯云通识-网络与安全云上网络负载均衡工作原理产品优势使用场景技术原理基础架构转发路径 私有网络产品概述VIP核心组成部分私有网络连接私有网络安全 产品优势应用场景访问公网对公网提供服务应用容灾部署混合…

【办公类-19-02】20240122图书EXCEL插入列并删除空行

作品展示 背景需求 上次23个班级班主任统计图书&#xff0c;写在EXCEL内 【办公类-19-01】20240108图书统计登记表制作&#xff08;23个班级&#xff09;EXCEL复制表格并合并表格-CSDN博客文章浏览阅读693次&#xff0c;点赞12次&#xff0c;收藏7次。【办公类-19-01】202401…

webrtc线程代码研究

webrtc线程类的实现集成了socket的收发&#xff0c;消息队列&#xff0c;值得研究&#xff0c;基于webrtc75版本。 主要类介绍 Thread类 虚线&#xff1a;继承 实线&#xff1a;调用 橙色&#xff1a;接口 Thread继承MessageQueueThread提供两个静态方法,分别用来创建带socke…

Asp.net core 框架入门

概述 appsettings.json&#xff1a;配置文件&#xff0c;数据库连接字符串配置信息 Program.cs&#xff1a;程序入口文件&#xff08;里面有个Main方法&#xff09; Startup.cs&#xff1a;启动配置文件 依赖项&#xff1a;管理项目所依赖的第三方组件的安装&#xff0c;配…

数字图像处理(实践篇)二十六 使用cvlib进行人脸检测、性别检测和目标检测

目录 1 安装cvlib 2 涉及的函数 3 实践 4 其他 cvlib一个简单,高级,易于使用的开源Python计算机视觉库。 1 安装cvlib # 安装依赖pip install opencv-python tensorflow# 安装cvlibpip install cvlib</

你不知道的git如何撤销回退版本

简言之&#xff1a;从1 回退到 3&#xff0c;在3版本通过回退记录(git reflog)找到它的上一条回退记录的hash值&#xff0c;复制1的hash值进行回退&#xff0c;执行git reset --hard 粘贴1的hash值进来&#xff0c;此时就回到1的版本了&#xff0c;执行git log即可看到1、2、3、…

HCIA——25FTP 的工作原理、功能、TFTP、控制连接、数据连接的选择、解答

学习目标&#xff1a; 计算机网络 1.掌握计算机网络的基本概念、基本原理和基本方法。 2.掌握计算机网络的体系结构和典型网络协议&#xff0c;了解典型网络设备的组成和特点&#xff0c;理解典型网络设备的工作原理。 3.能够运用计算机网络的基本概念、基本原理和基本方法进行…

Oracle 高级网络压缩 白皮书

英文版白皮书在这里 或 这里。 本文包括了对英文白皮书的翻译&#xff0c;和我觉得较重要的要点总结。 执行概述 Oracle Database 12 引入了一项新功能&#xff1a;高级网络压缩&#xff0c;作为高级压缩选项的一部分。 本文概述了高级网络压缩、其优点、配置细节和性能分析…