VTK使用交互器来从三维体数据中提取二维切片
VTK中鼠标消息是在交互类型对象(interactorstyle)中响应,因此通过为交互类型对象(interactorstyle)添加观察者(observer)来监听相应的消息,当消息触发时,由命令模式执行相应的回调函数。
vtkImageInteractionCallback继承自vtkCommand类,并覆盖父类函数Execute()。
该类提供了两个接口:SetImageReslice和SetInteractor。
SetImageReslice用以设置vtkImageSlice对象,vtkImageSlice根据设置的变换矩阵提取三维图像切片。SetInteractor用以设置vtkRenderWindowInteractor,vtkRenderWindowInteractor类对象负责每次提取切片后刷新视图。
class vtkImageInteractionCallback : public vtkCommand
{
public:static vtkImageInteractionCallback *New() //回调函数初始化函数{return new vtkImageInteractionCallback;}vtkImageInteractionCallback(){this->Slicing = 0;//切片提取标志位,为1时提取切片。用来检验鼠标左键是否已经按下this->ImageReslice = 0;//vtkImageSlice对象的变换矩阵,用来确定切面在三维图像中的位置this->Interactor = 0;}void SetImageReslice(vtkImageReslice *reslice){this->ImageReslice = reslice;}vtkImageReslice *GetImageReslice(){return this->ImageReslice;}void SetInteractor(vtkRenderWindowInteractor *interactor){this->Interactor = interactor;}vtkRenderWindowInteractor *GetInteractor(){return this->Interactor;}virtual void Execute(vtkObject * ,unsigned long event,void *){//具体内容放下面}
private:int Slicing;vtkImageReslice *ImageReslice;vtkRenderWindowInteractor *Interactor;
};
需要设置四个参数,窗位(ColorLevel)、窗宽(ColorWindow)、切片(Slice)和切片方向(Orientation)。按下鼠标左键拖动鼠标,可以调节图像的窗宽窗位,从而显示不同灰度范围内容;按下鼠标右键拖动鼠标可以放缩图像。当然这些交互操作可以由用户根据需要自己定义vtkInteractorStyle子类,并响应相应的操作。
( 窗宽是图像显示的灰度范围。一般显示器的灰度范围为256级,医学图像的灰度范围远远大于这个范围,所以显示器不足以显示所有的灰度级,需要使用窗宽来定义想要显示的灰度范围;
一般情况下,可以将灰度值高于窗宽范围的最大值时,将像素值显示为白色值(255);将灰度值低于窗宽范围的最小值时,将像素值显示为黑色值(0);增大窗宽范围,可以显示更多不同灰度组织结构,但会减低组织之间的对比度;减小窗宽范围,可以减少所显示不同灰度组织结构,也就增大了组织结构之间的对比度;
窗位是窗宽的中心位置;窗宽只是确定了像素上的可视化的范围,是一个具体值,单独依靠窗宽不能确定这个范围的上限是多少和下限是多少,需要配合使用窗位来确定;比如上一代码中:窗宽为2000,只能说明窗宽的范围是2000,不能说明是从0到2000,还是-1000到1000,设置窗位为500时,窗宽2000,就能表示可视灰度范围为-500到1500;)

而显示三维图像时,还需要确定当前显示切片和方向。vtkImageViewer2提供了SetSlice()函数设置切片号,SetSliceOrientationToXY()则将切片的方向设置为垂直XY平面方向。此外还可以设置为垂直YZ或者XZ平面方向,其对应函数分别为SetSliceOrientationToYZ()和SetSliceOrientationToXZ()。默认情况下切片方向为垂直于XY平面即沿着Z轴方向,根据设置的切片号获取Z轴方向的具体切片进行显示。
下面重点看Execute函数,该函数提供了具体的切片提取功能。在该函数里面,主要监听了三个消息:
vtkCommand::LeftButtonPressEvent,
vtkCommand::LeftButtonReleaseEvent,
vtkCommand::MouseMoveEvent,
前两个消息分别是鼠标左键的按下和弹起消息。当鼠标左键按下时,就设置切片提取标志为1,而当弹起时,将标志置为0。这样在鼠标移动时,只有在确定切片提取标志为1时,执行切片提取功能。
vtkCommand::MouseMoveEvent即为鼠标移动消息。当检测到该消息时,首先检查切片提取标志,当为1时提取切片。提取切片时,需要为vtkImageSlice对象设置变换矩阵。这里在函数开始时,首先获取了鼠标滑动的前后两次点的位置lastPos和currPos。然后根据两点的Y坐标差deltaY,计算新的中心点center并变换至vtkImageSlice当前变换矩阵中,得到变换中心点,将其设置到原来的变换矩阵matrix中,并设置到vtkImageSlice中,最后执行interactor->Render()即可不断的根据鼠标移动刷新图像。
virtual void Execute(vtkObject * ,unsigned long event,void *)
{vtkRenderWindowInteractor *interactor = GetInteractor();int lastPos[2];interactor->GetLastEventPosition(lastPos);int currPos[2];interactor->GetEventPosition(currPos);if (event == vtkCommand::LeftButtonPressEvent)//如果当前事件是鼠标左键按下{this->Slicing = 1; //标志位。当鼠标左键按下时,就设置切片提取标志为1}else if (event == vtkCommand::LeftButtonReleaseEvent)/如果当前事件是鼠标右键按下{this->Slicing = 0; //标志位 }else if (event == vtkCommand::MouseMoveEvent)/如果当前事件是鼠标移动{if (this->Slicing)//检验鼠标左键已经按下{vtkImageReslice *reslice = this->ImageReslice;//设置当前切片图像位置的矩阵为this->ImageReslice;但在更新之前,不会将这个矩阵数据填充到reslice中去//记下鼠标Y向变化的幅值大小int deltaY = lastPos[1] - currPos[1];//计算鼠标滑动的前后两次点的位置lastPos和currPos。然后计算两点的Y坐标的差deltaYreslice->Update();//使用更新操作,为reslice获取当前切片图像位置的矩阵double sliceSpacing = reslice->GetOutput()->GetSpacing()[2];获取reslice矩阵中的参数vtkMatrix4x4 *matrix = reslice->GetResliceAxes();//定义需要使用的新的中心店center转换至vtkImageSlice当前变换矩阵中的转换矩阵,但这里没有更新,就没有填充//重新定位切片需要经过的中心点double point[4];double center[4];point[0] = 0;point[1] = 0;point[2] = sliceSpacing*deltaY;point[3] = 1.0;matrix->MultiplyPoint(point, center);//填充矩阵matrix->SetElement(0, 3, center[0]);//填充矩阵matrix->SetElement(1, 3, center[1]);//填充矩阵matrix->SetElement(2, 3, center[2]);//填充矩阵interactor->Render();}else//没有触发上述三个事件{vtkInteractorStyle *style = vtkInteractorStyle::SafeDownCast(interactor->GetInteractorStyle());if (style){style->OnMouseMove();}}}
}
Command对象定义完毕后,即可为交互对象InteractorStyle添加观察者,响应鼠标消息。
这里主要是定义了vtkImageInteractionCallback对象,并设置vtkImageSlice对象和vtkRenderWindowInteractor对象。然后为交互对象vtkInteractorStyle添加观察者来监控相应的消息。
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =vtkSmartPointer<vtkRenderWindowInteractor>::New();//定义交互对象vtkSmartPointer<vtkInteractorStyleImage> imagestyle =vtkSmartPointer<vtkInteractorStyleImage>::New();//定义交互模式renderWindowInteractor->SetInteractorStyle(imagestyle);//设置交互模式renderWindowInteractor->SetRenderWindow(renderWindow);//连接交互器与显示窗口renderWindowInteractor->Initialize();//交互器初始化//****************建立 观察者-命令 模式****************//vtkSmartPointer<vtkImageInteractionCallback> callback =vtkSmartPointer<vtkImageInteractionCallback>::New();//设置回调函数callback->SetImageReslice(reslice);//为vtkImageSlice对象设置变换矩阵,确定矩阵对应的切片位置callback->SetInteractor(renderWindowInteractor);imagestyle->AddObserver(vtkCommand::MouseMoveEvent, callback);//鼠标移动对应的回调函数imagestyle->AddObserver(vtkCommand::LeftButtonPressEvent, callback);//按下鼠标左键对应的回调函数imagestyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, callback);//按下鼠标右键对应的回调函数renderWindowInteractor->Start();
这里主要是三个消息:
vtkCommand::LeftButtonPressEvent,
vtkCommand::LeftButtonReleaseEvent,
vtkCommand::MouseMoveEvent,
当响应到这三个消息时,立即执行vtkImageInteractionCallback的Execute函数,以便实现切片的实时提取和更新。完成以后,运行程序,当鼠标在图像上移动时,会发现图像会跟着鼠标的移动而变化。
相关文章:
VTK使用交互器来从三维体数据中提取二维切片
VTK中鼠标消息是在交互类型对象(interactorstyle)中响应,因此通过为交互类型对象(interactorstyle)添加观察者(observer)来监听相应的消息,当消息触发时,由命令模式执行相…...
NCBI 数据下载
网上介绍的那几种直接下载NCBI数据的方法大都下载速度很慢,但是EBI (European Bioinformatics Institute) 下载很快,而且它的数据库和NCBI是共享的,所以我们可以直接从 EBI 下载。 1 、 确定要下载的 SRA 编号; 2 、 EBI (https…...
【Rust】基础语法
变量,基本类型,函数,注释和控制流,这些几乎是每种编程语言都具有的编程概念。 这些基础概念将存在于每个 Rust 程序中,及早学习它们将使你以最快的速度学习 Rust 的使用。 变量 首先必须说明,Rust 是强类…...
JVM基础:类的生命周期详解
JDK版本:jdk8 IDEA版本:IntelliJ IDEA 2022.1.3 文章目录 一. 生命周期概述二. 加载阶段(Loading)2.1 加载步骤2.2 查看内存中的对象 三. 连接阶段(Linking)3.1 连接之验证3.2 连接之准备3.3 连接阶段之解析 四. 初始化阶段(Initialization)4.1 单个类的…...
【Canvas技法】在Canvas按圆周绘制图形或是标注文字时,角度累加的方向为顺时针,起点为x轴正向
【图解说明】 【核心代码】 // 画圆弧及方向for(var i0;i<4;i){var startMath.PI/2*i;var endstartMath.PI/2;var x1180*Math.cos(start);var y1180*Math.sin(start);var x2180*Math.cos(end);var y2180*Math.sin(end);ctx.beginPath();ctx.arc(0,0,180,start,end,false);ct…...
计算机网络-TCP断开连接阶段错误应对机制
连接断开阶段 四次挥手机制:TCP连接的断开需要四次挥手,这是因为双方都需要独立地关闭数据传输。第二次和第三次挥手不能合并,因为在回复第二次挥手的时候,可能还有数据没有接收完成,所以需要先回复ACK报文,…...
springboot动态使用DruidDataSource切换数据源(动态配置多个数据源)
1、添加依赖,在pom文件中添加 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>2.5.0</version></dependency><dependency><grou…...
P8786 [蓝桥杯 2022 省 B] 李白打酒加强版
【dfs题解】---只有50分 (头一回自己用dfs做出来了dp的hard等级的大题,从来没有拿50分这么高兴过哈哈哈哈哈) #include <bits/stdc.h> using namespace std; int n,m; long long ans0; const long long mol1e97; void dfs(int h,int d,int sum) {if(h<0|…...
没有网没有移动存储的情况下两台电脑如何互相传输数据
无网无移动存储情况下两台电脑数据互传探秘 一、直连网线传输数据二、局域网文件共享其他 在信息化时代的今天,电脑作为重要的数据处理工具,在日常生活和工作中扮演着不可或缺的角色。然而,有时我们会遇到一些特殊情况,如没有网络…...
如何用putty通过ssh连接ubuntu
1. 下载和安装PuTTY 访问PuTTY官网下载PuTTY的最新版本。 2. 打开PuTTY 解压下载的文件后,找到PuTTY文件并双击打开。 3. 配置SSH连接 在ubuntu下安装ssh服务在安装ssh时,我一直遇到一个问题,原因是我的虚拟机连不上网,反复实…...
java如何实现rabbitmq的消息确认机制和消息持久化机制配置和示例
在Java中,使用RabbitMQ的客户端库(通常是AMQP客户端库,如RabbitMQ的Java客户端)可以方便地实现消息确认机制和消息持久化机制。以下是如何实现这两个机制的示例。 1、消息确认机制 RabbitMQ支持两种类型的确认:生产者…...
react 组件:Suspense
允许在子组件完成加载前展示后备方案。 children:真正的 UI 渲染内容。如果 children 在渲染中被挂起,Suspense 边界将会渲染 fallback。 fallback:真正的 UI 未渲染完成时代替其渲染的备用 UI,它可以是任何有效的 React 节点。后…...
2024-4-5修改vscode的代理
今天在vs code 上面配置go环境的时候出现了以下的报错: 2024-04-05 16:18:00.786 [info] Installing golang.org/x/tools/goplslatest FAILED 2024-04-05 16:18:00.786 [info] { “code”: 1, “killed”: false, “signal”: null, “cmd”: “E:\Go\bin\go.exe in…...
python字符切片的规则
跟range一样有三个参数,分别是x:y:z,代表的含义分别为左边界,右边界(注意该范围是左闭右开的,也就是说取不到右值)和步长。 1. 切片是从左往右还是从右往左,看的是步长的正负,如果步…...
C++ 的内存安全与效率
在C编程中,内存安全和效率是两个至关重要的考虑因素。 内存安全涉及确保程序在分配和使用内存时不会发生错误,如内存泄漏、悬挂指针、越界访问、空指针解引用等; 效率则关注如何有效地使用内存资源,减少不必要的内存分配和释放操…...
Go 实战|使用 Wails 构建轻量级的桌面应用:仿微信登录界面 Demo
概述 本文探讨 Wails 框架的使用,从搭建环境到开发,再到最终的构建打包,本项目源码 GitHub 地址:https://github.com/mazeyqian/go-run-wechat-demo 前言 Wails 是一个跨平台桌面应用开发框架,他允许开发者利用 Go …...
c++取经之路(其五)——类和对象拷贝构造函数
概念:拷贝构造函数,只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。 特征: 1. 拷贝构造函数是构造函数的一个重载形式 如: 2. 拷贝…...
YOLOv8最新改进系列:融合最新顶会提出的HCANet网络中卷积和注意力融合模块(CAFM),有效提升小目标检测性能,大幅度拉升目标检测效果!遥遥领先!
YOLOv8最新改进系列:YOLOv8最新改进系列:融合最新顶会提出的HCANet网络中卷积和注意力融合模块(CAFM),有效提升小目标检测性能,大幅度拉升目标检测效果!遥遥领先! B站全文戳这里! 详细的改进教程以及源码…...
【计算机毕业设计】网上宠物商店管理系统——后附源码
🎉**欢迎来到我的技术世界!**🎉 📘 博主小档案: 一名来自世界500强的资深程序媛,毕业于国内知名985高校。 🔧 技术专长: 在深度学习任务中展现出卓越的能力,包括但不限于…...
详解Qt添加外部库
在Qt项目中添加外部库是一项常见任务,无论是静态库还是动态库都需要正确的配置才能让项目顺利编译链接。以下是详细步骤和不同场景下的配置方法: 方法一:手动编辑.pro文件 添加头文件路径: 在Qt项目中的.pro文件中使用INCLUDEPAT…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...
Windows 下端口占用排查与释放全攻略
Windows 下端口占用排查与释放全攻略 在开发和运维过程中,经常会遇到端口被占用的问题(如 8080、3306 等常用端口)。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口,帮助你高效解决此类问题。 一、准…...
验证redis数据结构
一、功能验证 1.验证redis的数据结构(如字符串、列表、哈希、集合、有序集合等)是否按照预期工作。 2、常见的数据结构验证方法: ①字符串(string) 测试基本操作 set、get、incr、decr 验证字符串的长度和内容是否正…...
