一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

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函数,以便实现切片的实时提取和更新。完成以后,运行程序,当鼠标在图像上移动时,会发现图像会跟着鼠标的移动而变化。
posted on 2021-01-06 15:25  一杯清酒邀明月  阅读(1760)  评论(0编辑  收藏  举报