Up: Diffusion Tensor Imaging
This chapter should help to get
started with the program code and expand it for further
needs. Appendix A is a documentation
of the most important classes, programs, and the choosen xml data representation.
Figure 9.1 shows class hierarchy. Only the main
dependencies are illustrated. The libraries used are
VTK for the visualization, CLAPACK for the mathematical computations
and Xerces-C for processing the xml-header. The rightmost column are
files containing functions that do not belong to a class and therefore
do not have any private members. The bottom row shows the main
applications that have been implemented. Only the main dependencies
have been indicated.
are classes implementing the low-level data
structures for a tensor respectively an eigensystem. Every operation that
acts on single elements of these types should be implemented in
the respective class.
contains the basic definitions of any field. The
class itself does not contain any field though. It mainly reads and
writes the xml-header. The information is stored as protected members
so only derived classes can access them directly. Functions are
provided to set and get the single characteristics of a data set, so
that accessing these values is equal for all types of data fields. The most
important informations stored in this class are the volume dimensions,
the voxel dimensions and the location of the data files.
For scalar data, i.e. floating point or integer numbers, no special
class has been implemented. That means that any function that works on
scalar fields has no private members and therefore all information
(volume and voxel dimensions) need to be provided when the functions are
Functions that use the tensor data
structure, that is 3 3 matrices for computations, are placed
in tensorfunctions.h. Like functions in
rsfunctions.h they do not belong to a class and so volume and
voxel dimensions need to be provided. The most interesting functions
are localWarpTensorField(...) for the local transformation of
the tensors after they have been displaced and
findPointsInFloatArray(...) to select the points with high
local structure. displaceTensorField(...) applies the
displacement field on every single component of a tensorfield.
all derived from basefield.h and inherit all its
members. When initializing one of this classes the actual field of the
respective type is
generated, which can be empty or read from a file.
The classes tensor.h, eigen.h and basefield.h build the bases
for the current implementation and are therefore documented in
Appendix A. For any class and function
collection the *.h file contains a small documentation for each
contains some basic functions to display
images. For scalar data the display is generated by writing a temporary
ppm file. Then a system call opens the generated file with
xv. The temporary files are all deleted when the destructor
of the class is called.
The tensor representation as ellipsoid uses VTK. In this case the VTK
renderer is used to display the image.
The remaining functions in this class can be used to display specific
properties of a tensor- or eigenfield.
is not a class but a collection of
functions. Any function that does not need a private member or
specific data structures such as tensors or eigensystems is placed in this
The program test is a collection of
different tests that have been done to check the code. It also has
been used to generate most of the examples in this report. Any test
routine is implemented as a function and the main program calls the
a function depending on the first parameter passed to test.
A simple call ./test will display the different options
are the main programs
and have been introduced in the previous chapters. By typing the
programs name a list of options is displayed. The options are
also explained in the respective section in Appendix A. mask is a simple program that takes one data
set as argument. It allows to mask the data sets by thresholding the
T2W image interactively.
can be used as soon as the data has been processed
with convert. It allows different filterings of the data and
to display the data sets as described in section
C++ Class hierarchy
andi.h was written by J.Rexilius, kfunctions.h by S.Warfield
includes some functions written by S.Warfield
Volumes are represented as arrays of tensors, eigensystems or floating
point numbers. The arrays are always indexed so that the x-dimension
is the ''fastest'' growing number. A position in a volume is
therefore the position
respective array, where is the x-dimension and
is the y-dimension.
The classes eigenfield.h and tensorfield.h provide
functions to access the positions by specifying all three
values or directly by providing the position with one value
Any private member begins with an underscore _. For example
the tensor-structure is a matrix
and stored as a floating point precision array
where the indexing is
The eigensystems are represented as 3 floating point numbers and 3
arrays of length 3 representing the respective eigenvectors:
float _lambda1, _lambda2, _lambda3;
float _vector1, _vector2, _vector3;
The eigensystems are sorted by the size of the eigenvector,
i.e. _lambda1 always represents the largest eigenvalue.
To store and process the data of the tensor- or eigenfield floating point precision numbers have
been chosen. This seems to be the best trade-off between memory usage
and precision. Problems only arose when computing the single value
decomposition to generate the local rotation of the tensors in the
nonrigid registration. Often, the resulting tensor was no more
symmetrical so that double precision when performing the local warping
The background images are all represented in unsigned short
No additional information is stored in the data files, i.e. no header
or leading bytes are present in the files. All additional information
has been placed in the xml-header file, which will be described next.
Functions that take a pointer to an array of any type as parameter never
check if the provided array is long enough to perform the requested
operations. Boundary checks are only performed when working on the
private member of a class, since the class member function knows the
size of its own data field.
To store information related to the data the xml-standard9.1 has been
used. This allows to collect all the information in a single, human
readable file. The interaction with C++ has been done using Xerces-C
. As this information is the same for all classes
(background, tensor- and eigenfields) only one file needs to be
stored. basefield.h provides the functions to read and write
the xml-file in a convenient manner. Most important when adding
information to the header the following steps have to be performed:
When changing the DTD the new elements should be marked as optional,
i.e. with either ? or * indicating that a xml-file
without this element is still a valid instance of this DTD. Otherwise
all current applications will
produce an error as they validate the provided xml-file with the DTD.
- Change the Document Type Definition (DTD)
- Add the new data to the protected members in
- Add the member in the constructor and destructor.
- Edit the read and write functions.
- Implement a function that allows to get and set the new member.
The functions that need to be edited in step 4 are:
void readdata(DOM_Node& node);
which are private members of basefield.h, and
void write_XMLHeader(const char xmlPath);
Eventually the function
which checks if all the values are present before the header is saved
into a file, also needs to be expanded.
Functions are provided to read a string, float number, and attribute
values. Numbers and strings are already read and written to the
xml-file, so that with simple copy-paste it should be possible to add new
The last step is optional, since a derived class has access to the
protected members. Nevertheless it is always useful to provide such
a function for direct access in an application or test program.
- ... xml-standard9.1
Up: Diffusion Tensor Imaging