004: VTK读入数据---vtkImageData详细说明

news/2025/1/15 12:37:23/

VTK医学图像处理---vtkImageData类

目录

VTK医学图像处理---vtkImageData类

简介:

1 Mricro软件的安装和使用

(1) Mricro安装

(2) Mricro转换DICOM为裸数据 

2 从硬盘读取数据到vtkImageData

3 vtkImageData转RGB或RGBA格式

4 练习

总结


简介:

        对于医学图像来说,vtkImageData是使用频率非常高的类,因为医学图像通常为较为规则的矩形或容积类型(三维),而vtkImageData类主要就是用于存储此类数据的,vtk中相关算法(比如阈值、缩放等)的输入和输出也是vtkImageData类,熟练掌握vtkImageData类非常重要。

        vtkDICOMImageReader类读取文件夹中的DICOM文件后,DICOM数据也存储在vtkImageData类中,本篇博文将跳过vtkDICOMImageReader类,直接从硬盘读取数据到vtkImageData类中,以增强大家对vtkImageData类的熟悉。

        由于DICOM格式非常负责,有专门的开源库对DICOM文件进行解析,比如DCMTK(DCMTK - dicom.offis.de),我们不打算详细介绍DICOM标准,因此我们将借助一个免费的软件Mricro(建议大家学会使用给软件,不管是开发还是做科研都非常有用)来将DICOM文件转换为原始数据,采用C语言从硬盘读取原始数据到vtkImageData类中。

        本博文主要包括为 Mricro的安装和使用,从硬盘读取数据到vtkImageData类,vtkImageData类的详细介绍。

1 Mricro软件的安装和使用

下载地址:MRIcro software guide (sc.edu)   

打开网页后,点击下图中红色方框中的链接开始下载。

(1) Mricro安装

下载后,解压会看到下面的图标,双击安装,提出是否安装,选择是,一路默认安装,详细步骤如下图:

(2) Mricro转换DICOM为裸数据 

 在开始栏中搜索Mricro,或从所有应用程序里面找到Mricro启动,启动后的界面为:

DICOM数据下载地址:【免费】VTK-医学图像处理博文中需要的DICOM测试数据资源-CSDN文库

下载DICOM数据解压,在Mricro软件的菜单项中选择 Import--Convert foreign to Analyze,跳出选择对话框。

在Number of Files中要输入待转换的DICOM文件张数, 例如这里我们转换10张,就在该输入框内填写10,记得勾选 Open sequential files:ignore filename选项,然后点击  Select。

在Conversion Options框中,继续点击 OK

这里有时候会跳出一个或两个提示框,不用管它,点击OK继续

到 Select 对话框后,随便选中一张待转换的DICOM文件,然后再文件名中输入要保存为裸数据的名称,点击  打开。

 

单击  打开后,会跳出导出保存框,在保存框中输入要保存的名称,点击  保存按钮进行保存。

 

打开DicomFiles所在文件夹,会发现里面多了两个文件:test.hdr和  test.img

其中 test.hdr文件中保存的数据的长宽高,空间位置等信息

test.img文件中就是纯粹的裸数据,接下来我们将读取test.img中的数据到vtkImageData中。

双击test.hdr,用Mricro软件打开它,在软件的左侧,可以看到test.img中的数据长为512,宽为512,一共有10张图像,数据类型为short。

2 从硬盘读取数据到vtkImageData

 老规矩,先看下代码,也可以拷贝到自己的项目中,运行下:

#define _CRT_SECURE_NO_WARNINGS#include "vtkImageMapToWindowLevelColors.h"
#include "vtkImageActor.h"
#include "vtkImageMapper3D.h"
#include "vtkImageData.h"
#include "vtkNew.h"
#include "vtkDICOMImageReader.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkCamera.h"
int ImageSlice = 0;
void main()
{vtkNew<vtkImageData> imageData;imageData->SetDimensions(512, 512, 10);imageData->SetSpacing(.49, .49, .7);imageData->SetOrigin(0.0, 0.0, 0.0);imageData->AllocateScalars(VTK_SHORT, 1);void *ptr = imageData->GetScalarPointer();size_t bSize = 512 * 512 * 10;FILE* pFile = fopen("D:\\DicomFiles\\test.img","rb+");if (NULL == pFile)return;fread(ptr, sizeof(short), bSize, pFile);fclose(pFile);int* ext = imageData->GetExtent();// map the input image through a lookup table and window / level itvtkNew<vtkImageMapToWindowLevelColors> windowLevel;windowLevel->SetWindow(1000);windowLevel->SetLevel(800);windowLevel->SetInputData(imageData);//vtkImageActor: draw an image in a rendered 3D scenevtkNew<vtkImageActor> imageActor;imageActor->SetDisplayExtent(ext[0], ext[1], ext[2], ext[3], ImageSlice, ImageSlice);imageActor->GetMapper()->SetInputConnection(windowLevel->GetOutputPort());// The renderer generates the image which is then displayed on the render window.vtkNew<vtkRenderer> renderer;renderer->AddActor(imageActor);renderer->SetBackground(.2,.2,.2);vtkCamera *cam = renderer->GetActiveCamera();if (cam){// 获取物体在三维空间中的原点,XYZ范围和中心//vtkImageData* idata = reader->GetOutput();vtkImageData* idata = imageData;double* origins = idata->GetOrigin(); // 三维坐标中的起点double* bounds = idata->GetBounds();  // 包围盒的xyz范围double* center = idata->GetCenter();  // 中心cam->SetFocalPoint(center);cam->SetPosition(center[0], center[1], center[2] - bounds[5]); // -1 if medical ?cam->SetViewUp(0, 1, 0);cam->SetClippingRange(0.1,1000);renderer->ResetCamera();}// The render window is the actual GUI window that appears on the computer screenvtkNew<vtkRenderWindow> renderWindow;renderWindow->SetSize(512, 512);renderWindow->AddRenderer(renderer);renderWindow->SetWindowName("Dicom Image");// The render window interactor captures mouse events// and will perform appropriate camera or actor manipulation// depending on the nature of the events.vtkNew<vtkRenderWindowInteractor> interactor;interactor->SetRenderWindow(renderWindow);// This starts the event loop and as a side effect causes an initial render.renderWindow->Render();interactor->Start();
}

主要修改的代码如下:

    vtkNew<vtkImageData> imageData;imageData->SetDimensions(512, 512, 10);imageData->SetSpacing(.49, .49, .7);imageData->SetOrigin(0.0, 0.0, 0.0);imageData->AllocateScalars(VTK_SHORT, 1);void *ptr = imageData->GetScalarPointer();size_t bSize = 512 * 512 * 10;FILE* pFile = fopen("D:\\DicomFiles\\test.img","rb+");if (NULL == pFile)return;fread(ptr, sizeof(short), bSize, pFile);fclose(pFile);

 SetDimensions 函数主要用来设置图像的长宽高信息

SetSpacing 是像素间距,图像的(512 - 1)* 0.49 就是图像宽的实际尺寸;

SetOrigin  是图像在世界坐标中的位置;

AllocateScalars有两个参数,第一个参数指定数据类型(16bit short);第二个参数1是 一个像素是有一个元素组成(对于BMP位图来说,一个像素是有RGB/RGBA组成的);

AllocateScalars 同时还分配内存空间;

GetScalarPointer函数可以获取分配内存空间的地址;有了地址,有了数据的大小,就可以直接从硬盘读取裸数据了。读取的代码用C语言来写的,这里就不解释了。

运行结果如下: 

3 vtkImageData转RGB或RGBA格式

接下来我们增加了个一个Convert2RGB函数,用户将vtkImageData中的数据转换为RGB格式的数据,并保存的硬盘,然后用Mricro打开查看。

完整源代码如下:

#define _CRT_SECURE_NO_WARNINGS#include "vtkImageMapToWindowLevelColors.h"
#include "vtkImageActor.h"
#include "vtkImageMapper3D.h"
#include "vtkImageData.h"
#include "vtkNew.h"
#include "vtkDICOMImageReader.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkCamera.h"
#include "vtkWindowLevelLookupTable.h"
int ImageSlice = 0;void Convert2RGB(vtkImageData* pData);void main()
{vtkNew<vtkImageData> imageData;imageData->SetDimensions(512, 512, 10);imageData->SetSpacing(.49, .49, .7);imageData->SetOrigin(0.0, 0.0, 0.0);imageData->AllocateScalars(VTK_SHORT, 1);void *ptr = imageData->GetScalarPointer();size_t bSize = 512 * 512 * 10;FILE* pFile = fopen("D:\\DicomFiles\\test.img","rb+");if (NULL == pFile)return;fread(ptr, sizeof(short), bSize, pFile);fclose(pFile);Convert2RGB(imageData);int* ext = imageData->GetExtent();// map the input image through a lookup table and window / level itvtkNew<vtkImageMapToWindowLevelColors> windowLevel;windowLevel->SetWindow(1000);windowLevel->SetLevel(800);windowLevel->SetInputData(imageData);//vtkImageActor: draw an image in a rendered 3D scenevtkNew<vtkImageActor> imageActor;imageActor->SetDisplayExtent(ext[0], ext[1], ext[2], ext[3], ImageSlice, ImageSlice);imageActor->GetMapper()->SetInputConnection(windowLevel->GetOutputPort());// The renderer generates the image which is then displayed on the render window.vtkNew<vtkRenderer> renderer;renderer->AddActor(imageActor);renderer->SetBackground(.2,.2,.2);vtkCamera *cam = renderer->GetActiveCamera();if (cam){// 获取物体在三维空间中的原点,XYZ范围和中心//vtkImageData* idata = reader->GetOutput();vtkImageData* idata = imageData;double* origins = idata->GetOrigin(); // 三维坐标中的起点double* bounds = idata->GetBounds();  // 包围盒的xyz范围double* center = idata->GetCenter();  // 中心cam->SetFocalPoint(center);cam->SetPosition(center[0], center[1], center[2] - bounds[5]); // -1 if medical ?cam->SetViewUp(0, 1, 0);cam->SetClippingRange(0.1,1000);renderer->ResetCamera();}// The render window is the actual GUI window that appears on the computer screenvtkNew<vtkRenderWindow> renderWindow;renderWindow->SetSize(512, 512);renderWindow->AddRenderer(renderer);renderWindow->SetWindowName("Dicom Image");// The render window interactor captures mouse events// and will perform appropriate camera or actor manipulation// depending on the nature of the events.vtkNew<vtkRenderWindowInteractor> interactor;interactor->SetRenderWindow(renderWindow);// This starts the event loop and as a side effect causes an initial render.renderWindow->Render();interactor->Start();
}void Convert2RGB(vtkImageData* pData)
{void *ptr = pData->GetScalarPointer();vtkNew<vtkWindowLevelLookupTable> table;table->SetWindow(1000);table->SetLevel(800);table->Build();table->BuildSpecialColors();long iCount = 512 * 512;void *rgbPtr = malloc(iCount * 3);unsigned char *desPtr = (unsigned char *)rgbPtr;table->MapScalarsThroughTable2(ptr, desPtr, VTK_UNSIGNED_SHORT, iCount, VTK_LUMINANCE, VTK_RGB);FILE* pFile = fopen("color2.img", "wb+");if (!pFile)return;fwrite(rgbPtr, 1, iCount * 3, pFile);fclose(pFile);free(rgbPtr);}

打开源代码所在文件夹,会发现多了一个color2.img,打开Mricro软件,将 color2.img拖到Mricro软件中,发现打开后是乱码,这是由于Mricro软件左侧的参数信息不正确。

将左侧X Y Z分别填入512  512 1

将Data,选中 24-bit rgb,

然后点击菜单中的Header,Save header;

保存完成后,双击 color2.hdr,打开,显示结果如下:

在这里例子中,我们只保存了一张RGB,而图像是有10张的,请自己动手,把10张图像全部保存成为RGB数据,练习一下。 

4 练习

(1)用vtkImageData读取一张BMP图像,并显示;

(2)将相机的位置这是在现在位置的反方向,对比下显示结果;

(3)将vtkImageData中的数据重新保存一份,用Mricro软件打开查看是否正确;

总结


http://www.ppmy.cn/news/1521973.html

相关文章

Dify 与 FastGPT 流程编排能力对比分析

Dify 与 FastGPT 流程编排能力对比分析 一、引言 在人工智能快速发展的今天&#xff0c;大语言模型&#xff08;LLM&#xff09;应用平台正在重塑各行各业的工作流程。其中&#xff0c;Dify 和 FastGPT 作为两款具有重要影响力的工具&#xff0c;凭借各自独特的流程编排能力&a…

SimD:基于相似度距离的小目标检测标签分配

摘要 https://arxiv.org/pdf/2407.02394 由于物体尺寸有限且信息不足&#xff0c;小物体检测正成为计算机视觉领域最具挑战性的任务之一。标签分配策略是影响物体检测精度的关键因素。尽管已经存在一些针对小物体的有效标签分配策略&#xff0c;但大多数策略都集中在降低对边界…

Faker在pytest中的应用

Faker在pytest中的应用主要体现在测试数据的准备上&#xff0c;通过生成逼真的伪数据来模拟真实场景&#xff0c;从而提高测试的覆盖率和可靠性。以下是一些具体的应用例子&#xff1a; 用户信息测试 在测试用户注册、登录等功能时&#xff0c;Faker可以生成大量的用户信息&a…

逻辑代数的基本定律,常用公式

目录 逻辑代数的基本定律 0-1律 同一律 互补律 还原律 交换律 结合律 分配律 摩根定理&#xff08;反演律&#xff09; 逻辑代数的常用公式 吸收律 冗余律 思考 异或运算的一些公式 逻辑代数的基本定律 最基本的与或非的运算。 0-1律 同一律 互补律 还原律 交…

统一NLP和目标检测的DETR(一)——self attention、encoder、decoder

主流目标检测算法劣势 YOLO系列&#xff0c;它基于anchor来做&#xff0c;少不了要用MNS&#xff0c;导致速度相对较慢。 但今天介绍一款DETR&#xff0c;基于VIT的目标检测算法。 那么我们需要先深入过一遍VIT。 Vision transform 1、传统RNN网络的问题 单向&#xff1a;只…

Java8 Stream流的基本使用

Java 8 中引入的 Stream API 是为了简化对集合类库&#xff08;例如 List 和 Set&#xff09;的操作&#xff0c;同时支持声明式编程风格和并行操作。Stream 不存储数据&#xff0c;而是对数据源进行流水线式操作&#xff0c;其设计核心是“将数据处理的每一步链接起来&#xf…

kubernetes集群下部署kafka+zookeeper单机部署方案

背景&#xff1a; 注&#xff1a;在kubernetes集群上部署单机版的zookeeperkafka服务&#xff0c;是采用了kubernetes中的deploment组件service组件pvc存储组件 1、部署zookeeper服务&#xff1a; 注&#xff1a;这里时候的镜像是&#xff1a;dockerhub.jiang.com/jiang-public…

【佳学基因检测】如何使用Letsencrypt对一个网站进行加密?

【佳学基因检测】如何使用Letsencrypt对一个网站进行加密&#xff1f; 更换为Let’s Encrypt证书涉及以下几个步骤&#xff1a; 1. 安装Certbot Certbot是Let’s Encrypt的客户端工具&#xff0c;它可以帮助你申请和管理证书。首先&#xff0c;你需要在服务器上安装Certbot。…