VTK CT重建(一) MPR 多层面重建 四视图

news2024/11/17 9:54:12

除了MPR之外,在CT重建后处理中还有很多别的常用方法,包括

  • 多层面重建(MPR)
  • 最大密度投影(MIP)
  • 最小密度投影(MinIP)
  • 表面阴影遮盖(SSD)
  • 容积漫游技术(VRT)
  • 曲面重建(CPR)
  • 虚拟内镜技术(VE)

后面有时间我会慢慢补;

MPR,全称是multi-planar reformation / reconstruction,是常用的医学图像后处理技术

1、多平面重建(MPR)是将扫描范围内所有的轴位图像叠加起来再对某些标线标定的重组线所指定的组织进行冠状、矢状位、任意角度斜位图像重组。
优点:
(1)能任意产生新的断层图像,而无需重复扫描。
(2)原图像的密度值被忠实保持到了结果图像上。
(3)曲面重组能在一幅图像里展开显示弯曲物体的全长。
缺点:

(1)难以表达复杂的空间结构
(2)曲面重组易造成假阳性。

 

 vtkImageReslice

vtkImageReslice类可以从体数据内的一点沿着不同的方向切出一个平面图像;
vtkImageReslice是图像几何过滤器的瑞士军刀:它可以以合理高效的任意组合排列、旋转、翻转、缩放、重采样、变形和填充图像数据。置换、重采样和填充等简单操作的效率与专用vtkImagePermute、vtkImageResample和vtkImagePad过滤器类似。 vtkimanageslice非常适合执行以下任务:
1) 对图像进行简单的旋转、缩放和平移。通常,最好先使用vtkImageChangeInformation使图像居中,这样缩放和旋转就发生在图像的中心而不是左下角。
2) 通过SetInformationInput()方法对一个数据集进行重采样,以匹配第二个数据集的体素采样,例如为了比较两个图像或合并两个图像。如果两幅图像不在同一坐标空间中,可以通过SetResliceTransform()方法同时应用线性或非线性变换。
3) 从图像体中提取切片。使用vtkImageReslice可以从体数据中获取到指定的正交、斜切方向的上的切面图像;最方便的方法是使用SetResliceAxesDirectionCosines()指定切片的方向。方向余弦表示输出体积的x、y和z轴。SetOutputDimensionality(2)方法用于指定要输出切片而不是Volume的对象。SetResliceAxesOrigin()方法用于提供切片将通过的(x,y,z)点。可以同时使用ResliceAxes和ResliceTransform,以便从已应用转换的Volume中提取切片。
3)内容是比较常用的内容,可以提取平行于XY平面、YZ平面、XZ平面的切片,还可以提取斜切切片;
 

使用 vtkImageReslice 重建 callback 用来处理鼠标,切换图层;

class vtkImageInteractionCallback : public vtkCommand
{
public:
	static vtkImageInteractionCallback *New()  
	{
		return new vtkImageInteractionCallback;
	}
	vtkImageInteractionCallback()
	{
		this->Slicing = 0;
		//this->ImageReslice = 0;
		this->Interactor = 0;
	}
	void SetImageReslice(vtkImageReslice *reslice)
	{
		//this->ImageReslice = reslice;
	}
    void SetImageMapToColors(vtkImageMapToColors *mapToColors)
    {
        this->mapToColors = mapToColors;
    }
	//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 *)
	{
		vtkRenderWindowInteractor *interactor = GetInteractor();
		int lastPos[2];
		interactor->GetLastEventPosition(lastPos);
		int currPos[2];
		interactor->GetEventPosition(currPos);
		int x = currPos[0];
		int y = currPos[1];
		printf("x:%d===y:%d==\n", x, y);
		if (event == vtkCommand::LeftButtonPressEvent)
		{
	 
			this->Slicing = 1; //标志位
	    
			if (x < 300 && y < 300)
			{
				mode = 3;    //#"CORONAL"
			}
			else if(x < 600 && y < 300 )
			{
				mode = 1;				//#"AXIAL"
			}
			else if (x > 300 && y > 300)
			{
				mode = 0;				//#"SAGITTAL"
			}				 
            else
				mode = 4;
		}
		else if (event == vtkCommand::LeftButtonReleaseEvent)
		{
			this->Slicing = 0; //标志位
		}
		else if (event == vtkCommand::MouseMoveEvent)
		{
			if (this->Slicing)//检验鼠标左键已经按下 正在执行操作
			{
				vtkImageReslice *reslice = ImageReslicex;
				if (0 == mode)
				{
					reslice = ImageReslicey;
				}
				else if(3 == mode)
				{
					reslice = ImageReslicez;
				}
				//else
				//	return;
				//记下鼠标Y向变化的幅值大小
				int deltaY = lastPos[1] - currPos[1];

                reslice->Update();
                double sliceSpacing = reslice->GetOutput()->GetSpacing()[2];
                vtkMatrix4x4 *matrix = reslice->GetResliceAxes();
                // move the center point that we are slicing through
                double point[4];
                double center[4];
                point[0] = 0.0;
                point[1] = 0.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]);
                //mapToColors->Update();
                interactor->Render();
            }
            else
            {
                vtkInteractorStyle *style = vtkInteractorStyle::SafeDownCast(
                    interactor->GetInteractorStyle());
                if (style)
                {
                    style->OnMouseMove();
                }
            }
        }
    }

private:
    int Slicing;
	int mode;
 
    vtkRenderWindowInteractor *Interactor;
    vtkImageMapToColors *mapToColors;
public:
		vtkImageReslice *ImageReslicex;
		vtkImageReslice *ImageReslicey;
		vtkImageReslice *ImageReslicez;
};

初始化三个 Actor

void initImageActor(double* Matrix,double * center, vtkSmartPointer<vtkImageCast>pImageCast,
	vtkSmartPointer<vtkImageReslice> imageReslice,vtkSmartPointer<vtkImageActor> actor)
{
	vtkMatrix4x4 *AxialResliceMatrix = vtkMatrix4x4::New();
	AxialResliceMatrix->DeepCopy(Matrix);

	AxialResliceMatrix->SetElement(0, 3, center[0]);
	AxialResliceMatrix->SetElement(1, 3, center[1]);
	AxialResliceMatrix->SetElement(2, 3, center[2]);

	//设置体数据来源
	imageReslice->SetInputConnection(pImageCast->GetOutputPort());
	//设置输出是一个切片,而不是一个卷
	imageReslice->SetOutputDimensionality(2);
	//pImageResliceX->SetResliceAxesDirectionCosines(sagittalX, sagittalY, sagittalZ);
	//设置vtkImageReslice的切面坐标系矩阵
	imageReslice->SetResliceAxes(AxialResliceMatrix);
	//pImageResliceX->SetResliceAxesOrigin(center);
	//设置切面算法的插值方式为线性插值
	imageReslice->SetInterpolationModeToLinear();
	imageReslice->Update();
	//pImageMapToColorsX->SetLookupTable(pWindowLevelLookupTable);
	//pImageMapToColorsX->SetInputConnection(pImageResliceX->GetOutputPort());
	actor->SetInputData(imageReslice->GetOutput());
	actor->SetPosition(0, 0, 0);
	actor->Update();
}

主函数:

先通过vtkMetaImageReader读取一副三维图像,获取图像范围、原点和像素间隔,由这三个参数可以计算图像的中心位置。
接下来定义了切面的变换矩阵axialElements,该矩阵的前三列分别表示X、Y和Z方向矢量,第四列为切面坐标系原点。通过修改切面坐标系原点,可以得到不同位置的切面图像。
然后将读取的图像作为vtkImageReslice的输入,通过函数SetResliceAxes()设置变换矩阵resliceAxis
 

int main()
{
	vtkSmartPointer<vtkImageReslice> pImageResliceX = vtkSmartPointer<vtkImageReslice>::New();
	vtkSmartPointer<vtkImageReslice> pImageResliceY = vtkSmartPointer<vtkImageReslice>::New();
	vtkSmartPointer<vtkImageReslice> pImageResliceZ = vtkSmartPointer<vtkImageReslice>::New();

	vtkSmartPointer<vtkXMLImageDataReader> pXMLImageDataReader = vtkSmartPointer<vtkXMLImageDataReader>::New();
	vtkSmartPointer<vtkImageCast> pImageCast = vtkSmartPointer<vtkImageCast>::New();


	vtkSmartPointer<vtkImageActor> pImageActorX = vtkSmartPointer<vtkImageActor>::New();
	vtkSmartPointer<vtkImageActor> pImageActorY = vtkSmartPointer<vtkImageActor>::New();
	vtkSmartPointer<vtkImageActor> pImageActorZ = vtkSmartPointer<vtkImageActor>::New();

	vtkSmartPointer<vtkRenderer> pRendererX = vtkSmartPointer<vtkRenderer>::New();
	vtkSmartPointer<vtkRenderer> pRendererY = vtkSmartPointer<vtkRenderer>::New();
	vtkSmartPointer<vtkRenderer> pRendererZ = vtkSmartPointer<vtkRenderer>::New();
	 
	vtkSmartPointer<vtkRenderer> pRenderer = vtkSmartPointer<vtkRenderer>::New();
	vtkSmartPointer<vtkRenderWindow> pRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();

	//vtkSmartPointer<vtkMetaImageReader> reader =
	//	vtkSmartPointer<vtkMetaImageReader>::New();
	//reader->SetFileName("D:/datasource/brain.mhd");
	//reader->Update();
	vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
	reader->SetDirectoryName("D:/datasource/fei/ScalarVolume_13");
	reader->Update();

	int extent[6];
	double spacing[3];
	double origin[3];

	reader->GetOutput()->GetExtent(extent);
	reader->GetOutput()->GetSpacing(spacing);
	reader->GetOutput()->GetOrigin(origin);

	// 计算中心位置。
	double center[3];
	center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]);
	center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]);
	center[2] = origin[2] + spacing[2] * 0.5 * (extent[4] + extent[5]);
   
	//轴状面
	double Axial[16] = {
			1, 0, 0, 0,
			0, 1, 0, 0,
			0, 0, 1, 0,
			0, 0, 0, 1 };
	//冠状面
	double Coronal[16] = {
			1, 0, 0, 0,
			0, 0, -1, 0,
			0, 1, 0, 0,
			0, 0, 0, 1 };
	//矢状面
	double Sagittal[16] = {
			0, 0, 1, 0,
			1, 0, 0, 0,
			0, 1, 0, 0,
			0, 0, 0, 1 };

 
	pImageCast->SetInputConnection(reader->GetOutputPort());
	pImageCast->SetOutputScalarTypeToChar();
	pImageCast->ClampOverflowOn();
	pImageCast->Update();
	//pImageCast->SetUpdateExtentToWholeExtent();

	//x
	initImageActor(Axial, center, pImageCast, pImageResliceX, pImageActorX);
 
	//y
	initImageActor(Coronal, center, pImageCast, pImageResliceY, pImageActorY);
  
	//z
	initImageActor(Sagittal, center, pImageCast, pImageResliceZ, pImageActorZ);
 
	 float fOpac = 0.5;


	pRendererX->AddActor(pImageActorX);
	pRendererY->AddActor(pImageActorY);
	pRendererZ->AddActor(pImageActorZ);

 
	//pRenderer->SetBackground(1, 1, 1);
	pRendererX->SetBackground(0, 0, 0);
	pRendererY->SetBackground(0, 0, 0);
	pRendererZ->SetBackground(0, 0, 0);

	/*
	    #  renderer 0: BOTTOM LEFT
		#  renderer 1: BOTTOM RIGHT
		#  renderer 2: TOP LEFT
		#  renderer 3: TOP RIGHT
	*/
	double ltView[4] = { 0, 0, 0.5, 0.5 }; 
	double rtView[4] = { 0.5, 0, 1, 0.5 }; 
	double lbView[4] = { 0, 0.5, 0.5, 1 }; 
	double rbView[4] = { 0.5, 0.5, 1, 1 }; 

	//pRenderer->SetViewport(0, 0, 0.6, 1);
	//pRendererX->SetViewport(0.6, 0.66, 1, 1);
	//pRendererY->SetViewport(0.6, 0.33, 1, 0.66);
	//pRendererZ->SetViewport(0.6, 0, 1, 0.33);

	pRenderer->SetViewport(rtView);
	pRendererX->SetViewport(lbView);
	pRendererY->SetViewport(rbView);
	pRendererZ->SetViewport(ltView);

	pRenderWindow->AddRenderer(pRendererX);
	pRenderWindow->AddRenderer(pRendererY);
	pRenderWindow->AddRenderer(pRendererZ);
	pRenderWindow->AddRenderer(pRenderer);

	vtkSmartPointer<vtkRenderWindowInteractor> pRenderWindowInteractor =
		vtkSmartPointer<vtkRenderWindowInteractor>::New();

	pRenderWindow->SetSize(600, 600);
  
	// add observer;
	vtkSmartPointer<vtkInteractorStyleImage> imagestyle =
		vtkSmartPointer<vtkInteractorStyleImage>::New();

	pRenderWindowInteractor->SetInteractorStyle(imagestyle);
	pRenderWindowInteractor->SetRenderWindow(pRenderWindow);
	pRenderWindowInteractor->Initialize();

	vtkSmartPointer<vtkImageInteractionCallback> callback =
		vtkSmartPointer<vtkImageInteractionCallback>::New();
	//callback->SetImageReslice(reslice);
	callback->ImageReslicex = pImageResliceX;
	callback->ImageReslicey = pImageResliceY;
	callback->ImageReslicez = pImageResliceZ;
	callback->SetInteractor(pRenderWindowInteractor);
	//callback->SetImageMapToColors(colorMap);

	imagestyle->AddObserver(vtkCommand::MouseMoveEvent, callback);
	imagestyle->AddObserver(vtkCommand::LeftButtonPressEvent, callback);
	imagestyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, callback);

	pRenderWindow->Render();
   
	pRenderWindowInteractor->Initialize();
	pRenderWindowInteractor->Start();
 
	return 0;
}

参考资料

1.vtkImageReslice Class Reference

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go validator参数校验器自定义规则及提示(自定义异常返回提示语)

原文连接&#xff1a;https://segmentfault.com/a/1190000040445612 笔者针对参数为指针的情况做了一点小优化 这里我们用validator用来做参数校验&#xff0c;gin默认的github.com/go-playground/validator&#xff0c;可以直接使用 除此之外还有一些其他的工具也挺好用的&am…

Linux基础指令

本文已收录至《Linux知识与编程》专栏&#xff01;作者&#xff1a;ARMCSKGT演示环境&#xff1a;CentOS 7 目录 前言 正文 查看当前用户whoami 查看当前目录路径pwd 清理屏幕clear 查看目录下文件指令ls 进入目录指令cd 以树状结构显示目录文件tree 创建普通文件指令t…

Leetcode.1669 合并两个链表

题目链接 Leetcode.1669 合并两个链表 Rating : 1428 题目描述 给你两个链表 list1和 list2&#xff0c;它们包含的元素分别为 n个和 m个。 请你将 list1中下标从 a到 b的全部节点都删除&#xff0c;并将list2接在被删除节点的位置。 示例 1&#xff1a; 输入&#xff1a;li…

rtsp实时流通过rtmp推送到服务端

ffmpeg可以实现这个功能。ffmpeg支持rtsp协议&#xff0c;也支持rtmp。在这个案例中rtsp是输入&#xff0c; rtmp是输出&#xff0c;ffmpeg实现了转码的功能。下面可出一个整体思路流程图。 如图1所示&#xff1a;在获取都rtsp流以后&#xff0c;解复用&#xff08;demux&…

检测之VOC转YOLO

文章目录检测所用数据有几种文件格式&#xff0c;我们对于检测&#xff0c;将使用VOC格式做为基础&#xff0c;与其它格式的的互转实现部分如下&#xff1a;检测系列相关文章参考如下链接&#xff1a; VOC数据的结构介绍及自定义生成&#xff0c;用labelimg自已标注VOC标准数据…

Notepad++作死,国产文本编辑器Notepad--发布

作死的Notepad Notepad 和 Notepad 都是基于 Windows 的文本编辑器&#xff0c;通常用于编写和编辑纯文本文件。 这两个应用程序都是简单的轻量级程序&#xff0c;提供基本的文本编辑功能。 Notepad是一口君经常使用的一款文本编辑软件&#xff0c;用了大概10年了。 然而Not…

配置并行(RH294)

当Ansible处理playbook的时候会顺序运行每个play确定play的主机列表之后Ansible将按顺序运行每个任务一般来说&#xff0c;所有主机必须在任何主机在play中启动下一个任务之前成功完成任务理论上&#xff0c;Ansible可以同时连接到play中的所有主机来执行每项任务Ansible所进行…

​力扣解法汇总1669. 合并两个链表

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。…

解决Vue启动失败报错:Module not found: Error: Can‘t resolve ‘less-loader‘

问题描述 今天想在网上找一个好看的登录页面&#xff0c;把别人的代码引入进来之后&#xff0c;发现项目编译不了&#xff0c;并且报错了&#xff1a; Module not found: Error: Can’t resolve ‘less-loader’ 分析问题 从错误的日志就可以看出来&#xff0c;是缺少了less-…

Linux: 关于 SIGCHLD 的更多细节

僵尸进程 何为僵尸进程&#xff1f; 一个进程使用fork创建子进程&#xff0c;如果子进程退出&#xff0c;而父进程并没有调用 wait 或 waitpid获取子进程的状态信息&#xff0c;那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程成为僵尸进程的因素 子进程 先…

AOP的一点浅薄理解

AOP思想应该怎么去理解&#xff01; Aspect&#xff08;切面&#xff09;&#xff1a; Aspect 声明类似于 Java 中的类声明&#xff0c;在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。 Joint point&#xff08;连接点&#xff09;&#xff1a;表示在程序中明确定义的点…

C语言学习笔记-变量

我们知道每一个程序的运行都需要内存&#xff0c;那么C语言的变量的定义是什么含义呢&#xff1f; 假如我花了200元买了一块4G内存条&#xff0c;然后我定义了一个int a ;就意味着从这4G的内存上要拿走4个字节&#xff0c;又定义了一个int b&#xff1b;那么b同样也要从4G的内存…

【OpenGL学习】OpenGL实现 基于Phong模型的基础光照

基于Phong模型的基础光照 在本节中&#xff0c;我们将利用 Phong 光照模型来完成一个简单的光照场景的渲染。 一、Phong 光照模型 Phong光照模型是20世纪70年代被提出的一种渲染逼真图像的方法&#xff0c;模型的提出者是越南出生的计算机图形学研究员Bui Tuong Phong&#…

JavaScript中的String和自定义对象~

String对象&#xff1a; 它是 JavaScript 的一种基本的数据类型 String 对象的 length 属性声明了该字符串中的字符数&#xff0c;String 类定义了大量操作字符串的方法&#xff0c;例如从字符串中提取字符或子串&#xff0c;或者检索字符或子串 需要注意的是&#xff0c;Ja…

单行文本域,多行文本域隐藏问题

多行文本域隐藏问题 overflow: hidden; 首先是溢出隐藏&#xff0c;不可或缺 display: -webkit-box; 以弹性盒模型显示 -webkit-box-orient: vertical; 盒模型元素的排列方式 -webkit-line-clamp: 3; 显示行数 <style>.postnameStyle{font-size: 30rpx;font-weight: …

【科研】ET-BERT资料库梳理

作者原repo链接 https://github.com/linwhitehat/ET-BERT 0.资料总库 分为数据模型语料库 1.数据集 包含fine-tuning数据集&#xff08;cstnet-tls 1.3&#xff09;与公开数据集&#xff08;USTC-TFC、VPN-app、VPN-service的数据包级和流级&#xff09;目录链接 1.1 微调…

【博客602】net.ipv4.conf.eth0.route_localnet的作用

net.ipv4.conf.eth0.route_localnet的作用 背景&#xff1a;默认情况下不能将本机的请求跳转/转发到回环接口上 在某些场景下会用在一台主机内网络流量重定向&#xff0c;比如将在本机回环设备中的数据包强行转发到另一台主机上。结果发现原本在正常的NAT场景中生效的iptables…

windbg抓一个windows蓝屏分析

前言 设备一直以来挺稳定&#xff0c;但还是小概率事件意外出现某设备突然蓝屏了。查看windows事件查看器提示计算机已经从检测错误后重新启动。检测错误: 0x0000009f (0x0000000000000003, 0xffffad0f4edc7570, 0xfffff8046a09ec20, 0xffffad0f4ef318a0)。已将转储的数据保存…

搭建不同网络训练MNIST

问题在之前的学习过程中&#xff0c;我们学习了如何搭建全连接神经网络训练Mnist数据集。初始时&#xff0c;全连接神经网络训练结果验证集和训练集的精确度不高&#xff0c;在对数据进行归一化&#xff0c;调参等操作提高了精确度。我们这次使用Le-Net5和VGG对MNIST进行训练&a…

STM32—串口

串口介绍 串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方式的扩展接口。串行接口&#xff08;Serial Interface&#xff09;是指数据一位一位地顺序传送。其特点是通信线路简单&#xff0c;只…