1. 程式人生 > >VTK教程系列:VTK基礎及應用開發教程

VTK教程系列:VTK基礎及應用開發教程

5.7 區域提取

5.7.1 提取感興趣區域 

感興趣區域(Volum of Interest)是指影象內部的一個子區域。在VTK中vtkExtractVOI類實現由使用者指定的區域範圍提取影象的子影象。該Filter的輸入和輸出都是一個vtkImageData,因此其結果可以直接作為影象儲存。

   1:      vtkSmartPointer<vtkBMPReader> reader =

   2:          vtkSmartPointer<vtkBMPReader>::New();

   3:      reader->SetFileName ( "lena.bmp" );

   4:      reader->Update();

   5:  

   6:      int dims[3];

   7:      reader->GetOutput()->GetDimensions(dims);

   8:  

   9:      vtkSmartPointer<vtkExtractVOI> extractVOI =

  10:          vtkSmartPointer<vtkExtractVOI>::New();

  11:      extractVOI->SetInputConnection(reader->GetOutputPort());

  12:      extractVOI->SetVOI(dims[0]/4.,3.*dims[0]/4.,dims[1]/4.,3.*dims[1]/4., 0, 0);

  13:      extractVOI->Update();

上例程式碼實現了提取一副影象的子區域。首先讀取一個影象,並獲取影象的維數。然後定義vtkExtractVOI物件,該物件接收兩個輸入一個是影象資料,第二個是區域大小。設定區域大小的函式原型:

void SetVOI(int _arg1, int _arg2, int _arg3, int _arg4, int _arg5, int _arg6)

void SetVOI(int _arg[])

其引數是提取的區域各個方向的大小,共6個引數,依次表示x方向最小值,x方向最大值,y方向最小值,y方向最大值,z方向最小值和z方向最大值。上例中由於讀取的是二維影象,因此z方向的區域為[0,0],而在x方向範圍為[ dims[0]/4 , 3*dims[0]/4 ],y方向範圍為[ dims[1]/4 , 3*dims[1]/4 ],即提取影象原圖中間1/4影象。執行結果如下:

 

圖5.18 提取感興趣區域

5.7.2 三維影象切片提取

切片是指三維影象中的一個切面對應的影象。切面可以是過影象內部一點且平行於XY、YZ、XZ平面的平面,也可以是任意的過三維影象內部一點任意方向的平面。通過提取切片可以方便的瀏覽和分析影象內部組織結構,是醫學影象瀏覽軟體中的一個重要的功能。在VTK中vtkImageReslice類實現影象切片提取功能。下面首先看一段切片提取的程式碼。

1:  vtkSmartPointer<vtkMetaImageReader> reader =

   2:     vtkSmartPointer<vtkMetaImageReader>::New();

   3:  reader->SetFileName ( " brain.mhd" );

   4:  reader->Update();

   5:   

   6:  int extent[6];

   7:  double spacing[3];

   8:  double origin[3];

   9:   

  10:  reader->GetOutput()->GetExtent(extent);

  11:  reader->GetOutput()->GetSpacing(spacing);

  12:  reader->GetOutput()->GetOrigin(origin);

  13:   

  14:  double center[3];

  15:  center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]);

  16:  center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]);

  17:  center[2] = origin[2] + spacing[2] * 0.5 * (extent[4] + extent[5]);

  18:   

  19:  static double axialElements[16] = {

  20:     1, 0, 0, 0,

  21:     0, 1, 0, 0,

  22:     0, 0, 1, 0,

  23:     0, 0, 0, 1 };

  24:   

  25:  vtkSmartPointer<vtkMatrix4x4> resliceAxes =

  26:     vtkSmartPointer<vtkMatrix4x4>::New();

  27:  resliceAxes->DeepCopy(axialElements);

  28:   

  29:  resliceAxes->SetElement(0, 3, center[0]);

  30:  resliceAxes->SetElement(1, 3, center[1]);

  31:  resliceAxes->SetElement(2, 3, center[2]);

  32:   

  33:   

  34:  vtkSmartPointer<vtkImageReslice> reslice =

  35:     vtkSmartPointer<vtkImageReslice>::New();

  36:  reslice->SetInputConnection(reader->GetOutputPort());

  37:  reslice->SetOutputDimensionality(2);

  38:  reslice->SetResliceAxes(resliceAxes);

  39:  reslice->SetInterpolationModeToLinear();

  40:   

  41:  vtkSmartPointer<vtkLookupTable> colorTable =

  42:     vtkSmartPointer<vtkLookupTable>::New();

  43:  colorTable->SetRange(0, 1000);

  44:  colorTable->SetValueRange(0.0, 1.0);

  45:  colorTable->SetSaturationRange(0.0, 0.0);

  46:  colorTable->SetRampToLinear();

  47:  colorTable->Build();

  48:   

  49:  vtkSmartPointer<vtkImageMapToColors> colorMap =

  50:     vtkSmartPointer<vtkImageMapToColors>::New();

  51:  colorMap->SetLookupTable(colorTable);

  52:  colorMap->SetInputConnection(reslice->GetOutputPort());

  53:   

  54:  vtkSmartPointer<vtkImageActor> imgActor =

  55:     vtkSmartPointer<vtkImageActor>::New();

  56:  imgActor->SetInput(colorMap->GetOutput());

  57:   

  58:  vtkSmartPointer<vtkRenderer> renderer =

  59:     vtkSmartPointer<vtkRenderer>::New();

  60:  renderer->AddActor(imgActor);

  61:  renderer->SetBackground(.4, .5, .6);

  62:   

  63:  vtkSmartPointer<vtkRenderWindow> renderWindow =

  64:     vtkSmartPointer<vtkRenderWindow>::New();

  65:  renderWindow->SetSize(500, 500);

  66:  renderWindow->AddRenderer(renderer);

  67:   

  68:  vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =

  69:     vtkSmartPointer<vtkRenderWindowInteractor>::New();

  70:  vtkSmartPointer<vtkInteractorStyleImage> imagestyle =

  71:     vtkSmartPointer<vtkInteractorStyleImage>::New();

  72:   

  73:  renderWindowInteractor->SetInteractorStyle(imagestyle);

  74:  renderWindowInteractor->SetRenderWindow(renderWindow);

  75:  renderWindowInteractor->Initialize();

  76:   

  77:  renderWindowInteractor->Start();

首先通過vtkMetaImageReader讀取一副醫學三維影象,並獲取得到影象範圍(extent),原點和畫素間隔;由這三個引數可以計算影象的中心位置center;接下來定義了切面的變換矩陣axialElements,該矩陣的前三列分別表示x、y和z方向向量,第四列為中心點座標;程式碼中的axialElements表示切面變換矩陣與當前座標系一致,且切面為過中心點center,並平行於XY平面的平面。當前,定義該切面時,也可以是其他平面,甚至是任意平面,但是必須要過影象內部點。下面給出了一個常用的變換矩陣:

static double coronalElements[16] = {

 1, 0, 0, 0,

 0, 0, 1, 0,

0,-1, 0, 0,

 0, 0, 0, 1 }; 提取平行於XZ平面的切片

static double sagittalElements[16] = {

 0, 0,-1, 0,

 1, 0, 0, 0,

 0,-1, 0, 0,

 0, 0, 0, 1 }; 提取平行於YZ平面的切片

static double obliqueElements[16] = {

 1, 0, 0, 0,

 0, 0.866025, -0.5, 0,

 0, 0.5, 0.866025, 0,

 0, 0, 0, 1 }; 提取斜切切片

注意使用這些變換矩陣的時候,需要將第四列替換為切片經過影象的一個點座標,上例中將影象的中心新增到axialElements矩陣,並通過函式SetResliceAxes設定變換矩陣,SetOutputDimensionality(2)指定輸出的影象為一個二維影象;而函式SetInterpolationModeToLinear()則指定了切面提取中的差值方式為線性差值,另外該類中還提供了其他的差值方式:

SetInterpolationModeToNearestNeighbor():最近鄰方式

SetInterpolationModeToCubic():三次線性差值

設定完畢後,執行Update()即可完成切面計算。執行結果如下圖:

 

圖5.19 切片提取

5.7.3 擴充套件

學習三維影象切面的提取後,我們在上節的程式上做一個擴充套件,實現一個稍微複雜的程式——通過滑動滑鼠來切換三維影象切片,這也是醫學影象處理軟體中一個很基本的功能。實現該功能難點是怎樣在VTK中控制滑鼠來實時提取影象切片。在前面的章節中已經介紹觀察者/命令(Observer/Command)模式,我們也採用這種機制來實現。VTK中滑鼠訊息是在互動型別物件(interactorstyle)中響應,因此通過為互動型別物件(interactorstyle)新增觀察者(observer)來監聽相應的訊息,當訊息觸發時,由命令模式執行相應的回撥函式。閒話少說,放程式碼。

1:  class vtkImageInteractionCallback : public vtkCommand

   2:  {

   3:  public:

   4:   

   5:      static vtkImageInteractionCallback *New()

   6:      {

   7:          return new vtkImageInteractionCallback;

   8:      }

   9:   

  10:      vtkImageInteractionCallback()

  11:      {

  12:          this->Slicing = 0;

  13:          this->ImageReslice = 0;

  14:          this->Interactor = 0;

  15:      }

  16:   

  17:      void SetImageReslice(vtkImageReslice *reslice)

  18:      {

  19:          this->ImageReslice = reslice;

  20:      }

  21:   

  22:      vtkImageReslice *GetImageReslice()

  23:      {

  24:          return this->ImageReslice;

  25:      }

  26:   

  27:      void SetInteractor(vtkRenderWindowInteractor *interactor)

  28:      {

  29:          this->Interactor = interactor;

  30:      }

  31:   

  32:      vtkRenderWindowInteractor *GetInteractor()

  33:      {

  34:          return this->Interactor;

  35:      }

  36:   

  37:      virtual void Execute(vtkObject *, unsigned long event, void *)

  38:      {

  39:          vtkRenderWindowInteractor *interactor = this->GetInteractor();

  40:   

  41:          int lastPos[2];

  42:          interactor->GetLastEventPosition(lastPos);

  43:          int currPos[2];

  44:          interactor->GetEventPosition(currPos);

  45:   

  46:          if (event == vtkCommand::LeftButtonPressEvent)

  47:          {

  48:              this->Slicing = 1;

  49:          }

  50:          else if (event == vtkCommand::LeftButtonReleaseEvent)

  51:          {

  52:              this->Slicing = 0;

  53:          }

  54:          else if (event == vtkCommand::MouseMoveEvent)

  55:          {

  56:              if (this->Slicing)

  57:              {

  58:                  vtkImageReslice *reslice = this->ImageReslice;

  59:   

  60:                  // Increment slice position by deltaY of mouse

  61:                  int deltaY = lastPos[1] - currPos[1];

  62:   

  63:                  reslice->Update();

  64:                  double sliceSpacing = reslice->GetOutput()->GetSpacing()[2];

  65:                  vtkMatrix4x4 *matrix = reslice->GetResliceAxes();

  66:                  // move the center point that we are slicing through

  67:                  double point[4];

  68:                  double center[4];

  69:                  point[0] = 0.0;

  70:                  point[1] = 0.0;

  71:                  point[2] = sliceSpacing * deltaY;

  72:                  point[3] = 1.0;

  73:                  matrix->MultiplyPoint(point, center);

  74:                  matrix->SetElement(0, 3, center[0]);

  75:                  matrix->SetElement(1, 3, center[1]);

  76:                  matrix->SetElement(2, 3, center[2]);

  77:                  interactor->Render();

  78:              }

  79:              else

  80:              {

  81:                  vtkInteractorStyle *style = vtkInteractorStyle::SafeDownCast(

  82:                      interactor->GetInteractorStyle());

  83:                  if (style)

  84:                  {

  85:                      style->OnMouseMove();

  86:                  }

  87:              }

  88:          }

  89:      }

  90:   

  91:  private:

  92:      int Slicing;

  93:      vtkImageReslice *ImageReslice;

  94:      vtkRenderWindowInteractor *Interactor;

  95:  };

vtkImageInteractionCallback繼承自vtkCommand類,並覆蓋父類函式Execute()。該類提供了兩個介面:SetImageReslice和SetInteractor。SetImageReslice用以設定vtkImageSlice物件,vtkImageSlice根據設定的變換矩陣提取三維影象切片。SetInteractor用以設定vtkRenderWindowInteractor,vtkRenderWindowInteractor類物件負責每次提取切片後重新整理檢視。

下面我們重點來看一下Execute函式,該函式提供了具體的切片提取功能。在該函式裡面,主要監聽了三個訊息:

vtkCommand::LeftButtonPressEvent,

vtkCommand::LeftButtonReleaseEvent,

vtkCommand::MouseMoveEvent,

前兩個訊息分別是滑鼠左鍵的按下和彈起訊息。當滑鼠左鍵按下時,就設定切片提取標誌為1,而當彈起時,將標誌置為0。這樣在滑鼠移動時,只有在確定切片提取標誌為1時,執行切片提取功能。

vtkCommand::MouseMoveEvent即為滑鼠移動訊息。當檢測到該訊息時,首先檢查切片提取標誌,當為1時提取切片。提取切片時,需要為vtkImageSlice物件設定變換矩陣。這裡在函式開始時,首先獲取了滑鼠滑動的前後兩次點的位置lastPos和currPos。然後根據兩點的Y座標差deltaY,計算新的中心點center並變換至vtkImageSlice當前變換矩陣中,得到變換中心點,將其設定到原來的變換矩陣matrix中,並設定到vtkImageSlice中,最後執行interactor->Render()即可不斷的根據滑鼠移動重新整理影象。

Command物件定義完畢後,即可為互動物件InteractorStyle新增觀察者,響應滑鼠訊息。這裡可以在上節的程式上進行修改,前面程式碼一致,只需要在最後新增如下程式碼:

1:      vtkSmartPointer<vtkImageInteractionCallback> callback =

   2:          vtkSmartPointer<vtkImageInteractionCallback>::New();

   3:      callback->SetImageReslice(reslice);

   4:      callback->SetInteractor(renderWindowInteractor);

   5:  

   6:      imagestyle->AddObserver(vtkCommand::MouseMoveEvent, callback);

   7:      imagestyle->AddObserver(vtkCommand::LeftButtonPressEvent, callback);

   8:      imagestyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, callback);

   9:  

  10:      renderWindowInteractor->Start();

這裡主要是定義了vtkImageInteractionCallback物件,並設定vtkImageSlice物件和vtkRenderWindowInteractor物件。然後為互動物件vtkInteractorStyle新增觀察者來監控相應的訊息,這裡主要是三個訊息:

vtkCommand::LeftButtonPressEvent,

vtkCommand::LeftButtonReleaseEvent,

vtkCommand::MouseMoveEvent,

當響應到這三個訊息時,立即執行vtkImageInteractionCallback的Execute函式,以便實現切片的實時提取和更新。完成以後,執行程式,當滑鼠在影象上移動時,會發現影象會跟著滑鼠的移動而變化,神奇吧?有興趣的話,還可以實現YZ平面、XZ平面切片提取,甚至是任意方向的切面提取。

==========歡迎轉載,轉載時請保留該宣告資訊==========

版權歸所有,更多資訊請訪問東靈工作室