1、测量类Widget
1)概述
与测量相关的主要 Widget如下:
- vtkDistanceWidget:用于在二维平面上测量两点之间的距离。
- vtkAngleWidget:用于二维平面的角度测量。
- vtkBiDimensionalWidget:用于测量二维平面上任意两个正交方向的轴长。
按照前面提到的步骤创建一个用于测量距离的 Widget,示例MeasurementWidget 演示了这个过程。
2)代码
private void TestMeasureDemo()
{
vtkBMPReader read = vtkBMPReader.New();
read.SetFileName("F:\\code\\VTK\\TestActiViz\\data\\lena.bmp");
read.Update();
vtkImageActor imageActor = vtkImageActor.New();
imageActor.SetInputData(read.GetOutput());
vtkRenderer renderer = vtkRenderer.New();
renderer.SetBackground(1, 1, 1);
vtkRenderWindow renderWindow = vtkRenderWindow.New();// renderWindowControl.RenderWindow;
renderWindow.AddRenderer(renderer);
renderer.AddActor(imageActor);
renderWindow.Render();
renderWindow.SetSize(400, 400);
vtkRenderWindowInteractor renderWindowInteractor = vtkRenderWindowInteractor.New();
renderWindowInteractor.SetRenderWindow(renderWindow);
vtkInteractorStyleImage style = vtkInteractorStyleImage.New();
renderWindowInteractor.SetInteractorStyle(style);
int widgetType = 2;
if (widgetType == 0)//vtkDistanceWidget
{
// 两个点之间的距离
// 用户只要用鼠标左键点击屏幕,然后松开鼠标并移至另外一个点,即会在两点之间生成一条线段,并有距离的测量值 。
vtkDistanceWidget distanceWidget = vtkDistanceWidget.New(); //实例化
distanceWidget.SetInteractor(renderWindowInteractor); //设置渲染容器交互器
distanceWidget.CreateDefaultRepresentation(); //创建默认的几何表达实体
//设置两点之间所测距离 的文本表示格式
((vtkDistanceRepresentation)distanceWidget.GetRepresentation()).SetLabelFormat("%-#6.3g px");
renderWindowInteractor.Initialize();
renderWindow.Render();
distanceWidget.On(); //激活实例
renderWindowInteractor.Start();
}
else if (widgetType == 1) //vtkAngleWidget
{
//角度测量
vtkAngleWidget angleWidget = vtkAngleWidget.New();
angleWidget.SetInteractor(renderWindowInteractor);
vtkAngleRepresentation2D angleRep = vtkAngleRepresentation2D.New();
angleRep.GetRay1().GetProperty().SetColor(1, 0, 0);
angleRep.GetRay2().GetProperty().SetColor(0, 1, 0);
angleRep.GetArc().GetProperty().SetColor(0, 0, 1);
angleWidget.SetRepresentation(angleRep);
renderWindowInteractor.Initialize();
renderWindow.Render();
angleWidget.On();
renderWindowInteractor.Start();
}
else if (widgetType == 2) //vtkBiDimensionalWidget
{
//测量二维平面上任意两个正交方向的轴长
vtkBiDimensionalWidget biDimensionalWidget = vtkBiDimensionalWidget.New();
biDimensionalWidget.SetInteractor(renderWindowInteractor);
biDimensionalWidget.CreateDefaultRepresentation();
//vtkBiDimensionalCallback vtkBiDimensionalCallback = (vtkBiDimensionalCallback)vtkBiDimensionalCallback.New();
biDimensionalWidget.InteractionEvt += BiDimensionalWidget_RenderEvt;
//biDimensionalWidget.AddObserver((uint)vtkCommand.EventIds.InteractionEvent, vtkBiDimensionalCallback, 0f);
// vtkCommand.Create
renderWindowInteractor.Initialize();
renderWindow.Render();
biDimensionalWidget.On();
renderWindowInteractor.Start();
}
}
private void BiDimensionalWidget_RenderEvt(vtkObject sender, vtkObjectEventArgs e)
{
if (sender is vtkBiDimensionalWidget biDimensionalWidget)
{
if (biDimensionalWidget.GetRepresentation() is vtkBiDimensionalRepresentation2D representation2D)
{
IntPtr data = Marshal.AllocHGlobal(sizeof(double) * 3);
representation2D.GetPoint1DisplayPosition(data);
double[] p1 = new double[3];
Marshal.Copy(data, p1, 0, 3);
representation2D.GetPoint2DisplayPosition(data);
double[] p2 = new double[3];
Marshal.Copy(data, p2, 0, 3);
representation2D.GetPoint3DisplayPosition(data);
double[] p3 = new double[3];
Marshal.Copy(data, p3, 0, 3);
representation2D.GetPoint4DisplayPosition(data);
double[] p4 = new double[3];
Marshal.Copy(data, p4, 0, 3);
Console.WriteLine(
$"p1:{p1[0]} {p1[1]} {p1[2]} " +
$"p2:{p2[0]} {p2[1]} {p2[2]} " +
$"p3:{p3[0]} {p3[1]} {p3[2]} " +
$"p4:{p4[0]} {p4[1]} {p4[2]} ");
}
}
}
3)效果
4)说明
以上示例中,使用了 vtkDistanceWidget 类来做二维空间的距离测量。先是实例化一个vtkDistanceWidget 实例;然后调用该类的 SetInteractor()函数来设置渲染窗口交互器;接着调用 CreateDefaultRepresentation()函数来创建默认的几何表达实体,即用十字形表示两个端点,端点之间使用带有刻度的直线连接。需要注意的是,在程序中调用SetLabelFormat()函数来设置两点之间所测距离的文本表示格式;最后调用0n()函数来激活vtkDistanceWidget实例。
程序运行后,用户只要用鼠标左键点击屏幕,然后松开鼠标并移至另外一个点,即会在两点之间生成一条线段,并有距离的测量值,程序运行结果如图所示。
角度测量的 vtkAngleWidget 以及二维正交方向长度测量的 vtkBiDimensionalWidget 的使用方法与 vtkDistanceWidget 类似,它们的二维几何表达形式分别为 vtkAngleRepresentation2D和 vtkBiDimensionalRepresentation2D。
2、标注类Widget
1)概述
在可视化应用程序中,经常会对某个对象做一些标注说明,比如在医学图像诊断中,常常会手动标注出被诊断为肿瘤的区域或者其他病变区域,并用文字等进行标注。又如,在气象领域中,会用一些颜色图标表示各个地理区域在某个时间段温度高低的分布情况等。VTK中,与标注相关的主要 Widget如下:
- vtkTextWidget:在渲染场景中生成一串标识文本,可以随意调整该文本在渲染场景中的位置,缩放其大小等。
- vtkScalarBarWidget:根据输入的数据在渲染场景中生成一个标量条,通过设置颜色查找表,可以用标量条上的颜色来指示输入的数据。渲染场景中的标量条可以随意移动、改变大小、设置不同的方向等。
- vtkCaptionWidget:用一个带线框及箭头的文本信息来标注某一对象。
- vtkOrientationMarkerWidget:渲染场景中所渲染数据的方向指示标志。在医学图像应用程序中有广泛的应用,比如,通过CT、MR 等扫描的数据,当将其导入可视化应用程序时,需要标识其上、下、左、右、前、后等方位。
- vtkBalloonWidget:当鼠标停留在渲染场景中的某个 Actor 一段时间后,会弹出提示信息。所提示的信息除了可以用文本表示,也可以用图像表示。
示例 AnnotationWidget 演示了以上几个 Widget 的用法。
2)代码
private void TestAnnotation()
{
vtkUnstructuredGridReader reader = vtkUnstructuredGridReader.New();
reader.SetFileName("F:\\code\\VTK\\TestActiViz\\data\\scalarBarWidgetTestData.vtk");
reader.Update();
vtkLookupTable lut = vtkLookupTable.New();
lut.Build();
vtkDataSetMapper mapper = vtkDataSetMapper.New();
mapper.SetInputData(reader.GetOutput());
double[] scalar = reader.GetOutput().GetScalarRange();
mapper.SetScalarRange(scalar[0], scalar[1]);
mapper.SetLookupTable(lut);
vtkActor actor = vtkActor.New();
actor.SetMapper(mapper);
vtkRenderer renderer = vtkRenderer.New();
renderer.AddActor(actor);
renderer.SetBackground(1, 1, 1);
vtkRenderWindow renderWindow = vtkRenderWindow.New();
renderWindow.AddRenderer(renderer);
renderWindow.SetWindowName("AnnotationWidget");
renderWindow.SetSize(400, 400);
renderWindow.Render();
vtkRenderWindowInteractor interactor = vtkRenderWindowInteractor.New();
interactor.SetRenderWindow(renderWindow);
vtkScalarBarActor scalarBarActor = vtkScalarBarActor.New();
scalarBarActor.SetOrientationToHorizontal();
scalarBarActor.SetLookupTable(lut);
// vtkScalarBarWidget
vtkScalarBarWidget scalarBarWidget = vtkScalarBarWidget.New();
scalarBarWidget.SetInteractor(interactor);
scalarBarWidget.SetScalarBarActor(scalarBarActor);
scalarBarWidget.On();
// vtkTextWidget
vtkTextActor textActor = vtkTextActor.New();
textActor.SetInput("VTK Widgets");
textActor.GetTextProperty().SetColor(0, 1, 0);
vtkTextWidget textWidget = vtkTextWidget.New();
vtkTextRepresentation textRepresentation = vtkTextRepresentation.New();
textRepresentation.GetPositionCoordinate().SetValue(0.15, 0.15);
textRepresentation.GetPosition2Coordinate().SetValue(0.7, 0.2);
textWidget.SetRepresentation(textRepresentation);
textWidget.SetInteractor(interactor);
textWidget.SetTextActor(textActor);
textWidget.On();
// vtkBalloonWidget
vtkBalloonRepresentation balloonrep = vtkBalloonRepresentation.New();
balloonrep.SetBalloonLayoutToImageRight();
vtkBalloonWidget balloonWidget = vtkBalloonWidget.New();
balloonWidget.SetInteractor(interactor);
balloonWidget.SetRepresentation(balloonrep);
balloonWidget.AddBalloon(actor, "This is a widget example");
balloonWidget.On();
//vtkOrientationMarkerWidget
vtkAxesActor iconActor = vtkAxesActor.New();
vtkOrientationMarkerWidget orientation = vtkOrientationMarkerWidget.New();
orientation.SetOutlineColor(0.93, 0.57, 0.13);
orientation.SetOrientationMarker(iconActor);
orientation.SetInteractor(interactor);
orientation.SetViewport(0, 0, 0.2, 0.2);
orientation.SetEnabled(1);
orientation.InteractiveOn();
vtkCaptionWidget
//vtkCaptionRepresentation captionRepresentation = vtkCaptionRepresentation.New();
//captionRepresentation.GetCaptionActor2D().SetCaption("Caption Widget");
//captionRepresentation.GetCaptionActor2D().GetTextActor().GetTextProperty().SetFontSize(20);
//double[] pos = new double[] { 0.5, 0, 0 };
//IntPtr dataPtr = Marshal.AllocHGlobal(sizeof(double) * 3);
//Marshal.Copy(pos, 0, dataPtr, 3);
//captionRepresentation.SetAnchorPosition(dataPtr);
//vtkCaptionWidget captionWidget = vtkCaptionWidget.New();
//captionWidget.SetInteractor(interactor);
//captionWidget.SetRepresentation(captionRepresentation);
//captionWidget.On();
renderWindow.Render();
interactor.Initialize();
interactor.Start();
}
3)效果
4)说明
以上 Widget 在使用时需要注意的地方是,除了指定Widget 的表达实体以外,某些Widget 还需要与其他 Actor 协同使用,比如 vkScalarBarWidget要与 vtkScalarBarActor 协同工作vtkTextWidget 要与 vtkTextActor 协同工作等。
3、分割/配准灯Widget
图像分割与配准是数字图像处理技术两大主要的应用领域,特别是在医学图像处理中,其应用更加广泛。著名的医学图像分割与配准工具包ITK(Insight Segmentation and RegistrationToolkit)的重要应用领域就是图像分割与配准。ITK 实现了许多经典的分割、配准算法,但不提供可视化的功能,因此,在应用中一般都会和 VTK 一起使用。由ITK负责分割、配准等数据处理,其处理结果由 VTK进行显示,必要时可以使用VTK的交互 Widget,从用户的交互过程中获取所需的数据,并向ITK的处理算法传递用户的参数设置。比如,对于区域增长算法,需要设置初始的种子点,而种子点的设置则可以使用VTK的vtkSeedWidget。与图像分割、配准应用相关的主要 Widget 如下。
- vtkContourWidget:绘制轮廓线。所绘制的轮可以是闭合的,也可以是不闭合的,取决于最后一个点的位置。
- vtkImageTracerWidget:绘制轨迹线。该类在手动分割图像中应用得较多。
- vtkSeedWidget:放置种子点。在基于种子点的分割算法中应用得较多。
- vtkCheckerboardWidget:在二维图像上生成棋盘格,而且可以控制棋盘格的数目。使用该类可以查看两幅图像配准后的重叠效果。
- vtkRectilinearWipeWidget:在二维图像上生成棋盘格,与 vtkCheckboardWidget 不同的是,该类不可以控制棋盘格的数目,所生成的棋盘格是固定的2X2,但是该2x2的棋盘格可以调节大小,主要也是应用于图像配准中。
4、其他Widget
除了以上介绍的 Widget 以外,VTK 还提供与绘图相关的 Widget,如 vtkXYPlotWidget;与动画、视频相关的 Widget,如 vkCameraWidget、vtkPlaybackWidget;与参数控制等相关的Widget,如 vtkCompassWidget、vtkSliderWidget、vtkCenteredSliderWidget 等;与数据探测提取等相关的 Widget,如vtkPlaneWidget、vtklmagePlaneWidget、vtkImplicitPlaneWidget2、vtkTensorProbeWidget;还有与空间变换相关的 Widget,如 vtkAffneWidget 等。
虽然每个 Widget 都有不同的功能及应用范围,但是它们的使用方法大同小异,基本都是遵循 前面所述的操作步骤。每个 Widget内部都会绑定不同的事件,在使用这些 Widget 类时,只要知道应该捕获哪些消息,然后根据具体的需求实现相应的回调函数即可,而 Widget的样式则由相应的 Represention 类进行表达。用户可以使用默认的表达实体或者指定其他的表达实体,这就是 vtkAbstractWidget里“交互/表达实体”分离的好处。