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