Parameterized mesh generation

7 downloads 0 Views 2MB Size Report
iniget, inidgt, nnodes, ipcoor double precision user(100), plotfm, dummy, scale, rinplt(5),. + coords(100000,3), alpha, radius, phi, pi double precision coeff(15,25), ...
Report number BMTE04.29

Parameterized mesh generation of the lower hind limb of a rat

M.A.J. Lourens BSc 0497358

Eindhoven University of Technology: Faculty of Biomedical Engineering; Group Soft Tissue Biomechanics and Engineering

Supervisors: Dr. Ir. C.W.J. Oomens Ir. A. Stekelenburg M.A.J. Cox Eindhoven, July 2004

1

Parameterized mesh generation of the lower hind limb of a rat Marcel A.J. Lourensa a

Department of Biomedical Engineering, Eindhoven University of Technology, PO Box 513, 5600 MB Eindhoven, The Netherlands

Abstract To get a better understanding of the pressure sore etiology a finite element model, an animal model and a cell model has been developed. It is now possible to detect muscle damage in vivo with MRI. Since high local stresses and strains maybe a trigger for local damage, it is useful to relate the local stresses and strains in a finite element model to local damage, seen on MRI. To do this we need a geometrically realistic 3-D mesh of the lower hind limb of a rat. The aim was to make such mesh from one basic mesh (cylinder with a hole). To describe the geometry of the exterior contour of the lower hind limb and the tibia parametric curves are used. A Matlab routine has been developed to get these parametric curves from MRI. A Sepran routine has been developed to use these parametric curves to deform the basic mesh towards the desired mesh. This method makes it possible to make quick a geometrically realistic 3-D mesh of the lower hind limb for different rats.

Introduction Pressure sores are localized areas of tissue degeneration ranging from redness of the skin to subcutaneous tissue necrosis extending into fat and muscle tissue [1]. They are primarily caused by prolonged mechanical loading at the interface between skin and supporting tissue [1]. The medical term for this disorder is decubitus [2]. Muscle tissue is more sensitive to mechanical loading and the most severe pressure sores develop in deep muscle layers near bony prominences [1]. To get more insight in the relationship between mechanical loading and local muscle damage, the Eindhoven University of Technology started the decubitus project. Data from cell models, animal models and finite element modelling is integrated to get a better grip on pressure sore etiology. The animal model is used to relate external mechanical loading to the location and amount of muscle damage. The cell model is used to study the relationship between deformation and cell damage. The finite element model is used to get insight in how external loads are transferred to local strains and stresses within the muscle. It is now possible in the animal model to detect, in vivo, damage to muscle tissue. The technique used for this is T2-weighted MRI. Small molecules exhibit a long T2, while larger molecules tend to have a shorter T2. Increased water mobility due to muscle damage leads to increased T2 relaxation time. This will lead to bright areas in T2weighted images. [3] Since high local stresses and strains maybe a trigger for local muscle damage, it is useful to relate the local stresses and strains in a finite element model to local damage, seen on T2-weighted images. To do this we need a geometrically realistic 3-D mesh of the lower hind limb of a rat.

2

The objective was the development of a parameterized geometrically realistic 3-D mesh of the hind limb of a rat. The mesh is parameterized to enable mesh generation for each individual experiment, starting from one basic mesh.

Method The mesh generation procedure consists of two steps. First a basic mesh is generated in Sepran. The finite element package Sepran was chosen, for its possibility to get access to the source code, which allows a large amount of freedom in using own routines or adjusting standard routines. Secondly the basic mesh is deformed towards the desired geometry, which is retrieved from MRI data [2]. Figure 1 outlines the basic principle of the mesh generation process (2-D).

Figure 1: Basic principle of the mesh generation process. Left: basic mesh, right: desired geometry [2]

Basic Mesh The basic mesh is a simple geometry consisting of a cylinder with a hole. Only two kinds of tissue are modeled, bone and muscle. Only the tibia is modeled and not the fibula, because in the animal model the indenter presses the muscles against the tibia. The tibia is represented by the hole. The remainder of the basic mesh represents the muscle. No distinctions are made between different muscles and are considered as a whole. In the neighborhood of the indenter contact surface we expect high stress and strain gradients [2]. So it must be possible to refine the basic mesh in these areas. The basic mesh consists of quadratic hexahedral elements (Bricks). In Sepran it is easy to mesh complex geometries using tetrahedrals. However, linear tetrahedrals may lead to incorrect results by high shear stresses and large displacements due to locking [2]. In the finite element model we expect high shear stresses, maybe trigger for muscle damage, so we have to use hexahedrals. By the way, less hexahedrals elements are required for the same numerical accuracy, which is desirable from a computational point of view. In Sepran it is not easy to fill the basic mesh with hexahedrals. To get a hexahedral basic mesh without distorted elements, the mesh is divided in a cross and four quarter circles. These parts are easier to fill with hexahedrals. Then the parts are brought together to form the 2-D basic mesh in the x-y plane, as shown in figure 2. (Appendix A: Sepran files)

3

Figure 2: the method to fill the basic mesh with hexahedrals

The 3-D basic mesh is formed by extrusion of the 2-D basic mesh in z direction.

Deformation of the exterior contour and the tibia In the second step the exterior contour and the tibia of the basic mesh are deformed towards the geometry of the lower hind limb of a rat. This geometry is obtained from transverse (x-y plane) T2-weighted MRI slices of the lower hind limb of Brown Norway rats (see figure 3). The images are obtained with a 6.3 Tesla MRI scanner, the field of view is 30 x 30 mm2, the matrix size is 128 x 128 and the slice thickness is 1 mm. The deformation of the exterior contour and the tibia of the basic mesh are done on the 2-D domain of the basic mesh [2]. This way, for each slice the x and y nodal displacements of the exterior contour and the tibia can be calculated. At the same time the nodes inside the mesh are reallocated, so that finally a regular element division is accomplished. Two 1-D Poisson equations, one for each coordinate direction, are solved separately on the 2-D domain of the basic mesh to receive a regular element division [2]. The Poisson equations are given by: ∂ 2ui ∂ 2ui + 2 = fi ∂x 2 ∂y

i = 1, 2

(1)

u1 = nodal displacement in x-direction u2 = nodal displacement in y-direction fi = source term By choosing fi =0, the Poisson equation ensures a regular division of the nodes on the domain, as the position ui is a linear function of the two coordinate directions [2]. Essential boundary conditions are needed to solve these Poisson equations. In this case the essential boundary condition are the x and y nodal displacements of the exterior contour and the tibia. In order to compute this x and y nodal displacements two parametric curves are used, one which describes the geometry of the exterior contour and one which describes the geometry of the tibia. Thus, for each MRI slice there are two parametric curves. 4

To get these parametric curves a Matlab routine detect.m has been developed, in which the exterior contour and the tibia are detected semi-automatic in a MRI slice (Appendix B: Matlab files). In this Matlab program the user selects in the first MRI slice the center point of the exterior contour and the center point of the tibia, and use these center points in all other slices. To detect the exterior contour a star shaped figure with the origin in the center point of the exterior contour is made. The angle (ϕ) between two successive lines of the star shaped figure depends on the number of sample points (N) to detect the exterior contour (ϕ= 2π/N). On each line of the star shaped figure there is looking for a minimal derivative, which may correspond with an edge. If the edge is correct the line is deleted and the program goes to the next line of the star shaped figure. Otherwise the user can select the edge manually before the program goes to the next line (see figure 3A). The same is done with the tibia, but now the origin of the star shaped figure is the center point of the tibia and the criterion for edge is maximal derivative (see figure 3B).

(A)

(B)

Figure 3: semi-automatic detection of the geometry in a MRI slice: (A) detection of the exterior contour; (B) detection of the tibia.

The x and y coordinates of the selected point on the exterior contour and the tibia are transformed to polar coordinates, centered round the center point of the exterior contour and the center point of the tibia, respectively. In this manner only the radius (r) is a function of the angle (ϕ). In figure 4 the r versus ϕ is plotted for the exterior contour.

5

Figure 4: the R versus ϕ plot of the selected points on the exterior contour. In this example the radius of the edge is determined between 0 and 2π with step size of 1/8π.

For the mesh only the coarse contour of the exterior and the tibia are needed. So the low frequencies are important. This means that only few points are needed to describe the two contours. In figure 4 only 16 points are selected (first and last point in the figure are the same point). As you can see in this figure it is difficult to get a smooth contour through these points. Interpolation is needed for a smooth contour. This interpolation can easily be done in the frequency domain, by using zero padding. A discrete Fourier transformation (DFT) is used to transform the selected points to the frequency domain. Figure 5A is the power spectrum of the DFT of the selected points in figure 4. By zero padding zeros are added in the middle of a DFT (see figure 5B).

Figure 5: (A) power spectrum of the DFT of the selected points in figure 4; (B) figure 5A with zero padding. In this example 112 zeros are added in the middle. In this way you go from 16 points to 128 points.

By zero padding in the frequency domain we increase the sample frequency, which is possible because the amplitudes of the extra points are set to zero. So they do not

6

contribute to the contour description. Thus zero padding in the frequency domain is the same as selecting a lot of points in the spatial domain and use a low pass filter in the frequency domain. After zero padding an inverse DFT is used to go back to the spatial domain. Figure 6 is the same as figure 4, but now with a lot of interpolated points between the selected points. In this example we go from 16 points to 128 points. It is now easy to fit a polynomial through the points in figure 6

Figure 6: the inverse DFT of figure 5B.

To fit a polynomial through the points in figure 6 the Matlab routine detect.m use the Matlab routine createpoly.m (Appendix B: Matlab files). The calculated polynomial of degree n is a description of the exterior contour or tibia of the MRI slice, which relates the radius r to the angle ϕ: r = a 0 + a1ϕ + a 2ϕ 2 + ... + a nϕ n

(2)

In this equation a 0 , a1 ...a n are the coefficients of the polynomial. The ϕ-axis is scaled between [-1 1] to avoid loss of figures in the evaluation of this polynomial. To ensure a closed loop a weight factor is used for the coinciding first and last point, which forces the polynomial to go through these points. Figure 7 show the closed exterior contour as given by equation (2). Figure 7: closed exterior contour

A Sepran routine createmesh3d.f (Appendix A: Sepran files) has been developed to solve the Poisson equations and uses the polynomials to calculate the essential 7

boundary conditions. Also the program rotates the basic mesh, so that the angle between the x-axis and the center point of the hole is the same as the angle between the x-axis and the center point of the tibia. The computation of the essential boundary conditions for the exterior is explained in figure 8 [2].

Figure 8: computation of the nodal displacements [2]

For all points on the exterior contour of the initial mesh the radius ri and the angle ϕ are determined [2]. The angle ϕ is kept constant and substituted in polynomial of the exterior contour (equation 2), which yields radius r of the desired cross-section contour [2]. The nodal displacements ∆x and ∆y can easily be expressed in terms of the difference ∆r = (r − ri ) [2]. The same can be done with the tibia, but now the radius ri and the angle ϕ of the points on the hole of the initial mesh with respect to center point of the tibia are determined and the polynomial of the tibia is used. The calculated nodal displacements are substituted in the solution vector and determine the essential boundary conditions of the Poisson equations, which are solved using the finite element package Sepran.

Results Figure 9A shows the basic mesh, which is used to make a geometrically realistic 3-D mesh of the lower hind limb of a rat, for each individual rat. It consists of 2780 quadratic hexahedral elements. The basic mesh is 10 mm high and has a radius of 5 mm. The radius of the hole is 1 mm. The distance between the center point of the basic mesh (origin) and the center point of the hole is 3 mm. The angle between the xaxis and the center point of the hole is 45o. Figure 9B represents a geometrically realistic 3-D mesh of the lower hind limb of a rat, which is made from the basic mesh. This mesh is made from 6 transverse (x-y plane) MRI slices. The distance in the z-direction between two successive slices is 2 mm. For each of the 6 cross-sections, a polynomial describing the exterior and a polynomial describing the tibia are determined. The displacements of the nodes of the

8

initial mesh, located between two successive slices are calculated, using a linear interpolation. In general this is the case, as the mesh may contain as many elements in z-direction as desired, where there are only 6 cross sections used.

(A)

(B)

Figure 9: (A) basic mesh; (B) geometrically realistic 3-D mesh of the lower hind limb of a rat

Figure 10 show two MRI slices with their corresponding geometrically realistic mesh slices.

9

Figure 10: MRI slices with their corresponding mesh slices

The MRI slices in figure 10 are from different rats. The upper MRI slice is from a rat before the indenter has press against the tibia. The other MRI slice is from a rat after the indenter has press against the tibia.

Discussion This mesh generation procedure has some restrictions. One restriction is that it does not work if the distance between the selected center point of the exterior contour (also center point of the basic mesh) and the selected center point of tibia are not in between 2-4 mm. In that case the center point of the tibia does not lie in the hole of the basic mesh, because the distance between the center point of the basic mesh (origin) and the center point of the hole is 3 mm and the radius of the hole is 1 mm (figure 11). To calculate the essential boundary condition we must determine the radius ri and the angle ϕ of the points on the hole of the initial mesh with respect to the center point of the tibia. The calculated angles have only a meaning for the polynomial, which describes the tibia, if the center point of the tibia lies inside the hole, see figure 8.

Center point of the

tibia

1mm Center point of the hole

3mm Origin Also center point of the outer contour

Figure 11: illustrate the case in which the center point of the tibia lies not in the hole of the basic mesh

Another restriction is that the tibia must lie almost straight in the lower hind limb of the rat. In the first MRI slice the center point of the tibia is selected and is used in the other slices as the origin of the star shaped figure to detect the tibia. If the tibia lies oblique in the lower hind limb the center point of the star shaped figure go to one side of the tibia from the first MRI slice (distal) to the last MRI slice (proximal). As a consequence of this there is no equal distribution of the detected points in the MRI slices above the first MRI slice, more points are detected on the side were the center point of the star shaped figure goes to. So it will be difficult to make a good description of the tibia in areas where a few points are detected. If the tibia lies extremely oblique it may happen that the center point of the star shaped figure lies out of the tibia in the MRI slices above the first MRI slice. In that case it is impossible to detect the tibia. A solution for this problem is to select in each MRI slice the center point of the tibia and use an average center point of the tibia to calculate the angle at 10

which the basic mesh must rotate (see method). Another option is to select more points to get enough points in the areas with few detected points. This method only works if the tibia does not lies too oblique. There are some sources of errors in the mesh generation process, which influence the geometric accuracy of the mesh. In some cross-section the MRI quality is not so good that it is difficult to distinguish the contours. So it is sometimes difficult to decide if the Matlab routine detect.m has detected the correct edges. Another error is introduced by the polynomial fit. Further only 6 cross-sections are used for the geometry reconstruction. For intermediate points a linear interpolation was carried out. Therefore an increase in the number of MRI cross-sections would improve accuracy. The aim was to develop a parameterized geometrically realistic 3-D mesh of the hind limb of a rat. But how realistic is this mesh? Only the tibia is modeled and not the fibula. There is no distinction between the different muscles and there is no skin layer.

Conclusion As can be concluded from the results, it is possible to make a geometrically realistic 3-D mesh for different rats from one basic mesh. The mesh generation process is almost automatic. A great advantage of this method is that you have to select only a few points on the exterior contour and the tibia to get an adequate description of these contours. This makes it possible to quickly generate a geometrically realistic 3-D mesh. The mesh can relatively easily be expanded, for example to add a skin layer. Both global and local refinements can be applied. In theory many internal boundaries can be modeled, however the generation of the basic mesh will become difficult and extremely complicated internal surfaces will occasionally cause degenerated elements.

References [1] [2]

[3]

E.H.M. Bosboom, “Deformation as a trigger for pressure sore related muscle damage”, Eindhoven University of Technology, PhD thesis, 2001. R.G.M. Breuls, “A 3-D finite element model of the tibialis anterior muscle of a rat”, Eindhoven University of Technology, MSc thesis MT 98.010, March 1998. N. Braakman, “Determination of the Passive Transverse Mechanical Properties of Skeletal Muscle under In Vivo Compression”, Eindhoven University of Technology, MSc thesis BMTE 03.40, October 2003.

11

Appendix A: Sepran files basismesh.msh # #basismesh.msh # #Sepran input file to generate the Basic Mesh # constants reals radius = 5 #Radius of the basic mesh in mm height = 10 #Height of the basic mesh in mm angle = 45 #Angle between the x-axis and the #center point of the hole in the degree ocdistance = 3 #Distance between origin and center point #of the hole in mm Lhole = 2 #Lhole = ocdistance - radius of the hole(1mm) Rhole = 4 #Rhole = ocdistance + radius of the hole(1mm) integers nelm_height = 10 nelm_ccross = 10 nelm_hccross= 5 nelm_scross = 4 nelm_qcircle= 6 nelm_line nelm_hole end

= 2 = 12

#Number of elements in the z-direction #Number of elements on the curved sides of #the cross #(Number of elements on the curved sides of #the cross)/2 #Number of elements on the straight sides of #the cross #Number of elements on the curved side of the #quarter cicle #Number of elements on the connection line #between the outer contour and the hole #Number of elements on the hole

mesh3d coarse (unit=1) #points on points p1 = pd2 = pd3 = p4 = pd5 = pd6 = p7 = pd8 = pd9 = p10 = pd11 = pd12 = p13 = pd14 = pd15 = pd16 = pd17 =

the bottom (0, 0, 0) ($radius, $angle, 0) ($radius, 74.668085, 0) (0, 3.5, 0) ($radius, 105.33191, 0) ($radius, 164.668085, 0) (-3.5, 0, 0) ($radius, 195.33191, 0) ($radius, 254.668085, 0) (0, -3.5, 0) ($radius, 285.33191, 0) ($radius, 344.668085, 0) (3.5, 0, 0) ($radius, 15.33191, 0) ($ocdistance, $angle, 0) ($Rhole, $angle, 0) ($Lhole, $angle, 0)

#points on the top pd18 = ($radius, $angle, $height)

12

pd19 p20 pd21 pd22 p23 pd24 pd25 p26 pd27 pd28 p29 pd30 pd31 pd32 pd33

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

($radius, 74.668085, $height) (0, 3.5, $height) ($radius, 105.33191, $height) ($radius, 164.668085, $height) (-3.5, 0, $height) ($radius, 195.33191, $height) ($radius, 254.668085, $height) (0, -3.5, $height) ($radius, 285.33191, $height) ($radius, 344.668085, $height) (3.5, 0, $height) ($radius, 15.33191, $height) ($ocdistance, $angle, $height) ($Rhole, $angle, $height) ($Lhole, $angle, $height)

#Definition of the curves curves #------------cross bottom---------------------------c1 = arc2(p2,p3,p1,nelm = $nelm_hccross) c2 = line2(p3,p4,nelm = $nelm_scross) c3 = line2(p4,p5,nelm = $nelm_scross) c4 = arc2(p5,p6,p1,nelm = $nelm_ccross) c5 = line2(p6,p7,nelm = $nelm_scross) c6 = line2(p7,p8,nelm = $nelm_scross) c7 = arc2(p8,p9,p1,nelm = $nelm_ccross) c8 = line2(p9,p10,nelm = $nelm_scross) c9 = line2(p10,p11,nelm = $nelm_scross) c10 = arc2(p11,p12,p1,nelm = $nelm_ccross) c11 = line2(p12,p13,nelm = $nelm_scross) c12 = line2(p13,p14,nelm = $nelm_scross) c13 = arc2(p14,p2,p1,nelm = $nelm_hccross) #------------hole bottom----------------------------c14 = circle2(p15,p16,p17,nelm = $nelm_hole) #------------connection between hole and cross------c15 = line2(p2,p16,nelm = $nelm_line) #------------curved side of the four quarter cicles-c16 = arc2(p3,p5,p1,nelm = $nelm_qcircle) c17 = arc2(p6,p8,p1,nelm = $nelm_qcircle) c18 = arc2(p9,p11,p1,nelm = $nelm_qcircle) c19 = arc2(p12,p14,p1,nelm = $nelm_qcircle) #translate cross, four quarter circles and the connection between #hole and cross curves over a distance in the z direction (top) c20 = translate c1(p18,p19) c21 = translate c2(p19,p20) c22 = translate c3(p20,p21) c23 = translate c4(p21,p22) c24 = translate c5(p22,p23) c25 = translate c6(p23,p24) c26 = translate c7(p24,p25) c27 = translate c8(p25,p26) c28 = translate c9(p26,p27) c29 = translate c10(p27,p28) c30 = translate c11(p28,p29) c31 = translate c12(p29,p30) c32 = translate c13(p30,p18)

13

c33 c34 c35 c36 c37 c38

= = = = = =

translate translate translate translate translate translate

c14(p32) c15(p18,p32) c16(p19,p21) c17(p22,p24) c18(p25,p27) c19(p28,p30)

#lines that connect the bottom and the top c39 = line2(p2,p18,nelm = $nelm_height) c40 = line2(p3,p19,nelm = $nelm_height) c41 = line2(p4,p20,nelm = $nelm_height) c42 = line2(p5,p21,nelm = $nelm_height) c43 = line2(p6,p22,nelm = $nelm_height) c44 = line2(p7,p23,nelm = $nelm_height) c45 = line2(p8,p24,nelm = $nelm_height) c46 = line2(p9,p25,nelm = $nelm_height) c47 = line2(p10,p26,nelm = $nelm_height) c48 = line2(p11,p27,nelm = $nelm_height) c49 = line2(p12,p28,nelm = $nelm_height) c50 = line2(p13,p29,nelm = $nelm_height) c51 = line2(p14,p30,nelm = $nelm_height) c52 = line2(p16,p32,nelm = $nelm_height) #Difinition of the surfaces surfaces #------------3D cross with hole-----------------------s1 = general6(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,// c12,c13,c15,-c14,-c15) s2 = translate s1(c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,c30,// c31,c32,c34,-c33,-c34) s3 = pipesurface6(c1,c20,c39,c40) s4 = pipesurface6(c2,c21,c40,c41) s5 = pipesurface6(c3,c22,c41,c42) s6 = pipesurface6(c4,c23,c42,c43) s7 = pipesurface6(c5,c24,c43,c44) s8 = pipesurface6(c6,c25,c44,c45) s9 = pipesurface6(c7,c26,c45,c46) s10 = pipesurface6(c8,c27,c46,c47) s11 = pipesurface6(c9,c28,c47,c48) s12 = pipesurface6(c10,c29,c48,c49) s13 = pipesurface6(c11,c30,c49,c50) s14 = pipesurface6(c12,c31,c50,c51) s15 = pipesurface6(c13,c32,c51,c39) s16 = pipesurface6(c15,c34,c39,c52) s17 = pipesurface6(c14,c33,c52) s18 = ordered surface(s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,// s14,s15,s16,-s17,-s16) #-------------four quarter cylinders------------------s19 = general6 (-c2,c16,-c3) s20 = translate s19(-c21,c35,-c22) s21 = pipesurface6(c16,c35,c40,c42) s22 = ordered surface (-s4,s21,-s5) s23 = general6 (-c5,c17,-c6) s24 = translate s23(-c24,c36,-c25) s25 = pipesurface6 (c17,c36,c43,c45) s26 = ordered surface (-s7,s25,-s8) s27 = general6 (-c8,c18,-c9) s28 = translate s27(-c27,c37,-c28) s29 = pipesurface6 (c18,c37,c46,c48)

14

s30 s31 s32 s33 s34

= = = = =

ordered surface (-s10,s29,-s11) general6 (-c11,c19,-c12) translate s31(-c30,c38,-c31) pipesurface6 (c19,c38,c49,c51) ordered surface (-s13,s33,-s14)

#Definition of the volumes volumes v1 = pipe14(s1,s2,s18) v2 = pipe14(s19,s20,s22) v3 = pipe14(s23,s24,s26) v4 = pipe14(s27,s28,s30) v5 = pipe14(s31,s32,s34) meshvolume velm1 = (v1,v5)

#final mesh

norenumber #plot the mesh plot, nosubmesh, eyepoint(10, 10, 30) end

createmesh3d program createmesh3d c c ********************************************************************* c c c Programmer: Roel Breuls c Modified by Marcel Lourens c Purpose: A mesh of the lower hind limb of a rat is generated. c c The geometry of the lower hind limb is reconstructed using c MRI-information c The MRI-information is used to define a set of contours in the c x-y plane, describing the outher contour and tibia of the lower c hind limb. c c A matlab program fits polynomals through these curves and c provides the coefficients of the polynomials. This program reads c these coefficients obtained with Matlab. c c A basic mesh of a cilinder is transformed to the mesh of the c geometry of the lower hind limb of a rat by imposing nodal c displacements to the mesh of the cilinder. The nodal c displacements are calculated in subroutine funcbc. c c The basic mesh is rotated, so that the angle between the x-axis c and the center point of the hole in the basic mesh is the same c as the angle between the x-axis and the center of the tibia. c c ********************************************************************* c

15

c INPUT / OUTPUT c c The mesh should be avaiable, using: sepmesh basismesh.msh c The problem definition is stored in basismesh.prb c The coefficients of the polynomials are stored in coefficients c and tibiacoefficients. c The rotating angle and the x and y coordinate of the center c point of the tibia are stored in center_basicmesh c ********************************************************************* implicit none integer bufsiz parameter (bufsiz = 400 000 000) integer ibuffr common ibuffr(bufsiz) double precision buffer(1) equivalence ( ibuffr(1), buffer(1) )

integer nbuffr, kbuffr, intlen, ibfree common /cbuffr/ nbuffr, kbuffr, intlen, ibfree save /cbuffr/ integer lmesh, lprob, i, j parameter (lmesh=500, lprob=1000) integer kmesh(lmesh), kprob1d(lprob,2), intmat1d(5,2), + kprob3d(lprob), idummy,isol1d(5,3), + isol3d(5), iuser(100), iinplt(15), + iniget, inidgt, nnodes, ipcoor double precision user(100), plotfm, dummy, scale, rinplt(5), + coords(100000,3), alpha, radius, phi, pi double precision coeff(15,25), tibiacoeff(15,25), + translate_rotate(3) integer polyorder(15), polyordert(15) common /coefficients/ coeff, tibiacoeff, polyorder, + polyordert, translate_rotate save /coefficients/

c

--- Initialize variable length arrays kmesh(1) = lmesh kprob3d(1) = lprob kprob1d(1,1) = lprob kprob1d(1,2) = lprob iuser(1) = 100 user(1) = 100d0

c c c c c c

--- Read coefficients of the polynomals (outher contour) The coefficients of the outher contour are stored in the following way: coeff(1) = number of coefficients (n1) of polynom 1

16

c c c c c c c

coeff(2) .... coeff(n1+1) ; actual values of the coefficients coeff(n1+2) number of coefficients (n2) of polynom 2 coeff(n1+3) .... coeff(n1+n2+3); actual values of the coefficients etc. write (*,*) 'Read coefficients ....' open (unit = 30, file='coefficients', status = 'old' ) do j = 1, 6 read (30,*) polyorder(j) do i = 1,polyorder(j)+1 read (30,*) coeff(j,i) end do end do close(30)

write (*,*) 'Read coefficients of the tibia ....' open (unit = 30, file='tibiacoefficients', status = 'old' ) do j = 1, 6 read (30,*) polyordert(j) do i = 1,polyordert(j)+1 read (30,*) tibiacoeff(j,i) end do end do close(30)

write (*,*) 'Read the rotating angle and the x and y coordinate +of the center point of the tibia...' open (unit = 30, file='center_basicmesh', status = 'old' ) do i = 1, 3 read (30,*) translate_rotate(i) end do close(30) c

--- Start Sepran and read mesh and plot undeformed mesh write(*,*) 'Read undeformed mesh ....' call start (0,0,-1,0) nbuffr = bufsiz open (unit = 30, file='meshoutput', status = 'old') call meshrd (0,30,kmesh) close(30) write(*,*) 'Rotate undeformed mesh ....'

c c

c

--- Get starting point of the nodal coordinates in the large buffer and store this in ipcoor call ini070(kmesh(23)) ipcoor = inidgt(kmesh(23)) --- Define pi pi=2d0*asin(1d0)

17

c --- number of nodes is stored in kmesh(8) nnodes = kmesh(8) c --- alpha is the angle of rotation alpha = translate_rotate(3) c --- loop over the nodes, get coords from buffer do i=1, nnodes do j=1,3 coords(i,j) = buffer(ipcoor -1 + (i-1)*3 + j) end do end do c --- loop over the nodes, calculate cylindrical coordinates do i= 1, nnodes radius = dsqrt(coords(i,1)**2d0 + coords(i,2)**2d0 )

+

if ( ( coords(i,1).eq.0d0 ).and. ( coords(i,2).ge.0d0) ) then phi = pi/2d0

+

elseif ( ( coords(i,1).eq.0d0).and. ( coords(i,2).lt.0d0) ) then phi = -pi/2d0 elseif ( coords(i,1).gt.0d0) then phi = datan( coords(i,2)/coords(i,1) ) elseif ( coords(i,1).lt.0d0) then phi = datan( coords(i,2)/coords(i,1) )-pi endif

c ---

calculate new coordinates coords(i,1) = radius * dcos(alpha + phi) coords(i,2) = radius * dsin(alpha + phi)

c ---

store new coords in buffer do j = 1, 3 buffer(ipcoor -1 + (i-1)*3 +j) = coords(i,j) end do end do

c

--- Set some parameters for the plotting routine plotfm=12d0 iinplt(1)=1 iinplt(2)=0 iinplt(3)=0 iinplt(4)=0 iinplt(5)=0 iinplt(6)=0 iinplt(7)=0 iinplt(8)=0 iinplt(9)=0 iinplt(10)=0 iinplt(11)=0 iinplt(12)=1 iinplt(13)=16

18

iinplt(14)=1 rinplt(1) rinplt(2) rinplt(3) rinplt(4) rinplt(5)

= = = = =

plotfm 1.d0 -25d0 -20d0 26d0

write (*,*) 'Plot basic mesh ....' call plotms (kmesh,iinplt,rinplt,dummy)

c

--- Create poisson equations call probdf ( 0, kprob3d, kmesh, idummy )

c

--- Deform outher contour write(*,*) 'Deform outher contour ....' call mkpois (kmesh, kprob1d, intmat1d, isol1d) call filcof ( iuser, user, kprob1d(1,1), kmesh, 1) call poisson (kmesh, kprob3d, kprob1d, intmat1d, isol3d, + isol1d,iuser, user) scale = 1.d0 call defmsh( kmesh, kprob3d, isol3d, scale)

c

--- Deform tibia write(*,*) 'Deform tibia ....' call mkpois (kmesh, kprob1d, intmat1d, isol1d) call poisson (kmesh, kprob3d, kprob1d, intmat1d, isol3d, + isol1d,iuser, user) call defmsh( kmesh, kprob3d, isol3d, scale)

c

--- Plot new mesh write(*,*) 'Plot new mesh ...' call plotms (kmesh,iinplt,rinplt,dummy)

c

--- Save mesh to 'spiermesh.msh' open (unit = 30, file='spiermesh.msh', status = 'unknown') call meshwr(30,kmesh) close(30)

c

--- Stop Sepran call finish( 0 ) end

c --------------------------------------------------------------------

19

c c SUBROUTINES c c -------------------------------------------------------------------function funcbc(ifunc,x,y,z) c c c c c c c

function which is used to calculate the nodal displacements The coordinates are first changed to cilindrical coordinates hence: (x,y,z)_cilinder --> (phi,r,z)_cilinder The polynom describing the MRI-contour is evolved, without changing the phi-coordinate; this gives a new r-coordinate.

c

deltar = rnew-rcilinder

c c c c c c

deltar is used to calculate both deltax and deltay if if if if

ifunc=1 ifunc=2 ifunc=3 ifunc=4

--> --> --> -->

value value value value

= = = =

deltax deltay deltax deltay

(outer contour) (tibia)

implicit none integer ifunc,t,zbottom,ztop double precision funcbc, x,y,z,x1,y1,phi,r0,deltax,deltay, + rnew,xnew,ynew,deltar,pi,r(15), zhlp, + slices double precision coeff(15,25), tibiacoeff(15,25), + translate_rotate(3) integer polyorder(15), polyordert(15) common /coefficients/ coeff, tibiacoeff, polyorder, + polyordert, translate_rotate if ((ifunc.eq.3).or.(ifunc.eq.4)) then x1 = x-translate_rotate(1) y1 = y-translate_rotate(2) endif if ((ifunc.eq.1).or.(ifunc.eq.2)) then x1 = x y1 = y endif c

--- Computation of deltax and deltay

c

--- Define pi pi=2d0*asin(1d0)

c

--- calculate cylindrical coordinates r0 = dsqrt(x1**2d0 + y1**2d0 )

c

---- first and second quadrant if (y1 .gt. 0) then

20

if (x1 .gt. 0) then phi=datan(y1/x1) endif if (x1 .lt. 0) then phi=datan(y1/x1)+pi end if endif c

---- third and fourth quadrant if (y1 .lt. 0) then if (x1 .gt. 0) then phi=datan(y1/x1) else phi=datan(y1/x1)-pi end if end if

c if (y1 .eq. 0) then if (x1 .gt. 0)then phi=0 else phi=pi end if end if if (x1 .eq. 0) then if (y1 .gt. 0) then phi = 0.5d0*pi else phi = -0.5d0*pi end if end if

c

--- Scale phi to [-1 1] phi=phi/pi

c

100 c

--- Calculate new r-coordinate rnew=0d0 do 100 t=1,6 r(t)=0d0 continue --- Initialize some variables zbottom=0 ztop=0

c

--- Calculate trunc(z) slices=6d0 zhlp=(z/(10d0/(slices-1d0))+1d0) zbottom=idint(zhlp) ztop=zbottom+1

c

--- Outer contour if ((ifunc .eq. 1) .or. (ifunc .eq. 2)) then do t=1,polyorder(zbottom)+1 r(zbottom)=r(zbottom)+coeff(zbottom,t)*

21

+

phi**(polyorder(zbottom)+1-t) end do do t=1,polyorder(ztop)+1 r(ztop)=r(ztop)+coeff(ztop,t)*phi**(polyorder(ztop)+1-t) end do rnew = r(zbottom)+(zhlp-zbottom)*(r(ztop)-r(zbottom)) deltar=rnew-r0

c c

--- Calculate the actual displacements which are stored in array isol deltax=deltar*dcos(phi*pi) deltay=deltar*dsin(phi*pi) if ( ifunc .eq. 1 ) then funcbc=deltax else funcbc=deltay endif endif

c

--- Tibia if ((ifunc .eq. 3) .or. (ifunc .eq. 4)) then do t=1,polyordert(zbottom)+1 r(zbottom)=r(zbottom)+tibiacoeff(zbottom,t)* + phi**(polyordert(zbottom)+1-t) end do

+

do t=1,polyordert(ztop)+1 r(ztop)=r(ztop)+tibiacoeff(ztop,t)* phi**(polyordert(ztop)+1-t) end do rnew = r(zbottom)+(zhlp-zbottom)*(r(ztop)-r(zbottom)) deltar=rnew-r0

c c

--- Calculate the actual displacements which are stored in array isol deltax=deltar*dcos(phi*pi) deltay=deltar*dsin(phi*pi) if ( ifunc .eq. 3 ) then funcbc=deltax else funcbc=deltay endif endif end

c ===================================================================== subroutine mkpois ( kmesh, kprob1d, intmat1d, isol1d) implicit none integer kmesh(*), kprob1d(1000,2), intmat1d(5,2), isol1d(5,2),

22

$

idummy, i,j

kprob1d(1,1) = 1000 kprob1d(1,2) = 1000 do j=1,2 do i=1,5 isol1d(i,j)=0 intmat1d(i,j)=0 end do do i=2,1000 kprob1d(i,j)=0 end do end do c

1: Poisson equation x-coordinate: call probdf ( 0, kprob1d(1,1), kmesh, idummy ) call commat ( 1, kmesh, kprob1d(1,1), intmat1d(1,1) ) call presdf ( kmesh, kprob1d(1,1), isol1d(1,1) )

c

2: Poisson equation y-coordinate: call probdf ( 0, kprob1d(1,2), kmesh, idummy ) call commat ( 1, kmesh, kprob1d(1,2), intmat1d(1,2) ) call presdf ( kmesh, kprob1d(1,2), isol1d(1,2) ) end

c ===================================================================== = subroutine poisson( kmesh, kprob3d, kprob1d, intmat1d, + isol3d, isol1d, iuser, user) implicit none integer kmesh(*), kprob1d(1000,2), intmat1d(5,2), isol1d(5,2), + kprob3d(1000), isol3d(5), iuser(100), i, + inpsol(21), matr(5), irhsd(5), idum double precision user(*), rdummy

c Create a solution vector isol3d call creatv (kmesh, kprob3d, isol3d, 0, rdummy) c Build and solve three separate Poisson problems to deform the mesh: do i =1,2

$

call build ( 0, matr, intmat1d(1,i), kmesh, kprob1d(1,i), irhsd, idum, isol1d(1,i), idum, iuser, user ) inpsol(1)=3 inpsol(2)=1 inpsol(3)=0 call solvel (inpsol, rdummy, matr, isol1d(1,i), irhsd,

23

$

$

intmat1d(1,i), kmesh, kprob1d(1,i), -1) call copydf ( isol1d(1,i), isol3d, kprob1d(1,i), kprob3d, 1, i, kmesh )

end do c

call printv(isol3d, kmesh, kprob3d, 'Solution vector: ')

c Remove right-hand side info from memory: call ini076 ( irhsd(1) ) c Remove matrix info from memory: call ini076 ( matr(3) ) call ini076 ( matr(5) ) end

basismesh.prb * * Define problem definition 3D * PROBLEM TYPES ELGRP1 = (TYPE = 260) ESSBOUNCOND END PROBLEM TYPES ELGRP1 = (TYPE = 800) ESSBOUNCOND degfd1=surfaces(s3) degfd1=surfaces(s21) degfd1=surfaces(s6) degfd1=surfaces(s25) degfd1=surfaces(s9) degfd1=surfaces(s29) degfd1=surfaces(s12) degfd1=surfaces(s33) degfd1=surfaces(s15) END ESSENTIAL BOUNDARY CONDITIONS func=1,degfd1,surfaces(s3) func=1,degfd1,surfaces(s21) func=1,degfd1,surfaces(s6) func=1,degfd1,surfaces(s25) func=1,degfd1,surfaces(s9) func=1,degfd1,surfaces(s29) func=1,degfd1,surfaces(s12) func=1,degfd1,surfaces(s33) func=1,degfd1,surfaces(s15)

24

END

PROBLEM TYPES ELGRP1 = (TYPE = 800) ESSBOUNCOND degfd1=surfaces(s3) degfd1=surfaces(s21) degfd1=surfaces(s6) degfd1=surfaces(s25) degfd1=surfaces(s9) degfd1=surfaces(s29) degfd1=surfaces(s12) degfd1=surfaces(s33) degfd1=surfaces(s15) END

ESSENTIAL BOUNDARY CONDITIONS func=2,degfd1,surfaces(s3) func=2,degfd1,surfaces(s21) func=2,degfd1,surfaces(s6) func=2,degfd1,surfaces(s25) func=2,degfd1,surfaces(s9) func=2,degfd1,surfaces(s29) func=2,degfd1,surfaces(s12) func=2,degfd1,surfaces(s33) func=2,degfd1,surfaces(s15) END

COEFFICIENTS elgrp1 (nparm = 20 ) # element 800, Poisson eqn. icoef1 = 0 # not yet used icoef2 = 0 # no upwinding icoef3 = 3 # default integration scheme icoef4 = 0 # coordinate system icoef5 = 0 # standard equation coef6 = 1d0 # alpha_11 coef7 = 0d0 # alpha_12 coef8 = 0d0 # alpha_13 coef9 = 1d0 # alpha_22 coef10 = 0d0 # alpha_23 coef11 = 1d0 # alpha_33 coef12 = 0d0 # v_1 coef13 = 0d0 # v_2 coef14 = 0d0 # v_3 coef15 = 0d0 # beta coef16 = 0d0 # f coef17 = 0d0 # rho c_p coef18 = 0d0 # not yet used coef19 = 0d0 # not yet used coef20 = 0d0 # not yet used END

25

**************************************** *Tibia * **************************************** PROBLEM TYPES ELGRP1 = (TYPE = 800) ESSBOUNCOND degfd1=surfaces(s17) END ESSENTIAL BOUNDARY CONDITIONS func=3,degfd1,surfaces(s17) END PROBLEM TYPES ELGRP1 = (TYPE = 800) ESSBOUNCOND degfd1=surfaces(s17) END ESSENTIAL BOUNDARY CONDITIONS func=4,degfd1,surfaces(s17) END

26

Appendix B: Matlab files detect.m function detect warning off format long % Choice the number of slices to detect the tibia and the outer % contour number = input('Number of slice to detect: '); H = fspecial('disk',4); coefficients = []; tibiacoefficients = []; for i = 1:number % Get user input and import fdf-file file = input('Filename: '); output = fdfread2(file,1); N = input('Number of samplepoints contour: '); M = input('Number of interpolated contour: '); Nt = input('Number of samplepoints tibia: '); Mt = input('Number of interpolated

(even) to detect the outer samples for the outer (even) to detect the samples for the tibia: ');

% Show slice imshow(output.image,[],'notruesize') hold on

% Determine the centerpoint in the first slice and use this % centerpoint in the other slices if i == 1 title('place centerpoint'); center = ginput(1); end % Determine the centerpoint of the tibia in the first slice and % use this centerpoint in the other slices if i == 1 title('place centerpoint of the tibia'); centert = ginput(1); end

% Semi-automatic edge detection of the outer contour disp('detect outer contour') title('agree: right mouse button - correct: left mouse button '); imsize = size(output.image); R = min([center(1), center(2), imsize(1)-center(1), imsize(2)-center(2)]); for n = 1:N [X, Y] = pol2cart(2*pi/N*(n-1), R);

27

b = line([center(1) + [0 X]], [center(2) + [0 Y]]); % Automatic detection blurred = imfilter(double(output.image), H); samples = interp2(blurred, center(1) + X/R * [1:R] , center(2) + Y/R * [1:R], 'cubic'); [a, edge] = min(diff(samples)); position = [center(1) + X/R * edge, center(2) + Y/R * edge]; a = plot(position(1), position(2), 'b.'); % Manual detection [x, y, button] = ginput(1); if button == 1 position = [x, y]; set(a, 'XData', x, 'YData', y); end [phi(n), g(n)]

= cart2pol(position(1)-center(1), position(2)-center(2));

delete(b); end % Apply DFT to g[n] and insert M-N zeros (outer contour) G = fft(g); G = [G(1:N/2) zeros(1, M-N) G(N/2+1:N)]; % Apply M-point inverse DFT to obtain the interpolated version of % g[n] (outer contour) g = M/N * ifft(G, M); g = sqrt(g .* conj(g)); phi = [0 : 2/M*pi : (2 - 2/M) * pi]; % Display interpolated version of g[n] (outer contour) title('finished'); [X, Y] = pol2cart(phi, g); plot(X+center(1), Y+center(2), 'r') %Make a polynoom which relates the radius g to the angle phi %(outer contour) coeff = createpoly(phi, g, center); coefficients = [coefficients, coeff]; clear phi g hold off

% Semi-automatic edge detection of the tibia % Show slice of the tibia output.imaget = output.image(centert(1)-40:centert(1)+30, centert(2)-20:centert(2)+20); imshow(output.imaget,[],'notruesize') hold on disp('detect tibia') title('agree: right mouse button - correct: left mouse button '); Rt = min([40, 20, imsize(1)-centert(1)+(centert(2)-20), imsize(2)-centert(2)+(centert(1)-40)]); for n = 1:Nt [Xt, Yt] = pol2cart(2*pi/Nt*(n-1), Rt);

28

b = line([centert(1)-(centert(2)-20) + [0 Xt]], [centert(2)-(centert(1)-40) + [0 Yt]]); % Automatic detection samples = interp2(double(output.imaget), centert(1)(centert(2)-20) + Xt/Rt * [1:Rt] , centert(2)-(centert(1)-40) + Yt/Rt * [1:Rt], 'cubic'); [a, edge] = max(diff(samples)); position = [centert(1)-(centert(2)-20) + Xt/Rt * edge, centert(2)-(centert(1)-40) + Yt/Rt * edge]; a = plot(position(1), position(2), 'b.'); % Manual detection [x, y, button] = ginput(1); if button == 1 position = [x, y]; set(a, 'XData', x, 'YData', y); end [phit(n), gt(n)]

= cart2pol(position(1)centert(1)+(centert(2)-20), position(2)-centert(2)+(centert(1)-40));

delete(b); end % Apply DFT to gt[n] and insert Mt-Nt zeros (tibia) Gt = fft(gt); Gt = [Gt(1:Nt/2) zeros(1, Mt-Nt) Gt(Nt/2+1:Nt)]; % Apply Mt-point inverse DFT to obtain the interpolated version % of gt[n] (tibia) gt = Mt/Nt * ifft(Gt, Mt); gt = sqrt(gt .* conj(gt)); phit = [0 : 2/Mt*pi : (2 - 2/Mt) * pi]; % Display interpolated version of gt[n] (tibia) title('finished'); [Xt, Yt] = pol2cart(phit, gt); plot(Xt+centert(1)-(centert(2)-20), Yt+centert(2)-(centert(1)-40), 'r') %Make a polynoom which relates the radius gt to the angle phit %(tibia) coefft = createpoly(phit, gt, center); tibiacoefficients = [tibiacoefficients, coefft]; clear phit gt hold off end

coefficients = coefficients'; save coefficients coefficients -ascii tibiacoefficients = tibiacoefficients'; save tibiacoefficients tibiacoefficients -ascii %Determine the center of the tibia in the basicmesh compared to the

29

%center X = centert(1)-center(1); Y = centert(2)-center(2); %Resolution [mm/pixel] resolution = 30/128; X = resolution*X; Y = resolution*Y; [phi, r] = cart2pol(X, Y); %The angle at which the basicmesh has to be rotated phi = phi+pi; phir = phi-1/4*pi; if phir < 0 phir = 2*pi-(1/4*pi-phi); end X = -X; Y = -Y; % The first two numbers in center_basicmesh are the X and y % coordinates of the center and the last one is the angle at which % the basicmesh has to be rotated center_basicmesh=[X Y phir]'; save center_basicmesh center_basicmesh -ascii

createpoly.m function coeff = cylpoly(phi, r, center) % % Subroutine, which calculates a parameter curve through % a set of data (phi,r), describing a closed loop in the polar plot % % % [coeff] = cylpoly(coord,ncoef) % % ********** Output *********** % % coeff = coefficients of the polynom, degree = PolyOrder % % ********** Input ************ % % r = radius of the data points [pixels] % phi = angle of the data points [rad] % %R.G.M.Breuls %Modified for matlab file detect.m by Marcel Lourens

% ********************************************************************* % Sort points %Resolution [mm/pixel] resolution = 30/128;

30

r

= resolution*r;

npoints = length(r); phi = phi-pi; [phi]=sort(phi); for t=1:npoints radius(t)=r(t); end % As the curve is a closed loop, add last point w (weight factor) % times to ensure the curve to go through this point. w=100; for t=1:w r1(t)=radius(1); phi1(t)=-pi; r1(npoints+w+t)=radius(1); phi1(npoints+w+t)=pi; end for t=1:npoints r1(t+w)=radius(t); phi1(t+w)=phi(t); end

% Scale phi axis to [-1 1] phi1=phi1/pi;

% Fit with a polynom of degree PolyOrder PolyOrder=20; BUTTON=0; p=polyfit(phi1,r1,PolyOrder); while BUTTON ~= 32 p t1 r2

= polyfit(phi1,r1,PolyOrder); = linspace(-1,1,200); = polyval(p,t1);

% Polar plot figure(2) polar(phi,radius,'m.');hold on polar(t1*pi,r2,'r') title(['Change order of polynomial Current is (' num2str(PolyOrder) ');']) text(-9,-10,'left/right mouse button --> up/down (space bar = done)') hold off [dummy1,dummy2,BUTTON]=ginput(1); if BUTTON ~= 32 if BUTTON == 1 PolyOrder=PolyOrder+1; else BUTTON == 2 PolyOrder=PolyOrder-1; end

31

end end close % The first number in coeff is the order of the polynom Polyorder coeff=[PolyOrder p];

fdfread2.m function output = fdfread_slice(imagename,slice); % % % % % % %

FDFREAD_SLICE reads an FDF (Flexible Data Format) image slice. OUTPUT = FDFREAD(IMAGENAME, SLICE) gives header and image IMAGENAME is a string with the name of the image. SLICE is the preferred slice. OUTPUT is a structure with image information from the header and the image data. OUTPUT.IMAGE parameters with the image in a 2D matrix.

% % %

Matlab V6.0 Edwin Heijman [email protected] December 2001 Modified for reading per slice by Marcel Lourens

fid % % % %

= fopen(imagename,'r','b'); Open the file in read mode and the file is a big-endian (UNIX) file for een litte-endian file (PC) use the 'l' parameter instead of the 'b'

if fid == -1, disp(['The image ' imagename ' does not exist']); X = 0; else if exist('infostring')==0, infostring='read';end; header = fread(fid,1); fheader=header; while header~=0, header = fread(fid,1); fheader = [fheader header]; end % De header is read and wil set in the text array Info Info = setstr(fheader); % Test if it is a fdf file if isempty(Info), disp([imagename ' is not an FDF-file']); elseif ~strcmp(Info(1:2),'#!'), disp([imagename ' is not an FDF-file']); end % ------------------------------------------------------------------% Read header and convert the data into a structure % ------------------------------------------------------------------% Spatial rank position = findstr(Info,'spatial_rank = "')+length('spatial_rank = "'); new_position = 1;

32

while (Info(position)~='"') & (new_position