天天看点

VTK笔记——拾取器Picker

拾取是可视化应用程序中常见的一种功能。拾取主要是用于选择数据和actor或者获取底层的数据值,比如说,拾取点,拾取actor,拾取cell等。

拾取器

VTK支持多种不同功能的拾取类型。vtkAbstractPicker是所有拾取类的基类,它定义了一些公共的API,允许用户通过GetPickPosition()来获取拾取位置。

继承图如下:

VTK笔记——拾取器Picker

如何选择

当我们面临太多选择的时候可能会困惑,究竟该用哪个?如何选择?首先我们得知道他们的基本区别。

简单的说,拾取器可分两类:

一类是基于硬件拾取,如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可以得到。

示例

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

VTK笔记——拾取器Picker

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

VTK笔记——拾取器Picker