[Insight-developers] New ITK data objects

Luis Ibanez luis.ibanez@kitware.com
Mon, 13 Jan 2003 22:37:17 -0500


Hi John,


If I understand correctly, your proposed class is describing
the concept of a N-valued function with one independent variable,

Something like

     R ----->  R^N

     t ------> { X1(t),X2(t),.... XN(t) }

Where "t" is the parameters and {X1,X2...XN} are the coordinates
of a point in N-D space associated with this parameter.

In the particular case of the chain codes the parameter "t"
will not go through the real domain but just the natural numbers
from 0 to (K-1}, where K is the chain length.

If this is the basic concept, you certainly can start this class
by deriving it from itk::FunctionBase, as Stephen suggested.


A close look itk::FunctionBase will show you that this class
couldn't be more generic !!

http://public.kitware.com/Insight/Doxygen/html/classitk_1_1FunctionBase.html

It just a class that accepts an input type and returns an
output type. Both the input and output are template parameters.
In other words, they can be *anything*.

The only API impossed by the class is the Evaluate() method.

By using this class you get the advantages of itk::Object for
free. This advantages include: SmartPointers and Event/Observers.


For example, the classical freeman code could be implemented
by selecting the input type to be unsigned int (since you don;t
use negative numbers to count the elements of the chain) and
the output could be an itk::Index< N > or a itk::Offset< N >.
(probably an Index<N> is better since in this way the chaincode
is associated to an image).


namespace itk {
class itk::FreemanChain : public
     FunctionBase< unsigned int,
                   Index<N> >
{

    typedef   FreemanChain Self;
    typedef   FunctionBase< unsigned int,
                            Index<N> >  Superclass;
    itkNewMacro(Self);
    typedef   Index<N>   IndexType;
protected:
    FreemanChain();
    ~FreemanChain();
public:
    IndexType Evaluate( unsigned int e );

    void SetChain( const char * chain )
        { m_Chain = chain; this->Modified(); }

    // more methods here....

private:
    IndexType      m_Start;  // initial index
    std::string    m_Chain;  // freeman codes

}; // end of class

}  // end of namespace itk



The evaluate method will return an index for every input "e"
in {0,1,2,...., K-1} for a chain of length K. The freeman
code will be stored in a std::string as the usual 1Byte
"0","1",...."7" if you use 8-connectivity.

I agree with you in that the chaincode is different from the
polygoncell and the boundary cell. It is ceirtainly worth to
add this family of countour classes to the toolkit, but they
don't have to initiate a new hierachy since they fit perfectly
as subclasses of FunctionBase.




Let's see below a possible instantiation of Fourier Smoothed
Contour. I assume that this class will be feed with a set of
points (or a set of indices) and will then interpolate a
continuous contour joining the points. The class could look
like:



namespace itk{
template < unsigned int N, class CoordRepType >
class FourierSmoothedContour :
    public FunctionBase< double, Point<CoordRepType,N> >
{
    typedef   FourierSmoothedContour Self;
    typedef   FunctionBase< double,
                            Point<CoordRefType,N> >  Superclass;
    typedef   Point<CoordRefType,N>  PointType;
    itkNewMacro(Self);
    typedef   VectorContainer< Index<N> > IndexContainer;
protected:
    FourierSmoothedContour();
    ~FourierSmoothedContour();
public:
    PointType Evaluate( double t )
     {
     // here use t to interpolate amont the indices in m_Indices
     }
    SetIndices( VectorContainer * indices ) {
       m_Indices = indices;
     }
private:
    IndexContainer::Pointer m_Indices;

};  // end of class

} // end of namespace itk




The parameter "t" in Evaluate() is a double, that presumably will
be in the range [0,1] and the result of the evaluation is an itk
point in N-D space.  Note that the Fourier contour doesn't have to
be a 2D class. And the implementation in N-D is not a "nightmare"
at all. It looks just like a 2D implementation since you simply
multiply the points by weights and the weights are the same no
matter what dimension is used for the points. The weights are given
by the fourier descriptors based on "t".

You can also implement chain codes in N-D without problem.
Of course this class in N-D represents a 1-D manifold,
and not a closed region as is the case only in 2-D.

I would not restrict this classes to be 2D since with very little
extra work (actually with less work...) you can keep them generic.
They will be useful for representing paths (or walks) in space.

Note that in some way ImageIterators are cousins of this classes.
Since they simply define an algorithm that generates the equivalent
of a chaincode and walk through it.  The chaincode class could
eventually be a mechanism for implementing the very much needed
Peano iterators, capable of exploring images. We could easily
implement an N-D image iterator that accepts a chaincode as
parameter and visit an image following the ImageIterators API

ChainCodeIterator it( image, region );
it.SetChainCode( chaincode );
it.GoToBegin()
while( !it.IsAtEnd() ) {
   PixelType value = it.Get();  evaluate the image.
   ++it; // move to the index given by next element in chaincode.
  }


---

Note that even the Circle classdoesn't have to be restricted to
2D. Think in the clinical applications...for example: Endoscopy.

You could think in using Parametric circles to represent contours
of the esophagus which is a 3D structure. There is no reason to
expect that the patient's esophagus can be perfectly aligned with
the slices of an MRI. So in general the circles representing
contours of this organ will be tilted with respect to the slices
and will lay natually in 3D. Your Circle class is still useful
defined over the range of Index<3> or over Point<double, 3>.

The internal ivars of the circle class are about the same
no matter what dimension it lies on. It will require a center
(which will be an Index<N> or a Point<double,N>) and a radius
(presumably a double).  The extra data that will catapult the
class in ND is a CovariantVector that will represent the normal
to the plane in which this circle lies in space.

You could also use the chain codes for representing centers
of blood vessels in 3-D (e.g. the medial line of the Aorta).
The only change will be in he interpretation of the codes
since in 3D you will have 26 potential neighbors to visits.



About the API:

Please avoid to use vnl classes in the API of your new ITK
classes. ITK has a good set of classes for representing space
concepts. Some of them actually use vnl classes internally,
but do not expose them to the API. You will find a description
of the Space representation classes in the SoftwareGuide.

http://public.kitware.com/Insight/Web/HTML/SoftwareGuide.pdf

You may also find useful to take a look at the BSpline classes.
Which also derive from FunctionBase. The Fourier smoothed contour
is basically a different implementation of an interpolation
method.

itkBSplineInterpolationWeightFunction.h
http://public.kitware.com/Insight/Doxygen/html/classitk_1_1BSplineInterpolationWeightFunction.html

You may want to consider using ContinuousIndex<N> instead of
Point<>, although the second is more interesting if you ever
expect to require computing the vector separating two of such
points. ContinuousIndex are accepted by image interpolators.
This is interesting since it is to expect that when walking
along a countour you may want to evaluate the pixels below....

With this, You could imagine a ContourToImageMetric, plug it
in the Registration Framework and segment the esophgus by
registering tilted circles with the image. Actually the
live-wire approach is not conceptually far from this...



-----


     Luis



====================================================================

galeotti wrote:

> I believe that both of these data structures are sufficiently general 
> and unique to justify making them seperate, new parent classes.  I think 
> that they should not descend from existing data structures for the 
> reasons given below.  Please let me know if my reasons are sufficient 
> and valid.  Thanks.
> 
> A chaincode is by nature represented as a starting index followed by a 
> sequence of local movements into neighboring pixels.  I believe (please 
> correct me if I'm wrong) that this is conceptually very different than 
> the indented purpose of the PolygonCell and PolygonBoundary classs. 
> Also, for efficiency reasons I store and frequently manipulate the 2D 
> steps of the chaincode as a single encoded byte.
> 
> Although ParametricFunction could syntatically descend from 
> SpatialFunction, I believe that it would be very counter-intuitive. 
> Parametric funcitons have a single independent variable that is defined 
> only over the domain from time = 0 to 1, but the have a multidimensional 
> spatial return value (the opposite of "typical" spatial functions).
> 
> My current ideal hierarchy of parametric function would be as shown 
> below.  What do you all think about making a special class for 2D 
> parametric functions?  Efficiently coding parametricfunction for ND 
> would be a nightmare, I think (especially for the Fourier smoothed 
> chaincode class descendent).
> 
> class ParametricFunction<outputtype>  // do-nothing parent class
> 
> class ParametricFunction2D<CoordinateType>
>     : public ParametricFunction< vnl_vector_fixed<CoordinateType,2> >
> 
> class ParametricFunction2DCircle<CoordinateType>
>     : public ParametricFunction2D<CoordinateType>
> class ParametricFunction2DPolygon<CoordinateType>
>     : public ParametricFunction2D<CoordinateType>
> class ParametricFunction2DFourierSmoothedChainCode<CoordinateType>
>     : public ParametricFunction2D
> 
> 
>   I think it makes more sense for chaincode and parametricfunction to 
> have their own class hierarchies, but I'd like to hear other developers 
> opinions, especially with regard to the reasons I have given.
> 
> Thank you,
> John
>