Chapter 5
Spatial Objects

This chapter introduces the basic classes that describe itk::SpatialObjects.

5.1 Introduction

We promote the philosophy that many of the goals of medical image processing are more effectively addressed if we consider them in the broader context of object processing. ITK’s Spatial Object class hierarchy provides a consistent API for querying, manipulating, and interconnecting objects in physical space. Via this API, methods can be coded to be invariant to the data structure used to store the objects being processed. By abstracting the representations of objects to support their representation by data structures other than images, a broad range of medical image analysis research is supported; key examples are described in the following.

Model-to-image registration.
A mathematical instance of an object can be registered with an image to localize the instance of that object in the image. Using SpatialObjects, mutual information, cross-correlation, and boundary-to-image metrics can be applied without modification to perform spatial object-to-image registration.
Model-to-model registration.
Iterative closest point, landmark, and surface distance minimization methods can be used with any ITK transform, to rigidly and non-rigidly register image, FEM, and Fourier descriptor-based representations of objects as SpatialObjects.
Atlas formation.
Collections of images or SpatialObjects can be integrated to represent expected object characteristics and their common modes of variation. Labels can be associated with the objects of an atlas.
Storing segmentation results from one or multiple scans.
Results of segmentations are best stored in physical/world coordinates so that they can be combined and compared with other segmentations from other images taken at other resolutions. Segmentation results from hand drawn contours, pixel labelings, or model-to-image registrations are treated consistently.
Capturing functional and logical relationships between objects.
SpatialObjects can have parent and children objects. Queries made of an object (such as to determine if a point is inside of the object) can be made to integrate the responses from the children object. Transformations applied to a parent can also be propagated to the children. Thus, for example, when a liver model is moved, its vessels move with it.
Conversion to and from images.
Basic functions are provided to render any SpatialObject (or collection of SpatialObjects) into an image.
IO.
SpatialObject reading and writing to disk is independent of the SpatialObject class hierarchy. Meta object IO (through itk::MetaImageIO) methods are provided, and others are easily defined.
Tubes, blobs, images, surfaces.
Are a few of the many SpatialObject data containers and types provided. New types can be added, generally by only defining one or two member functions in a derived class.

In the remainder of this chapter several examples are used to demonstrate the many spatial objects found in ITK and how they can be organized into hierarchies. Further the examples illustrate how to use SpatialObject transformations to control and calculate the position of objects in space.

5.2 Hierarchy

Spatial objects can be combined to form a hierarchy as a tree. By design, a SpatialObject can have one parent and only one. Moreover, each transform is stored within each object, therefore the hierarchy cannot be described as a Directed Acyclic Graph (DAG) but effectively as a tree. The user is responsible for maintaining the tree structure, no checking is done to ensure a cycle-free tree.

The source code for this section can be found in the file
SpatialObjectHierarchy.cxx.

This example describes how itk::SpatialObject can form a hierarchy. This first example also shows how to create and manipulate spatial objects.

  #include "itkSpatialObject.h"

First, we create two spatial objects and give them the names First Object and Second Object, respectively.

    using SpatialObjectType = itk::SpatialObject<3>;
  
    SpatialObjectType::Pointer object1 = SpatialObjectType ::New();
    object1->GetProperty().SetName("First Object");
  
    SpatialObjectType::Pointer object2 = SpatialObjectType ::New();
    object2->GetProperty().SetName("Second Object");

We then add the second object to the first one by using the AddChild() method. As a result object2 becomes a child of object1.

    object1->AddChild(object2);

Whenever the parameters of an object, including its parent-child relationships are changed, we must then call the Update() method so that the object-to-parent transforms and bounding box internally managed by each object are maintained. Calling Update() on an object automatically causes the Update() function of each child to be called.

    object1->Update();
object1-¿Update();

    if(object2->HasParent())
      {
      std::cout << "Name of the parent of the object2: ";
      std::cout << object2->GetParent()->GetProperty().GetName() << std::endl;
      }

To access the list of children of the object, the GetChildren() method returns a pointer to the (STL) list of children.

    SpatialObjectType::ChildrenListType  childrenList = object1->GetChildren();
    std::cout << "object1 has " << childrenList->size() << " child" << std::endl;
  
    SpatialObjectType::ChildrenListType::const_iterator it
      = childrenList->begin();
    while(it != childrenList->end())
      {
      std::cout << "Name of the child of the object 1: ";
      std::cout << (it)->GetProperty().GetName() << std::endl;
      ++it;
      }

Do NOT forget to delete the list of children since the GetChildren() function creates an internal list.

    delete childrenList;

An object can also be removed by using the RemoveChild() method, and then calling Update() on the now-orphaned child.

    object1->RemoveChild(object2);
    object2->Update();

Then, we can query the number of children an object has with the GetNumberOfChildren() method.

    std::cout << "Number of children for object1: ";
    std::cout << object1->GetNumberOfChildren() << std::endl;

The Clear() method erases all the information regarding the object as well as the data. This method is usually overloaded by derived classes. Note that the Parent-Child relationships of the object are NOT reset when Clear() is called; however, the object-to-parent transform is reset to Identity. As a result, Update() should be called before the object is re-used, to re-compute convenience member variables and values. To remove the children of a node, use the RemoveAllChildren() function.

    object1->Clear();
    object1->RemoveAllChildren();

The output of this first example looks like the following:

Name of the parent of the object2: First Object  
object1 has 1 child  
Name of the child of the object 1: Second Object  
Number of children for object1: 0

5.3 Transformations

The source code for this section can be found in the file
SpatialObjectTransforms.cxx.

s This example describes the different transformations and the Object and World ”spaces” associated with a spatial object.

Object Space
. SpatialObjects have one primary coordinate space that is readily available to them, their ObjectSpace. This is the space in which the object was inherently defined. No transforms are applied to the points/values that get/set into this space. All children of an object are added into this space.
ObjectToParentTransform
. SpatialObjects have only one transform that they directly control, their ObjectToParentTransform. This transform specifies how an object’s ObjectSpace is transformed to fit into its parent’s ObjectSpace. The ObjectToParentTransform is an affine transform, and it is confirmed to be invertible when assigned, or the assignment fails.
WorldSpace
. WorldSpace is not directly controlled by any SpatialObject except the SpatialObject at the top level of the parent-child tree hierarchy of Spatial Objects. That is, any SpatialObject that does not have a parent exists in a WorldSpace that is defined by applying its ObjectToParentTransform to its ObjectSpace.

Several member functions and variables are available to every SpatialObject so that they can readily access the WorldSpace in which they exist:

ProtectedComputeObjectToWorldTransform()
: This function is called whenever Update() is called. It composes the object’s ObjectToParentTransform with its parent’s cached ObjectToWorldTransform, to determine the transform from the object’s ObjectSpace to WorldSpace. This transform is always invertible. This call will cause all children objects to also update their cached ObjectToWorldTransform. This function should be called on the top level object (via Update()) once all children object’s ObjectToParentTransforms have been set. This function should be called on children objects when their ObjectToParentTransforms have been changed.
GetObjectToWorldTransform()
: Returns the cached ObjectToWorldTransform. It is the user’s responsibility to call Update() (and thereby ProtectedComputeObjectToWorldTransform()) when necessary, prior to calling GetObjectToWorldTransform(), otherwise the returned transform may be ”stale.”
SetObjectToWorldTransform()
: This function updates the object’s ObjectToParentTransform, using an inverse of the parent’s cached ObjectToWorldTransform, so that the composition of those transforms equal the transform passed to this function. If an object has no parent, its ObjectToParentTransform is equal to its ObjectToWorldTransform.

Like the first example, we create two spatial objects and give them the names First Object and Second Object, respectively.

    using SpatialObjectType = itk::SpatialObject<2>;
    using TransformType = SpatialObjectType::TransformType;
  
    SpatialObjectType::Pointer object1 = SpatialObjectType ::New();
    object1->GetProperty().SetName("First Object");
  
    SpatialObjectType::Pointer object2 = SpatialObjectType ::New();
    object2->GetProperty().SetName("Second Object");
    object1->AddChild(object2);

First we define a scaling factor of 2 for the object2. This is done by setting the Scale of the ObjectToParentTransform.

Note that this scaling would also apply to the children of object2, if it had children. If you wish to scale an object, but not its children, then those children aren’t actually ”children”, but they are siblings. So, you should insert a that holds the object and its children. Then you can manipulate the object’s transform/scaling independent of its siblings in that group, and if you wish to transform the object and its simblings, you apply that transform to the group.

    double scale[2];
    scale[0]=2;
    scale[1]=2;
    object2->GetModifiableObjectToParentTransform()->Scale(scale);

Next, we apply an offset on the ObjectToParentTransform to object1 which will also cauase a translation of its child, object2.

    TransformType::OffsetType object1Offset;
    object1Offset[0] = 4;
    object1Offset[1] = 3;
    object1->GetModifiableObjectToParentTransform()->SetOffset(object1Offset);

To realize the previous operations on the transformations, we should invoke the Update() that recomputes all dependent transformations.

By calling this function on object1, it will also descend to its children, thereby also updating the objectToWorldTransform for object2.

    object1->Update();

We can now display the ObjectToWorldTransform for both objects. One should notice that the only valid members of the Affine transformation are a Matrix and an Offset. For instance, when we invoke the Scale() method the internal Matrix is recomputed to reflect this change.

The AffineTransform performs the following computation

X′ = R ⋅(S⋅X - C)+ C+ V
(5.1)

Where R is the rotation matrix, S is a scaling factor, C is the center of rotation and V is a translation vector or offset. Therefore the affine matrix M and the affine offset T are defined as:

M = R⋅S
(5.2)

T =C + V - R⋅C
(5.3)

This means that Scale() and GetOffset() as well as the GetMatrix() might not be set to the expected value, especially if the transformation results from a composition with another transformation since the composition is done using the Matrix and the Offset of the affine transformation.

Next, we show the two affine transformations corresponding to the two objects.

First, the ObjectToParentTransform for object2:

    std::cout << "object2 ObjectToParent Matrix: " << std::endl;
    std::cout << object2->GetObjectToParentTransform()->GetMatrix() << std::endl;
    std::cout << "object2 ObjectToParent Offset: ";
    std::cout << object2->GetObjectToParentTransform()->GetOffset() << std::endl;

Second, the ObjectToWorldTransform that is cached, derived from the parent-child hierarchy and the composition of the corresponding ObjectToParentTransform, for object2:

    std::cout << "object2 ObjectToWorld Matrix: " << std::endl;
    std::cout << object2->GetObjectToWorldTransform()->GetMatrix() << std::endl;
    std::cout << "object2 ObjectToWorld Offset: ";
    std::cout << object2->GetObjectToWorldTransform()->GetOffset() << std::endl;

We can also update an object’s ObjectToParentTransform by changing its ObjectToWorldTransform and then calling ComputeObjectToParentTransform(), which changes the ObjectToParentTransform so as to achieve the cached ObjectToWorldTransform.

    TransformType::OffsetType Object1ToWorldOffset;
    Object1ToWorldOffset[0] = 3;
    Object1ToWorldOffset[1] = 3;
    object2->GetModifiableObjectToWorldTransform()->SetOffset(
      Object1ToWorldOffset);
    object2->ComputeObjectToParentTransform();

Finally, we display the resulting affine transformations. First, for the ObjectToParentTransform.

    std::cout << "object2 ObjectToParent Matrix: " << std::endl;
    std::cout << object2->GetObjectToParentTransform()->GetMatrix() << std::endl;
    std::cout << "object2 ObjectToParent Offset: ";
    std::cout << object2->GetObjectToParentTransform()->GetOffset() << std::endl;

Second, for the ObjectToWorldTransform.

    std::cout << "object2 ObjectToWorld Matrix: " << std::endl;
    std::cout << object2->GetObjectToWorldTransform()->GetMatrix() << std::endl;
    std::cout << "object2 ObjectToWorld Offset: ";
    std::cout << object2->GetObjectToWorldTransform()->GetOffset() << std::endl;

Also, as a child is disconnected from its parent, it should not move; so its ObjectToParentTransform should be updated to match its ]codeObjectToWorldTransform.

    object1->RemoveChild( object2 );
    object2->Update();
  
    std::cout << "object2 ObjectToWorld Matrix: " << std::endl;
    std::cout << object2->GetObjectToWorldTransform()->GetMatrix() << std::endl;
    std::cout << "object2 ObjectToWorld Offset: ";
    std::cout << object2->GetObjectToWorldTransform()->GetOffset() << std::endl;
    std::cout << "object2 ObjectToParent Matrix: " << std::endl;
    std::cout << object2->GetObjectToParentTransform()->GetMatrix() << std::endl;
    std::cout << "object2 ObjectToParent Offset: ";
    std::cout << object2->GetObjectToParentTransform()->GetOffset() << std::endl;

The output of this second example looks like the following:

object2 IndexToObject Matrix:  
2 0  
0 2  
object2 IndexToObject Offset: 0  0  
object2 IndexToWorld Matrix:  
2 0  
0 2  
object2 IndexToWorld Offset: 4  3  
object1 IndexToWorld Matrix:  
1 0  
0 1  
object1 IndexToWorld Offset: 3  3  
object2 IndexToWorld Matrix:  
2 0  
0 2  
object2 IndexToWorld Offset: 7  6

5.4 Types of Spatial Objects

This section describes in detail the variety of spatial objects implemented in ITK.

5.4.1 ArrowSpatialObject

The source code for this section can be found in the file
ArrowSpatialObject.cxx.

This example shows how to create an itk::ArrowSpatialObject. Let’s begin by including the appropriate header file.

  #include "itkArrowSpatialObject.h"

The itk::ArrowSpatialObject, like many SpatialObjects, is templated over the dimensionality of the object.

    using ArrowType = itk::ArrowSpatialObject<3>;
    ArrowType::Pointer myArrow = ArrowType::New();

The position of the arrow in the object (local) coordinate frame is done using the SetPositionInObjectSpace() method. By default the position is set to the origin of the space. This is the ”tip” of the arrow.

    ArrowType::PointType pos;
    pos.Fill(1);
    myArrow->SetPositionInObjectSpace(pos);

The length of the arrow in the local coordinate frame is done using the SetLengthInObjectSpace() method. By default the length is set to 1. This is the euclidean distance spanned by the arrow’s tail from its tip (position).

    myArrow->SetLengthInObjectSpace(2);

The direction of the arrow can be set using the SetDirectionInObjectSpace() method. This is the direction the tail of the arrow extends from the position. By default the direction is set along the X axis (first direction).

    ArrowType::VectorType direction;
    direction.Fill(0);
    direction[1] = 1.0;
    myArrow->SetDirectionInObjectSpace(direction);

5.4.2 BlobSpatialObject

The source code for this section can be found in the file
BlobSpatialObject.cxx.

itk::BlobSpatialObject defines an N-dimensional blob. This class derives from itk::itkPointBasedSpatialObject. A blob is defined as a list of points which compose the object.

Let’s start by including the appropriate header file.

  #include "itkBlobSpatialObject.h"

BlobSpatialObject is templated over the dimension of the space. A BlobSpatialObject contains a list of SpatialObjectPoints. Basically, a SpatialObjectPoint has a position and a color.

  #include "itkSpatialObjectPoint.h"

First we declare several standard type definitions.

Every Point-Based SpatialObject also provides type definitions for their SpatialObject point type (e.g., BlobPointType for BlobSpatialObject) as well as for a physical space point (e.g., an itk::Point).

  

Then, we create a list of points and we set the position of each point in the local coordinate system using the SetPositionInObjectSpace() method. We also set the color of each point to be red.

   BlobType::BlobPointListType list;
  
    for( unsigned int i=0; i<4; i++)
      {
      BlobPointType p;
      PointType pnt;
      pnt[0] = i;
      pnt[1] = i+1;
      pnt[2] = i+2;
      p.SetPositionInObjectSpace(pnt);
      p.SetRed(1);
      p.SetGreen(0);
      p.SetBlue(0);
      p.SetAlpha(1.0);
      list.push_back(p);
      }

Next, we create the blob and set its name using the SetName() function. We also set its Identification number with SetId() and we add the list of points previously created and call Update() so that the object can update its transforms, bounding boxes, and other cached convenience member variables.

    BlobPointer blob = BlobType::New();
    blob->GetProperty().SetName("My Blob");
    blob->SetId(1);
    blob->SetPoints(list);
    blob->Update();

The GetPoints() method returns a reference to the internal list of points of the object.

     BlobType::BlobPointListType pointList = blob->GetPoints();
     std::cout << "The blob contains " << pointList.size();
     std::cout << " points" << std::endl;

Then we can access the points using standard STL iterators and GetPositionInWorldSpace() and GetColor() functions return respectively the position and the color of the point.

GetPositionInWorldSpace applies the objectToParent transforms of all of the parent objects to this point. Since this object has no parents and since this object’s objecttoParent transform is the identify transform (by default), these world space positions are the same as the object space positions that were set.

    BlobType::BlobPointListType::const_iterator it = blob->GetPoints().begin();
    while(it != blob->GetPoints().end())
      {
      std::cout << "Position = " << (it).GetPositionInWorldSpace() << std::endl;
      std::cout << "Color = " << (it).GetColor() << std::endl;
      ++it;
      }

5.4.3 EllipseSpatialObject

The source code for this section can be found in the file
EllipseSpatialObject.cxx.

itk::EllipseSpatialObject defines an n-Dimensional ellipse. Like other spatial objects this class derives from itk::SpatialObject. Let’s start by including the appropriate header file.

  #include "itkEllipseSpatialObject.h"

Like most of the SpatialObjects, the itk::EllipseSpatialObject is templated over the dimension of the space. In this example we create a 3-dimensional ellipse.

    using EllipseType = itk::EllipseSpatialObject<3>;
    EllipseType::Pointer myEllipse = EllipseType::New();

Then we set a radius for each dimension. By default the radius is set to 1. Additionally, after setting the SpatialObject’s radius, we call Update() to update all transforms, bounding box, and other convenience variables within the class that its other member functions (e.g., IsInsideInWorldSpace()) depend upon.

    EllipseType::ArrayType radius;
    for (unsigned int i = 0; i<3; ++i)
      {
      radius[i] = i;
      }
  
    myEllipse->SetRadiusInObjectSpace(radius);
    myEllipse->Update();

Or if we have the same radius in each dimension we can do

    myEllipse->SetRadiusInObjectSpace(2.0);
    myEllipse->Update();

We can then display the current radius by using the GetRadiusInObjectSpace() function:

    EllipseType::ArrayType myCurrentRadius = myEllipse->GetRadiusInObjectSpace();
    std::cout << "Current radius is " << myCurrentRadius << std::endl;

Like other SpatialObjects, we can query the object if a point is inside the object by using the IsInsideInWorldSpace(itk::Point) function. This function expects the point to be in world coordinates.

    itk::Point<double,3> insidePoint;
    insidePoint.Fill(1.0);
    if (myEllipse->IsInsideInWorldSpace(insidePoint))
      {
      std::cout << "The point " << insidePoint;
      std::cout << " is really inside the ellipse" << std::endl;
      }
  
    itk::Point<double,3> outsidePoint;
    outsidePoint.Fill(3.0);
    if (!myEllipse->IsInsideInWorldSpace(outsidePoint))
      {
      std::cout << "The point " << outsidePoint;
      std::cout << " is really outside the ellipse" << std::endl;
      }

All spatial objects can be queried for a value at a point. The IsEvaluableAtInWorldSpace() function returns a boolean to know if the object is evaluable at a particular point.

     if (myEllipse->IsEvaluableAtInWorldSpace(insidePoint))
      {
      std::cout << "The point " << insidePoint;
      std::cout << " is evaluable at the point " << insidePoint << std::endl;
      }

If the object is evaluable at that point, the ValueAtInWorldSpace() function returns the current value at that position. Most of the objects returns a boolean value which is set to true when the point is inside the object and false when it is outside. However, for some objects, it is more interesting to return a value representing, for instance, the distance from the center of the object or the distance from the boundary.

    double value;
    myEllipse->ValueAtInWorldSpace(insidePoint,value);
    std::cout << "The value inside the ellipse is: " << value << std::endl;

Like other spatial objects, we can also query the bounding box of the object by using GetMyBoundingBoxInWorldSpace(). The resulting bounding box is the world space.

    const EllipseType::BoundingBoxType  boundingBox
      = myEllipse->GetMyBoundingBoxInWorldSpace();
    std::cout << "Bounding Box: " << boundingBox->GetBounds() << std::endl;

5.4.4 GaussianSpatialObject

The source code for this section can be found in the file
GaussianSpatialObject.cxx.

This example shows how to create a itk::GaussianSpatialObject which defines a Gaussian in a N-dimensional space. This object is particularly useful to query the value at a point in physical space. Let’s begin by including the appropriate header file.

  #include "itkGaussianSpatialObject.h"

The itk::GaussianSpatialObject is templated over the dimensionality of the object.

    using GaussianType = itk::GaussianSpatialObject<3>;
    GaussianType::Pointer myGaussian = GaussianType::New();

The SetMaximum() function is used to set the maximum value of the Gaussian.

    myGaussian->SetMaximum(2);

The radius of the Gaussian is defined by the SetRadius() method. By default the radius is set to 1.0.

    myGaussian->SetRadiusInObjectSpace(3);

The standard ValueAt() function is used to determine the value of the Gaussian at a particular point in physical space.

    itk::Point<double,3> pt;
    pt[0]=1;
    pt[1]=2;
    pt[2]=1;
    double value;
    myGaussian->ValueAtInWorldSpace(pt, value);
    std::cout << "ValueAtInWorldSpace(" << pt << ") = " << value << std::endl;

5.4.5 GroupSpatialObject

The source code for this section can be found in the file
GroupSpatialObject.cxx.

A itk::GroupSpatialObject does not have any data associated with it. It can be used to group objects or to add transforms to a current object. In this example we show how to use a GroupSpatialObject.

Let’s begin by including the appropriate header file.

  #include "itkGroupSpatialObject.h"

The itk::GroupSpatialObject is templated over the dimensionality of the object.

    using GroupType = itk::GroupSpatialObject<3>;
    GroupType::Pointer myGroup = GroupType::New();

Next, we create an itk::EllipseSpatialObject and add it to the group.

    using EllipseType = itk::EllipseSpatialObject<3>;
    EllipseType::Pointer myEllipse = EllipseType::New();
    myEllipse->SetRadiusInObjectSpace(2);
  
    myGroup->AddChild(myEllipse);

We then translate the group by 10mm in each direction. Therefore the ellipse is translated in physical space at the same time.

    GroupType::VectorType offset;
    offset.Fill(10);
    myGroup->GetModifiableObjectToParentTransform()->SetOffset(offset);
    myGroup->Update();

We can then query if a point is inside the group using the IsInsideInWorldSpace() function. We need to specify in this case that we want to consider all the hierarchy, therefore we set the depth to 2.

    GroupType::PointType point;
    point.Fill(10);
    std::cout << "Is my point " << point << " inside?: "
      <<  myGroup->IsInsideInWorldSpace(point,2) << std::endl;

Like any other SpatialObjects we can remove the ellipse from the group using the RemoveChild() method.

    myGroup->RemoveChild(myEllipse);

The source code for this section can be found in the file
SceneSpatialObject.cxx.

This example describes how to use the itk::GroupSpatialObject. A GroupSpatialObject contains a collection of SpatialObjects. This example begins by including the appropriate header file.

  #include "itkGroupSpatialObject.h"

An GroupSpatialObject is templated over the dimension of the space which requires all the objects referenced by the GroupSpatialObject to have the same dimension.

First we define some type definitions and we create the GroupSpatialObject.

    using GroupSpatialObjectType = itk::GroupSpatialObject<3>;
    GroupSpatialObjectType::Pointer scene = GroupSpatialObjectType::New();

Then we create two itk::EllipseSpatialObjects.

    using EllipseType = itk::EllipseSpatialObject<3>;
    EllipseType::Pointer ellipse1 = EllipseType::New();
    ellipse1->SetRadiusInObjectSpace(1);
    ellipse1->SetId(1);
    EllipseType::Pointer ellipse2 = EllipseType::New();
    ellipse2->SetId(2);
    ellipse2->SetRadiusInObjectSpace(2);

Then we add the two ellipses into the GroupSpatialObject.

    scene->AddChild(ellipse1);
    scene->AddChild(ellipse2);

We can query the number of object in the GroupSpatialObject with the GetNumberOfObjects() function. This function takes two optional arguments: the depth at which we should count the number of objects (default is set to infinity) and the name of the object to count (default is set to ITK_NULLPTR). This allows the user to count, for example, only ellipses.

    std::cout << "Number of objects in the GroupSpatialObject = ";
    std::cout << scene->GetNumberOfChildren() << std::endl;

The GetObjectById() returns the first object in the GroupSpatialObject that has the specified identification number.

    std::cout << "Object in the GroupSpatialObject with an ID == 2: "
              << std::endl;
    scene->GetObjectById(2)->Print(std::cout);

Objects can also be removed from the GroupSpatialObject using the RemoveSpatialObject() function.

    scene->RemoveChild(ellipse1);

The list of current objects in the GroupSpatialObject can be retrieved using the GetChildren() method. Like the GetNumberOfChildren() method, GetChildren() can take two arguments: a search depth and a matching name.

    GroupSpatialObjectType::ObjectListType  myObjectList =
      scene->GetChildren();
    std::cout << "Number of children in the GroupSpatialObject = ";
    std::cout << myObjectList->size() << std::endl;

In some cases, it is useful to define the hierarchy by using ParentId() and the current identification number. This results in having a flat list of SpatialObjects in the GroupSpatialObject. Therefore, the GroupSpatialObject provides the FixParentChildHierarchyUsingParentIds() method which reorganizes the Parent-Child hierarchy based on identification numbers.

    scene->FixParentChildHierarchyUsingParentIds();

The scene can also be cleared by using the RemoveAllChildren() function.

    scene->RemoveAllChildren();

5.4.6 ImageSpatialObject

The source code for this section can be found in the file
ImageSpatialObject.cxx.

An itk::ImageSpatialObject contains an itk::Image but adds the notion of spatial transformations and parent-child hierarchy. Let’s begin the next example by including the appropriate header file.

  #include "itkImageSpatialObject.h"

We first create a simple 2D image of size 10 by 10 pixels.

    using Image = itk::Image<short,2>;
    Image::Pointer image = Image::New();
    Image::SizeType size = {{ 10, 10 }};
    Image::RegionType region;
    region.SetSize(size);
    image->SetRegions(region);
    image->Allocate();

Next we fill the image with increasing values.

    using Iterator = itk::ImageRegionIterator<Image>;
    Iterator it(image,region);
    short pixelValue =0;
  
    for(it.GoToBegin(); !it.IsAtEnd(); ++it, ++pixelValue)
      {
      it.Set(pixelValue);
      }

We can now define the ImageSpatialObject which is templated over the dimension and the pixel type of the image.

    using ImageSpatialObject = itk::ImageSpatialObject<2,short>;
    ImageSpatialObject::Pointer imageSO = ImageSpatialObject::New();

Then we set the itkImage to the ImageSpatialObject by using the SetImage() function.

    imageSO->SetImage(image);
    imageSO->Update();

At this point we can use IsInside(), ValueAt() and DerivativeAt() functions inherent in SpatialObjects. The IsInside() value can be useful when dealing with registration.

    using Point = itk::Point<double,2>;
    Point insidePoint;
    insidePoint.Fill(9);
  
    if( imageSO->IsInsideInWorldSpace(insidePoint) )
      {
      std::cout << insidePoint << " is inside the image." << std::endl;
      }

The ValueAtInWorldSpace() returns the value of the closest pixel, i.e no interpolation, to a given physical point.

    double returnedValue;
    imageSO->ValueAtInWorldSpace(insidePoint,returnedValue);
    std::cout << "ValueAt(" << insidePoint << ") = " << returnedValue
              << std::endl;

The derivative at a specified position in space can be computed using the DerivativeAt() function. The first argument is the point in physical coordinates where we are evaluating the derivatives. The second argument is the order of the derivation, and the third argument is the result expressed as a itk::Vector. Derivatives are computed iteratively using finite differences and, like the ValueAt(), no interpolator is used.

    ImageSpatialObject::DerivativeVectorType returnedDerivative;
    imageSO->DerivativeAtInWorldSpace(insidePoint,1,returnedDerivative);
    std::cout << "First derivative at " << insidePoint;
    std::cout << " = " << returnedDerivative << std::endl;

5.4.7 ImageMaskSpatialObject

The source code for this section can be found in the file
ImageMaskSpatialObject.cxx.

An itk::ImageMaskSpatialObject is similar to the itk::ImageSpatialObject and derived from it. However, the main difference is that the IsInsideInWorldSpace() returns true if the pixel intensity in the image is not zero.

The supported pixel types does not include itk::RGBPixel, itk::RGBAPixel, etc. So far it only allows to manage images of simple types like unsigned short, unsigned int, or itk::Vector. Let’s begin by including the appropriate header file.

  #include "itkImageMaskSpatialObject.h"

The ImageMaskSpatialObject is templated over the dimensionality.

    using ImageMaskSpatialObject = itk::ImageMaskSpatialObject<3>;

Next we create an itk::Image of size 50x50x50 filled with zeros except a bright square in the middle which defines the mask.

    using PixelType = ImageMaskSpatialObject::PixelType;
    using ImageType = ImageMaskSpatialObject::ImageType;
    using Iterator = itk::ImageRegionIterator< ImageType >;
  
    ImageType::Pointer image = ImageType::New();
    ImageType::SizeType size = {{ 50, 50, 50 }};
    ImageType::IndexType index = {{ 0, 0, 0 }};
    ImageType::RegionType region;
  
    region.SetSize(size);
    region.SetIndex(index);
  
    image->SetRegions( region );
    image->Allocate(true); // initialize buffer to zero
  
    ImageType::RegionType insideRegion;
    ImageType::SizeType insideSize   = {{ 30, 30, 30 }};
    ImageType::IndexType insideIndex = {{ 10, 10, 10 }};
    insideRegion.SetSize( insideSize );
    insideRegion.SetIndex( insideIndex );
  
    Iterator it( image, insideRegion );
    it.GoToBegin();
  
    while( !it.IsAtEnd() )
      {
      it.Set( itk::NumericTraits< PixelType >::max() );
      ++it;
      }

Then, we create an ImageMaskSpatialObject.

    ImageMaskSpatialObject::Pointer maskSO = ImageMaskSpatialObject::New();

We then pass the corresponding pointer to the image.

    maskSO->SetImage(image);
    maskSO->Update();

We can then test if a physical itk::Point is inside or outside the mask image. This is particularly useful during the registration process when only a part of the image should be used to compute the metric.

    ImageMaskSpatialObject::PointType  inside;
    inside.Fill(20);
    std::cout << "Is my point " << inside << " inside my mask? "
      << maskSO->IsInsideInWorldSpace(inside) << std::endl;
    ImageMaskSpatialObject::PointType  outside;
    outside.Fill(45);
    std::cout << "Is my point " << outside << " outside my mask? "
      << !maskSO->IsInsideInWorldSpace(outside) << std::endl;

5.4.8 LandmarkSpatialObject

The source code for this section can be found in the file
LandmarkSpatialObject.cxx.

itk::LandmarkSpatialObject contains a list of itk::SpatialObjectPoints which have a position and a color. Let’s begin this example by including the appropriate header file.

  #include "itkLandmarkSpatialObject.h"

LandmarkSpatialObject is templated over the dimension of the space.

Here we create a 3-dimensional landmark.

    using LandmarkType = itk::LandmarkSpatialObject<3>;
    using LandmarkPointer = LandmarkType::Pointer;
    using LandmarkPointType = LandmarkType::LandmarkPointType;
    using PointType = LandmarkType::PointType;
  
    LandmarkPointer landmark = LandmarkType::New();

Next, we set some properties of the object like its name and its identification number.

    landmark->GetProperty().SetName("Landmark1");
    landmark->SetId(1);

We are now ready to add points into the landmark. We first create a list of SpatialObjectPoint and for each point we set the position and the color.

    LandmarkType::LandmarkPointListType list;
  
    for (unsigned int i=0; i<5; ++i)
      {
      LandmarkPointType p;
      PointType pnt;
      pnt[0] = i;
      pnt[1] = i+1;
      pnt[2] = i+2;
      p.SetPositionInObjectSpace(pnt);
      p.SetColor(1,0,0,1);
      list.push_back(p);
      }

Then we add the list to the object using the SetPoints() method. Calling Update() afterwards ensures that World Space representations are also updated to match changes in the SpatialObject’s parameters.

    landmark->SetPoints(list);
    landmark->Update();

The current point list can be accessed using the GetPoints() method. The method returns a reference to the (STL) list.

    size_t nPoints = landmark->GetPoints().size();
    std::cout << "Number of Points in the landmark: " << nPoints << std::endl;
  
    LandmarkType::LandmarkPointListType::const_iterator it
      = landmark->GetPoints().begin();
    while(it != landmark->GetPoints().end())
      {
      std::cout << "Position: " << (it).GetPositionInObjectSpace() << std::endl;
      std::cout << "Color: " << (it).GetColor() << std::endl;
      ++it;
      }

5.4.9 LineSpatialObject

The source code for this section can be found in the file
LineSpatialObject.cxx.

itk::LineSpatialObject defines a line in an n-dimensional space. A line is defined as a list of points which compose the line, i.e a polyline. We begin the example by including the appropriate header files.

  #include "itkLineSpatialObject.h"

LineSpatialObject is templated over the dimension of the space. A LineSpatialObject contains a list of LineSpatialObjectPoints. A LineSpatialObjectPoint has a position, n-1 normals and a color. Each normal is expressed as a itk::CovariantVector of size N.

First, we define some type definitions and we create our line.

    using LineType = itk::LineSpatialObject<3>;
    using LinePointer = LineType::Pointer;
    using LinePointType = LineType::LinePointType;
  
    using PointType = LineType::PointType;
    using CovariantVectorType = LineType::CovariantVectorType;
  
    LinePointer Line = LineType::New();

We create a point list and we set the position of each point in the local coordinate system using the SetPosition() method. We also set the color of each point to red.

The two normals are set using the SetNormal() function; the first argument is the normal itself and the second argument is the index of the normal.

    LineType::LinePointListType list;
  
    for (unsigned int i=0; i<3; ++i)
      {
      LinePointType p;
      PointType pnt;
      pnt[0] = i;
      pnt[1] = i+1;
      pnt[2] = i+2;
      p.SetPositionInObjectSpace(pnt);
      p.SetColor(1,0,0,1);
  
      CovariantVectorType normal1;
      CovariantVectorType normal2;
      for (unsigned int j=0; j<3; ++j)
        {
        normal1[j]=j;
        normal2[j]=j2;
        }
  
      p.SetNormalInObjectSpace(normal1,0);
      p.SetNormalInObjectSpace(normal2,1);
      list.push_back(p);
      }

Next, we set the name of the object using SetName(). We also set its identification number with SetId() and we set the list of points previously created.

    Line->GetProperty().SetName("Line1");
    Line->SetId(1);
    Line->SetPoints(list);
    Line->Update();

The GetPoints() method returns a reference to the internal list of points of the object.

     LineType::LinePointListType pointList = Line->GetPoints();
     std::cout << "Number of points representing the line: ";
     std::cout << pointList.size() << std::endl;

Then we can access the points using standard STL iterators. The GetPosition() and GetColor() functions return respectively the position and the color of the point. Using the GetNormal(unsigned int) function we can access each normal.

     LineType::LinePointListType::const_iterator it = Line->GetPoints().begin();
     while (it != Line->GetPoints().end())
       {
       std::cout << "Position = " << (it).GetPositionInObjectSpace()
         << std::endl;
       std::cout << "Color = " << (it).GetColor() << std::endl;
       std::cout << "First normal = " << (it).GetNormalInObjectSpace(0)
         << std::endl;
       std::cout << "Second normal = " << (it).GetNormalInObjectSpace(1)
         << std::endl;
       std::cout << std::endl;
       ++it;
       }

5.4.10 MeshSpatialObject

The source code for this section can be found in the file
MeshSpatialObject.cxx.

A itk::MeshSpatialObject contains a pointer to an itk::Mesh but adds the notion of spatial transformations and parent-child hierarchy. This example shows how to create an itk::MeshSpatialObject, use it to form a binary image, and write the mesh to disk.

Let’s begin by including the appropriate header file.

  #include "itkSpatialObjectToImageFilter.h"
  #include "itkMeshSpatialObject.h"
  #include "itkSpatialObjectReader.h"
  #include "itkSpatialObjectWriter.h"

The MeshSpatialObject wraps an itk::Mesh, therefore we first create a mesh.

    using MeshTrait = itk::DefaultDynamicMeshTraits< float, 3, 3 >;
    using MeshType = itk::Mesh< float, 3, MeshTrait >;
    using CellTraits = MeshType::CellTraits;
    using CellInterfaceType = itk::CellInterface< float, CellTraits >;
    using TetraCellType = itk::TetrahedronCell< CellInterfaceType >;
    using PointType = MeshType::PointType;
    using CellType = MeshType::CellType;
    using CellAutoPointer = CellType::CellAutoPointer;
    MeshType::Pointer myMesh = MeshType::New();
  
    MeshType::CoordRepType testPointCoords[4][3]
      = { {0,0,0}, {9,0,0}, {9,9,0}, {0,0,9} };
  
    MeshType::PointIdentifier tetraPoints[4] = {0,1,2,4};
  
    int i;
    for(i=0; i < 4; ++i)
      {
      myMesh->SetPoint(i, PointType(testPointCoords[i]));
      }
  
    myMesh->SetCellsAllocationMethod(
        MeshType::CellsAllocatedDynamicallyCellByCell );
    CellAutoPointer testCell1;
    testCell1.TakeOwnership(  new TetraCellType );
    testCell1->SetPointIds(tetraPoints);
    myMesh->SetCell(0, testCell1 );

We then create a MeshSpatialObject which is templated over the type of mesh previously defined...

    using MeshSpatialObjectType = itk::MeshSpatialObject< MeshType >;
    MeshSpatialObjectType::Pointer myMeshSpatialObject
      = MeshSpatialObjectType::New();

... and pass the Mesh pointer to the MeshSpatialObject

    myMeshSpatialObject->SetMesh(myMesh);
    myMeshSpatialObject->Update();

The actual pointer to the passed mesh can be retrieved using the GetMesh() function, just like any other SpatialObjects.

    myMeshSpatialObject->GetMesh();

The GetBoundingBox(), ValueAt(), IsInside() functions can be used to access important information.

    std::cout << "Mesh bounds : "
      << myMeshSpatialObject->GetMyBoundingBoxInWorldSpace()->GetBounds()
      << std::endl;
    MeshSpatialObjectType::PointType myPhysicalPoint;
    myPhysicalPoint.Fill(1);
    std::cout << "Is my physical point inside? : "
      << myMeshSpatialObject->IsInsideInWorldSpace(myPhysicalPoint) << std::endl;

Now that we have defined the MeshSpatialObject, we can save the actual mesh using the itk::SpatialObjectWriter. In order to do so, we need to specify the type of Mesh we are writing.

    using WriterType = itk::SpatialObjectWriter< 3, float, MeshTrait >;
    WriterType::Pointer writer = WriterType::New();

Then we set the mesh spatial object and the name of the file and call the the Update() function.

    writer->SetInput(myMeshSpatialObject);
    writer->SetFileName("myMesh.meta");
    writer->Update();

Reading the saved mesh is done using the itk::SpatialObjectReader. Once again we need to specify the type of mesh we intend to read.

    using ReaderType = itk::SpatialObjectReader< 3, float, MeshTrait >;
    ReaderType::Pointer reader = ReaderType::New();

We set the name of the file we want to read and call update

    reader->SetFileName("myMesh.meta");
    reader->Update();

Next, we show how to create a binary image of a MeshSpatialObject using the itk::SpatialObjectToImageFilter. The resulting image will have ones inside and zeros outside the mesh. First we define and instantiate the SpatialObjectToImageFilter.

    using ImageType = itk::Image< unsigned char, 3 >;
    using GroupType = itk::GroupSpatialObject< 3 >;
    using SpatialObjectToImageFilterType =
        itk::SpatialObjectToImageFilter< GroupType, ImageType >;
    SpatialObjectToImageFilterType::Pointer imageFilter =
      SpatialObjectToImageFilterType::New();

Then we pass the output of the reader, i.e the MeshSpatialObject, to the filter.

    imageFilter->SetInput(  reader->GetGroup()  );

Finally we trigger the execution of the filter by calling the Update() method. Note that depending on the size of the mesh, the computation time can increase significantly.

    imageFilter->Update();

Then we can get the resulting binary image using the GetOutput() function.

    ImageType::Pointer myBinaryMeshImage = imageFilter->GetOutput();

5.4.11 SurfaceSpatialObject

The source code for this section can be found in the file
SurfaceSpatialObject.cxx.

itk::SurfaceSpatialObject defines a surface in n-dimensional space. A SurfaceSpatialObject is defined by a list of points which lie on the surface. Each point has a position and a unique normal. The example begins by including the appropriate header file.

  #include "itkSurfaceSpatialObject.h"

SurfaceSpatialObject is templated over the dimension of the space. A SurfaceSpatialObject contains a list of SurfaceSpatialObjectPoints. A SurfaceSpatialObjectPoint has a position, a normal and a color.

First we define some type definitions

    using SurfaceType = itk::SurfaceSpatialObject<3>;
    using SurfacePointer = SurfaceType::Pointer;
  
    using SurfacePointType = SurfaceType::SurfacePointType;
    using CovariantVectorType = SurfaceType::CovariantVectorType;
    using PointType = SurfaceType::PointType;
  
    SurfacePointer surface = SurfaceType::New();

We create a point list and we set the position of each point in the local coordinate system using the SetPosition() method. We also set the color of each point to red.

    SurfaceType::SurfacePointListType list;
  
    for( unsigned int i=0; i<3; i++)
      {
      SurfacePointType p;
      PointType pnt;
      pnt[0] = i;
      pnt[1] = i+1;
      pnt[2] = i+2;
      p.SetPositionInObjectSpace(pnt);
      p.SetColor(1,0,0,1);
      CovariantVectorType normal;
      for(unsigned int j=0;j<3;j++)
        {
        normal[j]=j;
        }
      p.SetNormalInObjectSpace(normal);
      list.push_back(p);
      }

Next, we create the surface and set his name using SetName(). We also set its Identification number with SetId() and we add the list of points previously created.

    surface->GetProperty().SetName("Surface1");
    surface->SetId(1);
    surface->SetPoints(list);
    surface->Update();

The GetPoints() method returns a reference to the internal list of points of the object.

     SurfaceType::SurfacePointListType pointList = surface->GetPoints();
     std::cout << "Number of points representing the surface: ";
     std::cout << pointList.size() << std::endl;

Then we can access the points using standard STL iterators. GetPosition() and GetColor() functions return respectively the position and the color of the point. GetNormal() returns the normal as a itk::CovariantVector.

     SurfaceType::SurfacePointListType::const_iterator it
       = surface->GetPoints().begin();
     while(it != surface->GetPoints().end())
       {
       std::cout << "Position = " << (it).GetPositionInObjectSpace()
         << std::endl;
       std::cout << "Normal = " << (it).GetNormalInObjectSpace() << std::endl;
       std::cout << "Color = " << (it).GetColor() << std::endl;
       std::cout << std::endl;
       it++;
       }

5.4.12 TubeSpatialObject

itk::TubeSpatialObject is a class for the representation of tubular structures using SpatialObjects. In particular, it is intended to be used to represent vascular networks extracted from 2D and 3D images. It can also be used to represent airways, nerves, bile ducts, and more.

The class itk::DTITubeSpatialObject is derived from this class and adds constructs for representing fiber tracts from diffusion tensor images.

The source code for this section can be found in the file
TubeSpatialObject.cxx.

itk::TubeSpatialObject defines an n-dimensional tube. A tube is defined as a list of centerline points which have a position, a radius, some normals and other properties. Let’s start by including the appropriate header file.

  #include "itkTubeSpatialObject.h"

TubeSpatialObject is templated over the dimension of the space. A TubeSpatialObject contains a list of TubeSpatialObjectPoints.

First we define some type definitions and we create the tube.

    using TubeType = itk::TubeSpatialObject<3>;
    using TubePointer = TubeType::Pointer;
  
    using TubePointType = TubeType::TubePointType;
  
    using PointType = TubeType::PointType;
    using CovariantVectorType = TubePointType::CovariantVectorType;
  
    TubePointer tube = TubeType::New();

We create a point list and we set:

  1. The position of each point in the local coordinate system using the SetPosition() method.
  2. The radius of the tube at this position using SetRadius().
  3. The two normals at the tube is set using SetNormal1() and SetNormal2().
  4. The color of the point is set to red in our case.

    TubeType::TubePointListType list;
    for (i=0; i<5; ++i)
      {
      TubePointType p;
      PointType pnt;
      pnt[0] = i;
      pnt[1] = i+1;
      pnt[2] = i+2;
      p.SetPositionInObjectSpace(pnt);
      p.SetRadiusInObjectSpace(1);
      CovariantVectorType normal1;
      CovariantVectorType normal2;
      for (unsigned int j=0; j<3; ++j)
        {
        normal1[j]=j;
        normal2[j]=j2;
        }
  
      p.SetNormal1InObjectSpace(normal1);
      p.SetNormal2InObjectSpace(normal2);
      p.SetColor(1,0,0,1);
  
      list.push_back(p);
      }

Next, we create the tube and set its name using SetName(). We also set its identification number with SetId() and, at the end, we add the list of points previously created.

    tube->GetProperty().SetName("Tube1");
    tube->SetId(1);
    tube->SetPoints(list);
    tube->Update();

The GetPoints() method return a reference to the internal list of points of the object.

    TubeType::TubePointListType pointList = tube->GetPoints();
    std::cout << "Number of points representing the tube: ";
    std::cout << pointList.size() << std::endl;

The ComputeTangentAndNormals() function computes the normals and the tangent for each point using finite differences.

    tube->ComputeTangentAndNormals();

Then we can access the points using STL iterators. GetPosition() and GetColor() functions return respectively the position and the color of the point. GetRadius() returns the radius at that point. GetNormal1() and GetNormal1() functions return a itk::CovariantVector and GetTangent() returns a itk::Vector.

    TubeType::TubePointListType::const_iterator it = tube->GetPoints().begin();
    i=0;
    while(it != tube->GetPoints().end())
      {
      std::cout << std::endl;
      std::cout << "Point #" << i << std::endl;
      std::cout << "Position: " << (it).GetPositionInObjectSpace() << std::endl;
      std::cout << "Radius: " << (it).GetRadiusInObjectSpace() << std::endl;
      std::cout << "Tangent: " << (it).GetTangentInObjectSpace() << std::endl;
      std::cout << "First Normal: " << (it).GetNormal1InObjectSpace()
        << std::endl;
      std::cout << "Second Normal: " << (it).GetNormal2InObjectSpace()
        << std::endl;
      std::cout << "Color = " << (it).GetColor() << std::endl;
      it++;
      i++;
      }

5.4.13 DTITubeSpatialObject

The source code for this section can be found in the file
DTITubeSpatialObject.cxx.

itk::DTITubeSpatialObject derives from itk::TubeSpatialObject. It represents a fiber tracts from Diffusion Tensor Imaging. A DTITubeSpatialObject is described as a list of centerline points which have a position, a radius, normals, the fractional anisotropy (FA) value, the ADC value, the geodesic anisotropy (GA) value, the eigenvalues and vectors as well as the full tensor matrix.

Let’s start by including the appropriate header file.

  #include "itkDTITubeSpatialObject.h"

DTITubeSpatialObject is templated over the dimension of the space. A DTITubeSpatialObject contains a list of DTITubeSpatialObjectPoints.

First we define some type definitions and we create the tube.

    using DTITubeType = itk::DTITubeSpatialObject<3>;
    using DTITubePointType = DTITubeType::DTITubePointType;
    using PointType = DTITubeType::PointType;
  
    DTITubeType::Pointer dtiTube = DTITubeType::New();

We create a point list and we set:

  1. The position of each point in the local coordinate system using the SetPosition() method.
  2. The radius of the tube at this position using SetRadius().
  3. The FA value using AddField(DTITubePointType::FA).
  4. The ADC value using AddField(DTITubePointType::ADC).
  5. The GA value using AddField(DTITubePointType::GA).
  6. The full tensor matrix supposed to be symmetric definite positive value using SetTensorMatrix().
  7. The color of the point is set to red in our case.

    DTITubeType::DTITubePointListType list;
    for (i=0; i<5; ++i)
      {
      DTITubePointType p;
      PointType pnt;
      pnt[0] = i;
      pnt[1] = i+1;
      pnt[2] = i+2;
      p.SetPositionInObjectSpace(pnt);
      p.SetRadiusInObjectSpace(1);
      p.AddField(DTITubePointType::FA,i);
      p.AddField(DTITubePointType::ADC,2i);
      p.AddField(DTITubePointType::GA,3i);
      p.AddField("Lambda1",4i);
      p.AddField("Lambda2",5i);
      p.AddField("Lambda3",6i);
      auto  v = new float[6];
      for(unsigned int k=0;k<6;k++)
        {
        v[k] = k;
        }
      p.SetTensorMatrix(v);
      delete[] v;
      p.SetColor(1,0,0,1);
      list.push_back(p);
      }

Next, we create the tube and set its name using SetName(). We also set its identification number with SetId() and, at the end, we add the list of points previously created.

    dtiTube->GetProperty().SetName("DTITube");
    dtiTube->SetId(1);
    dtiTube->SetPoints(list);
    dtiTube->Update();

The GetPoints() method return a reference to the internal list of points of the object.

    DTITubeType::DTITubePointListType pointList = dtiTube->GetPoints();
    std::cout << "Number of points representing the fiber tract: ";
    std::cout << pointList.size() << std::endl;

Then we can access the points using STL iterators. GetPosition() and GetColor() functions return respectively the position and the color of the point.

    DTITubeType::DTITubePointListType::const_iterator it
      = dtiTube->GetPoints().begin();
    i=0;
    while(it != dtiTube->GetPoints().end())
      {
      std::cout << std::endl;
      std::cout << "Point #" << i << std::endl;
      std::cout << "Position: " << (it).GetPositionInObjectSpace() << std::endl;
      std::cout << "Radius: " << (it).GetRadiusInObjectSpace() << std::endl;
      std::cout << "FA: " << (it).GetField(DTITubePointType::FA) << std::endl;
      std::cout << "ADC: " << (it).GetField(DTITubePointType::ADC) << std::endl;
      std::cout << "GA: " << (it).GetField(DTITubePointType::GA) << std::endl;
      std::cout << "Lambda1: " << (it).GetField("Lambda1") << std::endl;
      std::cout << "Lambda2: " << (it).GetField("Lambda2") << std::endl;
      std::cout << "Lambda3: " << (it).GetField("Lambda3") << std::endl;
      std::cout << "TensorMatrix: " << (it).GetTensorMatrix()[0] << " : ";
      std::cout << (it).GetTensorMatrix()[1] << " : ";
      std::cout << (it).GetTensorMatrix()[2] << " : ";
      std::cout << (it).GetTensorMatrix()[3] << " : ";
      std::cout << (it).GetTensorMatrix()[4] << " : ";
      std::cout << (it).GetTensorMatrix()[5] << std::endl;
      std::cout << "Color = " << (it).GetColor() << std::endl;
      ++it;
      ++i;
      }

5.5 Read/Write SpatialObjects

The source code for this section can be found in the file
ReadWriteSpatialObject.cxx.

Reading and writing SpatialObjects is a fairly simple task. The classes itk::SpatialObjectReader and itk::SpatialObjectWriter are used to read and write these objects, respectively. (Note these classes make use of the MetaIO auxiliary I/O routines and therefore have a .meta file suffix.)

We begin this example by including the appropriate header files.

  #include "itkSpatialObjectReader.h"
  #include "itkSpatialObjectWriter.h"
  #include "itkEllipseSpatialObject.h"

Next, we create a SpatialObjectWriter that is templated over the dimension of the object(s) we want to write.

    using WriterType = itk::SpatialObjectWriter<3>;
    WriterType::Pointer writer = WriterType::New();

For this example, we create an itk::EllipseSpatialObject.

    using EllipseType = itk::EllipseSpatialObject<3>;
    EllipseType::Pointer ellipse = EllipseType::New();
    ellipse->SetRadiusInObjectSpace(3);

Finally, we set to the writer the object to write using the SetInput() method and we set the name of the file with SetFileName() and call the Update() method to actually write the information.

    writer->SetInput(ellipse);
    writer->SetFileName("ellipse.meta");
    writer->Update();

Now we are ready to open the freshly created object. We first create a SpatialObjectReader which is also templated over the dimension of the object in the file. This means that the file should contain only objects with the same dimension.

    using ReaderType = itk::SpatialObjectReader<3>;
    ReaderType::Pointer reader = ReaderType::New();

Next we set the name of the file to read using SetFileName() and we call the Update() method to read the file.

    reader->SetFileName("ellipse.meta");
    reader->Update();

To get the objects in the file you can call the GetGroup() method. Calls to GetGroup() returns an pointer to a itk::GroupSpatialObject.

    ReaderType::GroupType  group = reader->GetGroup();
    std::cout << "Number of objects in the group: ";
    std::cout << group->GetNumberOfChildren() << std::endl;

5.6 Statistics Computation via SpatialObjects

The source code for this section can be found in the file
SpatialObjectToImageStatisticsCalculator.cxx.

This example describes how to use the itk::SpatialObjectToImageStatisticsCalculator to compute statistics of an itk::Image only in a region defined inside a given itk::SpatialObject.

  #include "itkSpatialObjectToImageStatisticsCalculator.h"

We first create a test image using the itk::RandomImageSource

    using ImageType = itk::Image< unsigned char, 2 >;
    using RandomImageSourceType = itk::RandomImageSource< ImageType >;
    RandomImageSourceType::Pointer randomImageSource
      = RandomImageSourceType::New();
    ImageType::SizeValueType size[2];
    size[0] = 10;
    size[1] = 10;
    randomImageSource->SetSize(size);
    randomImageSource->Update();
    ImageType::Pointer image = randomImageSource->GetOutput();

Next we create an itk::EllipseSpatialObject with a radius of 2. We also move the ellipse to the center of the image.

    using EllipseType = itk::EllipseSpatialObject<2>;
    EllipseType::Pointer ellipse = EllipseType::New();
    ellipse->SetRadiusInObjectSpace(2);
    EllipseType::PointType offset;
    offset.Fill(5);
    ellipse->SetCenterInObjectSpace(offset);
    ellipse->Update();

Then we can create the itk::SpatialObjectToImageStatisticsCalculator.

    using CalculatorType = itk::SpatialObjectToImageStatisticsCalculator<
      ImageType, EllipseType >;
    CalculatorType::Pointer calculator = CalculatorType::New();

We pass a pointer to the image to the calculator.

    calculator->SetImage(image);

We also pass the SpatialObject. The statistics will be computed inside the SpatialObject (Internally the calculator is using the IsInside() function).

    calculator->SetSpatialObject(ellipse);

At the end we trigger the computation via the Update() function and we can retrieve the mean and the covariance matrix using GetMean() and GetCovarianceMatrix() respectively.

    calculator->Update();
    std::cout << "Sample mean = " << calculator->GetMean() << std::endl;
    std::cout << "Sample covariance = " << calculator->GetCovarianceMatrix();