一、VTK图像创建
1、VTK 图像数据结构
数字图像文件内容由两个部分组成:图像头信息和数据。图像头信息定义了图像的基本信息,主要包括起点位置(Origin)、像素间隔(Space)和维数(Dimension)。通过这三个参数即可确定图像空间位置和大小。
图像数据即为图像像素的像素值,一般采用一维数组来表示和存储。
2、VTK 图像创建
1)图像源 Source
VTK 中内置了多个创建图像的Source类,利用这些Source 类可以快速创建图像。以 vtkImageCanvasSource2D为例。该Source 类的功能是创建一个画布(空白图像),并提供了多种几何图形(点、线段、圆、矩形以及图像等)的绘制填充功能。
#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkGenericOpenGLRenderWindow.h>int main(int argc, char *argv[])
{QApplication a(argc, argv);vtkSmartPointer<vtkImageCanvasSource2D> canvas = vtkSmartPointer<vtkImageCanvasSource2D>::New();canvas->SetScalarTypeToUnsignedChar();canvas->SetNumberOfScalarComponents(1);canvas->SetExtent(0, 100, 0, 100, 0, 0);canvas->SetDrawColor(0, 0, 0, 0);canvas->FillBox(0,100,0,100);canvas->SetDrawColor(255, 0, 0, 0);canvas->FillBox(20,40,20,40);canvas->Update();// Create actorsvtkSmartPointer<vtkImageActor> redActor = vtkSmartPointer<vtkImageActor>::New();redActor->SetInputData(canvas->GetOutput());// Setup renderersvtkSmartPointer<vtkRenderer> redRenderer = vtkSmartPointer<vtkRenderer>::New();redRenderer->AddActor(redActor);redRenderer->ResetCamera();redRenderer->SetBackground(1.0, 1.0, 1.0);// Setup render windowvtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();renderWindow->AddRenderer(redRenderer);QVTKOpenGLNativeWidget w;w.setWindowTitle("ImageCanvasSource2D");w.setRenderWindow(renderWindow);w.show();return a.exec();
}
运行效果:
除了 vkImageCanvasSource2D外,VTK还提供了其他类似的Source 类来快速生成特定的图像,例如 vtkImageEllipsoidSource,该类根据指定的中心以及各个轴的半径来生成一个前景为椭圆(球)的二值图像;vtkImageGaussianSource 类生成一幅像素值服从高斯分布的图像;vtkImageGridSource用于生成网格线图像;vtkImageNoiseSource 生成一个像素值为随机数的噪声图像;vtkImageSinusoidSource生成的图像像素值由正弦函数决定 。
2) 直接创建图像
#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkInformation.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkGenericOpenGLRenderWindow.h>int main(int argc, char *argv[])
{QApplication a(argc, argv);vtkSmartPointer<vtkImageData> img = vtkSmartPointer<vtkImageData>::New();vtkSmartPointer<vtkInformation> info = vtkSmartPointer<vtkInformation>::New();img->SetDimensions(16,16,1);img->SetScalarType(VTK_UNSIGNED_CHAR,info);img->SetNumberOfScalarComponents(1,info);img->AllocateScalars(info);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);vtkSmartPointer<vtkRenderer> redRenderer = vtkSmartPointer<vtkRenderer>::New();redRenderer->AddActor(redActor);redRenderer->ResetCamera();redRenderer->SetBackground(1.0, 1.0, 1.0);vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();renderWindow->AddRenderer(redRenderer);QVTKOpenGLNativeWidget w;w.setWindowTitle("CreateVTKImageData");w.setRenderWindow(renderWindow);w.show();return a.exec();
}
首先定义vtklmageData指针,然后指定图像的维数,而图像的原点和像素间隔则都是采用默认值,因此不需要设置。SetScalarType指定图像的每个像素值的数据类型为unsigned char,SetNumberOfScalarComponents则指定了每个像素值的数据成分为1,每个像素值为1个标量值,参数设置完毕后,调用AllocateScalars()分配内存,生成图像数据。图像生成后,默认所有像素值为0。可以通过访问图像数据数组来设每个像素值GetScalarPointer()即返回图像的数据数组(图像数据数组都采用一维数组),然后根据图像的大小,访问每个像素并为其赋值。生成的图像如下所示:
二、VTK图像显示
1、vtklmageViewer2
vtklmageViewer2 中封装了 VTK 图像显示的可视化渲染引擎,包括 vtkActor、vtkRender、vtkRenderWindow、vtkInteractorStypelmage等对象,可以方便地完成图像显示和交互。该类提供的主要交互操作有图像放缩、窗宽窗位调节,并提供切片选择及切片方向设置接口,尤其适合三维图像的切片显示。
#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkMetaImageReader.h>
#include <vtkImageViewer2.h>
#include <vtkRenderer.h>
#include <QVTKOpenGLNativeWidget.h>int main(int argc, char *argv[])
{QApplication a(argc, argv);vtkSmartPointer<vtkMetaImageReader> reader = vtkSmartPointer<vtkMetaImageReader>::New();reader->SetFileName("D:/data/brain.mhd");reader->Update();vtkSmartPointer<vtkImageViewer2> imageViewer = vtkSmartPointer<vtkImageViewer2>::New();imageViewer->SetInputConnection(reader->GetOutputPort());imageViewer->GetRenderer()->SetBackground(1.0, 1.0, 1.0);QVTKOpenGLNativeWidget w;imageViewer->SetupInteractor(w.interactor()); //初始化交互器imageViewer->SetRenderWindow(w.renderWindow()); //初始化渲染窗口w.setWindowTitle("DisplayImageExample");w.show();imageViewer->SetColorLevel(500); //窗位imageViewer->SetColorWindow(2000); //窗宽imageViewer->SetSlice(40); //切片索引imageViewer->SetSliceOrientationToXY(); //切片方向return a.exec();
}
vtklmageViewer2显示三维图像的某个切片:
1)窗宽/窗位的概念
窗宽是图像显示的灰度范围。一般显示器的灰度范围为256级,而医学图像的灰度范围则远远大于该范围,因此通过显示器显示时不能显示所有灰度级,需要使用窗宽来定义欲显示的灰度范围。当灰度值高于该范围的最大值时,均以白影显示;当低于该范围时,均以黑色显示。若增大窗宽,则显示具有不同灰度值的组织结构增多,但是会降低组织之间的对比度,若减小窗宽,则可视的不同灰度组织结构会减少,同时增大组织结构的对比度。
2)医学图像二维视图
切片(Slice)或切面是三维图像比较常用的概念,尤其在医学图像中,不同方向的切面都有特定的名字,分别是:矢状面(SagitalPlane),沿着身体前后径所做的与地面垂直的切面;冠状面(CoronalPlane),沿着身体左右径所做的与地面垂直的切面;横断面(Transverse/AxialPlane),是指横断身体与地面平行的切面。
2、vtklmageActor
vtkImageActor 是一个三维图像渲染 Actor,通过纹理映射将图像映射到一个多边形上进行显示。
#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkBMPReader.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>int main(int argc, char *argv[])
{QApplication a(argc, argv);vtkSmartPointer<vtkBMPReader> reader = vtkSmartPointer<vtkBMPReader>::New();reader->SetFileName ("D:/data/lena.bmp");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<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();renderWindow->AddRenderer(renderer);QVTKOpenGLNativeWidget w;w.setRenderWindow(renderWindow);w.setWindowTitle("DisplayImageExample");w.show();return a.exec();
}
运行效果:
需要注意的是,vtkImageActor 接收的图像数据 vtkImageData像素类型必须为unsigned char,如果类型不符合要求,在显示图像前需要先将图像数据类型转换为unsigned char。
3、图像融合
图像融合是利用图像的不透明度来合成图像。在 VTK 中,用类 vtkImageBlend 实现图像的融合。 vtkmageBlend 可以接收多个图像输入,其输出为融合图像。输出图像的像素间隔、原点、范围以及像素组分个数与第一个图像一致。
#include <QApplication>
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkImageBlend.h>
#include <vtkRenderer.h>
#include <vtkJPEGReader.h>
#include <vtkImageActor.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <QVTKOpenGLNativeWidget.h>int main(int argc, char *argv[])
{QApplication a(argc, argv);vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();reader->SetFileName ("D:/data/lena-gray.jpg");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->AddActor(originalActor1);originalRenderer1->ResetCamera();originalRenderer1->SetBackground(1.0, 1.0, 1.0);originalRenderer1->SetViewport(leftViewport);vtkSmartPointer<vtkRenderer> originalRenderer2 = vtkSmartPointer<vtkRenderer>::New();originalRenderer2->AddActor(originalActor2);originalRenderer2->ResetCamera();originalRenderer2->SetBackground(1.0, 1.0, 1.0);originalRenderer2->SetViewport(midViewport);vtkSmartPointer<vtkRenderer> blendRenderer = vtkSmartPointer<vtkRenderer>::New();blendRenderer->AddActor(blendActor);blendRenderer->ResetCamera();blendRenderer->SetBackground(1.0, 1.0, 1.0);blendRenderer->SetViewport(rightViewport);vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();renderWindow->AddRenderer(originalRenderer1);renderWindow->AddRenderer(originalRenderer2);renderWindow->AddRenderer(blendRenderer);QVTKOpenGLNativeWidget w;w.resize(640,320);w.setRenderWindow(renderWindow);w.setWindowTitle("ImageBlendExample");w.show();return a.exec();
}
运行效果: