拾取是可視化應用程式中常見的一種功能。拾取主要是用于選擇資料和actor或者擷取底層的資料值,比如說,拾取點,拾取actor,拾取cell等。
拾取器
VTK支援多種不同功能的拾取類型。vtkAbstractPicker是所有拾取類的基類,它定義了一些公共的API,允許使用者通過GetPickPosition()來擷取拾取位置。
繼承圖如下:
如何選擇
當我們面臨太多選擇的時候可能會困惑,究竟該用哪個?如何選擇?首先我們得知道他們的基本差別。
簡單的說,拾取器可分兩類:
一類是基于硬體拾取,如vtkPropPicker及其子類、vtkAreaPicker及子類
另一類是基于幾何拾取,如vtkPicker和它的子類
硬體拾取的優點是快速,但擷取資訊有限
幾何拾取則獲得的資訊多,但速度相對要慢一些
vtkPropPicker使用硬體拾取的政策來确定所拾取的vtkProp對象,包括拾取點的世界坐标。它通常比vtkAbstractPropPicker的其它子類速度要快,但它不能傳回所拾取對象的詳細資訊。
vtkAreaPicker及其子類vtkRenderedAreaPicker也是基于硬體實作的,同樣無法擷取到拾取對象的詳細資訊,他們的作用是選擇螢幕上的對象。
vtkPicker是基于軟體實作的拾取類,具體實作是基于vtkProp對象的包圍盒來拾取對象的。它在拾取時,會從相機目前位置投射一條光線到拾取點,所投射光線會與某個prop3D對象的包圍盒相交,當然,通過這種方式有可能會拾取到多個prop3D對象,最後傳回的是所投射光線與對象包圍盒相交最多的對象。GetProp3Ds()方法可以傳回與投射光線相交的所有prop3D對象。vtkPicker拾取速度相對較快,但無法擷取單一拾取。
vtkPicker有三個子類,其中兩個分别是vtkPointPicker和vtkCellPicker,它們可以擷取所拾取對象的詳細資訊,如點的ID,單元的ID等。
vtkPointPicker用于拾取單個點,傳回ID和坐标值。拾取時,它也是通過相機目前位置投射一條光線到拾取點,然後将光線周圍且位于容差範圍内的點投射至光線上,最後傳回的是距離相機最近的點以及該點所對應的actor。它比父類vtkPicker拾取速度要慢,但比兄弟類vtkCellPicer要快。因為引入了容差,是以可以傳回單一的拾取對象。
vtkCellPicker用于拾取某個單元,并傳回交點的資訊,比如,交點所對應的單元ID、世界坐标和參數化單元坐标。與vtkPointPicker類似,它拾取時也是投射一條光線至拾取點,在一定容差範圍内确定光線是否與actor底層幾何相交,最後傳回沿着光線最靠近相機的單元及其對應的對象。vtkCellPicker是所有拾取類中最慢的一個,但擷取資訊是最多的。通過指定容差,可以傳回單一的拾取對象。
基本用法
定義picker并初始化
auto picker = vtkSmartPointer<PickerType>::New();
picker->PickFromListOn();
添加拾取的Prop對象
vtkImageActor* imageActor = imageViewer->GetImageActor();
picker->AddPickList(imageActor);
綁定自定義事件
auto callback = vtkSmartPointer<vtkImageInteractionCallback>::New();
callback->SetPicker(picker);
拾取Pick:
注意:Pick方法需要一個renderer作為參數。與渲染器相關的actor都是拾取的對象。另外,第三個參數通常都設定為0.0,它是與Z-buffer相關的值。
擷取世界坐标
double pos[3];
picker->GetPickPosition(pos);
值得注意:
就上面的需求而言,這裡應該選用vtkPropPicker,在vtk7.1.0中,可以拾取到坐标值,但在vtk8.2.0,不能擷取到坐标,使用vtkPointPicker可以得到。
示例
示例示範了通過滑鼠移動在一個二維圖像上拾取像素值,并判斷是否在圖像區域上。
PickPixel2.cxx
#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;
}
Ref
VTK互動之拾取
VTKExamples/Cxx/Images/PickPixel2
vtkAbstractPicker Class Reference