首页 > 其他 > 详细

vtkImageViewer2 解析

时间:2021-01-17 10:13:24      阅读:73      评论:0      收藏:0      [点我收藏+]

       本文代码版本:VTK-8.2.

        vtkImageViewer2 是一个非常实用的类,它简化了我们通过管线处理数据的工作, 因为它内部封装了一些对象,并将它们连接成管线, 这些对象有 vtkRendererWindow, vtkRenderer, vtkImageActor 和 vtkImageMapToWindowLevelColors。 vtkImageViewer2 天然地提供了一些图像的基本功能——渲染,绽放,平移以及翻层, 同时也支持在XY, YZ 或 XZ 不同方向上进行切换以显示不同的切片。因此,研究这个类对我们理解和学习 VTK 的图像处理非常有帮助。

  当 vtkImageViewer2 被构造时,它将对上面所列举的对像进行初始化。

vtkImageViewer2::vtkImageViewer2()
{
  this->RenderWindow    = nullptr;  
  this->Renderer        = nullptr;  
  this->ImageActor      = vtkImageActor::New();
  this->WindowLevel     = vtkImageMapToWindowLevelColors::New();
  this->Interactor      = nullptr;
  this->InteractorStyle = nullptr;

  this->Slice = 0;
  this->FirstRender = 1;
  this->SliceOrientation = vtkImageViewer2::SLICE_ORIENTATION_XY;

  // Setup the pipeline

  vtkRenderWindow *renwin = vtkRenderWindow::New();
  this->SetRenderWindow(renwin);
  renwin->Delete();

  vtkRenderer *ren = vtkRenderer::New();
  this->SetRenderer(ren);
  ren->Delete();

  this->InstallPipeline();
}

        在上面的代码中的最后一行,对数据管线进行初始化, 下面是初始化代码。

 1 void vtkImageViewer2::InstallPipeline()
 2 {
 3   if (this->RenderWindow && this->Renderer)
 4   {
 5     this->RenderWindow->AddRenderer(this->Renderer);
 6   }
 7 
 8   if (this->Interactor)
 9   {
10     if (!this->InteractorStyle)
11     {
12       this->InteractorStyle = vtkInteractorStyleImage::New();
13       vtkImageViewer2Callback *cbk = vtkImageViewer2Callback::New();
14       cbk->IV = this;
15       this->InteractorStyle->AddObserver(

16 vtkCommand::WindowLevelEvent, cbk);
17       this->InteractorStyle->AddObserver(
18         vtkCommand::StartWindowLevelEvent, cbk);
19       this->InteractorStyle->AddObserver(
20         vtkCommand::ResetWindowLevelEvent, cbk);
21       cbk->Delete();
22     }
23 
24     this->Interactor->SetInteractorStyle(this->InteractorStyle);
25     this->Interactor->SetRenderWindow(this->RenderWindow);
26   }
27 
28   if (this->Renderer && this->ImageActor)
29   {
30     this->Renderer->AddViewProp(this->ImageActor);
31   }
32 
33   if (this->ImageActor && this->WindowLevel)
34   {
35     this->ImageActor->GetMapper()->SetInputConnection(
36       this->WindowLevel->GetOutputPort());
37   }
38 }

        在上面对数据管线初始化代码中,对WindowLevelEvent、StartWindowLevelEvent、ResetWindowLevelEvent 三个事件进行了监听。至此,数据管线建立完毕。 我们下面看一看各项功能,vtkImageViewer2 是如何实现的。首先要看的就是 SetInputData 函数。

1 void vtkImageViewer2::SetInputData(vtkImageData *in)
2 {
3   this->WindowLevel->SetInputData(in);
4   this->UpdateDisplayExtent();
5 }

        在SetInputData 函数中, 首先将输入的 vtkImageData 传给 WindowLevel 对象(它是vtkImageMapToWindowLevelColors的实例), 然后调用 UpdateDisplayExtent () 来更新显示范围。下面就看一看在 UpdateDisplayExtent 函数中,vtkImageViewer2 做了些什么。

 1 void vtkImageViewer2::UpdateDisplayExtent()
 2 {
 3   vtkAlgorithm *input = this->GetInputAlgorithm();
 4   if (!input || !this->ImageActor)
 5   {
 6     return;
 7   }
 8 
 9   input->UpdateInformation();
10   vtkInformation* outInfo = input->GetOutputInformation(0);
11   int *w_ext = outInfo->Get(
12     vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
13 
14   // Is the slice in range ? If not, fix it
15 
16   int slice_min = w_ext[this->SliceOrientation * 2];
17   int slice_max = w_ext[this->SliceOrientation * 2 + 1];
18   if (this->Slice < slice_min || this->Slice > slice_max)
19   {
20     this->Slice = static_cast<int>((slice_min + slice_max) * 0.5);
21   }
22 
23   // Set the image actor
24 
25   switch (this->SliceOrientation)
26   {
27     case vtkImageViewer2::SLICE_ORIENTATION_XY:
28       this->ImageActor->SetDisplayExtent(
29         w_ext[0], w_ext[1], w_ext[2], w_ext[3], this->Slice, this->Slice);
30       break;
31 
32     case vtkImageViewer2::SLICE_ORIENTATION_XZ:
33       this->ImageActor->SetDisplayExtent(
34         w_ext[0], w_ext[1], this->Slice, this->Slice, w_ext[4], w_ext[5]);
35       break;
36 
37     case vtkImageViewer2::SLICE_ORIENTATION_YZ:
38       this->ImageActor->SetDisplayExtent(
39         this->Slice, this->Slice, w_ext[2], w_ext[3], w_ext[4], w_ext[5]);
40       break;
41   }
42 
43   // Figure out the correct clipping range
44 
45   if (this->Renderer)
46   {
47     if (this->InteractorStyle &&
48         this->InteractorStyle->GetAutoAdjustCameraClippingRange())
49     {
50       this->Renderer->ResetCameraClippingRange();
51     }
52     else
53     {
54       vtkCamera *cam = this->Renderer->GetActiveCamera();
55       if (cam)
56       {
57         double bounds[6];
58         this->ImageActor->GetBounds(bounds);
59         double spos = bounds[this->SliceOrientation * 2];
60         double cpos = cam->GetPosition()[this->SliceOrientation];
61         double range = fabs(spos - cpos);
62         double *spacing = outInfo->Get(vtkDataObject::SPACING());
63         double avg_spacing =
64           (spacing[0] + spacing[1] + spacing[2]) / 3.0;
65         cam->SetClippingRange(
66           range - avg_spacing * 3.0, range + avg_spacing * 3.0);
67       }
68     }
69   }
70 }

        这个函数的实现代码中有两行注释, 正是这两行注释将这个函数的代码分成了三个部分。 第一部分代码的功能是根据新传入的 vtkImageData 和显示的切片方向(SliceOrientation变量)来确定切片范围,并默认将切片位置设置为中间位置。第二部分代码的功能是为 ImageActor 设置显示范围。这两部分代码确定了要显示的数据在 vtkImageData 中的位置与范围。剩下的就是调整摄像机的位置与裁切范围,将要显示的数据显示出来,而这就是第三部分代码的功能。至此我们设置的数据就显示出来了。

  我们可以通过调用 SetSliceOrientationToXY (), SetSliceOrientationToXZ() 和 SetSliceOrientationToYZ() 来显示不同方向的切片图像。而这三个函数都是使用恰当的参数在其内部调用 SetSliceOrientation 来实现的。 下面展示的是 SetSliceOrientation 的代码, 看看它做了些什么。

 1 void vtkImageViewer2::SetSliceOrientation(int orientation)
 2 {
 3   if (orientation < vtkImageViewer2::SLICE_ORIENTATION_YZ ||
 4       orientation > vtkImageViewer2::SLICE_ORIENTATION_XY)
 5   {
 6     vtkErrorMacro("Error - invalid slice orientation " << orientation);
 7     return;
 8   }
 9 
10   if (this->SliceOrientation == orientation)
11   {
12     return;
13   }
14 
15   this->SliceOrientation = orientation;
16 
17   // Update the viewer
18 
19   int *range = this->GetSliceRange();
20   if (range)
21   {
22     this->Slice = static_cast<int>((range[0] + range[1]) * 0.5);
23   }
24 
25   this->UpdateOrientation();
26   this->UpdateDisplayExtent();
27 
28   if (this->Renderer && this->GetInput())
29   {
30     double scale = this->Renderer->GetActiveCamera()->GetParallelScale();
31     this->Renderer->ResetCamera();
32     this->Renderer->GetActiveCamera()->SetParallelScale(scale);
33   }
34 
35   this->Render();
36 }

        在 SetSliceOrientation 函数中首先使用成员变量保存了参数 orientation 的值, 然后调用 UpdateOrientation () 来更新显示的切片方向,并更新显示范围。 最后将显示的比例调整到显示上一个切片方向时所使用的比例。UpdataDisplayExtent 函数, 我们在上面已经解析过了,所以下面重点观察一下 UpdateOrientation () 函数的实现。

 1 void vtkImageViewer2::UpdateOrientation()
 2 {
 3   // Set the camera position
 4 
 5   vtkCamera *cam = this->Renderer ? this->Renderer->GetActiveCamera() : nullptr;
 6   if (cam)
 7   {
 8     switch (this->SliceOrientation)
 9     {
10       case vtkImageViewer2::SLICE_ORIENTATION_XY:
11         cam->SetFocalPoint(0,0,0);
12         cam->SetPosition(0,0,1); // -1 if medical ?
13         cam->SetViewUp(0,1,0);
14         break;
15 
16       case vtkImageViewer2::SLICE_ORIENTATION_XZ:
17         cam->SetFocalPoint(0,0,0);
18         cam->SetPosition(0,-1,0); // 1 if medical ?
19         cam->SetViewUp(0,0,1);
20         break;
21 
22       case vtkImageViewer2::SLICE_ORIENTATION_YZ:
23         cam->SetFocalPoint(0,0,0);
24         cam->SetPosition(1,0,0); // -1 if medical ?
25         cam->SetViewUp(0,0,1);
26         break;
27     }
28   }
29 }

        在 UpdateSliceOrientation 函数中,它只是根据不同的切片方向,来调整摄像机而已。 首先调整的是摄像机的焦点,然后是位置,最后是向上方向,就完成了对切片方向的改变。在不同的方向上,我们可以调用 SetSlice () 函数来显示在视线方向上不同位置的切片,以下是SetSlice() 的实现方法。

 1 void vtkImageViewer2::SetSlice(int slice)
 2 {
 3   int *range = this->GetSliceRange();
 4   if (range)
 5   {
 6     if (slice < range[0])
 7     {
 8       slice = range[0];
 9     }
10     else if (slice > range[1])
11     {
12       slice = range[1];
13     }
14   }
15 
16   if (this->Slice == slice)
17   {
18     return;
19   }
20 
21   this->Slice = slice;
22   this->Modified();
23 
24   this->UpdateDisplayExtent();
25   this->Render();
26 }

        在 SetSlice() 函数中,首先确定了 参数传入的切片位置位于 Slice 的合理范围之内,并将其存入成员变量 Slice 中, 然后调用 UpdateDisplayExtent () 函数应用新的切片位置,最后更新显示范围和渲染。 

 

       至此,关于vtkImageViewer2 的主要功能全部介绍完毕。 

 

vtkImageViewer2 解析

原文:https://www.cnblogs.com/biaohuang/p/14287854.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!