欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > VTK编程指南<十二>:VTK图像数据结构及图像创建与显示

VTK编程指南<十二>:VTK图像数据结构及图像创建与显示

2024/12/31 1:02:04 来源:https://blog.csdn.net/m0_37251750/article/details/144524367  浏览:    关键词:VTK编程指南<十二>:VTK图像数据结构及图像创建与显示

  数字图像是一种重要的多媒体数据,广泛应用于工业生产、生物医学、地质、气象等重要领域。数字图像处理技术具有重要的应用价值。图像是VTK里非常重要的一种数据结构。本章重点讲解VTK在数字图像处理应用方面的相关技术。

1、VTK图像数据结构

  数字图像文件内容由两个部分组成:图像头信息和数据。图像头信息定义了图像的基本
信息,主要包括起点位置(Origin)、像素间隔(Space)和维数(Dimension)。通过这三个参数即可确定图像空间位置和大小。图像可以看作空间中的一个规则的网格,网格中的每个最小单元称为像素(二维)或者体素(三维),网格在每个方向上的像素或者体素个数即为图像在该方向的维数。像素索引表示每个像素在图像网格中的位置,是图像内部的网格坐标。

  在医学图像中,每个图像除了内部坐标外,还存在一个世界坐标,这个世界坐标依赖于成像设备。在医学图像中,起点位置、像素间隔和图像维数决定了世界坐标系。这样通过起点位置、像素间隔和像素索引即可计算每个像素的世界坐标。

  图5-1表示一个4x2x3体素的图像,即图像的维数,每一个小球表示一个像素;而图像的原点为(5.1,10.0,6.5),两个像素之间的间隔表示像素间隔,每个方向的像素间隔分别为1.5、1.5 和1.8。
在这里插入图片描述

  图像数据即为图像像素的像素值,一般采用一维数组来表示和存储。已知像素索引和图像维数的情况下,即可计算每个像素对应的像素值。通常图像的像素值为一个标量,例如灰度图像;图像像素值也可以是一个矢量,例如梯度图像;另外,图像像素值还可以是张量,如弥散张量成像(Diffusion Tensor Imaging,DTI)。医学图像处理中经常使用的是灰度图像。

  这里需要注意:灰度图像像素或者体素的数据类型在一般的灰度图像处理中不需要考虑因为其范围默认为0~255,可以采用一个unsigned char类型来表示。但是在医学图像处理中,256灰度级远远不能满足要求,因此灰度范围往往大于256级。

  常见医学图像的像素数据类型为 unsigned short,灰度范围为 0~65536。另外,有时为了精度的考虑,也会使用 int、float或者 double 类型,因此需要格外注意。由前面章节内容可知,VTK 中图像数据结构由 vtkImageData 类表示。利用 vtkImageData可以方便地创建、读写和访问图像数据。下面以VTK图像创建为起点,一步步走进VTK图像处理的世界。

2、VTK图像创建

2.1 VTK图像源 Source

  VTK中内置了多个创建图像的 Source 类,利用这些 Source 类可以快速创建图像。这里以vtkImageCanvasSource2D为代表进行说明。该Source类的功能是创建一个画布(空白图像),并提供了多种几何图形(点、线段、圆、矩形以及图像等)的绘制填充功能。
在这里插入图片描述

#include <iostream>#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);// VTK was built with vtkRenderingOpenGL2
VTK_MODULE_INIT(vtkInteractionStyle);#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>int main()
{vtkSmartPointer<vtkImageCanvasSource2D> canvas =vtkSmartPointer<vtkImageCanvasSource2D>::New();canvas->SetScalarTypeToUnsignedChar();canvas->SetNumberOfScalarComponents(3);canvas->SetExtent(0, 400, 0, 400, 0, 0);canvas->SetDrawColor(255, 0, 0.0);canvas->FillBox(0, 100, 0, 100);canvas->Update();canvas->SetDrawColor(0, 255, 0.0);canvas->FillBox(100, 200, 100, 200);canvas->Update();// Create actorsvtkSmartPointer<vtkImageActor> redActor =vtkSmartPointer<vtkImageActor>::New();redActor->SetInputData(canvas->GetOutput());// Define viewport ranges// (xmin, ymin, xmax, ymax)double redViewport[4] = { 0.0, 0.0, 1.0, 1.0 };// Setup renderersvtkSmartPointer<vtkRenderer> redRenderer =vtkSmartPointer<vtkRenderer>::New();redRenderer->SetViewport(redViewport);redRenderer->AddActor(redActor);redRenderer->ResetCamera();redRenderer->SetBackground(0.2, 0.2, 0.2);// Setup render windowvtkSmartPointer<vtkRenderWindow> renderWindow =vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(redRenderer);renderWindow->SetSize(480, 480);renderWindow->Render();renderWindow->SetWindowName("ImageCanvasSource2D");// Setup render window interactorvtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =vtkSmartPointer<vtkRenderWindowInteractor>::New();vtkSmartPointer<vtkInteractorStyleImage> style =vtkSmartPointer<vtkInteractorStyleImage>::New();renderWindowInteractor->SetInteractorStyle(style);// Render and start interactionrenderWindowInteractor->SetRenderWindow(renderWindow);renderWindowInteractor->Initialize();renderWindowInteractor->Start();return EXIT_SUCCESS;
}

  上面的代码片段中,先定义了一个vtkImageCanvasSource2D对象,然后设置画布的像素数据类型、像素组分数目和画布的大小;然后,利用画布的FiIlIBox()方法在画布中绘制两个矩形,并通过画布的 SetDrawColor()方法来设置两个矩形的颜色。

  除了vtkImageCanvasSource2D 外,VTK还提供了其他类似的Source 类来快速生成特定的图像,例如vtkImageEllipsoidSource,该类根据指定的中心以及各个轴的半径来生成一个前景为椭圆(球)的二值像;vtkImageGaussianSource 类生成一幅像素值服从高斯分布的图像vtkImageGridSource 用于生成网格线图像;vtkImageNoiseSource 生成一个像素值为随机数的噪声像。vtkImageSinusoidSource 生成的图像像素值由正弦函数决定。

2.2 直接创建图像

  利用上述图像Source可以快速地生成特定的图像,但是实际应用中它们用到得较少。VTK中可以手动生成图像,然后根据需要进行像素赋值。
在这里插入图片描述

#include <iostream>#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);// VTK was built with vtkRenderingOpenGL2
VTK_MODULE_INIT(vtkInteractionStyle);#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderWindow.h>
#include <vtkType.h>int main()
{vtkSmartPointer<vtkImageData> img = vtkSmartPointer<vtkImageData>::New();img->SetDimensions(16, 16, 1);// 分配标量数据,并设置数据类型为VTK_UNSIGNED_CHARimg->AllocateScalars(VTK_UNSIGNED_CHAR, 1);unsigned char* ptr = (unsigned char*)img->GetScalarPointer();for (int i = 0; i < 16 * 16 * 1; i++){*ptr++ = i % 256;}vtkSmartPointer<vtkImageActor> redActor =vtkSmartPointer<vtkImageActor>::New();redActor->SetInputData(img);double redViewport[4] = { 0.0, 0.0, 1.0, 1.0 };vtkSmartPointer<vtkRenderer> redRenderer =vtkSmartPointer<vtkRenderer>::New();redRenderer->SetViewport(redViewport);redRenderer->AddActor(redActor);redRenderer->ResetCamera();redRenderer->SetBackground(1.0, 1.0, 1.0);vtkSmartPointer<vtkRenderWindow> renderWindow =vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(redRenderer);renderWindow->SetSize(640, 480);renderWindow->Render();renderWindow->SetWindowName("CreateVTKImageData");vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =vtkSmartPointer<vtkRenderWindowInteractor>::New();vtkSmartPointer<vtkInteractorStyleImage> style =vtkSmartPointer<vtkInteractorStyleImage>::New();renderWindowInteractor->SetInteractorStyle(style);renderWindowInteractor->SetRenderWindow(renderWindow);renderWindowInteractor->Initialize();renderWindowInteractor->Start();return EXIT_SUCCESS;
}

  先定义 vtkImageData 对象,然后指定图像的维数,而图像的原点和像素间隔则都是采用默认值,因此不需要设置。SetScalarTypeToUnsignedChar()指定图像的像素数据类型为 unsigned
char,SetNumberOfScalarComponents()则指定每个像素值的组分数为 1,即每个像素值为1个标量值。参数设置完毕,调用 AllocateScalars()分配内存,生成图像数据。图像生成后,默认所有像素值为 0。可以通过访问图像数据数组来设置每个像素值,vtkImageData:: GetScalarPointer()即返回图像的数据数组(图像数据数组采用一维数组,参考本书3.6节的内容),然后根据图像的大小,访问每个像素并为其赋值。

3、VTK图像显示

3.1 vtkImageViewer2

  VTK早期的版本提供了vtkImageViewer 类来显示图像,随着版本的发展,目前使用vtkImageViewer2来代替vtkImageViewer 实现图像的显示。vtkImageViewer2 中封装了VTK图像显示的可视化渲染引擎,包括vtkActor、vtkRender、vtkRenderWindow、vtkInteractorStypeImage等对象,可以方便地完成图像显示和交互。该类提供的主要交互操作有图像放缩、窗宽窗位调节,并提供切片选择及切片方向设置接口,尤其适合三维图像的切片显示。下述代码说明了使vtkImageViewer2显示图像的方法。

在这里插入图片描述

#include <iostream>#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);// VTK was built with vtkRenderingOpenGL2
VTK_MODULE_INIT(vtkInteractionStyle);#include <vtkSmartPointer.h>
#include <vtkImageViewer2.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkMetaImageReader.h>//测试图像:../data/brain.mhd
int main(int argc, char* argv[])
{std::string sFile = "../data/brain.mhd";vtkSmartPointer<vtkMetaImageReader> reader =vtkSmartPointer<vtkMetaImageReader>::New();reader->SetFileName(sFile.c_str());reader->Update();vtkSmartPointer<vtkImageViewer2> imageViewer =vtkSmartPointer<vtkImageViewer2>::New();imageViewer->SetInputConnection(reader->GetOutputPort());vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =vtkSmartPointer<vtkRenderWindowInteractor>::New();imageViewer->SetupInteractor(renderWindowInteractor);imageViewer->SetColorLevel(500);imageViewer->SetColorWindow(2000);imageViewer->SetSlice(40);imageViewer->SetSliceOrientationToXY();imageViewer->Render();imageViewer->GetRenderer()->SetBackground(1.0, 1.0, 1.0);imageViewer->SetSize(640, 480);imageViewer->GetRenderWindow()->SetWindowName("DisplayImageExample");renderWindowInteractor->Start();return EXIT_SUCCESS;
}

  这里为了更好地演示vtkImageViewer2的功能,以一幅三维医学图像为例进行说明。首先使用 vtkMetalmageReader 读入一个*.mhd 格式的图像,然后定义vtkImageViewer2 对象显示图像。为了实现鼠标、键盘消息响应,定义vtkRenderWindowInteractor 对象,并通过vtkImageViewer2::SetInteractor()设置交互对象。最后设置了窗位(ColorLevel)、窗宽(ColorWindow)、切片索引(Slice)和切片方向(Orientation)等参数。

  在渲染窗口中,按下鼠标左键拖动鼠标,可以调节图像的窗宽窗位,从而显示不同灰度范围内容;按下鼠标右键拖动鼠标可以缩放图像。当然,这些交互操作可以由用户根据需要自己定义vtkInteractorStyle子类,并响应相应的操作。

1.窗宽/窗位的概念
  窗宽是图像显示的灰度范围。一般显示器的灰度范围为256级,而医学图像的灰度范围则远远大于该范围,因此通过显示器显示时不能显示所有灰度级,需要使用窗宽来定义欲显示的灰度范围。当灰度值高于该范围的最大值时,均以白影显示;当低于该范围时,均以黑色显示。若增大窗宽,则显示具有不同灰度值的组织结构增多,但是会降低组织之间的对比度,若减小窗宽,则可视的不同灰度组织结构会减少,同时增大组织结构的对比度。

  窗位是窗宽的中心位置(见图5-5)。窗宽只是确定了CT图像灰度范围上的可视部分范围,还需要窗位来确定可视灰度范围的具体位置。同样的窗宽,会根据窗位的位置变化来显示不同的组织结构。比如,窗宽为200,当窗位为100时,可视灰度范围为0-200:当窗位为500时,可视灰度范围为400~600。当窗宽窗位确定以后,显示时底层会将可视灰度范围转换到256灰度级进行显示。
在这里插入图片描述

2.医学图像二维视图
  切片(Slice)或切面是三维图像比较常用的概念,尤其在医学图像中,不同方向的切面
都有特定的名字(见图5-6),分别是:矢状面(Sagital Plane),沿着身体前后径所做的与地面垂直的切面;冠状面(Coronal Plane),沿着身体左右径所做的与地面垂直的切面:横断面(Transverse/AxialPlane),是指横断身体与地面平行的切面。设置切片的方向即是通过不同的方向来观察人体内部组织结构。
在这里插入图片描述
  常见的医学图像可视化软件通常会提供四个视图用来显示图像:横断面视图、矢状面视图、冠状面视图和三维视图,例如图5-7所示的视图是来自德国癌症研究中心的MITK3M3医学图像处理软件。
在这里插入图片描述

  类vtkImageViewer2提供了SetSlice()函数来设置切片索引,SetSliceOrientationToXY()则将切片的方向设置为垂直XY平面方向。此外还可以设置为垂直YZ或者XZ平面方向,其对应函数分别为SetSliceOrientationToYZ(和 SetSliceOrientationToXZ()。默认情况下切片方向为垂直于XY平面即沿着乙轴方向,根据设置的切片索引获取乙轴方向的具体切片进行显示。

3.2 vtkImageActor

  vtkImageActor 是一个三维图像渲染Actor,通过纹理映射将图像映射到一个多边形上进行显示。使用vtkImageActor 较vtkImageViewer2 要复杂一些,需要建立完整的渲染管线:包括 vtkImageActor、vtkRender、vtkRenderWindow 和 vtkRenderWindowInteractor。另外,作为二
维图像浏览器,不需要在三维空间中进行旋转操作,因此还需要为vtkRenderWindowlInteractor
定义一个vtkInteractorStylelmage 对象。下例中使用vtkImageActor 建立图像渲染管线来显示图
像并实现交互功能。
在这里插入图片描述

#include <iostream>#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);// VTK was built with vtkRenderingOpenGL2
VTK_MODULE_INIT(vtkInteractionStyle);#include <vtkSmartPointer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkBMPReader.h>
#include <vtkImageActor.h>//测试图像:../data/lena.bmp
int main(int argc, char* argv[])
{std::string sFile = "../data/lena.bmp";vtkSmartPointer<vtkBMPReader> reader =vtkSmartPointer<vtkBMPReader>::New();reader->SetFileName(sFile.c_str());reader->Update();vtkSmartPointer<vtkImageActor> imgActor =vtkSmartPointer<vtkImageActor>::New();imgActor->SetInputData(reader->GetOutput());vtkSmartPointer<vtkRenderer> renderer =vtkSmartPointer<vtkRenderer>::New();renderer->AddActor(imgActor);renderer->SetBackground(1.0, 1.0, 1.0);vtkSmartPointer<vtkRenderWindow> renderWindow =vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(renderer);renderWindow->SetSize(640, 480);renderWindow->Render();renderWindow->SetWindowName("DisplayImageExample2");vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =vtkSmartPointer<vtkRenderWindowInteractor>::New();vtkSmartPointer<vtkInteractorStyleImage> style =vtkSmartPointer<vtkInteractorStyleImage>::New();renderWindowInteractor->SetInteractorStyle(style);renderWindowInteractor->SetRenderWindow(renderWindow);renderWindowInteractor->Initialize();renderWindowInteractor->Start();return EXIT_SUCCESS;
}

4、VTK图像融合

  前面介绍的两种显示图像的方法都是在窗口中显示一幅图像。在实际应用中经常需要在窗口中同时显示多幅图像,这就会用到图像融合技术。图像融合是利用图像的不透明度来合
成图像。在VTK中,用类vtkImageBlend实现图像的融合。vtkImageBlend 可以接收多个图像输入,其输出为融合图像。输出图像的像素间隔、原点、范围以及像素组分个数与第一个图像一致。该类提供了两种融合模式。

  第一种是标准模式,也是默认的融合方式。第二种是混合模式(Compound)。该模式下输出结果利用不透明度的和做归一化。当像素的不透明度 alpha*opacity 小于或等于阈值 threshold 时,该像素会被忽略。

在这里插入图片描述

#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkImageBlend.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkJPEGReader.h>
#include <vtkImageActor.h>
#include <vtkImageCast.h>#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);// VTK was built with vtkRenderingOpenGL2
VTK_MODULE_INIT(vtkInteractionStyle);//测试图像:../data/lena-gray.jpg
int main(int argc, char* argv[])
{std::string sFile = "../data/lena-gray.jpg";vtkSmartPointer<vtkJPEGReader> reader =vtkSmartPointer<vtkJPEGReader>::New();reader->SetFileName (sFile.c_str());reader->Update();vtkSmartPointer<vtkImageCanvasSource2D> imageSource = vtkSmartPointer<vtkImageCanvasSource2D>::New();imageSource->SetNumberOfScalarComponents(1);imageSource->SetScalarTypeToUnsignedChar();imageSource->SetExtent(0, 512, 0, 512, 0, 0);imageSource->SetDrawColor(0.0);imageSource->FillBox(0, 512, 0, 512);imageSource->SetDrawColor(255.0);imageSource->FillBox(100,400,100,400);imageSource->Update();vtkSmartPointer<vtkImageBlend> imageBlend = vtkSmartPointer<vtkImageBlend>::New();imageBlend->AddInputData(reader->GetOutput());imageBlend->AddInputData(imageSource->GetOutput());imageBlend->SetOpacity(0, 0.4);imageBlend->SetOpacity(1, 0.6);imageBlend->Update();// Create actorsvtkSmartPointer<vtkImageActor> originalActor1 =vtkSmartPointer<vtkImageActor>::New();originalActor1->SetInputData(reader->GetOutput());vtkSmartPointer<vtkImageActor> originalActor2 =vtkSmartPointer<vtkImageActor>::New();originalActor2->SetInputData(imageSource->GetOutput());vtkSmartPointer<vtkImageActor> blendActor =vtkSmartPointer<vtkImageActor>::New();blendActor->SetInputData(imageBlend->GetOutput());// Define viewport ranges// (xmin, ymin, xmax, ymax)double leftViewport[4] = {0.0, 0.0, 0.33, 1.0};double midViewport[4] = {0.33, 0.0, 0.66, 1.0};double rightViewport[4] = {0.66, 0.0, 1.0, 1.0};// Setup renderersvtkSmartPointer<vtkRenderer> originalRenderer1 =vtkSmartPointer<vtkRenderer>::New();originalRenderer1->SetViewport(leftViewport);originalRenderer1->AddActor(originalActor1);originalRenderer1->ResetCamera();originalRenderer1->SetBackground(1.0, 1.0, 1.0);vtkSmartPointer<vtkRenderer> originalRenderer2 =vtkSmartPointer<vtkRenderer>::New();originalRenderer2->SetViewport(midViewport);originalRenderer2->AddActor(originalActor2);originalRenderer2->ResetCamera();originalRenderer2->SetBackground(1.0, 1.0, 1.0);vtkSmartPointer<vtkRenderer> blendRenderer =vtkSmartPointer<vtkRenderer>::New();blendRenderer->SetViewport(rightViewport);blendRenderer->AddActor(blendActor);blendRenderer->ResetCamera();blendRenderer->SetBackground(1.0, 1.0, 1.0);vtkSmartPointer<vtkRenderWindow> renderWindow =vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(originalRenderer1);renderWindow->AddRenderer(originalRenderer2);renderWindow->AddRenderer(blendRenderer);renderWindow->SetSize( 640, 320 );renderWindow->Render();renderWindow->SetWindowName("ImageBlendExample");vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =vtkSmartPointer<vtkRenderWindowInteractor>::New();vtkSmartPointer<vtkInteractorStyleImage> style =vtkSmartPointer<vtkInteractorStyleImage>::New();renderWindowInteractor->SetInteractorStyle(style);renderWindowInteractor->SetRenderWindow(renderWindow);renderWindowInteractor->Initialize();renderWindowInteractor->Start();return EXIT_SUCCESS;
}

该示例中先读入一幅灰度图像,另外生成一个二值图像;然后定义了vtkImageBlend 对象,SetInput()函数设置两个图像作为输入。这里设置输入图像时,由于可以输入多个图像,因此需要给定图像的id号来设置输入。SetOpacity()函数用于设置对应id号的图像不透明度的大小,当不透明度为1.0时,为完全不透明。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com