VTK笔记——拾取器Picker

  很多时候,医疗软件避免不了人机交互,而拾取器在人机交互中扮演着重要的角色,也是经常被用到,比如说,拾取点,拾取actor,拾取cell等等。

拾取类

VTK很强大,也很周到,提供了多种拾取类。
在这里插入图片描述

如何选择

有时候选择太多也不是一件好事。那要怎样选择?我们得知道他们的区别。
简单的说,拾取器可分两类:
一类是基于硬件拾取,vtkPropPicker及其子类
另一类是基于几何拾取,vtkPicker和它的子类
硬件拾取的快速,但获取信息有限
几何拾取获得的信息多,但速度要慢一些

示例

示例演示了通过鼠标移动在一个二维图像上拾取像素值,并判断是否在图像区域上。
在这里插入图片描述

基本流程

1
using PickerType = vtkPropPicker; // vtkPointPicker

定义picker并初始化

1
2
auto picker = vtkSmartPointer<PickerType>::New();
picker->PickFromListOn();

添加拾取的Prop对象

1
2
vtkImageActor* imageActor = imageViewer->GetImageActor();
picker->AddPickList(imageActor);

绑定自定义事件

1
2
auto callback = vtkSmartPointer<vtkImageInteractionCallback>::New();
callback->SetPicker(picker);

获取世界坐标

1
2
double pos[3];
picker->GetPickPosition(pos);

值得注意:
就上面的需求而言,这里应该选用vtkPropPicker,在vtk7.1.0中,可以拾取到坐标值,但在vtk8.2.0,不能获取到坐标,使用vtkPointPicker可以得到,目前原因未知。

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#include <vtkAssemblyPath.h>
#include <vtkCell.h>
#include <vtkCommand.h>
#include <vtkCornerAnnotation.h>
#include <vtkImageActor.h>
#include <vtkImageCast.h>
#include <vtkImageData.h>
#include <vtkImageNoiseSource.h>
#include <vtkImageViewer2.h>
#include <vtkInteractorStyleImage.h>
#include <vtkMath.h>
#include <vtkPointData.h>
#include <vtkPropPicker.h>
#include <vtkPointPicker.h>
#include <vtkCellPicker.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkTIFFReader.h>
#include <vtkTextProperty.h>

using PickerType = vtkPointPicker;

// Template for image value reading
template <typename T>
void vtkValueMessageTemplate(vtkImageData* image, int* position,
std::string& message)
{
T* tuple = ((T*)image->GetScalarPointer(position));
int components = image->GetNumberOfScalarComponents();
for (int c = 0; c < components; ++c)
{
message += vtkVariant(tuple[c]).ToString();
if (c != (components - 1))
{
message += ", ";
}
}
message += " )";
}

// The mouse motion callback, to pick the image and recover pixel values
class vtkImageInteractionCallback : public vtkCommand
{
public:
static vtkImageInteractionCallback* New()
{
return new vtkImageInteractionCallback;
}

vtkImageInteractionCallback()
{
this->Viewer = NULL;
this->Picker = NULL;
this->Annotation = NULL;
}

~vtkImageInteractionCallback()
{
this->Viewer = NULL;
this->Picker = NULL;
this->Annotation = NULL;
}

void SetPicker(PickerType* picker)
{
this->Picker = picker;
}

void SetAnnotation(vtkCornerAnnotation* annotation)
{
this->Annotation = annotation;
}

void SetViewer(vtkImageViewer2* viewer)
{
this->Viewer = viewer;
}

virtual void Execute(vtkObject*, unsigned long vtkNotUsed(event), void*)
{
vtkRenderWindowInteractor* interactor =
this->Viewer->GetRenderWindow()->GetInteractor();
int* clickPos = interactor->GetEventPosition();
vtkRenderer* renderer = this->Viewer->GetRenderer();
//vtkRenderer* renderer = interactor->FindPokedRenderer(clickPos[0], clickPos[1]);
vtkImageActor* actor = this->Viewer->GetImageActor();
vtkImageData* image = this->Viewer->GetInput();
vtkInteractorStyle* style =
dynamic_cast<vtkInteractorStyle*>(interactor->GetInteractorStyle());

// Pick at the mouse location provided by the interactor
this->Picker->Pick(clickPos[0], clickPos[1], 0.0, renderer);

// There could be other props assigned to this picker, so
// make sure we picked the image actor
vtkAssemblyPath* path = this->Picker->GetPath();
bool validPick = false;
if (path)
{
vtkCollectionSimpleIterator sit;
path->InitTraversal(sit);
for (int i = 0; i < path->GetNumberOfItems() && !validPick; ++i)
{
auto node = path->GetNextNode(sit);
if (actor == dynamic_cast<vtkImageActor*>(node->GetViewProp()))
{
validPick = true;
}
}
}

if (!validPick)
{
this->Annotation->SetText(0, "Off Image");
interactor->Render();
// Pass the event further on
style->OnMouseMove();
return;
}

// Get the world coordinates of the pick
double pos[3];
this->Picker->GetPickPosition(pos);

int image_coordinate[3];

int axis = this->Viewer->GetSliceOrientation();
switch (axis)
{
case vtkImageViewer2::SLICE_ORIENTATION_XZ:
image_coordinate[0] = vtkMath::Round(pos[0]);
image_coordinate[1] = this->Viewer->GetSlice();
image_coordinate[2] = vtkMath::Round(pos[2]);
break;
case vtkImageViewer2::SLICE_ORIENTATION_YZ:
image_coordinate[0] = this->Viewer->GetSlice();
image_coordinate[1] = vtkMath::Round(pos[1]);
image_coordinate[2] = vtkMath::Round(pos[2]);
break;
default: // vtkImageViewer2::SLICE_ORIENTATION_XY
image_coordinate[0] = vtkMath::Round(pos[0]);
image_coordinate[1] = vtkMath::Round(pos[1]);
image_coordinate[2] = this->Viewer->GetSlice();
break;
}

std::string message = "Location: ( ";
message += vtkVariant(image_coordinate[0]).ToString();
message += ", ";
message += vtkVariant(image_coordinate[1]).ToString();
message += ", ";
message += vtkVariant(image_coordinate[2]).ToString();
message += " )\nValue: ( ";

switch (image->GetScalarType())
{
vtkTemplateMacro(
(vtkValueMessageTemplate<VTK_TT>(image, image_coordinate, message)));

default:
return;
}

this->Annotation->SetText(0, message.c_str());
interactor->Render();
style->OnMouseMove();
}

private:
vtkImageViewer2* Viewer; // Pointer to the viewer
PickerType* Picker; // Pointer to the picker
vtkCornerAnnotation* Annotation; // Pointer to the annotation
};

int main(int argc, char* argv[])
{
auto imageViewer = vtkSmartPointer<vtkImageViewer2>::New();

// Verify input arguments
if (argc != 2)
{
std::cout << argv[0] << " Required parameters: (tif) Filename" << std::endl
<< "missing..." << std::endl;
std::cout << "A noise image will be created!" << std::endl;

// create a noise image
auto noiseSource = vtkSmartPointer<vtkImageNoiseSource>::New();
noiseSource->SetWholeExtent(0, 512, 0, 512, 0, 0);
noiseSource->SetMinimum(0.0);
noiseSource->SetMaximum(65535.0);

// cast noise image to unsigned short
auto imageCast = vtkSmartPointer<vtkImageCast>::New();
imageCast->SetInputConnection(noiseSource->GetOutputPort());
imageCast->SetOutputScalarTypeToUnsignedShort();
imageCast->Update();

// connect to image viewer pipeline
imageViewer->SetInputConnection(imageCast->GetOutputPort());
}
else
{
// Parse input argument
std::string inputFilename = argv[1];

// Read the image
auto tiffReader = vtkSmartPointer<vtkTIFFReader>::New();
if (!tiffReader->CanReadFile(inputFilename.c_str()))
{
std::cout << argv[0] << ": Error reading file " << inputFilename
<< std::endl;
return EXIT_FAILURE;
}
tiffReader->SetFileName(inputFilename.c_str());

// connect to image viewer pipeline
imageViewer->SetInputConnection(tiffReader->GetOutputPort());
}

// Picker to pick pixels
auto picker = vtkSmartPointer<PickerType>::New();
picker->PickFromListOn();

// Give the picker a prop to pick
vtkImageActor* imageActor = imageViewer->GetImageActor();
picker->AddPickList(imageActor);

// disable interpolation, so we can see each pixel
imageActor->InterpolateOff();

// Visualize
auto renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
imageViewer->SetupInteractor(renderWindowInteractor);
imageViewer->SetSize(600, 600);

vtkRenderer* renderer = imageViewer->GetRenderer();
renderer->ResetCamera();
renderer->GradientBackgroundOn();
renderer->SetBackground(0.6, 0.6, 0.5);
renderer->SetBackground2(0.3, 0.3, 0.2);

// Annotate the image with window/level and mouse over pixel
// information
auto cornerAnnotation = vtkSmartPointer<vtkCornerAnnotation>::New();
cornerAnnotation->SetLinearFontScaleFactor(2);
cornerAnnotation->SetNonlinearFontScaleFactor(1);
cornerAnnotation->SetMaximumFontSize(20);
cornerAnnotation->SetText(0, "Off Image");
cornerAnnotation->SetText(3, "<window>\n<level>");
cornerAnnotation->GetTextProperty()->SetColor(1, 0, 0);

imageViewer->GetRenderer()->AddViewProp(cornerAnnotation);

// Callback listens to MouseMoveEvents invoked by the interactor's style
auto callback = vtkSmartPointer<vtkImageInteractionCallback>::New();
callback->SetViewer(imageViewer);
callback->SetAnnotation(cornerAnnotation);
callback->SetPicker(picker);

// InteractorStyleImage allows for the following controls:
// 1) middle mouse + move = camera pan
// 2) left mouse + move = window/level
// 3) right mouse + move = camera zoom
// 4) middle mouse wheel scroll = zoom
// 5) 'r' = reset window/level
// 6) shift + 'r' = reset camera
vtkInteractorStyleImage* imageStyle = imageViewer->GetInteractorStyle();
imageStyle->AddObserver(vtkCommand::MouseMoveEvent, callback);

renderWindowInteractor->Initialize();
renderWindowInteractor->Start();

return EXIT_SUCCESS;
}

Reference

VTK交互之拾取
VTKExamples/Cxx/Images/PickPixel2
vtkAbstractPicker Class Reference
在这里插入图片描述