5.1 VTK图像创建
5.1.1 VTK图像数据结构
数字图像文件内容由两个部分组成:图像头信息和数据。
图像头信息定义了图像的基本信息:主要包括起点位置(Origin)、像素间隔(Space)和维度(Dimension)。
在医学图像中,起点位置、像素间隔和图像维度决定了世界坐标系。
图像数据即为图像像素的像素值,一般采用以为数组来表示和存储。
图像像素值可以是标量(灰度图像)、矢量(梯度图像)或张量(弥散张量成像,Diffusion Tensor Imaging,DTI)
在医学图像处理中,灰度范围大于256级。常见的医学图像的像素数据类型为unsigned short,灰度范围为0~65536。也会使用int、float或者double类型。
5.1.2 VTK图像创建
1. 图像源Source
VTK中内置了多个创建图像的Source类,利用这些Source类可以快速创建图像。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | ///****************************************************/ ///* Examples/Chap05/5.1_ImageCanvasSource2D.cpp */ ///****************************************************/ #include <vtkSmartPointer.h> #include <vtkImageCanvasSource2D.h> #include <vtkImageActor.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkInteractorStyleImage.h> int main() { // 生成图像序列的文件名数组 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()); // define viewport ranges // (xmin,ymin,xmax,ymax) double redViewport[4] = { 0.0,0.0,1.0,1.0 }; // setup renderers vtkSmartPointer<vtkRenderer> redRenderer = vtkSmartPointer<vtkRenderer>::New(); redRenderer->SetViewPoint(redViewport); redRenderer->AddActor(redActor); redRenderer->ResetCamera(); redRenderer->SetBackground(1.0, 1.0, 1.0); // setup render window vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(redRenderer); renderWindow->SetSize(640, 480); renderWindow->Render(); renderWindow->SetWindowName("ImageCanvasSource2D"); // Setup render window interactor vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New(); renderWindowInteractor->SetInteractorStyle(style); // Render and start interactor renderWindowInteractor->SetRenderWindow(renderWindow); renderWindowInteractor->Initialize(); renderWindowInteractor->Start(); return 0; } |
2. 直接创建图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | ///****************************************************/ ///* Examples/Chap05/5.1_CreateVTKImageData.cpp */ ///****************************************************/ #include <vtkSmartPointer.h> #include <vtkImageData.h> #include <vtkInformation.h> #include <vtkImageActor.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkInteractorStyleImage.h> #include <vtkImageViewer2.h> int main() { //vtk的新版本在vtkImageData类中取消了SetScalarTypeToUnsignedChar()方法; //现在仅能用如下方法设置: //static void SetScalarType(int, vtkInformation* meta_data); vtkSmartPointer<vtkImageData> img = vtkSmartPointer<vtkImageData>::New(); vtkSmartPointer<vtkInformation> info = vtkSmartPointer<vtkInformation>::New(); img->SetDimensions(10, 10, 10); img->SetScalarType(VTK_UNSIGNED_CHAR, info); img->SetNumberOfScalarComponents(1, info);//每个像素需要表示的组份 =1是指标量图 img->AllocateScalars(info);//很重要 分配内存,生成图像数据,图像生成后,默认所有像素值为0 unsigned char *ptr = (unsigned char*)img->GetScalarPointer(); for (int i = 0; i < 10*10*10; ++i) { *ptr++ = i % 256; } // Create actors vtkSmartPointer<vtkImageActor> redActor = vtkSmartPointer<vtkImageActor>::New(); redActor->SetInputData(img); // define viewport ranges // (xmin,ymin,xmax,ymax) double redViewport[4] = { 0.0,0.0,1.0,1.0 }; // setup renderers vtkSmartPointer<vtkRenderer> redRenderer = vtkSmartPointer<vtkRenderer>::New(); redRenderer->SetViewPoint(redViewport); redRenderer->AddActor(redActor); redRenderer->ResetCamera(); redRenderer->SetBackground(1.0, 1.0, 1.0); // setup render window vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(redRenderer); renderWindow->SetSize(640, 480); renderWindow->Render(); renderWindow->SetWindowName("ImageCanvasSource2D"); // Setup render window interactor vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New(); renderWindowInteractor->SetInteractorStyle(style); // Render and start interactor renderWindowInteractor->SetRenderWindow(renderWindow); renderWindowInteractor->Initialize(); renderWindowInteractor->Start(); return 0; } |
5.2 VTK图像显示
5.2.1 vtkImageViewer2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ///****************************************************/ ///* Examples/Chap05/5.2_DisplayImageExample.cpp */ ///****************************************************/ #include <vtkSmartPointer.h> #include <vtkMetaImageReader.h> #include <vtkImageViewer2.h> int main(int argn, char* argv[]) { vtkSmartPointer<vtkMetaImageReader> reader = vtkSmartPointer<vtkMetaImageReader>::New(); reader->SetFileName(argv[1]); reader->Update(); // 显示读取的单幅PNG图像 vtkSmartPointer<vtkImageViewer2> imageViewer = vtkSmartPointer<vtkImageViewer2>::New(); imageViewer->SetInputConnection(reader->GetOutputPort()); vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); imageViewer->SetColorLevel(500); imageViewer->SetColorWindow(2000); imageViewer->SetSlice(40); //默认显示第50个切片(即第50层) imageViewer->SetSliceOrientationToXY(); /*imageViewer->SetSliceOrientationToYZ(); imageViewer->SetSliceOrientationToXZ();*/ imageViewer->SetupInteractor(interactor); imageViewer->Render(); interactor->Start(); return EXIT_SUCCESS; } |
1. 窗宽/窗位的概念
a)窗宽是图像显示的灰度范围。
一般显示器的灰度范围为256级,医学图像的灰度范围远大于该范围。
显示器不能显示所有灰度级,通过窗宽来定义欲显示的灰度范围。当灰度值高于该范围最大值,均以白影显示;低于该范围时,均以黑影显示。
增大窗宽:不同灰度值的组织结构增多,降低组织之间的对比度
减小窗宽:不同灰度值的组织结构减少,增大组织之间的对比度
b)窗位是窗宽的中心位置。
窗宽确定CT图像灰度范围上的可视部分范围,窗位确定可视灰度范围的具体位置。
窗宽窗位确定后,显示时底层会将可视灰度范围转换到256灰度级进行显示。
2.医学图像二维视图
切片或切面是三维图像比较常用的概念。医学图像中,不同方向的切面有特定的名字。
矢状面(Sagital Plane):沿着身体前后所做的与地面垂直的切面;
冠状面(Coronal Plane):沿着身体左右所做的与地面垂直的切面;
横断面(Transverse/Axial Plane),是指横断身体与地面平行的切面。
设置切片的方向是通过不同的方向来观察人体的内部组织结构。
5.2.2 vtkImageActor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | #include <vtkSmartPointer.h> #include <vtkBMPReader.h> #include <vtkImageActor.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkInteractorStyleImage.h> int main(int argn,char* argv[]) { vtkSmartPointer<vtkBMPReader> reader = vtkSmartPointer<vtkBMPReader>::New(); reader->SetFileName(argv[1]); reader->Update(); // Create actors vtkSmartPointer<vtkImageActor> imgActor = vtkSmartPointer<vtkImageActor>::New(); imgActor->SetInputData(reader->GetOutput()); // setup renderers vtkSmartPointer<vtkRenderer> redRenderer = vtkSmartPointer<vtkRenderer>::New(); redRenderer->AddActor(imgActor); redRenderer->ResetCamera(); redRenderer->SetBackground(.4, .5, .6); // setup render window vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(redRenderer); renderWindow->SetSize(500, 500); // Setup render window interactor vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New(); renderWindowInteractor->SetInteractorStyle(style); // Render and start interactor renderWindowInteractor->SetRenderWindow(renderWindow); renderWindowInteractor->Initialize(); renderWindowInteractor->Start(); return EXIT_SUCCESS; } |
注意:vtkImageActor接收的图像数据vtkImageData像素类型必须为unsigned char,如果类型不符合要求,在显示图像前需要先将图像数据类型转换为unsigned char.
5.2.3 图像融合
在实际应用中经常需要在窗口中同时显示多幅图像,用到图像融合技术。
图像融合是利用图像的不透明度来合成图像,VTK中,用类vtkImageBlend实现图像的融合。
经测试源码不能调用 SetInputData(id, vtkImageData)
结果会报错,报错如下:
1 | vtkCompositeDataPipeline (004A4880): Input for connection on index 0 input port index 1 for algorithm vtkImageBlend(004A1958) is of type vtkImageData, but a vtkImageStencilData is required. |
当vtkImageBlend的inputData大于2个对象时,需要通过AddInputData来设置InputData,demo如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | ///****************************************************/ ///* Examples/Chap05/5.2_ImageBlendExample.cpp */ ///****************************************************/ #include <vtkSmartPointer.h> #include <vtkJPEGReader.h> #include <vtkImageCanvasSource2D.h> #include <vtkImageBlend.h> #include <vtkImageData.h> #include <vtkImageActor.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkInteractorStyleImage.h> int main(int argn, char* argv[]) { // 读入灰度图像 vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New(); reader->SetFileName("D://1.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()); // 记得 #include <vtkImageData.h> imageBlend->AddInputData(imageSource->GetOutput()); // 不能使用SetInputData会报错 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->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); // setup render window vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(originalRenderer1); renderWindow->AddRenderer(originalRenderer2); renderWindow->AddRenderer(blendRenderer); renderWindow->SetSize(640, 320); renderWindow->Render(); renderWindow->SetWindowName("ImageBlendExample"); // Setup render window interactor vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New(); renderWindowInteractor->SetInteractorStyle(style); // Render and start interactor renderWindowInteractor->SetRenderWindow(renderWindow); renderWindowInteractor->Initialize(); renderWindowInteractor->Start(); return EXIT_SUCCESS; } |