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软件打开查看是否正确;