1.创建网格
网格类用于表示空间中的形状。
- 它源于itk::PointSet类,因此继承了与点相关的所有功能以及对与点关联的像素数据的访问。
- Mesh类也是n维的,这使得它的使用具有很大的灵活性。
- 实际上,Mesh类可以看作是一个点集,其中添加了许多不同维度和形状的单元格(也称为元素)。网格中的单元格使用它们的点标识符根据现有的点定义
与PointSet相同,ITK中有静态和动态两种基本的网格样式。
- 第一个用于使用点的数量可预知,并不需要对其修改操作。
- 第二个支持插入和删除点。
- 区分这两种样式的原因是为了方便对其行为进行微调,以优化性能和内存管理。
实例:
#include "itkMesh.h"
typedef float PixelType;
const unsigned int Dimension = 3;
typedef itk::Mesh< PixelType, Dimension > MeshType;
MeshType::Pointer mesh = MeshType::New();
MeshType::PointType p0;
MeshType::PointType p1;
MeshType::PointType p2;
MeshType::PointType p3;
// first point ( -1, -1, 0 )
p0[0]= -1.0;
p0[1]= -1.0;
p0[2]= 0.0;
// second point ( 1, -1, 0 )
p1[0]= 1.0;
p1[1]= -1.0;
p1[2]= 0.0;
// third point ( 1, 1, 0 )
p2[0]= 1.0;
p2[1]= 1.0;
p2[2]= 0.0;
// fourth point ( -1, 1, 0 )
p3[0]= -1.0;
p3[1]= 1.0;
p3[2]= 0.0;
mesh->SetPoint( 0, p0 );
mesh->SetPoint( 1, p1 );
mesh->SetPoint( 2, p2 );
mesh->SetPoint( 3, p3 );
std::cout << "Points = " << mesh->GetNumberOfPoints() << std::endl;
typedef MeshType::PointsContainer::Iterator PointsIterator;
PointsIterator pointIterator = mesh->GetPoints()->Begin();
PointsIterator end = mesh->GetPoints()->End();
while( pointIterator != end )
{
MeshType::PointType p = pointIterator.Value(); // access the point
std::cout << p << std::endl; // print the point
pointIterator; // advance to next point
}
2.嵌入单元
itk::Mesh中包含了多种单元类型,典型的有:
- itk::LineCell
- itk::TriangleCell
- itk::QuadrilateralCell
- itk::TetrahedronCell.
网格管理单元格和点的主要区别是
- 点是通过复制存储在点容器中
- 而单元格是使用指针存储在单元格容器中
使用指针的原因是单元格在网格上使用c++多态性。
- 这意味着网格只知道有指向泛型单元格的指针,泛型单元格是所有特定单元格类型的基类。
- 这种架构使得在同一个网格中组合不同的单元格类型成为可能。另一方面,点属于单一类型,占用的内存较小,因此可以高效地复制它们
通过指针管理细胞增加了另一个层次的复杂性,因为现在有必要建立一个协议来明确谁负责分配和释放细胞的内存。
- 该协议以一种称为CellAutoPointer的特定类型的指针的形式实现。
这款指针基于itk::AutoPointer,与智能指针有许多不同之处。
- CellAutoPointer有一个指向实际对象的内部指针和一个布尔标志,用于指示CellAutoPointer是否负责释放细胞内存,无论何时需要对其进行销毁
下面的代码创建了一条多边形线,以演示网格中单元管理的最简单情况。这里使用的唯一单元格类型是线单元。
#include "itkLineCell.h"
typedef MeshType::CellType CellType;
typedef itk::LineCell< CellType > LineType;
typedef CellType::CellAutoPointer CellAutoPointer;
MeshType::Pointer mesh = MeshType::New();
MeshType::PointType p0;
MeshType::PointType p1;
MeshType::PointType p2;
p0[0] = -1.0; p0[1] = 0.0; p0[2] = 0.0;
p1[0] = 1.0; p1[1] = 0.0; p1[2] = 0.0;
p2[0] = 1.0; p2[1] = 1.0; p2[2] = 0.0;
mesh->SetPoint( 0, p0 );
mesh->SetPoint( 1, p1 );
mesh->SetPoint( 2, p2 );
//下面的代码创建了两个CellAutoPointers,并用new初始化它们。
//在本例中创建的实际单元格类型是LineCell。
///CellAutoPointer通过使用TakeOwnership()方法获取接收的指针的所有权。
//尽管这看起来很冗长,但它是必要的,以便从代码中明确表明自动发送器承担内存释放的责任。
CellAutoPointer line0;
CellAutoPointer line1;
line0.TakeOwnership( new LineType );
line1.TakeOwnership( new LineType );
//设置line的索引关联
line0->SetPointId( 0, 0 ); // line between points 0 and 1
line0->SetPointId( 1, 1 );
line1->SetPointId( 0, 1 ); // line between points 1 and 2
line1->SetPointId( 1, 2 );
//使用SetCell()方法将单元格插入网格中。
//它需要一个标识符和对单元格的自动转换。
//网格将获得AutoPointer所指向的单元格的所有权。
//这是通过SetCell()方法在内部完成的。
mesh->SetCell( 0, line0 );
mesh->SetCell( 1, line1 );
//作为SetCell()方法的参数之后,CellAutoPointer不再拥有单元格的所有权。
//在不首先确保另一个单元格的所有权的情况下,不要再次使用相同的CellAutoPointer作为SetCell()的参数,这一点很重要。
std::cout << "Cells = " << mesh->GetNumberOfCells() << std::endl;
//与点类似,可以使用迭代器访问网格中的单元格容器。
//单元格迭代器的特征可以从网格中提取出来,并用于定义局部类型。
typedef MeshType::CellsContainer::Iterator CellIterator
CellIterator cellIterator = mesh->GetCells()->Begin();
CellIterator end = mesh->GetCells()->End();
//最后,使用一个标准循环遍历所有单元格。
//注意,Value()方法用于从CellIterator获取单元格的实际指针。
//还要注意,返回的值是指向泛型单元格类型的指针。
while( cellIterator != end )
{
MeshType::CellType * cellptr = cellIterator.Value();
//为了作为实际的LineCell类型使用,这些指针必须向下映射。
//安全的向下转换使用动态转换操作符执行,如果转换不能安全执行,该操作符将抛出异常。
LineType * line = dynamic_cast<LineType *>( cellptr );
std::cout << line->GetNumberOfPoints() << std::endl;
++cellIterator;
}
3.管理单元数据
与定制数据与网格中的点关联的方式相同,也可以将定制数据与单元格关联。与单元格关联的数据类型可能与与点关联的数据类型不同。但是,默认情况下,这两种类型是相同的。下面的示例说明如何访问与单元格关联的数据。该方法类似于用于访问点数据的方法。
#include "itkMesh.h"
#include "itkLineCell.h"
typedef float PixelType;
typedef itk::Mesh< PixelType, 2 > MeshType;
typedef MeshType::CellType CellType;
typedef itk::LineCell< CellType > LineType;
MeshType::Pointer mesh = MeshType::New();
//创建一个网格并插入一些点。
//注意点的dimension与网格的dimention匹配
//插入一个点序列,它看起来像log()函数的图形。
typedef MeshType::PointType PointType;
PointType point;
const unsigned int numberOfPoints = 10;
for(unsigned int id=0; id<numberOfPoints; id++)
{
point[0] = static_cast<PointType::ValueType>( id ); // x
point[1] = log( static_cast<double>( id ) ); // y
mesh->SetPoint( id, point );
}
//通过使用点标识符,将创建一组line cells并与现有的点关联。
//在这个简单的例子中,单元标识符可以从点标识符推导出,因为行单元格的排序方式相同。
CellType::CellAutoPointer line;
const unsigned int numberOfCells = numberOfPoints-1;
for(unsigned int cellId=0; cellId<numberOfCells; cellId++)
{
line.TakeOwnership( new LineType );
line->SetPointId( 0, cellId ); // first point
line->SetPointId( 1, cellId+1 ); // second point
mesh->SetCell( cellId, line ); // insert the cell
}
//通过使用SetCellData()方法,将与单元格关联的数据插入到itk::Mesh中。
//它要求用户提供一个标识符和要插入的值。标识符应该匹配插入的一个单元格。
//在这个简单的示例中,单元格标识符的方形被用作单元格数据。注意在赋值中使用了对PixelType的静态转换。
for(unsigned int cellId=0; cellId<numberOfCells; cellId++)
{
mesh->SetCellData( cellId, static_cast<PixelType>( cellId * cellId ) );
}
typedef MeshType::CellDataContainer::ConstIterator CellDataIterator;
CellDataIterator cellDataIterator = mesh->GetCellData()->Begin();
CellDataIterator end = mesh->GetCellData()->End();
while( cellDataIterator != end )
{
//注意,Value()方法用于获取数据条目的实际值。
//将PixelType元素复制到局部变量cellValue中。
PixelType cellValue = cellDataIterator.Value();
std::cout << cellValue << std::endl;
++cellDataIterator; }
注意
- MeshType::CellsContainer::Iterator cellIterator中的cellIterator调用value方法,返回的是指向实际位置的指针
- MeshType::CellDataContainer::ConstIterator cellDataIterator中的cellDataIterator调用value方法,返回的是指针所在位置的数据值