学习笔记12 |
您所在的位置:网站首页 › 窗宽和窗位的概念和应用 › 学习笔记12 |
读代码
1.![]() app。exist只有在最后一个窗口关闭时,才会产生效果。 app.exec_()的存在使得,软件不会闪退。为什么会这样,是因为app.exec_()的含义是,最后一个窗口关闭后,程序才停止。 ProjectMainWindow.h里写这个类继承自哪里 有什么方法和属性ProjectMainWindow.cpp里写每个方法具体如何实现 3.#ifndef起到的效果是防止一个源文件两次包含同一个头文件,而不是防止两个源文件包含同一个头文件。网上很多资料对这一细节的描述都是错误的。事实上,防止同一头文件被两个不同的源文件包含这种要求本身就是不合理的,头文件存在的价值就是被不同的源文件包含。 假如你有一个C源文件,它包含了多个头文件,比如头文件A和头文件B,而头文件B又包含了头文件A,则最终的效果是,该源文件包含了两次头文件A。如果你在头文件A里定义了结构体或者类类型(这是最常见的情况),那么问题来了,编译时会报大量的重复定义错误。 一般格式是这样的: #ifndef #define ...... ...... #endif 在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h #ifndef _STDIO_H_ #define _STDIO_H_ 4.Qt中的顶层窗口称为MainWindow,属于类QMainWindow,QMainWindow也是继承于QWidget。通过子类化QMainWindow可以创建一个应用程序的窗口。 MainWindow的结构分为五个部分:菜单栏(Menu Bar)、工具栏(Toolbars)、停靠窗口(Dock Widgets)、状态栏(Status Bar)和中央窗口(Central Widget)。可以用下面的图形表示之。 5.在创建Qt widget Application中,可以选择创建带ui的或者不带ui的应用程序。如果选择带.ui的应用,则MainWindow是Ui命名空间的MainWindow。 6. 信号和槽是Qt应用开发的基础,它可是将两个毫无关系的对象连接在一起,槽和普通的C++函数是一样的,只是当它和信号连接在一起后,当发送信号的时候,槽会自动被调用只有加入了Q_OBJECT,你才能使用QT中的signal和slot机制。所以,为了使用信号槽,必须继承QObject。凡是QObject类(不管是直接子类还是间接子类),都应该在第一行代码写上Q_OBJECT。 7.注意,由于 moc 只处理头文件中的标记了Q_OBJECT的类声明,不会处理 cpp 文件中的类似声明。因此,如果我们的类位于 main.cpp 中,是无法得到 moc 的处理的。解决方法是,我们手动调用 moc 工具处理 main.cpp,并且将 main.cpp 中的#include “xxx.h”改为#include “moc_xxx.h”就可以了。不过,这是相当繁琐的步骤,为了避免这样修改,我们还是将其放在头文件中。许多初学者会遇到莫名其妙的错误,一加上Q_OBJECT就出错,很大一部分是因为没有注意到这个宏应该放在头文件中。 8.vtkRenderer个人理解就是个窗口渲染器,也是vtk的核心,没了这玩意儿你没法去操作你的vtkActor,你的vtkInteractorStyle也没有挂载区域 9.vtkImageReslice,功能非常强大,有“瑞士军刀”的美誉。它不仅可以提取切片,还有图像旋转,翻转,重采样,变形等功能,并且效率还很高 10.vtkDistanceWidget:用于在二维平面上测量两点之间的距离。(交互部件_测量类Widget的应用) 创建Widget的一般步骤: (1.)实例化Widget; (2.)指定渲染窗口交互器。Widget可以通过它来监听用户事件。 (3.)必要时使用观察者/命令模式创建回调函数。与widget交互时,它会调用一些通用的VTK事件(94个事件列表),如StartInteractionEvent、InteractionEvent、EndInteractionEvent。用户通过监听这些事件并作出响应,从而可以更新数据、可视化参数或者应用程序的用户图形界面。 (4.)创建合适几何表达实体。用SetRepresentation()函数把他与Widget关联起来,或者使用Widget默认的几何表达实体。 (5.)最后,必须激活Widget,使其在渲染场景中显示。默认情况下,按键用于激活Widget,使其在场景中可见。 11.VTKImagePlaneWidget 是一个3D交互部件,用来重切图像数据。必须先调用 SetInteractor()函数设置交互器。也可以通过SetCurrentRenderer()设置渲染器,默认情况下渲染器都会从你所设置的交互器重获取。 该部件的核心功能是由vtkImageReslice类提供的,该类能够快速获取体数据的切片。通过获得该类的数据输出到纹理映射管道上,可以获取其切面的纹理贴图。 为了能够获取的切片的纹理贴图,需要通过setInputConnection()函数来给部件设置体数据,此时体素的拾取功能才会被激活。如果没有设置体数据,请调用SetTextureVisibility(int)函数关闭纹理映射。 该部件主要通过鼠标来完成对部件的交互,具体操作如下: (1) 按下鼠标中键(滚轮)+移动光标 用来控制部件方向和位置。 如图中4条蓝色的线将VTKImagePlaneWidget分为9个区域,其中将光标移到红色数字所代表的的区域,可以将切片沿着垂直于平面法向量的方向旋转;当把光标移到橘黄色光数字所代表的区域,可以改变平面法向量从而翻转整个平面;当把光标移动到center区域,可以沿着当前平面法向量的方向平移切片。 (2)按下鼠标左键+移动光标 可以拾取切片的坐标值和体素值(显示在渲染窗口的左下角) 当光标位于切片内,按下鼠标左键,会出现十字交叉线来锁定你所拾取的体素。 (3 ) 按下鼠标右键+移动光标 改变切片的窗位窗宽(显示在渲染窗口的左下角)。(窗技术(Window Technique)是医生用 以观察不同密度正常组织或病变的一种显示技术,其包括窗宽(window width)和窗位(window level)。由于各种不同组织结构或病变具有不同的像素值,因些欲显示某一组织结构细节时,应选择适合观察组织结构的窗宽窗位,以获得显示最佳效果。) (4)Shift键+鼠标中键 缩放切片 向上移动光标放大切片,向下移动光标缩小切片 (5)Ctri键+鼠标中键 当光标位于center区域,可以任意方向移动切片;当位于橘黄色数字所代表区域,可以拉伸或缩短该边界;当位于红色数字所代表的区域,可以拉伸或缩短该角所组成的边界 内存释放问题: 在给切片设置体数据后(建立了体数据与vtkImagePlaneWidget连接),再删除体数据和vtkImagePlaneWidget对象时候,发现读入的体数据无法得到释放, 解决办法: 删除tkImagePlaneWidget对象之前,先断开与体数据之间的连接, m_ImagePlaneWidget->SetInputData(nullptr); m_ImagePlaneWidget->SetInputConnection(nullptr); 在构造函数中调用setupUi//setupUi(this)是由.ui文件生成的类的构造函数, //这个函数的作用是对界面进行初始化,它按照我们在Qt设计器里设计的样子把窗体画出来, //把我们在Qt设计器里面定义的信号和槽建立起来。 //也可以说,setupUi是我们画界面和写程序之间的桥梁。 void setupUi(QMainWindow *MainWindow){} //最开始为写好的CTViewer.ui文件(xml)-->变成了ui_CTViewer.h文件(里边有上述方法,完成了u界面) //最开始为ProjectMainWindow.h.h文件-->变成了moc_ProjectMainWindow.cpp 使用GetOutput()的时候,返回的是imageData,GetOutputPort(),返回的是ImageDataAlgorithmvtkActor->vtkRenderer->vtkRenderWindowQVTKWidget类非常重要,是沟通Qt和VTK的桥梁,可以将VTK的图形窗口嵌入到Qt的窗口中this->qvtkWidget->SetRenderWindow(riw[0]->GetRenderWindow()); riw[0]->SetupInteractor( this->qvtkWidget->GetRenderWindow()->GetInteractor()); //完成vtk和qvtkwidget的连接作用 //交互器interactor qt界面中使用中文: button->setText(QApplication::translate("", "好吗?", 0, QApplication::UnicodeUTF8));(2)主函数中调用:
QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8")); 然后界面中使用: button->setText(QObject::tr("你好吗?")); 法二比较方便, 只需要在主函数中设置一次. (3)button->setText(QObject::trUtf8("你好吗?")); 与法二差不多. 允许用户通过GetPickPosition()来获取拾取位置。 vtkCellPicker是vtkPicker的子类,它返回选择单元的ID以及坐标。它也是通过发射光线与Actor相交而拾取对象的,除了返回坐标值,Actor和Mapper,它也返回在指定容差内沿着光线,最靠近光线的那个单元的ID。vtkPointPicker::GetCellId()可以获取单元的ID号。vtkCellPicker可以单一拾取,其速度是所有Picker中最慢的,当然,提供的信息也是最丰富的。vtkCellPicker是所有拾取类中最慢的一个,但获取信息是最多的。通过指定容差,可以返回单一的拾取对象。但如果想返回拾取的点或者单元的话,得用它的子类vtkCellPicker和vtkPointPicker vtkPicker里其中有一个方法:virtual void SetTolerance(double)—-是设置容差。 18.选择拾取类定义了一个纯虚函数Pick(),它的作用是给定一个选择点(屏幕坐标系统里的,以像素为单位)以及一个Renderer,然后通过调用GetPickPosition()方法,生成世界坐标系统的坐标值。 Pick()函数的原型如下: virtual int Pick (double selectionX, double selectionY, double selectionZ, vtkRenderer *renderer) 通常都把selectionZ设为0,与Renderer相关联的actor就是拾取的对象范围。 Picker可以分为两类,分别是用几何方法拾取(典型的例子是光线投射)和基于硬件拾取 19..类vtkDicomImageReader可用于读取DICOM图像,但该类的功能很不完善,虽然VTK 最初是因医学图像可视化而诞生。但VTK对DICOM图像的读写操作却很不支持,比如该类不支持多帧DICOM图像的读取,而且VTK 也没有实现对 DICOM图像的写操作, 即没有提供类vlkDlCOMlmageWriter。 对 DICOM图像支持较好的函数库主要有 GDCM和 DCMTK。著名的医学图像分割与配准工具包 ITK 就是封装了 GDCM函数库进行 DICOM图像的读写。 围绕人体的某一部位作一个接一个的断面扫描,所以扫描后得到的图像是多层的图像,而我们把一层层的图像在z轴上堆叠起来就可以形成三维图像(这就涉及到三维重建的问题),这时,每一层的图像我们都可以存在dicom文件中(当然,dicom文件不是单纯的像素信息,它还有很多的数据头部信息),如下图,我们的目的就是要把在这些数据头部信息和像素信息从一系列dicom文件中读取出来。 1.用户层面:itkImageFileReader(读) itkImageFileWriter(写) 内部实现:由内部ImageIO对象具体负责图像文件读写操作,该对象通过对象工厂根据用户输入文件类型生成相应的ImageIO对象 ITK和VTK混合编程显示ITK读取的图像 #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageToVTKImageFilter.h" #include "vtkImageViewer.h" #include "vtkRenderWindowInteractor.h" int main(int argc, char **argv) { typedef itk::Image ImageType; typedef itk::ImageFileReader ReaderType; //图像读取类 typedef itk::ImageToVTKImageFilter ConnectorType; //VTK和ITK链接器 ReaderType::Pointer reader = ReaderType::New();//由itk::ImageFileReader生成的reader来读取文件 ConnectorType::Pointer connector = ConnectorType::New(); reader->SetFileName("E:\\TestData\\lenna.jpg"); connector->SetInput(reader->GetOutput());//把读取的图片传到itk::ImageToVTKImageFilter里 connector->Update(); vtkImageViewer *viewer = vtkImageViewer::New(); vtkRenderWindowInteractor *interactor = vtkRenderWindowInteractor::New(); viewer->SetInputData(connector->GetOutput());//itk::ImageToVTKImageFilter里 的图片传到vtkImageViewer里,因为经过转换了 viewer->SetupInteractor(interactor); viewer->SetColorWindow(255); //设置窗宽 viewer->SetColorLevel(128); //设置窗位 viewer->Render(); interactor->Initialize(); interactor->Start(); return EXIT_SUCCESS; } itk::ImageFileReader--->itk::ImageToVTKImageFilter--->vtkImageViewer vtkImageReslice四视图中冠状视面、矢状面和横断面(显示过图像内部一点且平行于XY、YZ、XZ平面的平面 3.h头文件是编译时必须的(声明),lib是链接时需要的(静态库中的lib:该lib包含函数代码本身(即包括函数的索引,也包括实现),在编译时直接将代码加入程序当中 动态库中的lib:该lib包含了函数所在的dll文件和文件中函数位置的信息(索引),函数实现代码由运行时加载在进程空间中的dll提供),dll是运行时需要的(运行时需要调用的动态链接库)。pdb为程序数据二进制文件,pdb文件保存着调试和项目状态信息,包含了编译后程序指向源代码的位置信息,用于调试的时候定位到源代码,主要是用来方便调试的。release后建议删掉,有利于保护程序。 当你只修改自己动态库的cpp文件,那么只需要覆盖旧的dll文件,调用该动态库的exe文件就可以使用你修改的内容。但是你修改了头文件,那就麻烦了,调用该动态库的exe文件需要重新重新生成。 4.类vtkRenderWindowInteractor即渲染窗口交互器 响应鼠标/按键/时钟事件的交互机制 当它从窗口系统中监听到感兴趣的事件(消息)时,通过调用InvokeEvent()函数将平台相关的事件翻译成VTK事件,而这些VTK事件是平台 独立的,然后再路由至vtkInteractorObserver或其子类,再由已经对该事件进行注册的vtkInteractorObserver或其子类相应具体的操作。 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |