VTK 圖像基本操作_三維圖像切片交互提取(回調函數、觀察者-命令模式)


1.鼠標滑動提取三維圖像切片

學習三維圖像切面的提取后,我們可以實現一個稍微復雜的程序——通過滑動鼠標來切換三維圖像切片,這也是醫學圖像處理軟件中一個很基本的功能。實現該功能難點是怎樣在VTK中控制鼠標來實時提取圖像切片。我們采用觀察者/命令(Observer/Command)模式機制來實現。
VTK中鼠標消息是在交互類型對象(interactorstyle)中響應,因此通過為交互類型對象(interactorstyle)添加觀察者(observer)來監聽相應的消息,當消息觸發時,由命令模式執行相應的回調函數。
代碼設計如下:
 1 #include <vtkAutoInit.h>
 2 VTK_MODULE_INIT(vtkRenderingOpenGL);  3  
 4 #include <vtkSmartPointer.h>
 5 #include <vtkMetaImageReader.h>
 6 #include <vtkMatrix4x4.h>
 7 #include <vtkLookupTable.h>
 8 #include <vtkImageMapToColors.h>
 9 #include <vtkImageActor.h>
 10 #include <vtkRenderer.h>
 11 #include <vtkRenderWindow.h>
 12 #include <vtkRenderWindowInteractor.h>
 13 #include <vtkInteractorStyleImage.h>
 14  
 15 #include <vtkCommand.h> //建立“觀察者/命令”模式監聽鼠標消息 完成交互
 16 #include <vtkImageReslice.h>
 17 #include <vtkImageData.h>
 18 class vtkImageInteractionCallback : public vtkCommand  19 {  20 public:  21     static vtkImageInteractionCallback *New() //回調函數初始化函數
 22  {  23         return new vtkImageInteractionCallback;  24  }  25  vtkImageInteractionCallback()  26  {  27         this->Slicing = 0;  28         this->ImageReslice = 0;  29         this->Interactor = 0;  30  }  31     void SetImageReslice(vtkImageReslice *reslice)  32  {  33         this->ImageReslice = reslice;  34  }  35     vtkImageReslice *GetImageReslice()  36  {  37         return this->ImageReslice;  38  }  39     void SetInteractor(vtkRenderWindowInteractor *interactor)  40  {  41         this->Interactor = interactor;  42  }  43     vtkRenderWindowInteractor *GetInteractor()  44  {  45         return  this->Interactor;  46  }  47     virtual void Execute(vtkObject * ,unsigned long event,void *)  48  {  49         vtkRenderWindowInteractor *interactor = GetInteractor();  50         int lastPos[2];  51         interactor->GetLastEventPosition(lastPos);  52         int currPos[2];  53         interactor->GetEventPosition(currPos);  54  
 55         if (event == vtkCommand::LeftButtonPressEvent)  56  {  57             this->Slicing = 1; //標志位 
 58  }  59         else if (event == vtkCommand::LeftButtonReleaseEvent)  60  {  61             this->Slicing = 0; //標志位 
 62  }  63         else if (event == vtkCommand::MouseMoveEvent)  64  {  65             if (this->Slicing)//檢驗鼠標左鍵已經按下 正在執行操作
 66  {  67                 vtkImageReslice *reslice = this->ImageReslice;  68                 //記下鼠標Y向變化的幅值大小
 69                 int deltaY = lastPos[1] - currPos[1];  70  
 71                 reslice->Update();  72                 double sliceSpacing = reslice->GetOutput()->GetSpacing()[2];  73                 vtkMatrix4x4 *matrix = reslice->GetResliceAxes();  74                 //重新定位切片需要經過的中心點
 75                 double point[4];  76                 double center[4];  77                 point[0] = 0;  78                 point[1] = 0;  79                 point[2] = sliceSpacing*deltaY;  80                 point[3] = 1.0;  81                 matrix->MultiplyPoint(point, center);  82                 matrix->SetElement(0, 3, center[0]);  83                 matrix->SetElement(1, 3, center[1]);  84                 matrix->SetElement(2, 3, center[2]);  85  
 86                 interactor->Render();  87  }  88             else
 89  {  90                 vtkInteractorStyle *style = vtkInteractorStyle::SafeDownCast(  91                     interactor->GetInteractorStyle());  92                 if (style)  93  {  94                     style->OnMouseMove();  95  }  96  }  97  }  98  }  99 private: 100     int Slicing; 101     vtkImageReslice *ImageReslice; 102     vtkRenderWindowInteractor *Interactor; 103 }; 104 //**********************************************************************************// 105 int main() 106 { 107     vtkSmartPointer<vtkMetaImageReader> reader =
108         vtkSmartPointer<vtkMetaImageReader>::New(); 109     reader->SetFileName("brain.mhd"); 110     reader->Update(); 111  
112     int extent[6]; 113     double spacing[3]; 114     double origin[3]; 115  
116     reader->GetOutput()->GetExtent(extent); 117     reader->GetOutput()->GetSpacing(spacing); 118     reader->GetOutput()->GetOrigin(origin); 119  
120     double center[3]; 121     center[0] = origin[0] + spacing[0] * 0.5 * (extent[0] + extent[1]); 122     center[1] = origin[1] + spacing[1] * 0.5 * (extent[2] + extent[3]); 123     center[2] = origin[2] + spacing[2] * 0.5 * (extent[4] + extent[5]); 124  
125     static double axialElements[16] = { 126         1, 0, 0, 0, 127         0, 1, 0, 0, 128         0, 0, 1, 0, 129         0, 0, 0, 1
130  }; 131  
132     vtkSmartPointer<vtkMatrix4x4> resliceAxes =
133         vtkSmartPointer<vtkMatrix4x4>::New(); 134     resliceAxes->DeepCopy(axialElements); 135  
136     resliceAxes->SetElement(0, 3, center[0]); 137     resliceAxes->SetElement(1, 3, center[1]); 138     resliceAxes->SetElement(2, 3, center[2]); 139  
140     vtkSmartPointer<vtkImageReslice> reslice =
141         vtkSmartPointer<vtkImageReslice>::New(); 142     reslice->SetInputConnection(reader->GetOutputPort()); 143     reslice->SetOutputDimensionality(2); 144     reslice->SetResliceAxes(resliceAxes); 145     reslice->SetInterpolationModeToLinear(); 146  
147     vtkSmartPointer<vtkLookupTable> colorTable =
148         vtkSmartPointer<vtkLookupTable>::New(); 149     colorTable->SetRange(0, 1000); 150     colorTable->SetValueRange(0.0, 1.0); 151     colorTable->SetSaturationRange(0.0, 0.0); 152     colorTable->SetRampToLinear(); 153     colorTable->Build(); 154  
155     vtkSmartPointer<vtkImageMapToColors> colorMap =
156         vtkSmartPointer<vtkImageMapToColors>::New(); 157     colorMap->SetLookupTable(colorTable); 158     colorMap->SetInputConnection(reslice->GetOutputPort()); 159  
160     vtkSmartPointer<vtkImageActor> imgActor =
161         vtkSmartPointer<vtkImageActor>::New(); 162     imgActor->SetInputData(colorMap->GetOutput()); 163  
164     vtkSmartPointer<vtkRenderer> renderer =
165         vtkSmartPointer<vtkRenderer>::New(); 166     renderer->AddActor(imgActor); 167     renderer->SetBackground(.4, .5, .6); 168  
169     vtkSmartPointer<vtkRenderWindow> renderWindow =
170         vtkSmartPointer<vtkRenderWindow>::New(); 171     renderWindow->SetSize(500, 500); 172     renderWindow->AddRenderer(renderer); 173  
174     vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
175         vtkSmartPointer<vtkRenderWindowInteractor>::New(); 176     vtkSmartPointer<vtkInteractorStyleImage> imagestyle =
177         vtkSmartPointer<vtkInteractorStyleImage>::New(); 178  
179     renderWindowInteractor->SetInteractorStyle(imagestyle); 180     renderWindowInteractor->SetRenderWindow(renderWindow); 181     renderWindowInteractor->Initialize(); 182     //****************建立 觀察者-命令 模式****************// 183     vtkSmartPointer<vtkImageInteractionCallback> callback =
184         vtkSmartPointer<vtkImageInteractionCallback>::New(); 185     callback->SetImageReslice(reslice); 186     callback->SetInteractor(renderWindowInteractor); 187  
188     imagestyle->AddObserver(vtkCommand::MouseMoveEvent, callback); 189     imagestyle->AddObserver(vtkCommand::LeftButtonPressEvent, callback); 190     imagestyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, callback); 191  
192     renderWindowInteractor->Start(); 193  
194     return 0; 195 }

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添加觀察者,響應鼠標消息。
這里主要是定義了vtkImageInteractionCallback對象,並設置vtkImageSlice對象和vtkRenderWindowInteractor對象。然后為交互對象vtkInteractorStyle添加觀察者來監控相應的消息,這里主要是三個消息:
vtkCommand::LeftButtonPressEvent,
vtkCommand::LeftButtonReleaseEvent,
vtkCommand::MouseMoveEvent,
當響應到這三個消息時,立即執行vtkImageInteractionCallback的Execute函數,以便實現切片的實時提取和更新。完成以后,運行程序,當鼠標在圖像上移動時,會發現圖像會跟着鼠標的移動而變化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM