一、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 actors
vtkSmartPointer<vtkImageActor> redActor = vtkSmartPointer<vtkImageActor>::New();
redActor->SetInputData(canvas->GetOutput());
// Setup renderers
vtkSmartPointer<vtkRenderer> redRenderer = vtkSmartPointer<vtkRenderer>::New();
redRenderer->AddActor(redActor);
redRenderer->ResetCamera();
redRenderer->SetBackground(1.0, 1.0, 1.0);
// Setup render window
vtkSmartPointer<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 actors
vtkSmartPointer<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 renderers
vtkSmartPointer<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();
}
运行效果: