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

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

目录

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

简介:

[1 Mricro软件的安装和使用](#1 Mricro软件的安装和使用)

[(1) Mricro安装](#(1) Mricro安装)

[(2) Mricro转换DICOM为裸数据](#(2) Mricro转换DICOM为裸数据)

[2 从硬盘读取数据到vtkImageData](#2 从硬盘读取数据到vtkImageData)

[3 vtkImageData转RGB或RGBA格式](#3 vtkImageData转RGB或RGBA格式)

[4 练习](#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

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

cpp 复制代码
#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 it
	vtkNew<vtkImageMapToWindowLevelColors> windowLevel;
	windowLevel->SetWindow(1000);
	windowLevel->SetLevel(800);
	windowLevel->SetInputData(imageData);

	//vtkImageActor: draw an image in a rendered 3D scene
	vtkNew<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 screen
	vtkNew<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();
}

主要修改的代码如下:

cpp 复制代码
    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打开查看。

完整源代码如下:

cpp 复制代码
#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 it
	vtkNew<vtkImageMapToWindowLevelColors> windowLevel;
	windowLevel->SetWindow(1000);
	windowLevel->SetLevel(800);
	windowLevel->SetInputData(imageData);

	//vtkImageActor: draw an image in a rendered 3D scene
	vtkNew<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 screen
	vtkNew<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软件打开查看是否正确;

总结

相关推荐
A懿轩A29 分钟前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导34 分钟前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香34 分钟前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工2 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
唐诺8 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨9 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客9 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin9 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin