二维轮廓线(等值线)提取属于标量可视化技术范围,常用于提取一个研究区域的轮廓边界,如在气象中应用中,常根据气象卫星或数值模拟的结果,按照每个地理位置点确定的属性值,分析区域内的降雨情况,提取不同降雨量的区域边界(如大雨、中雨、小雨的区域边界),在有限元分析中,常用于提取位移等值线等,在VTK中轮廓线提取功能主要面对的是规则网格数据集,对于非规则网格数据,必须要进行插值计算,生成规则网格数据集,由于规则网格数据其构成单元为正方形或长方形,所以提取轮廓线常采用移动四边形法(Marching Squares),该方法为移动立方体算法(Marching Cubes)的二维表达,因为本文是以实用性为主,所以对这些算法的理论不加以详细介绍,有关该方法的详细信息请参考《三维数据场可视化》(唐圣泽等著)这本书,本文从实用性的角度,对该方法做一个说明,该方法的主要核心是:
1、 定义规则网格数据顶点的属性值
2、 确定提取区域的属性值
3、 用提取区域的属性值和每个网格数据的每个单元进行比对,确定其是否在该单元内,如果在该单元内,要用插值的方法确定其和该单元边界的交点,然后继续比对下一个单元,如果不在该单元内,继续比对下一个单元。
4、 将各个单元的交点相连成线,即为边界区域。
下图给出了一个比对的示意:
在该示意图中,提取属性值为5的边界区域,假设从左到从上到下依次为单元编号,图中共有16个单元,构成第一个单元4个顶点按逆时针排列的属性值分别为0、1、3、1,均小于5,则该单元没有要提取的边界点,第一个单元顶点按逆时针排列的顺序为1、3、6、1,在3、6边及6、1边上分别有交点,计算出交点位置,依次对比各个单元,最终得到提取区域的边界,如,图中实线所画的闭合曲线就是提取的边界。
下面,以这个示意图数据为例,说明如何用VTK提供的功能提取轮廓线,以网格中心点为坐标原点,网格间距假设为一个单位,可确定各个顶点的坐标,示例代码如下:
#include "stdafx.h"
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkFloatArray.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include <vtkLookupTable.h>
#include <vtkContourFilter.h>
#include <vtkProperty.h>
int _tmain(int argc, _TCHAR* argv[])
{
int i;
/定义网格的顶点坐标
vtkPoints *points = vtkPoints::New();
/第1行5个点
points->InsertNextPoint(-2.0,2.0,0.0);
points->InsertNextPoint(-1.0,2.0,0.0);
points->InsertNextPoint(0.0,2.0,0.0);
points->InsertNextPoint(1.0,2.0,0.0);
points->InsertNextPoint(2.0,2.0,0.0);
/第2行5个点
points->InsertNextPoint(-2.0,1.0,0.0);
points->InsertNextPoint(-1.0,1.0,0.0);
points->InsertNextPoint(0.0,1.0,0.0);
points->InsertNextPoint(1.0,1.0,0.0);
points->InsertNextPoint(2.0,1.0,0.0);
/第3行5个点
points->InsertNextPoint(-2.0,0.0,0.0);
points->InsertNextPoint(-1.0,0.0,0.0);
points->InsertNextPoint(0.0,0.0,0.0);
points->InsertNextPoint(1.0,0.0,0.0);
points->InsertNextPoint(2.0,0.0,0.0);
/第4行5个点
points->InsertNextPoint(-2.0,-1.0,0.0);
points->InsertNextPoint(-1.0,-1.0,0.0);
points->InsertNextPoint(0.0,-1.0,0.0);
points->InsertNextPoint(1.0,-1.0,0.0);
points->InsertNextPoint(2.0,-1.0,0.0);
/第5行5个点
points->InsertNextPoint(-2.0,-2.0,0.0);
points->InsertNextPoint(-1.0,-2.0,0.0);
points->InsertNextPoint(0.0,-2.0,0.0);
points->InsertNextPoint(1.0,-2.0,0.0);
points->InsertNextPoint(2.0,-2.0,0.0);
/定义单元,每个顶点建立一个四边形单元,共计个单元
static vtkIdType pts[16][4]={{0,5,6,1}, {1,6,7,2}, {2,7,8,3},
{3,8,9,4}, {5,10,11,6}, {6,11,12,7},{7,12,13,8},{8,13,14,9},
{10,15,16,11},{11,16,17,12},{12,17,18,13},{13,18,19,14},
{15,20,21,16},{16,21,22,17},{17,22,23,18},{18,23,24,19}};
/创建对象
vtkPolyData *pGrid=vtkPolyData::New();
vtkCellArray *polys=vtkCellArray::New();
/设定单元
for(i=0;i<16;i++)polys->InsertNextCell(4,pts[i]);
/设定每个顶点的标量值
/存储标量值
vtkFloatArray *scalars = vtkFloatArray::New();
/第1行
scalars->InsertTuple1(0,0);
scalars->InsertTuple1(1,1);
scalars->InsertTuple1(2,1);
scalars->InsertTuple1(3,3);
scalars->InsertTuple1(4,2);
/第2行
scalars->InsertTuple1(5,1);
scalars->InsertTuple1(6,3);
scalars->InsertTuple1(7,6);
scalars->InsertTuple1(8,6);
scalars->InsertTuple1(9,3);
/第3行
scalars->InsertTuple1(10,3);
scalars->InsertTuple1(11,7);
scalars->InsertTuple1(12,9);
scalars->InsertTuple1(13,7);
scalars->InsertTuple1(14,3);
/第4行
scalars->InsertTuple1(15,2);
scalars->InsertTuple1(16,7);
scalars->InsertTuple1(17,8);
scalars->InsertTuple1(18,6);
scalars->InsertTuple1(19,2);
/第5行
scalars->InsertTuple1(20,1);
scalars->InsertTuple1(21,2);
scalars->InsertTuple1(22,3);
scalars->InsertTuple1(23,4);
scalars->InsertTuple1(24,3);
/创建多边形数据
pGrid->SetPoints(points);
/设定单元类型为多边形
pGrid->SetPolys(polys);
/设定每个顶点的标量值
pGrid->GetPointData()->SetScalars(scalars);
points->Delete();
polys->Delete();
scalars->Delete();
/定义颜色映射表
vtkLookupTable *pColorTable=vtkLookupTable::New();
/设置颜色表中的颜色
pColorTable->SetHueRange(0.667,0.887);
pColorTable->SetNumberOfColors(255);
pColorTable->Build();
/数据映射,使用默认颜色映射表
vtkPolyDataMapper *cubeMapper = vtkPolyDataMapper::New();
cubeMapper->SetInput(pGrid);
/设置标量值范围
cubeMapper->SetScalarRange(0,9);
cubeMapper->SetLookupTable(pColorTable);
vtkActor *cubeActor = vtkActor::New();
cubeActor->SetMapper(cubeMapper);
/提取轮廓线
vtkContourFilter *pContourFilter=vtkContourFilter::New();
/设置提取的标量值
pContourFilter->SetValue(0,5.0);
pContourFilter->SetInput(pGrid);
vtkPolyDataMapper *pContouMapper=vtkPolyDataMapper::New();
pContouMapper->SetInput((vtkPolyData *)pContourFilter->GetOutput());
/禁用属性数据绘制颜色
pContouMapper->ScalarVisibilityOff();
vtkActor *pContoActor = vtkActor::New();
pContoActor->SetMapper(pContouMapper);
pContoActor->GetProperty()->SetColor(1.0,1.0,0.0);
pContoActor->GetProperty()->SetLineWidth(10.0);
/绘制
vtkCamera *camera = vtkCamera::New();
camera->SetPosition(1,1,1);
camera->SetFocalPoint(0,0,0);
vtkRenderer *renderer = vtkRenderer::New();
vtkRenderWindow *renWin = vtkRenderWindow::New();
renWin->AddRenderer(renderer);
vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
iren->SetRenderWindow(renWin);
renderer->AddActor(cubeActor);
renderer->AddActor(pContoActor);
renderer->SetActiveCamera(camera);
renderer->ResetCamera();
renderer->SetBackground(1,1,1);
renWin->SetSize(300,300);
renWin->Render();
iren->Start();
/删除对象
pGrid->Delete();
cubeMapper->Delete();
cubeActor->Delete();
camera->Delete();
renderer->Delete();
renWin->Delete();
iren->Delete();
pColorTable->Delete();
return 0;
}
在VTK中vtkContourFilter过滤器类实现了轮廓线提取功能,该过滤器接收多边形数据集作为输入,并将生成的多边形数据集作为输出,程序运行后的结果如下: