Visit DIX: German-Spanish-German dictionary | diccionario Alemán-Castellano-Alemán | Spanisch-Deutsch-Spanisch Wörterbuch

next up previous contents [cite] [home]
Next: Conclusion Up: Diffusion Tensor Imaging Previous: Classification   Contents

Subsections

Implementation

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.


Class Hierarchy

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.

tensor.h and eigen.h

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.

basefield.h

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.

rsfunctions.h

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 called.

tensorfunctions.h

Functions that use the tensor data structure, that is 3 $\times$ 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.


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 function.

tensorfield.h, eigenfield.h, and background.h

are then 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.

display.h

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.

rsfunctions.h

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 file.

test

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 available.

convert, mask, rigidreg and nonrigidreg

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.

main

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 4.3.

Figure 9.1: C++ Class hierarchy
$\ast$ andi.h was written by J.Rexilius, kfunctions.h by S.Warfield
$\ast\ast$ includes some functions written by S.Warfield
\includegraphics[width = 1.0\textwidth]{images/program.eps}

Data Representation

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 $i, j, k$ in a volume is therefore the position $i + j\cdot X + k\cdot X\cdot Y$ in the respective array, where $X$ is the x-dimension and $Y$ is the y-dimension. The classes eigenfield.h and tensorfield.h provide functions to access the positions $i, j, k$ by specifying all three values or directly by providing the position with one value $c = i +
j\cdot X + k\cdot X\cdot Y$.

Any private member begins with an underscore _. For example the tensor-structure is a $3 \times 3$ matrix and stored as a floating point precision array
float _t[9];
where the indexing is

\begin{displaymath}
\mathtt{
\left(\begin{array}{ccc}
\_t[0] &\_t[1] &\_t[2]\\
...
...t[4] &\_t[5]\\
\_t[6] &\_t[7] &\_t[8]\\
\end{array}\right)
}
\end{displaymath}

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[3], _vector2[3], _vector3[3];
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 is necessary.

The background images are all represented in unsigned short precision.

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.


XML Header

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 [19]. 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:
  1. Change the Document Type Definition (DTD)
  2. Add the new data to the protected members in basefield.h.
  3. Add the member in the constructor and destructor.
  4. Edit the read and write functions.
  5. Implement a function that allows to get and set the new member.
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.

The functions that need to be edited in step 4 are:
void readdata(DOM_Node& node);
void readmanualy();
which are private members of basefield.h, and
void write_XMLHeader(const char xmlPath[]);
Eventually the function
void assert_values();
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 data.

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.


Footnotes

... xml-standard9.1
http://www.w3.org/XML/

next up previous contents [cite] [home]
Next: Conclusion Up: Diffusion Tensor Imaging Previous: Classification   Contents
Raimundo Sierra 2001-07-19