Survey

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Survey

Transcript

Automatic feature extraction of 3D anatomic data. Bart Coppieters Promotoren: prof. dr. ir. Benedict Verhegghe, prof. dr. Katharina D'Herde Begeleiders: Germano Gomes, Peter Mortier Scriptie ingediend tot het behalen van de academische graad van Burgerlijk ingenieur in de computerwetenschappen Vakgroep Mechanische constructie en productie Voorzitter: prof. dr. ir. Joris Degrieck Vakgroep Anatomie, embryologie, histologie en medische fysica Voorzitter: prof. dr. Hubert Thierens Faculteit Ingenieurswetenschappen Academiejaar 2007-2008 Forewords My special thanks go out to Germano Gomes. Without his appreciated guidance throughout this academic year my work would not have been like it is up to date. In the hours we spent together behind his desk many ideas critical for the success of my work originated. Also I would like to thank my promoters for the chance they gave to make a study about this subject. Furthermore I would like to thank my parents, not only for this year, but for all the years that I got the opportunity to study and to grow as a person. At last I dedicate words of thankfulness to my girlfriend, Vanesa, and my friends who were important to me for keeping me motivated and just for all the wonderful moments the past few years. Bart Coppieters, June 1st 2008 Toelating tot bruikleen "De auteur geeft de toelating deze masterproef voor consultatie beschikbaar te stellen en delen van de masterproef te kopiëren voor persoonlijk gebruik. Elk ander gebruik valt onder de beperkingen van het auteursrecht, in het bijzonder met betrekking tot de verplichting de bron uitdrukkelijk te vermelden bij het aanhalen van resultaten uit deze masterproef." Permission for usage "The author gives the permission to make this thesis available for consultation and to copy parts of this thesis for personal use. Any other use is subject to the limitations of copyright, particularly with regard to the obligation to specify the source when quoting results from this thesis." Bart Coppieters, June 1st 2008 2 Automatic feature extraction of 3D anatomic data. Bart Coppieters Scriptie ingediend tot het behalen van de academische graad van Burgerlijk ingenieur in de computerwetenschappen Academiejaar 2007-2008 Promotoren: prof. dr. ir. Benedict Verhegghe, prof. dr. Katharina D'Herde Begeleiders: Germano Gomes, Peter Mortier Vakgroep Mechanische constructie en productie Voorzitter: prof. dr. ir. Joris Degrieck Vakgroep Anatomie, embryologie, histologie en medische fysica Voorzitter: prof. dr. Hubert Thierens Faculteit Ingenieurswetenschappen Summary The objective of this work is the development of a tool with Matlab® with which virtual palpation can be done in an automatic way. Virtual palpation comes down to locating predefined features in 3D models of the human skeletal system. In this work we have focused on the extraction of features of the upper limb bones: the humerus, the scapula, the clavicle, the ulna and the radius. User interaction has to be limited to a minimum in order to make the feature extraction an autonomic procedure. Keywords Matlab®, GUI, automatic feature extraction, anatomy 3 Table of contents Chapter 1 Introduction............................................................................................. 1 Chapter 2 The anatomy of the upper limb bones .................................................. 4 2.1 Humerus .......................................................................................................... 5 2.1.1 Upper End...................................................................................................... 5 2.1.2 The Body or Shaft of the humerus ................................................................. 6 2.1.3 The Lower End .............................................................................................. 7 2.2 The Scapula .................................................................................................... 7 2.2.1 Surfaces......................................................................................................... 8 2.2.2 Borders .......................................................................................................... 8 2.2.3 Angles............................................................................................................ 9 2.3 Clavicle ............................................................................................................ 9 2.3.1 Lateral Third................................................................................................. 10 2.3.2 Medial Two-thirds......................................................................................... 10 2.4 Ulna ............................................................................................................... 11 2.4.1 The Upper End ............................................................................................ 11 2.4.2 The Body or Shaft ........................................................................................ 11 2.4.3 The Lower End ............................................................................................ 12 2.5 Radius ........................................................................................................... 12 2.5.1 The Upper End ............................................................................................ 12 2.5.2 The Body ..................................................................................................... 13 2.5.3 The Lower End ............................................................................................ 13 2.6 Features to be extracted................................................................................ 14 Chapter 3 The graphical user interface (GUI) ...................................................... 15 3.1 Step by step explanation of the use of the GUI ............................................. 16 3.1.1 Make the GUI visible.................................................................................. 16 3.1.2 Read in a 3D model of a bone ................................................................... 18 3.1.3 Do all the critical procedures for the selected bone ................................... 19 3.1.4 Feature visualization and curvature calculation ......................................... 21 3.1.5 Reset the GUI to its initial state.................................................................. 23 3.2 A comparison between Stl files and patch objects ........................................... 24 3.3 Bone-specific coordinate systems .................................................................... 26 3.3.1 Humerus .................................................................................................... 27 3.3.2 Scapula...................................................................................................... 28 3.3.3 Clavicle ...................................................................................................... 29 3.3.4 Ulna ........................................................................................................... 30 3.3.5 Radius........................................................................................................ 31 Chapter 4 The humerus ......................................................................................... 33 4.1 The orientation of the humerus...................................................................... 35 4.2 Extraction of the lateral and the medial epicondyle ....................................... 46 4.3 Extraction of the head of the humerus ........................................................... 47 4.4 Extraction of the greater tubercle................................................................... 53 4.5 4.6 4.7 4.8 4.9 Extraction of the lesser tubercle .................................................................... 57 Extraction of the trochlea ............................................................................... 60 Extraction of the capitulum ............................................................................ 63 Extraction of the topmost point of the humerus ............................................. 65 Extraction of the metaphyseal cylinder .......................................................... 65 Chapter 5 The scapula........................................................................................... 70 5.1 The orientation of the scapula ....................................................................... 71 5.2 Extraction of the inferior angle ....................................................................... 80 5.3 Extraction of the superior angle ..................................................................... 80 5.4 Extraction of the tip of the acromion .............................................................. 83 5.5 Extraction of the acromial angle .................................................................... 87 5.6 Extraction of the tip of the coracoid process .................................................. 91 5.7 Extraction of the root of the spine .................................................................. 94 5.8 Extraction of the glenoid cavity ...................................................................... 98 Chapter 6 The clavicle ......................................................................................... 102 6.1 The orientation of the clavicle...................................................................... 102 6.2 Extraction of the anterior sternoclavicular joint ............................................ 110 6.3 Extraction of the posterior acromioclavicular joint........................................ 111 Chapter 7 The ulna............................................................................................... 112 7.1 The orientation of the ulna ........................................................................... 113 7.2 Extraction of the apex of the olecranon ....................................................... 120 7.3 Extraction of the coronoid process .............................................................. 121 7.4 Extraction of the styloid process .................................................................. 123 7.5 Extraction of the ulnar head......................................................................... 123 Chapter 8 The radius ........................................................................................... 127 8.1 The orientation of the radius ........................................................................ 127 8.2 Extraction of the radial head ........................................................................ 135 8.3 Extraction of the radial head periphery ........................................................ 138 8.4 Extraction of the styloid process .................................................................. 140 Chapter 9 Results ................................................................................................ 142 9.1 Visualization ................................................................................................ 142 9.2 V-Palp.......................................................................................................... 148 9.3 Manual......................................................................................................... 149 9.4 Results......................................................................................................... 150 Chapter 10 Conclusion .......................................................................................... 155 Appendix A: Anatomical Nomenclature................................................................. 157 Appendix B: Matlab® code ..................................................................................... 158 B.1 The GUI and functions ................................................................................. 159 2 B.2 B.3 B.4 B.5 B.6 B.7 Humerus ..................................................................................................... 167 Scapula........................................................................................................ 183 Clavicle ........................................................................................................ 198 Ulna ............................................................................................................. 203 Radius ......................................................................................................... 210 General functions ......................................................................................... 218 Appendix C: The Stl format..................................................................................... 222 C.1 Format Specifications ...................................................................................... 222 C.2 Format Specifications ...................................................................................... 223 Appendix D: Curvature ............................................................................................ 225 D.1 Matlab implementation..................................................................................... 225 Bibliography ............................................................................................................. 235 3 Chapter 1 Introduction Clinical studies and orthopedic operative actions produce a huge amount of medical image content, such as x-rays, digital scans, ct scans, … Usually all these images are stocked in very large databases which makes a scientific analysis of these images a very time consuming task. For example, it would be difficult to carry out the measurement of the length of the clavicle for classification purposes. First the image, for example an x-ray, has to be retrieved from the database and subsequently a manual measurement of the length has to be done. The fact the procedure is carried out by a human person increases the probability of inter-observer errors throughout the measuring. Therefore it should be made possible to carry out some analyses in an automatic manner. The above justified our search for an automatic way in discovering predefined anatomical landmarks of the human body in medical images. With automatic is meant that the whole procedure of getting those established points of the body, is done without the interaction of a human person. The search for landmarks on medical images is also referred to as virtual palpation [1]. It is used mainly to quantify individual morphologic parameters from medical imaging: - Limb length - Limb orientation - Joint angle - Distance between various skeletal locations. In combination with the results of a manual palpation process, where the medical images are replaced by the real human specimen, supplementary complex analyses can be undertaken: - Accurate modeling of joint kinematics during musculoskeletal analysis - Precise alignment of orthopedic tools according to the individual anatomy of a patient. 1 With this said, a framework is sketched of which the real objective of our work will be a part. The idea is to design a tool in the programming environment of Matlab® with which users can locate in an automatic manner the position of predefined features of a determined set of bones. As virtual palpation assumes the interaction of the healthcare professional with the medical images to localize the wanted features, reproducibility of these extracted positions is not a certainty. Another person working with the same images will probably get other results although their intention is the same. Our goal is to develop a tool that will operate as a black box and that will produce for a given input bone always the same output. The input are 3D bone models of: -Humerus -Scapula -Clavicle -Ulna -Radius. All these bones belong to the upper extremity of the human body. In Chapter 2 their anatomical properties are briefly described. With features we mean the representations of specific locations (landmark) of a bone. For example if a structure of a bone must be extracted that has a shape like a sphere, the feature associated with it would be the specification of the sphere, in this case the sphere’s center and radius. In the end of Chapter 2 a list of all the extracted features for each bone is given. The tool has a simple front end graphical user interface (GUI) that will allow the user to determine the bone and the feature of the bone he or she is interested in. In Chapter 3 the layout and the functionality of the GUI is described and explained. 2 Selecting the bone is the only user interaction that can be done as the algorithms that run the extraction of the features, are carried out automatically without any help of the user. There is no need to give in parameters or to select points onto the surface of the 3D model. Every single feature extraction will operate completely and successfully on its own. Though the results are dependent on the quality of the input data. Chapters 4 to 8 give for each bone an extensive explanation of the functions that were used to estimate the desired features of it. Chapter 9 gives a brief summary of the results that we achieved. Despite the absence of an extensive amount of reference data, this concluding chapter will clearly demonstrate the capabilities of the program. In the final chapter critical remarks are given. 3 Chapter 2 The anatomy of the upper limb bones In this chapter is briefly talked about the anatomical properties exhibited by each bone that will be processed throughout the rest of this work. The following image will make clear which bones will play a central role. These bones are, as can be seen on Figure 2.1, all located in the upper part of the human body. 3 2 1. Humerus 1 2. Scapula 3. Clavicle 4. Ulna 5. Radius 4 5 Figure 2.1 This chapter serves solely as an introduction to the anatomy for those readers who are not familiar with it. It is not our intention to do an extensive study of the anatomy of the bones of interest as this can be found in any anatomy reference book such as ([2], [3], [4]). Each of the bones of Figure 2.1 will be explained from an anatomical point of view, but only with respect to our work. The content of this chapter is based on [4]. At the end of the chapter, after the anatomy is more or less clear, a table will clarify which features of which bones are being extracted. 4 In Appendix A the anatomy-specific terms that are used, are explained. These will be used frequently, not only in this chapter but also in the chapters to come. 2.1 Humerus The humerus is the longest and largest bone of the upper extremity. It fits between the scapula and the clavicle above and the ulna and the radius below. It is divisible into a body and two epiphysis, the upper and the lower end. 2.1.1 Upper End (Figure 2.2) The upper end consists of a large rounded head (a). Furthermore it consists of two eminences, the greater (d) and lesser (e) tubercles. These give attachment for shoulder muscles. Figure 2.2: Anterior aspect of left humerus, the upper end. The head (a), hemispherical of shape, is directed upward, medialward, and a little backward, and articulates with the glenoid cavity of the scapula. Its circumference is termed the anatomical neck (b). Below the tubercles is another constriction, called the surgical neck (c). The greater tubercle (d) is situated lateral to the head and lesser tubercle (e). The lateral surface of the greater tubercle is convex, rough, and continuous with the lateral surface of the body of the humerus. The lesser tubercle (e), although smaller, is more prominent than the greater: it is situated in front, and is directed medialward. 5 The tubercles are separated by a deep groove, the intertubercular groove (f). 2.1.2 The Body or Shaft of the humerus (Figure 2.3) The body of the humerus can be seen as cylindrical in its upper half and below it has less or more a prismatic shape. Therefore one can distinct three borders and three surfaces. Figure 2.3: Anterior (left) and posterior (right) aspect of left humerus. The anterior border runs from the front of the greater tubercle to the coronoid fossa below. About its center it forms the anterior boundary of the deltoid tuberosity (h). The lateral border runs from the back part of the greater tubercle to the lateral epicondyle (i). Its center is traversed by a broad but shallow oblique depression, the radial sulcus (j). The medial border runs from the lesser tubercle to the medial epicondyle (k). 6 2.1.3 The Lower End (Figure 2.4) The lower end terminates below in a broad, articular surface, which is divided into two parts by a slight ridge. Extending on either side are the lateral and medial epicondyles (i and k). The lateral portion of this surface consists of a smooth, rounded eminence, named the capitulum (l) of the humerus. It is only present on the front part of the bone. This elevation articulates with the head of the radius. Above the front part of the capitulum is a slight depression, the radial fossa (m). The medial portion of the articular surface is named the trochlea (n), and presents a deep depression between two well-marked borders. Figure 2.4: Lower end of left humerus. Anterior aspect (left) and posterior aspect (right). Above the front part of the trochlea (n) is a small depression, the coronoid fossa (g), which receives the coronoid process of the ulna during flexion of the forearm. Above the back part of the trochlea (n) is a deep triangular depression, the olecranon fossa (o), in which the summit of the olecranon of the ulna is received in extension of the forearm. The epicondyles are continuous above with the supracondylar ridges (p = lateral, q = medial). 2.2 The Scapula The scapula forms the posterior part of the shoulder girdle. It is a flat, somewhat triangular bone, with two surfaces, three borders, and three angles. 7 2.2.1 Surfaces Because of its flat structure the scapula has two main surfaces: the costal or ventral surface and the dorsal surface. The costal surface (Figure 2.5, left) presents a broad concavity, the subscapular fossa (a). The dorsal surface (Figure 2.5, right) is arched from above downward and is subdivided by a bony projection, the spine of the scapula (c), into the supraspinatus fossa (d) and the infraspinatus fossa (e). Figure 2.5: Left: Costal aspect of left scapula. Right : Dorsal aspect of left scapula. The spine of the scapula gradually becomes more elevated until it runs into a forward pointing hook called acromion (b). The acromion, on its own right, forms the top of the shoulder. It overhangs the glenoid cavity (f). In the center of its medial border it articulates with the acromial end of the clavicle. 2.2.2 Borders The scapula has three borders: the superior border (A), the lateral border (B) and the medial border (C). 8 2.2.3 Angles There are also three angles: the superior angle (D), the inferior angle (E) and the lateral angle(F). Situated on the lateral angle is the glenoid cavity (f). This is a shallow articular surface which is directed lateralward and forward and articulates with the head of the humerus. The part below is broader than the part above and its vertical diameter is the longest. Below the glenoid cavity is the infraglenoid tuberosity (i) situated. At its summit is a little elevation, the supraglenoid tuberosity (j). Figure 2.6: Lateral aspect of left scapula. The last significant structure associated with the scapula is the coracoid process (g). It is a thick process attached by a broad base to the upper part of the neck of the scapula (k).. 2.3 Clavicle The clavicle is a long bone that connects the arm to the body. It is placed nearly horizontally at the upper and anterior part of the thorax, immediately above the first rib. It articulates on its medial end with the sternum, and laterally with the acromion of the scapula. 9 It presents a double curvature, with the convexity being directed forward at the sternal end (a), and the concavity at the acromial end (b). Its lateral third is flattened from above downward, while its medial two-thirds is of a pyramidal form. Figure 2.7: Superior aspect of left clavicle. 2.3.1 Lateral Third (Figure 2.7 and 2.8 at the right side) The lateral third has two surfaces, an upper and a lower; and two borders, an anterior and a posterior. At the posterior border of its inferior surface is a rough eminence, the conoid tubercle (c). From this tuberosity an oblique ridge, the trapezoid ridge (d), runs forward and lateralward, and affords attachment to the trapezoid ligament. Figure 2.8: Inferior aspect of left clavicle. 2.3.2 Medial Two-thirds (Figure 2.7 and 2.8 at the left side) The medial two-thirds are made up by the prismatic portion of the bone, which is curved in a convex manner in front and in a concave manner behind. 10 On the medial part of its inferior surface is a broad rough surface, the costal tuberosity (e). 2.4 Ulna The ulna is a long bone, prismatic in form, situated at the medial side of the forearm between the humerus above and the hand below. It is therefore parallel with the radius. It is divisible into a body and two end parts. Its upper end, of great thickness and strength, forms a large part of the elbow-joint. The bone diminishes in size from above downward. At its smaller lower end there is a connection to the hand by the wrist joint. 2.4.1 The Upper End (Figure 2.9) The upper end presents two curved processes, the olecranon (a) and the coronoid process (c). It has also two concave, articular cavities, the semilunar (b) and radial notches (d). Figure 2.9: Upper end of left ulna. Anterior aspect. 2.4.2 The Body or Shaft The body at its upper part is prismatic in form; its central part is straight; its lower part is rounded, smooth, and bent a little lateralward. It has three borders in the longitudinal direction and thus 3 surfaces. 11 2.4.3 The Lower End (Figure 2.10) The lower end of the ulna is small, and presents two eminences. The lateral and larger is rounded and plays a role in articulation. It is termed the head of the ulna (e). The medial, narrower and more projecting, is a non-articular eminence, termed the styloid process (f). The styloid process descends a little lower than the head. Figure 2.10: The left ulna. Antero-lateral aspect. 2.5 Radius The radius is, just like the ulna, a bone present in the forearm. More specified it is situated on the lateral side of the ulna. It has a body and two ends. Its upper end is small, and forms only a small part of the elbow-joint. On the contrary, its lower end is large, and forms the main part of the wrist-joint. It is a long bone, prismatic in form and slightly curved longitudinally. 2.5.1 The Upper End (Figure 2.11) The upper end presents a head, neck, and tuberosity. The head (a) is of a cylindrical shape. On its upper surface is a shallow cup (b) for articulation with the capitulum of the humerus. The head is supported on a round, smooth, and constricted portion called the neck (c). Beneath the neck, on the medial side, is a prominent eminence, the radial tuberosity (d). 12 Figure 2.11: Left radius. Anterior view of the upper end. 2.5.2 The Body (Figure 2.12) The body is narrower above than below and slightly curved, so as to be convex lateralward. It presents three borders and hence three surfaces. Figure 2.12: Left radius: upper end, shaft and lower end. 2.5.3 The Lower End (Figure 2.13) The lower end is large, of quadrilateral form, and has two articular surfaces: one below, for the carpus (the cluster of bones in the hand between the radius and ulna and the metacarpus), and another at the medial side, for the ulna. The carpal articular surface is triangular of shape and is divided in two parts. The lateral, triangular (e), articulates with the scaphoid bone (hand bone of the carpus). The medial, quadrilateral part (f) articulates with the lunate bone (hand bone of carpus). The articular surface for the ulna is called the ulnar notch of the radius (g). It articulates with the head of the ulna. 13 This lower end of the bone has three non-articular surfaces: volar, dorsal, and lateral. The lateral surface runs obliquely downward into a strong, conical projection, the styloid process (h). Figure 2.13: Left radius. Anterior view of the lower end. 2.6 Features to be extracted The following table gives an overview of the main features to be extracted automatically by the tool. The significance of each of these can be found in the papers under column ‘References’. Bones Features Humerus Lateral epicondyle. Medial epicondyle. Head. Lesser tubercle. References Greater tubercle. Trochlea. Capitulum. Topmost point. Metaphyseal cylinder. Scapula Inferior angle. Superior angle. Acromial Tip. Acromial angle. Tip of coracoid process. Root of the spine. Glenoid cavity. Clavicle Ulna Anterior sternoclavicular joint. Posterior acromioclavicular joint. [1],[8],[9],[10],[11] [1],[11] Apex of the olecranon. Coronoid process. Styloid process. Ulnar head. Radius [1],[5],[6],[7],[11] Radial head. Radial head periphery. Styloid process. [1],[11] [1],[11] 14 Chapter 3 The graphical user interface (GUI) A graphical user interface (GUI) is a graphical display that contains devices, or components, that enable a user to perform interactive tasks. To perform these tasks, the user of the GUI does not have to create a script or type commands at the command line. Often, the user does not have to know the details of the task at hand. In this chapter the developed Graphical User Interface (GUI) is described and explained. Not only the layout is discussed, but also the functionality associated with each component in the GUI will have its explanation. The development environment used for making the GUI and all of its associated functions is Matlab®. In Matlab® there is a specialized graphical user interface development environment available that provides a set of tools for creating GUIs, GUIDE. These tools simplify the process of laying out and programming GUIs. First of all the layout of the GUI can be set by just populating it with GUI components (such as buttons, panels, radio buttons,…). This population process is done in the GUIDE layout editor and is based on clicking and dragging the needed component into the layout area. Subsequently when you save your GUI layout, GUIDE automatically generates a Matlab-file (M-file) that you can use to control how the GUI works. This Mfile provides code to initialize the GUI and contains a framework for the GUI callbacks; the routines that execute in response to user-generated events such as a mouse click. Using the standard Matlab-file editor, you can add code to the callbacks to perform the functions you want. Section 3.1 will go step by step through the use of the GUI as experienced by the user. The readers that are interested in the Matlab® code that is responsible for the behavior of the GUI are referred to Appendix B.1. In the appendix remarks are given to clarify the code. 15 3.1 Step by step explanation of the use of the GUI In this section we will give for each possibility to interact with the GUI the functional consequences of this interaction. Every step will be accompanied with a screenshot of the GUI on that given moment for clarification. In the development process of the GUI, we took the ease of usability as a starting point. Therefore the GUI is constructed in such a manner that it takes away the possibility of making mistakes while working with it. Each step in interacting with the GUI will be selfexplanatory. This will be clear after reading this chapter. 3.1.1 Make the GUI visible The origin of the visible GUI is the layout of the GUI. This is at all times available for alteration in the GUIDE layout editor. In Figure 3.1 the eventual layout of our tool is depicted in the GUIDE layout editor. Figure 3.1 depicts a set of buttons, panes, radiobuttons, popupmenus and editable text. These will be visible for the user dependent on his interactions with the GUI. In order to work with the GUI, it has to be started up. This is done by running the M-file that contains the underlying Matlab® code of the GUI. This file is originally created when the GUI layout is saved for the first time. Starting up this file will first carry out the initialization function that will define the initial look and feel of the GUI that will be visualized. In this function parameters can be set, components can be made invisible, files can be loaded… In Figure 3.1 it can be seen that the components are distributed over the editor space on top of each other. If beginning to work with the tool, the user must not see all these components, therefore in our initialization function, all the components, except 1 button, are made invisible. The first GUI visualization accessible for the user is depicted in Figure 3.2. 16 Figure 3.1: The layout of our GUI Figure 3.2 17 3.1.2 Read in a 3D model of a bone As seen in Figure 3.2 the only possible interaction is pushing the button ‘Read in 3D bone model’. Clicking this button will execute the callback associated to it, pushOpen_Callback. Basically this will launch a standard dialog box for retrieving the files that correspond to 3D models of the bones. In Figure 3.3 the illustration is given. Figure 3.3 We have worked with 3D models represented by binary Stl files. An StL (“StereoLithography”) file is a triangular representation of a 3-dimensional surface geometry. The surface is broken down logically into a series of small triangles (facets). Each facet is described by a perpendicular direction and three points representing the vertices (corners) of the triangle. More information about the Stl format can be found in Appendix C. After the user has picked an Stl file, it will be read in. The format to which the data in the file will be transformed is a graphics object, patch, that is inherent to Matlab®. A discussion about the difference between the Stl file and the patch object representation is given in section 3.2. Important for now is that we have the representation of the 3D model of a bone at our disposal. This information will be saved so it can be handed 18 over to other callbacks that will execute the feature extraction processes for the read in bone. Subsequently a panel becomes visible. A panel is nothing more than a container component to unite a set of components. This panel contains 5 radiobuttons that correspond to the 5 bones that are studied. Figure 3.4 represents the current state of the GUI. Figure 3.4 In the continuation of this section we will work with an arbitrarily chosen 3D model of a humerus. The subsequent actions taken and explanations given can be generalized to the 3D models of the other bones (scapula, clavicle, radius, ulna). 3.1.3 Do all the critical procedures for the selected bone The user must specify the bone of which he just has read in a 3D model. If the read 3D model is a scapula, the user will have to click the radiobutton ‘Scapula’. The callback corresponding to the radiobutton is the logical heart of the GUI. In this routine all the processes responsible for extracting all the features of a given bone are carried out. The following flowchart gives the sequence of routines if a radiobutton associated with a bone is selected. 19 Orient the bone Extract all the bone’s features Visualize the bone (only the bone not the features) In the following 5 chapters the functions responsible for the first 2 actions, orienting and extracting, are discussed in detail. A chapter is dedicated to each bone because this is where the most work and complexity is found. The last visualization step of this callback is for each type of bone the same. The output bone of the 2 previous actions is always a right sided one. This choice was made for ease of locating the features. More information about this issue is given in the following chapters that are dedicated to each processed type of bone. If the input bone was a left sided one, we want it to be visualized as a left sided one. Thus a reverse transformation from the right sided counterpart (as used in the extraction procedures) to its original left side is done first. The transformation to do this changing of sides is a standard mirror transformation with respect to the y-z plane. The features that were extracted have also positions that are defined with respect to the right bone. So in case of a left bone, the extracted features have to be mirrored in the same way as the bone. For now only the bone will be visualized, the features are visualized in a later step. This visualization of the bone happens in a newly opened window which only contains an axis for drawing the 3D model of the oriented bone. Apart from the callback that is executed by clicking the radiobutton, the look of the GUI will be changed. This will give the user options to interact again with the GUI to complete his objective. Figure 3.5 gives the reached state of the GUI. First of all the radiobuttons panel ‘Bones’ is removed from the GUI. By doing so, the user can not click again on another radiobutton This would carry out the functional processes associated with another bone type and hence it will produce faulty results. Secondly 2 panels become visible: a panel with the extractable features of the selected 20 bone (Figure 3.5, a) and a panel that will give the user the opportunity to calculate the curvature of the bone (Figure 3.5, b). The panel represented by a comes accompanied with 2 panels that will hold the coordinates of the extracted features. In 3.3 will be elaborated on the two types of coordinates. b c a e d Figure 3.5 On the right side of figure 3.5 the bone is showed. It is drawn in the normal Cartesian reference system representing 3D space. Visualizing the bone includes implicitly also showing the 3 blue arrows (Figure 3.5, c). They are calculated to form another, bonespecific reference system as defined in [11]. In section 3.3 the functions for forming those reference systems are outlined and discussed. The bone is now depicted in 2 reference systems, a global one, the Cartesian system, and a local one, the bone-specific system. Therefore there are 2 panels to display the position of the features in both reference systems. These panels are shown in Figure 3.5 as respectively d and e. 3.1.4 Feature visualization and curvature calculation In the GUI as shown in Figure 3.5 the user can undertake two actions: selecting features of the processed bone or calculate the bone’s curvature. 21 3.1.4.1 Feature visualization All the features of the selected bone are contained in the feature panel (Figure 3.5, a). When the user clicks a radiobutton associated with a feature, the feature will be depicted onto the 3D model of the bone. Besides that, the coordinates of its position are shown in d and e of Figure 3.5. If the feature consists of visualizing a geometrical shape, all the parameters for drawing this geometric shape will be displayed in the reference system panels. For example the feature ‘Center of head’ is represented by a sphere and therefore its center and radius are given. The user can select a random selection of features to be visualized together. It is also possible to deselect a feature. This corresponds to deleting the visualization of the feature from the 3D model of the bone. Figure 3.6 gives a possible current look for the GUI. Figure 3.6 3.1.4.2 Mean curvature calculation This can be done by manipulating b in Figure 3.5. This panel contains a popupmenu with which the user can specify with which neighborhood the mean curvature calculation has to be done. Through the calculation each point of the 3D model gets a value which represents its mean curvature. This is a measure of extrinsic curvature of a 22 surface. Regions of the model that exhibit convexity have negative mean curvature values, concave regions will have positive values of mean curvature. The outcome of this interaction is a second window which will hold the 3D model depicted in function of the mean curvature information. Figure 3.7 illustrates this frame. Figure 3.7 The algorithm to do the curvature calculation is outlined in [12]. Our Matlab® implementation of the algorithm is outlined in Appendix D. 3.1.5 Reset the GUI to its initial state Once the user has terminated his processing of a bone and he wants to start with a new one, the GUI has to be reset to its initial state as in Figure 3.2. This can be achieved in three ways. Or you can push the button ‘Read in 3D model’, or you can close the window where the 3D model is depicted, or you can close the frame where the 3D model’s curvature is shown (if this frame is visible). 23 3.2 A comparison between Stl files and patch objects The starting point of all the processing is reading in an Stl file. This is done by the following statement. [x,y,z,n] = stlread(‘filename.stl’); The Stl file, ‘filename.stl’ describes a triangular mesh. This is a 3D model constructed by connected triangles. The output of the function is a patch object defined by the matrices x, y and z. x, y and z are 3-by-N matrices that together represent the patch object. Together with this patch representation of the 3D model stlread outputs n, a 3-by-N matrix containing in its columns the normalized normals to the facets (triangles) of the 3D model. This will be of importance for the mean curvature calculation. But the emphasis lies for now on x, y and z. A patch object is a graphics object recognized by Matlab®. It is one or more polygons defined by the coordinates of its vertices. In this case the polygons are triangles. Therefore the 3 matrices have sizes 3-by-N, where N is the number of facets (triangles). Thus each column of the 3 matrices represents a facet. For example: the first column of matrix x represents the 3 x-coordinates of the 3 vertices of the facet associated with the first column. The first column of matrix y holds the 3 y-coordinates of the vertices of the same facet. And finally matrix z does the same, but for the zcoordinates. To make it more clear: if we make a new 3-by-3 matrix with as the first column the m-th column of x, as the second column the m-th column of y and as the third column the m-th column of z, we get all the coordinates of the 3 vertices of the mth facet of the model. In Figure 3.9 an illustration is given to clarify the use of the 3 matrices x, y and z. 24 Matrix z v2 z3 y3 v1 Matrix y v3 x3 Matrix x Figure 3.9: A random facet of which vertex v3 has coordinates x3, y3, z3. The 3 matrices x, y and z will be given as input arguments to the Matlab® function patch which will draw the 3D model in space. As x, y and z are useful for drawing; they are not for processing because they are 3. Therefore it is rewarding in some situations to transform the 3 matrices to one M-by-3 matrix, which will hold in each row the x-, y- and z-coordinates of one vertex of the model. The technique to do this transformation is self-explanatory and is based on matrix procedures. An attentive reader will ask himself questions about the transformation of 3 3-by-N matrices to 1 M-by-3 matrix. Why do we go from N to M, with M<N? This is due to the fact that in the original matrices every vertex is represented several more times because each vertex belongs theoretically to more than one facet. And each column represents a facet in these matrices, so the same vertex will appear in several columns. Part of the transformation to one matrix is discarding these ‘redundant’ vertices. Speaking of redundant vertices is theoretically incorrect. They do add information to the data. Precisely because of the repeated occurrences of all vertices in the representation of the patch object, it is possible to determine the triangles. This on its turn holds information about which points are connected with which other points. So indirectly it holds the connectivity information of the binary Stl file. 25 For a lot of procedures the connectivity is not needed and therefore we downscale the 3 matrices to 1 matrix. On the other hand, for other procedures, for example drawing and the functions associated with calculating the curvature of the 3D models of the bones, the connectivity is a requirement and therefore this downscaling must not be done. In the functions you will therefore see switches from the 3 matrices representing the patch object to the matrix or list of points. To summarize: they represent the same, but serve other purposes. In the end a 3D model of a bone can be represented by 2 structures: or 3 matrices which will contain connectivity information or 1 matrix without this information. The agreement for the rest of the work is: if we talk about the patch object defined by the 3 matrices, we will talk about a patch object of vertices. This is the patch representation of the 3D model. On the other hand, if we talk about the simplified matrix, we will talk about a list of (data)points. The 3D model is treated like a set of data in the this case. This will be referred to as the list representation of the 3D model. 3.3 Bone-specific coordinate systems In 3.1.3 we talked about the concept of a bone-specific coordinate system for each of the 5 bones (humerus, scapula, clavicle, ulna and radius). Moreover we agree on using the term ‘local reference system’. The definition for each dedicated coordinate system is given in [11]. In this section we will illustrate each of these systems on top of the corresponding bone, and we will explain the construction of them. The coordinate systems are based on the positions of specified features and thus each system will be different for each bone. Hence for example, 2 different clavicles have 2 different local coordinate systems. The features that are needed to define the coordinate systems, will all be extracted by our tool. The coordinate systems are calculated in our tool for the right sided bones that were output of the orientation procedure, so whenever a left bone has to be visualized, the 26 local reference system has to be mirrored in the same way as the bone – with respect to the y-z plane. For the Matlab® code of the following functions the reader is referred to Appendix B.1. 3.3.1 Humerus The local coordinate system associated with the humerus, (Oh, Xh, Yh, Zh), can be defined once three features are available: the center of the head (Figure 3.10, left, yellow), the lateral (Figure 3.10, left, red) and the medial epicondyle (Figure 3.10, left, blue). Construction of the system is as follows. Figure 3.10 - The origin, Oh, is coincident with the center of the head. -Yh: the line connecting the center of the head and the midpoint between the two epicondyles (Figure 3.10, left, purple), pointing to the center of the head. -Xh: the line perpendicular to the plane formed by the center of the head and the two epicondyles, pointing anteriorly. -Zh: the line perpendicular to the two former defined axes, pointing laterally. 27 For finding directions perpendicular to a given plane it is sufficient to do the cross product of two vectors defining the plane. The execution of this coordinate system determination is done in function ashum (Appendix B.1.2). 3.3.2 Scapula The local coordinate system of the scapula (Os, Xs, Ys, Zs) is defined with: the acromial angle(Figure 3.11, blue), the inferior angle (Figure 3.11, red) and the root of the spine of the scapula (Figure 3.11, yellow). Construction of the system is as follows. Figure 3.11 -The origin, Os, is coincident with the acromial angle. -Zs: the line connecting the root of the spine with the acromial angle, pointing towards the acromial angle. -Xs: the line perpendicular to the plane formed by the acromial angle, the inferior angle and the root of the spine, pointing anteriorly. 28 -Ys: the line perpendicular to the two former defined axes, pointing upward. The routine of calculating this local coordinate system is done by the function asscap. 3.3.3 Clavicle For the local coordinate system of the clavicle (Oc, Xc, Yc, Zc) as defined in [8] the 2nd component of the local coordinate system of the thorax is needed [8]. As this axis is unavailable a good approximation has to be chosen for the direction of Yt, the y-axis of the thorax. The axis Yt can be approximated by the global z-axis Zglobal if the clavicle is oriented like in the human body. The most anterior point of the sternoclavicular joint (SC, Figure 3.12, blue) and the most posterior point of the acromioclavicular joint (AC, Figure 3.12, red) are involved too in the construction of (Oc, Xc, Yc, Zc). Figure 3.12 -The origin, Oc, is coincident with SC, the most anterior point of the sternoclavicular joint (blue marker). -Zc: the line connecting SC and AC, pointing to AC. -Xc: the line perpendicular to Zc and Yt, pointing forward -Yc: the line perpendicular to Xc and Zc, pointing upward. 29 The routine of calculating this local coordinate system is done by the function asclav. 3.3.4 Ulna In [3] is not a definition given for a coordinate system for the ulna as a bone on its own. A definition is given for the entire forearm. But for this definition features from both bones of the forearm, ulna and radius, are needed. As only features of one bone will be made available by our tool, we had to seek for a solution. This solution is delivered in the shape of a newly designed coordinate system, in which only features of the ulna are required. The features are the apex of the olecranon (OL, Figure 3.13, red), the tip of the coronoid process (COR, Figure 3.13, blue) and a point that will help in defining the coordinate system. This is not contained in the list of features for the ulna. In Figure 3.13 this helping point is depicted as the yellow marker. We will call it AUX. Figure 3.13 The coordinate system (Ou, Xu, Yu, Zu) is constructed as follows: The origin, Ou, is the midpoint between COR and AUX. 30 Xu: the line connecting COR and AUX, pointing towards COR. Yu: the line perpendicular to the plane defined by COR, OL and AUX, pointing laterally. Zu: the line perpendicular to the former defined axis Xu and Yu, pointing anteriorly. Constructing this coordinate system given the expected features happens in function asulna. Extracting AUX happens in auxUlna. (appendix B.1.2) 3.3.5 Radius For the radius the same problem is encountered. [3] doesn’t give a definition for the local coordinate system of the radius based on only features of the radius. A new definition is made. In this definition we need the center of the head of the radius (see 8.2 for a clear definition)(RH, Figure 3.14, blue), the styloid process (SP, Figure 3.14, red) and a helping point. The helping point is the center of the ulnar notch (UN, Figure 3.14, yellow). UN is not extracted as a feature in the GUI. The code of this function ulnarNotchRadius is found in Appendix B.1.2. The coordinate system (Or, Xr, Yr, Zr): The origin, Or, is UN. Zr: the line connecting UN and RH, pointing towards UN Xr:. the line perpendicular to the plane defined by UN, RH and SP, pointing anteriorly. Yr: the line perpendicular to the former defined axis Xr and Zr, pointing laterally. Constructing this coordinate system given the required features happens in function asradius. 31 Figure 3.14 32 Chapter 4 The humerus In this chapter all the functional procedures associated with the humerus are described. As seen in the second chapter about the anatomy there are 9 features to be extracted. Each extracted feature corresponds to a specified function which will be invoked each time the chosen feature has to be localized. Further there is one function of considerable importance, the function responsible for the orienting of the 3D model of the humerus. This function is carried out first in order to have some sort of unambiguous reference orientation for further processing. The outcome of the orientation procedure is generic for every type of humerus, independent of the size of the Stl file representing the 3D model of the humerus, the side of the bone (right or left), and most important the orientation and position of the original 3D model. We arbitrarily defined the reference orientation to the orientation of the humerus if the arm is in its neutral vertical position. If we talk about orientation in the rest of the work, we mean a model’s orientation and its position to not repeat it every time. In the following flowchart the sequence of actions for the processing of a read in 3D model of a humerus is given. 33 Extract lateral epicondyle Extract medial epicondyle Extract head of humerus Extract greater tubercle Orient humerus Extract lesser tubercle Extract trochlea Extract capitulum Extract topmost point Extract metaphyseal cylinder All of these functions will be invoked as a result of a manipulation of the GUI. This is already explained in the previous chapter about the GUI and its functionality. The main component responsible for the execution of all of the functions involving the humerus is the radiobutton ‘Humerus’ in the panel ‘Bones’. Once pushed the orientation is executed and further all of the features are extracted. Than, choosing the radiobuttons associated with a specific feature will only carry out the visualizing of the position of the feature on the patch object representing the considered humerus. In the continuation of this chapter (and in the other chapters dedicated to the other processed bones of the human body (scapula, clavicle, ulna or radius)) first the orientation function is described which will be followed by the description of the functions responsible for the extraction of the features of interest. For each of these functions a section is provided. 34 Each description will begin with a short explanation of the purpose of the function. Figures will be given as a clarifying mean. The second part will show the followed procedure in an algorithmic way. This will help in understanding the used algorithm in a code-independent manner. Each step in the algorithm will be described and justified. Each paragraph about a functional procedure of a bone will start with the definition of the function as used in our code in Matlab®. It is important to stress the fact that the name of the function is not an exact requirement. It is only the name we chose in developing our tool and with this name it is easier to refer to this function throughout the guiding text. The important information that is given by these statements is the type and number of input and output arguments. The exact form of these arguments is described to a greater extent in the code of the functions. If talked about x-axis, y-axis or z-axis we mean the components of the global coordinate system as defined in 3.3. In the accompanying figures this is made explicit by talking about Xglobal, Yglobal and Zglobal. This will also hold for the following chapters dedicated to the other bones. The actual code in Matlab® accompanied with remarks is outlined in appendix B.2. 4.1 The orientation of the humerus As stated before, this function is executed first. The Stl file of the bone will undergo several transformations, i.e. linear translations and rotations in 3D space, in order to be positioned upright onto the z-axis with its anterior face oriented towards the negative yaxis. Important to mention here is also the fact that a left bone is detected and transformed into a right bone through taking its mirror image. This will be helpful in the feature extraction processes, because doing so, only right bones have to be taken into account. In the visualization processes that follow, the mirror image of an original left bone will be mirrored once again to show the original left bone. The result of all of these transformations is a uniformly oriented right bone. All of the feature extraction functions will get this oriented bone as an input. 35 Figure 4.1 shows the position and orientation of the bone of the output. Figure 4.1: Resulting orientation. Left: superior view as seen from the positive z-axis. Right: Anterior view as seen from the negative y-axis. Now that the purpose of the function is clear, the followed procedure is outlined subsequently. The code in Matlab ® can be found in appendix B.2.1. Reading chapter 3 makes clear that the only data of the bone we have are the 3 matrices x1, y1 and z1 that represent the patch object of the bone. Secondly we also have the matrix n1, consisting of the normals to the facets of the 3D model. These four matrices are the output arguments of the previous executed stlread function. The statement that executes function orientHumerus is: [x2, y2, z2, n2, e1, e2, lowest, side] = orientHumerus(x1, y1, z1, n1) The output arguments given back are described in the following steps that make up the orientation process of the humerus. 36 Step 1: Get a list of points The first step in the procedure is to transform the 3 matrices (x1, y1 and z1) to one matrix of all the points of the patch object. In paragraph 3.2 is justified this transformation and is stated the agreement to talk about a list of points rather than a matrix. Each row in the list holds x-, y- and z-coordinates of the position of the corresponding point. Each point appears only once in the list. This list, say L, serves as a starting point to the algorithm. Step 2: Calculate the 2 extreme points along the length of the humerus The humerus is a bone of a specific form; it can be seen as a long bone in a prominent direction. The idea of the orientation function is to orient the humerus along the z-axis with its long side. To do this we need firstly its principal axis (the long side) and select 2 points of this axis, a and b. Secondly if we can force these two points to lie on the zaxis, we will be sure that the 3D model of the humerus will be positioned around the zaxis. These two points that lie on the principal axis, are not points of the bone but are projections of points of the 3D model. We arbitrarily chose a and b to represent the two most extreme projections of points of the 3D model onto the principal axis. It is not only important to know which are these points, but it is also necessary to know which point is near the proximal end of the humerus (on its head) and which point is near the distal end. This will be clarified next. To get a and b a specific function is invoked: [a, b] = findRefP(L). This takes the list L as input argument and delivers a and b as output arguments. a will be the most distal point and b is the most proximal point (near the head of the humerus) on the principal axis. Figure 4.2 shows these two points, the blue point represents a and the red point represents b. 37 Figure 4.2 There are several steps performed in findRefP. First a principal component analysis on the data in list L is carried out. This is done by a function predefined in Matlab®, princomp, and gives back 2 matrices: COEFF and SCORE. COEFF is a 3-by-3 matrix, each column containing coefficients for one principal component. The columns are in order of decreasing component variance, thus the principal component, the one we need, can be found in the first column of COEFF. SCORE contains the principal component scores (projections); that is, the representation of each point of L in the principal component space. This is an M-by-3 matrix, with M the number of points in L. The principal component space (coordinate system) is constructed by the 3 base vectors in COEFF and its origin is the mean of all the data points of L. The first column of SCORE gives for each point its score or projection or coefficient on the principal component axis, the first column of COEFF. The only thing to do now is calculating the minimum and the maximum of the first column of SCORE, this will yield the two extreme projections on the principal axis. a is now the point with the smallest score and b is the point with the biggest score. 38 Next thing to do is making sure which point is near the top and which to the bottom of the humerus. To solve this problem, the specific geometry of the humerus offers the solution. The top of the humerus consists of a head which approximates very closely to a sphere, whereas the distal end of the humerus does not approximate at all to a sphere. Therefore the tactic used is to select in a uniform manner a set of points near a and also near b, say setA and setB. These two sets of points can now be given to a function that calculates the best fit to a sphere. The set that yields the best result through the sphere fitting is surely the one next to the top of the humerus. The fitting procedure is based on the least squares fitting method. So in the end we have determined a as the extreme point on the principal axis near the bottom and b the extreme point near the top. Step 3: Do a translation defined by point a This is a standard procedure in which the point a, corresponding to the distal end of the humerus, defines the translation and is translated to the origin (0, 0, 0) of the global coordinate system. All the other points undergo the same translation. Figure 4.3 gives the resulting orientation. Figure 4.3 39 Step 4: Do a sequence of rotations to get the other point, b, onto the z-axis Basically this step consists of 2 rotations, one rotation around the z-axis and one rotation around the y-axis. The angles which to use in the two rotations can be found very easily through simple trigonometric formulas. As a result the point b, corresponding to the proximal end of the humerus, will lie on the positive z-axis and the bone is oriented along the z-axis. In the code we wrote it is possible that one rotation more is necessary. This possible rotation is one over 180 degrees, because it is possible that the rotated top point b is situated on the negative z-axis as result of the 2 rotations. This is because the angles used in the first two rotations are smaller than 90 degrees. So for example, if point b lies originally below the x-y plane, it will end up on the negative z-axis. Figure 4.4 illustrates such a situation. Figure 4.4 40 This consideration has to be made with every rotation throughout the extent of the work. Therefore with every rotation where this situation is possible, counteracts must be taken. Step 5: Localize the two epicondyles on the distal end of the humerus First of all, it is important to note that for now it is not necessary to know which the lateral is and which the medial epicondyle is. This would be a huge task to discover in this moment because we don’t know the orientation in the x-y plane, nor the side of the bone. Just the coordinates of the two epicondyles in random sequence are needed. For this extraction we made the function epicondyles: [e1 e2] = epicondyles(L). L is the list of translated and rotated data points and e1 and e2 are two 1-by-3 arrays of the positions of the coordinates of the two epicondyles. The function does basically a principal component analysis of the lower 15 % of the data points in L. We can talk about ‘lower’ now, because the bone is already oriented along the z-axis in an upright position. Each point in this list of points has a score onto the principal component axis. These scores and principal components are the output arguments of the Matlab® function princomp like in Step 1. Because the epicondyles are the two extreme points on this principal component axis, we have to find the points with the smallest and biggest scores with respect to this axis. These will be the two epicondyles which will be helpful in the following orientation. The epicondyles already are localized. So carrying out the orientation of the humerus implicitly carries out the feature extraction of the epicondyles. Step 6: Determine which one is the lateral and which one is the medial epicondyle The determination of the real identity of the epicondyles is based on the specific geometry of the distal end of the humerus. Through visualization of a number of bones (the 13 3D models we worked with) it became clear that the medial epicondyle has a smaller Euclidian distance to the lowest point of the bone (most distal point of trochlea), than the lateral epicondyle. So a comparison between the point in L with the smallest zcoordinate and both epicondyles is sufficient to know the side of the epicondyles. The 41 medial epicondyle is named e1 and the lateral epicondyle is named e2. These two variables e1 and e2 can be found amongst the output arguments of orientHumerus. Figure 4.5 illustrates the logic of this step. The blue marker is the medial epicondyle, the red one is the lateral epicondyle. The blue marker has a smaller distance to the lowest point (yellow) than the red marker. Figure 4.5 By now it is not yet clear whether the bone is a left one or a right one, but that doesn’t matter. Step 7: Do a rotation around the z-axis to get the medial epicondyle in the x-z plane on the positive x-axis This step only involves calculating the correct angle and rotating the whole bone over this angle around the z-axis. It is possible that after this rotation the medial epicondyle lies on the negative x-axis. This occurs when the medial epicondyle has an x-value that 42 is smaller than 0 before this rotation. In this case a supplementary rotation of 180 degrees around the z-axis is needed to get the medial epicondyle onto the positive xaxis. Figure 4.6 gives the current orientation as seen from a superior view (from the positive z-axis). The medial epicondyle (blue) is situated in the x-z plane; its ycoordinate is 0. Figure 4.6 Step 8: Determine whether it is a bone of the right or a bone of the left arm Again this procedure is based on the observation of the humeri to our disposal. In all cases the relative positions of the medial epicondyle and the lowest point (most distal point of trochlea) give the solution. As the trochlea, as an articular surface, lies more anteriorly than the medial epicondyle, it will determine the anterior aspect of the humerus. With the medial epicondyle in its position as defined in step 7 we can make the following conclusion. If the lowest point has a smaller y-value than the medial epicondyle e1, which is 0, we can be sure that the bone belongs to a right arm. In the opposite situation we conclude that the processed humerus belongs to a left arm. In figure 4.7 with the positions of the medial epicondyle (blue) and the lowest point (yellow) we can conclude that the bone is a right sided one. The y-coordinate of the lowest point is smaller than the y-coordinate of the medial epicondyle 43 Step 9: In case of a bone of a left arm, transform it to a bone of right arm Transformation to the ‘same’ humerus of the right arm happens through mirroring all of the points of L regarding to the x-z plane because of the current orientation of the bone. So every y-coefficient will get an opposite sign: alpha becomes –alpha for all points in the second column of the list. It is very important not to forget to give back the specification of the side of the bone as an output argument of the function. This logical has the name side in the set of the output arguments. If side equals 1, the processed bone is a left one. The specification will be of use by the visualization of the bone and features where a left bone has to be visualized as a left bone. Figure 4.7: Medial view of the distal end of the humerus. So as a result of the function the humerus is oriented in a uniform way along the z-axis with its anterior face pointing towards the negative y-axis, and it is in all cases a humerus of a real or an imaginary right arm. As a finalizing remark for this paragraph it must be said that the output arguments, x2, y2 and z2 are 3-by-m matrices that make up the oriented patch object of the bone. This can be misleading because throughout the biggest part of the algorithm is spoken 44 about the list representation L rather than the patch representation of the 3D model of the humerus. This is not an error. We chose to give the patch representation as an output argument for subsequent use. That will be the visualization of the bone and this can only be done in Matlab® with the definition of the patch object. This is explained in 3.2. If one will examine the code in appendix B.2.1, he will see that the transformation functions: translation and rotation will use patch representations as input and output arguments. The reason is simple, if we would have used lists of points we would have got problems to visualize the also become bone. It is namely impossible to go from a list representation in a uniform way to its original patch representation, because we do not know the connectivity of the vertices (points) and because a lot of points are discarded in the primary process to go from the patch representation to the list of points. Therefore these transformation functions work with the patch representations of the 3D models. 45 4.2 Extraction of the lateral and the medial epicondyle The extraction of the position of the two epicondyles of the humerus is already done in the prior orientation process. So the whole procedure is outlined there in steps 5 and 6. Basically it is divided in two parts. In a first part the localization of the two epicondyles is done. After this step, it is not yet clear which epicondyle the medial and which epicondyle the lateral is. This step is described in Step 5 of 4.1. In the second part, described in Step 6 of 4.1, the side of each epicondyle is determined. This is based on the comparison of the distances to the most distal part of the trochlea. In Figure 4.8 the two epicondyles produced by the extraction process are depicted. The medial epicondyle is the blue one, the lateral epicondyle is the red one. Figure 4.8: Anterior and slightly lateral view of a right humerus with both epicondyles 46 4.3 Extraction of the head of the humerus The extraction of the head of the humerus aspires to visualize the real head of the humerus as a perfect sphere. We use the term ‘real head’ to make sure the reader knows it is the articulation surface that articulates with the glenoid cavity of the scapula. Its articulating function in the human body justifies to a great extent the presumption that this resembles a lot to a part of a sphere. The extraction procedure outlined next involves fitting points of this articulation surface to an entire sphere. This fitted sphere will be referred to as the head of the humerus. Visually analyzing this part of the humerus makes clear that this sphere is situated close to the principal axis of the humerus. The function responsible for this extraction is named headHum. [center radius] = headHum(L, e1, e2) The two output arguments are the center of the head and its radius, the two requirements to draw a sphere. The input arguments are list L of the points of the oriented right bone, the medial epicondyle e1 and the lateral epicondyle e2. In figure 4.9 the purpose of the function becomes clear. Figure 4.9: Left: anterior view of the proximal end of a right humerus . Right: the same bone as the left image but with the visualization of the fitted sphere. 47 The procedure itself is based mainly on isolating a good selection of the points of the 3D model that are part of the articulation surface, and giving this set of points as input to a function carrying out the fitting of these points to a sphere based on the least squares fitting technique. Step 1: Select the topmost part of the humerus The first task is to select from the list of points L those points of the proximal end of the humerus. We name this new list U. In a following step these points will be given to a sphere fitting function in order to get a rough sphere approximation of all the points of the proximal end. This list U contains points with a z-value bigger than 87% of the total length of the oriented humerus. The total length is defined as the difference between the point with the biggest z-coordinate and the point with the smallest z-coordinate of the oriented 3D model of the right humerus. The choice for 87% is a result of a try and error process with the 13 available 3D models and yields the best results. Figure 4.10 shows the list of points contained in U Figure 4.10: The set of points U in red. 48 Step 2: Do a sphere fit of U As a result this step gives the center and the radius of the rough sphere through the fitting procedure. The center is called c_rough and its radius r_rough. The purpose of these parameters is to help selecting the points of the articular surface of the head of the humerus. In Figure 4.11 it is clear that this sphere does not approximate the articular surface on the medial side (left side of the figure). Figure 4.11: Superior view of the fitted rough sphere. Step 3: Select the points of the articular surface of the head The procedure is based on the findings in [13]. As already stated, the articulation surface is like a cut off sphere. We call it the articular margin surface and we are looking for points of this margin surface. You can draw a perpendicular to this surface in the center point of it, say line P. We call the center point of the margin surface (where P intersects with the surface), pTheoretic. This point is surely needed, but also the points in his proximity. To select those neighboring points we used line P to define planes perpendicular to it. If we draw such a perpendicular plane on a distance dTheoretic away from p in the direction of the inside of the head and take all the points that lie above this plane in the direction of p, we get some points of the articular margin surface. 49 This would be an easy task if the user could interact with the bone and do the drawings. This tool is trying to eliminate errors caused by user subjective input, so no user input is allowed. Thus the difficulty now is the specification of the critical parameters P and d. The starting points for getting P are the two epicondyles e1 and e2, and the center and the radius of the rough sphere, c_rough and r_rough. First we calculate the line between the epicondyles, the transepicondylar. In [13] is defined the retroversion angle. The retroversion angle is the angle between the transepicondylar and P in the x-y plane (transverse plane). Not only it is defined in [13], they also measured a number of 65 humeri of women and men of all ages and detected the retroversion angle of them, concluding that this angle has a mean of 17.9 degrees. So if we have the vector that represents the direction of the transepicondylar and rotate it in the x-y plane over an angle of 17.9 degrees we get a good approximation of the direction of P in the x-y-plane. In [13] is also defined the inclination angle. This is the angle between the shaft of the humerus and P in the x-z-plane (frontal plane). A resembling study in [13] yields as the mean of the inclination angle a value of 129.6 degrees. So we can rotate the vector that represents the current direction of P in the x-y plane over an angle of (129.6 – 90) degrees around the y-axis. This will give us the vector that represents the final direction of P. To define P as a line we need to agree on a point that would be a point of P. Arbitrarily we have picked the center of the rough sphere, c_rough, because it is relatively close to the eventual center of the real head. So now we have the definition of P. It contains c_rough, the center of the rough sphere, and p, the intersection of P with the rough sphere. The next thing is drawing planes perpendicular to P in a certain point between c_rough and p, say on a Euclidean distance of d away from p. We used as d 70% of the radius of the rough sphere. This radius is nothing more than the Euclidean distance between c_rough and p. The choice for this value of d is the result of a trial and error process. With this value we got in all the processed humeri the best result. 50 Than use the plane at a distance of d from p perpendicular to P for making a list of points. We select the points above this plane in the direction of p. This list of points is named M. Figure 4.12 illustrates the concept. Figure 4.12: Above: The yellow marker is c_rough, the blue marker is p and the green one is a point on a distance d of p in which we will draw the perpendicular planes to P, the blue line. Left: Superior view. The red line is the transepicondylar. So the angle between the red and the blue line is the retroversion angle. Right: Anterior view. The angle between the blue line and the shaft of the humerus is the inclination angle. Below: Left:The plane created, in blue. Right: The eventual points of M in red. 51 Step 4: Do a sphere fit of the points in M Ultimately this list of points, that represent points of the articular margin surface, is handed over to a function carrying out the fitting to a sphere of these points. This will yield a sphere, defined by its center and its radius, which closely approximates the real head of the humerus. These representations of the center and the head are the output arguments of the function headHum that is mentioned in the start of this paragraph. Figure 4.13 shows the calculated sphere on top of the 3D model of the oriented humerus from a superior view. If we compare this with Figure 4.11, we see that the articular surface (on the left side) is much better approximated. Figure 4.13: Superior view of the fitted sphere that approximates the head of the humerus. In the code this last step is carried out in an iterative manner. The center and radius of the sphere, c_new and r_new, are now used in defining a new P and a new d in the same manner as specifying the first definition of P and d. With these new parameters we get a new list M by following the same selection procedure. This new list of points is handed over to the sphere fitting function to become a new center and radius of the newly calculated sphere which will be used to define a new P and d. This iterative process is stopped when the distance between the previous and the new center of the sphere is smaller than a predefined value. The optimum stop condition was found by trial and error. It can’t be taken too small, because in this case the iteration will possibly 52 never stop. On the other hand it can’t be taken too big, because in this case the approximation of the sphere won’t give the desired result. 4.4 Extraction of the greater tubercle In this section the extraction of the position of the greater tubercle is described. The greater tubercle is a large tuberosity located laterally on the proximal epiphysis. Figure 4.14 shows in the black oval the location of the tubercle. The purpose is to associate 1 point with this tubercle and extract that point. The definition of this point that will represent the greater tubercle is given in [1]. Figure 4.14: Left: anterior view of the head of the right humerus. Right: lateral view. The function responsible for this extraction in our tool is named greaterTubHum. [greater] = greaterTubHum(L, cHead, rHead) The output argument is the position of the greater tubercle onto the 3D model of the oriented bone. The input arguments are list L of the points of the oriented bone, the center of the head of the humerus, cHead, and the radius of the head of the humerus, rHead. This means that this function must be executed after the extraction of the head of the humerus, which is described in paragraph 4.3. 53 Briefly worded, greaterTubHum will look at all the points of the proximal end of the humerus (the rough head) and calculate orthogonal distances of these points to the sphere representing the head of the sphere. The point on the lateral side of the bone with the biggest distance to this head is defined as the greater tubercle. Step 1: Define a search space in which to look for the greater tubercle. This search space is situated laterally and superiorly. This is a very logical step. Basic anatomy and logic justifies to not consider all of the points that make up the humerus as possible candidates for being the greater tubercle. For example, while looking at a humerus it becomes very clear that it is not necessary to take the distal half of the humerus into account to look for the greater tubercle because it is situated in the proximal half. The search space, say S, is thus a list of points which will contain the greater tubercle. We defined our search space considering the uniform orientation of 4.1. The 3 coordinates (x_p, y_p, z_p) of each point of the 3D model are tested against 3 well chosen conditions, 1 for each coordinate. In this way we can isolate a rectangular space that is part of 3D space. ‘Well chosen’ has to be interpreted in the sense of ‘uniform applicable’. It must make sense for all sizes of humeri. Therefore we can’t use absolute numbers in our conditions. Conditions like ‘the x-value must be bigger than 100’ can be helpful for one 3D model of a humerus, but useless for another because it is for example 2 times smaller than the former and thus will the stated condition produce other unwanted points. This is why we will use statements as ‘the maximum of’ or ‘50% of the minimum of’ in our conditions. The above explanation must be generalized to all the situations in which we had to look for a certain region of a 3D model. The following conditions were used with respect to the current orientation of the 3D model: -The x-coordinate has to be smaller than 0 -> lateral aspect is targeted 54 -The y-coordinate must lie between 0.3 * max(all y-coordinates) and 0.3 * min(all y-coordinates). -The z-coordinate must be bigger than min(all z-coordinates) + (max(all zcoordinates) - min(all z-coordinates)) * 0.93 -> the upper end. In the above conditions max(.) is the function that selects the maximum of its input and min(.) the function that select the minimum of its input. For a point to be allowed to enter the search space S all 3 conditions must hold. Step 2: Discard those points from the search space that lie inside the sphere that represents the head of the humerus This step is based on the fact that the greater tubercle is situated for sure outside of the sphere of the head. For every point in S its Euclidean distance to the center of the head, cHead, is calculated and compared with the radius of the head, rHead. If the distance to cHead is smaller than rHead, the point lies into the sphere and is discarded from S. 55 Figure 4.15: The red markers represent the points of S. The head of the sphere is modeled by the yellow sphere. Figure 4.15 gives the final points in S. Step 3: Calculate for each point in S the orthogonal distance to the sphere representing the head of the humerus Practically this is done by calculating for each point in S the Euclidean distance to cHead and subtracting from this distance the radius rHead. Step 4: Select the point in S with the biggest orthogonal distance to the sphere This point is the greater tubercle. Figure 4.16 shows the position of the greater tubercle on the 3D model. 56 Figure 4.16: A laterosuperior view of the right humerus. The blue dot represents the greater tubercle. 4.5 Extraction of the lesser tubercle In this section the extraction of the position of the lesser tubercle is described. The lesser tubercle is a small tuberosity located on the anterior aspect of the proximal epiphysis. Figure 4.17 shows in the black oval the location of the entire tubercle. The purpose is to associate 1 point with this tubercle and extract that point. The definition of this point that will represent the lesser tubercle is given in [1]. The function responsible for this extraction in our tool is named lesserTubHum. [lesser] = lesserTubHum(L, cHead, rHead) The output argument is the position of the lesser tubercle onto the 3D model of the oriented right bone. The input arguments are list L of the points of the oriented right bone, the center of the head of the humerus, cHead, and the radius of the head of the 57 humerus, rHead. This means that this function also has to be executed after the extraction procedure of the head of the humerus, which is described in paragraph 4.3. Figure 4.17: Left: Superior view of a right oriented humerus. Right: Lateral view of the same bone The technique used to get the lesser tubercle is parallel to the technique used to get the greater tubercle. A list of points outside of the sphere of the head on the anterior side is made. All the orthogonal distances of these points to the sphere are calculated. Ultimately the lesser tubercle is the point that has the biggest orthogonal distance. By using this procedure we have tried to approach as accurately as possible the definition of the virtual palpation of the lesser tubercle as explained in [1]. The following steps are completely the same as the respective steps of 4.4, only some parameters are different. Step 1: Define a search space in which to look for the greater tubercle. The search space, say S, constructed here is the result of applying the following rules to all of the points in L. Of course these rules make only sense in the current orientation of the 3D model of the bone. -The x-coordinate must lie between 0.3 * max(all x-coordinates) and 0.4 * min(all x-coordinates). -The y-coordinate must be smaller than 0 -> anterior aspect is targeted 58 -The z-coordinate must be bigger than min(all z-coordinates) + (max(all zcoordinates) -min(all z-coordinates)) * 0.90 -> the proximal end is targeted For a point to be allowed to be added to the search space all 3 conditions must hold for the coordinates of the point. Step 2: Discard those points from the search space that lie inside the sphere that represents the head of the humerus For every point in S its Euclidean distance to the center of the head, cHead, is calculated and compared with the radius of the head, rHead. If the distance to cHead is smaller than rHead, the point lies into the sphere and is discarded from S. Step 3: calculate for each point in S the orthogonal distance to the sphere Practically this is done by calculating for each point in S the Euclidean distance to cHead and subtracting from this distance the radius rHead. Step 4: select the point in S with the biggest orthogonal distance to the sphere This point is the lesser tubercle. Figure 4.18 is an illustration of the critical parameters of the extraction procedure together with the eventual position of the lesser tubercle. 59 Figure 4.18: The blue marker represents the position of the lesser tubercle. The red dots are the points of the search space S. The yellow sphere is the representation of the head of the humerus. 4.6 Extraction of the trochlea This procedure will determine the localization of the trochlea as a circle. In Chapter 2 the trochlea is described. It is a distally located articular surface that articulates with the ulna. Because of its shape the edge of the trochlea can be represented by a circle in the 3D space. If we say extraction of the trochlea, we mean the extraction of its edge. A 3D circle is specified by its center, its radius and its normal, which defines the orientation. Figure 4.19 specifies the location of the trochlea onto a 3D model of an oriented right humerus. The humerus is looked at from an anterior view. The function responsible for this extraction in our tool is named trochHum. [center, radius, normal] = trochHum(L, lowest, med_e) The output arguments are the three parameters that define a 3D circle. The input arguments are list L of the points of the oriented bone, the point of the humerus with the smallest z-value, lowest, and the position of the medial epicondyle, med_e. 60 Figure 4.19 The method of working can be summarized as follows: a selection of well chosen points is handed over to a function which will fit these points to a circle in 3D. This circle can be seen as the representation of the trochlea. Step 1: Make a list of points that are part of the trochlea Because the trochlea is targeted it is very logical to select the points of the trochlea. This wanted list is called fitL. Again some conditions must be met by the 3 coordinates of the points in order to be found suitable for fitL. It caught the eye that the most distal part of the trochlea is also the most distal part of the entire humerus. Therefore this most distal point could be considered of being part of the trochlea without exception. In our implementation this point is the input argument lowest, but it could also be calculated very easily as being the point with the smallest z-value in the current orientation of the 3D model. This point will help in defining the conditions for selecting the points for fitL. The orientation of the bone will be the second helping hand and finally we also use input argument, med_e, to do this job. All the conditions are based on the study of the anatomy of the humerus and thorough observation of the humerus in its oriented state. -The x-coordinate has to lie in a very small interval around the x-value of lowest. Arbitrarily we chose this interval to be of length 2 mm with lowest the midpoint. 61 -The y-coordinate will not play a roll in the selection process. -The z-coordinate has to be smaller than the z-coordinate of the medial epicondyle, because for every humerus the trochlea is situated more distal than the medial epicondyle. If all the above conditions are fulfilled by the coordinates of a certain point, the point will be added to fitL. Step 2: Do the first fitting to a circle of the points in fitL This step will yield the position of the center of the circle, its radius and the normal to its plane. Step 3: Calculate Euclidean distances between the recently calculated center and the points of fitL This step is the start of a refining phase. Step 2 gave a good approximation of the circle, but in most cases outliers were present. These outliers had a negative effect on the correctness of the position and orientation of the calculated circle. Therefore a second fitting phase will take place with as input list fitL of which the outliers are discarded. Step 4: remove the outliers of fitL When is a point an outlier? We use the mean, mu, and standard deviation, sigma, of the set of distances of the points in fitL to the center of the circle of Step 2. With this information an outlier is defined as a point smaller than mu – 3*sigma or bigger than mu + 3 * sigma. Step 5: Do the second fitting to a circle of the points of the reduced list fitL This step will yield the final and sought center, radius and normal to the plane of the 3D circle representing the trochlea. Figure 4.20 illustrates the calculated circle (blue) together with the points with which the circle was fitted (red). 62 Figure 4.20: Anterior and slightly medial view of the distal end of a 3D model of a right humerus 4.7 Extraction of the capitulum The capitulum is a rounded eminence on the lateral side of the distal end of the humerus. It articulates with the cup shaped depression on the head of the radius. Therefore it can be approximated by a sphere. The subject of this paragraph will be the algorithm used to extract the capitulum as an approximation of a sphere. This sphere will be referred to as the capitulum. No definition about this feature is given in [1]. The function responsible for this extraction in our tool is named capitHum. [center, radius] = capitHum(L, lat_e) The output arguments are the two parameters that define the sphere. The input arguments are list L of points of the oriented right humerus and the specification of the lateral epicondyle, lat_e. The method of working is parallel to that of the head of the humerus and that of the trochlea: the right set of points is isolated and given as input to a function that will carry out the least square fitting procedure to yield a sphere. 63 Step 1: Make a list of points that are part of the capitulum The idea is to get a sufficient set of points of the capitulum in order to use them as input for the fitting procedure. For a sphere fit at least 4 points are required. This list is called fitL. As in all the cases where we want to isolate a certain set of points, some conditions are defined. The starting point is the orientation of the bone as performed in 4.1 together with the knowledge of the lateral epicondyle, lat_e. For the possible points of fitL the following conditions must be fulfilled -The x-coordinate has to be smaller than the x-coordinate of the lateral epicondyle divided by 2 -The z-coordinate must be smaller than the z-coordinate of the lateral epicondyle. As the capitulum lies a bit more anterior, fitL will with the above conditions also contain points that do not constitute to the capitulum. For example the distal points of the posterior part must not be involved in fitL. To sort them out we use a supplementary selection procedure. All points in fitL are projected onto (0, -1, -1) to concede to the requisite of the capitulum lying a bit more anterior, but also distal. The point with the biggest projection, pFitL, is with certainty a point of the capitulum. We now will make a new, more refined fitL, fitL2, that will contain the already mentioned point, pFitL, together with the points on a well chosen distance to this point. This distance is set to the distance between pFitL and lat_e divided by 4. This was the resulting configuration of this distance through a trial and error process of the 13 3D models of the humerus. Step 2: Do a sphere fit of the list fitL2 This step will produce the fitting of fitL2 to a sphere and hand over the center and the radius of the sphere. These parameters are sufficient to draw the sphere. This is illustrated in Figure 4.21. 64 Figure 4.21: The sphere representation of the capitulum in yellow. The points used in the sphere fitting procedure, fitL2, in red. 4.8 Extraction of the topmost point of the humerus The extraction of the topmost point of the humerus is logically nothing more than determining the most proximal point of the humerus, i.e. the point on top of the head of the humerus. [top] = topHum(L) The output argument is the position of the uppermost point. The input argument is the list of points of the already oriented right humerus. We will select the highest point, i.e. the point with the biggest z-value in the orientation as calculated by the anteriorly executed function orientHumerus. 4.9 Extraction of the metaphyseal cylinder The purpose of this procedure is to visualize the metaphysael cylinder; the cylinder which best fits the shape of the upper humeral shaft. The exact definition of this feature cannot be found in [1]. The function responsible for this extraction is named cylHum. [radius under upper] = cylHum(L, cHead, rHead) 65 The three output arguments are the radius of the cylinder, the uppermost and the undermost point on the axis of the calculated cylinder. Thus the three parameters that define the calculated cylinder. The input arguments are list L of the points of the oriented right humerus, the center of the head of the humerus, cHead, and the radius of the head of the humerus, rHead. This means that this function must be executed after the extraction of the head of the humerus, which is described in paragraph 4.3. . The procedure itself is based mainly on isolating the part of the 3D model of the bone defined as the metaphyseal cylinder. Because of the absence of a clear, unambiguous definition, we decided to define two reproducible values and these will serve as an upper and under boundary for the z-values of the points that represent the metaphyseal cylinder. Next, the set of points that lie between the boundaries is given as input to a function carrying out the fitting to a cylinder. This fitting process is based on the least squares fitting technique. Step 1: Define the under boundary for the z-coordinate By examining a total of 13 3D models of humeri, it became clear that by looking a humerus from an anterior view a curve can be observed about halfway of the bone on its lateral side. Above this curve the lateral edge is more or less straight. This gave us the idea to use the height of the position of the curve as the under boundary. This choice has two reasons. First of all the shaft above this curve is straight and will therefore yield better results if given to a cylinder fitting function. Secondly this approach was taken in [13]. Implementing this idea was difficult. To locate a curve on a 3D model in space of discrete data points is not a straightforward operation. We tried to use the mean curvature calculation (2.1 and Appendix D) of the points of the entire bone and use this information to locate the curve talked about. This approach was not successful because the information given by the mean curvature calculation was insufficient to determine the curve with certainty. 66 This brought us to another approach in which we modeled the anterior view by projecting all the points of the 3D model onto the x-z plane. This 2 dimensional representation of the bone shows the contour of the lateral side on the half of the negative x-axis. There is not a way to easily locate the points on the edge because of the discrete nature of the representation of the bone. So we looked to the points above a certain z-value, and distributed all the points in intervals of length 8 with respect to the z-value. Of each such interval we selected the points with the minimal x-value. These points were collected in a list, say latSide2D. This list of 2-dimensional points represents the contour of the lateral edge of the 3D model as seen from an anterior view. This is illustrated in Figure 4.22. Figure 4.22: The contour of the lateral edge of the right humerus as seen from an anterior view. The vertical axis represents the z-axis. The next phase is to locate the curve. This is done by comparing consecutive (with respect to the z-value) points in the list latSide2D. When a descending-ascending condition is met, the curve is detected. The height or z-value corresponding to the curve is named under and is the undermost boundary we were looking for. In Figure 4.22 this boundary appears around a z-value of 178. 67 Step 2: Define the upper boundary for the z-coordinate This step is less complex than the previous one. We want the cylinder to approximate the shaft of the humerus. Logically the shaft ends proximally when the head begins. This logic is sufficient for determining upper, the upper boundary for the z-coordinate. We use the results of the extraction of the center of the head of the humerus. The parameter upper is now defined as the z-value that is rHead smaller than the z-value of the center of the head, cHead (4.3). In this manner we model the z-value of the beginning of the head of the humerus and at the end of the shaft of the humerus. This step is illustrated in Figure 4.23. The blue plane is drawed perpendicular to the z-axis in the z-value as above stated. In our example, this is at a value of 278 ( = z-coordinate of cHead (300) – rHead (22)). Figure 4.23 Step 3: Fit the data between under and upper to a cylinder We add to fitL those points which z-values that lie between under and upper. Than we hand over the list fitL to a function that will fit these points to a cylinder. The outcome of this process are the parameters to define the metaphyseal cylinder. Figure 4.24 visualizes the oriented right humerus and the metaphyseal cylinder associated with that humerus in green. 68 Figure 4.24: Anterior, medial view of the right humerus and the metaphyseal cylinder. 69 Chapter 5 The scapula In this chapter all the functional procedures associated with the scapula are written down and explained. The table in the end of Chapter 2 shows that there are 7 features to extract. Each of these feature extractions will be carried out by a corresponding function. For these functions to execute without problems, first the bone has to be oriented in an unambiguous way. Hence there is one more function to perform this orientation function. The approach is completely parallel to that of the humerus. Extract inferior angle Extract superior angle Extract acromial tip Orient scapula Extract acromial angle Extract tip of coracoid process Extract root of spine Extract glenoid labrum The connection with the GUI follows the same logic as used with the humerus. The main component responsible for the execution of all of the functions involving the scapula is the radio button ‘Scapula’ in the panel of the bones. Once pushed this button first the orientation is executed and further on all of the features are extracted. The user can now visualize these features on top of the patch object by manipulating the radio buttons in the panel ‘Features scapula’ containing all the extracted features. If talked about x-axis, y-axis or z-axis we mean the components of the global coordinate system as defined in 3.3. 70 We had a total of 13 3D models of scapulas to our disposal to make and test the functions outlined in this chapter. This chapter will have the same structure as Chapter 4 and also the code in Matlab® for every function is provided in Appendix B.3. 5.1 The orientation of the scapula The idea is the same as for every orientation for every bone. We chose arbitrarily a reference orientation, with which the feature extractions can be done in a uniform way. After the necessary linear translations and rotations of the 3D model of the bone, its side is detected and mirrored, if needed, to its right counterpart. For the subsequent visualization of the bone and its features, all of this has to be mirrored again, with respect to the same plane, in order to get the bone’s original side. The intended final orientation can be seen in Figure 5.1 and 5.2. In this orientation the bone of the right arm is aligned around the z-axis from above to below. Because of the approximated triangular shape of the scapula, our aim is to get the plane of this ‘triangle’ to concede with the x-z plane. Ultimately the anterior aspect of the scapula will be oriented towards the positive y-axis. The function used to carry out this obligatory initial orientation is called orientScapula. [x2, y2, z2, n2, ext1, ext2, side] = orientScapula(x1, y1, z1, n1) The input and output arguments are almost the same as for the orientation function of the humerus, orientHum of 4.1. The only difference is the presence of the two variables ext1 and ext2. What those are is explained below. Matrix n1 of the normals is also an input argument, because it has to undergo all the same transformations to keep the normals consistent with the transformed facets of the 3D model. This is necessary to carry out the curvature calculation. 71 Figure 5.1 Left: Posterior view of the oriented right scapula. The bone is looked at from the negative y-axis. Right: Lateral view of the oriented right scapula. The bone is looked at from the positive x-axis. Figure 5.2 Superior view of the oriented right scapula. The bone is looked at from the positive z-axis. 72 Throughout the following steps we will orient the bone as given in Figure 5.3. As will be noticed, this bone is a left sided one and its orientation in space is completely random. Figure 5.3 Step 1: Make the list representation of the input 3D model The list is called L. Step 2: Define a translation based on the mean of L This mean, meanL, is nothing more than the mean of the x-, y- and z-values of the points in L. Once obtained meanL, a translation of all the points of L must take place. The mean point coincides now with the origin of the global reference system. This is showed in Figure 5.4. meanL is represented by the blue marker. 73 Step 3: Get for each point in L its norm, i.e. the Euclidean distance to the origin and select the point with the biggest norm The point with the biggest norm is for every regular scapula irrespective to its shape and size the inferior angle. This is the conclusion of the observation and testing with normal scapulas. In not one case we found an exception. We call this point that has the maximum norm maxNorm. In Figure 5.4 this is represented by the yellow marker. Figure 5.4: meanX in blue. maxNorm in yellow. Step 4: Do a sequence of rotations of the bone in order to get point maxNorm to lie on z-axis The sequence of rotations talked about is one rotation around the z-axis to get maxNorm in the x-z plane, and one rotation around the y-axis to get maxNorm onto the z-axis. 74 The purpose of this step is to get the scapula aligned along the z-axis. This step will fulfill this purpose, but it is possible that the bone lies upside down now, like in Figure 5.5. Because the rotations only rotate around angles smaller then 90 degrees, this occurs when maxNorm has originally a z-value that is bigger than zero. As with all rotations, the normals in n1 have to be rotated too. Figure 5.5: The 3D model of the scapula lies upside down with respect to the z-axis. Step 5: Do subsequent transformations to get the so far oriented bone to stand in an upright position in the space defined by the positive x-axis The bone by now has a part above the x-y plane and also below this plane, and it is possibly upside down oriented (like in Figure 5.5). The purpose of this step is to correct the possible upside down position and to force the bone to lie above the x-y plane. First a translation defined by maxNorm is executed, this is a translation in the direction of the z-axis. The point maxNorm forms now the origin of 3D space. 75 If the bone lies upside down, like in Figure 5.6, a supplementary rotation of 180 degrees around the x- or the y-axis is needed. The 3D model of the bone after this possible rotation has the correct up down orientation along the z-axis and all the points have z-values bigger than 0. Figure 5.7 proves this. Figure 5.6 Figure 5.7 Step 6: Make the triangular plane concede with the x-z plane of 3D space This step only involves rotations around the z-axis, because the up down orientation already is completed. The logic of this step is based mainly on the specific shape of the scapula. First thing to do is to make a list of all the points that lie above the half of the longitudinal length of the scapula, that is along the z-axis. Thus with a z-value bigger than 50% this length. This list of points, say upperL, is used as input data to a principal component analysis. In Step 2 of 4.1 this procedure is explained extensively. The analysis will yield a principal component axis for the data in upperL. Because of the specific shape of the upper half of a regular scapula, this axis will have for each and every bone more or less a same direction. It will go from the medial side upwards to the lateral side of the scapula where it ends between the acromion and the coracoid process. Each point of 76 upperL has a score with respect to this principal component axis. The points of upperL which have the maximal and minimal scores (projections), the extreme points, will be selected and used throughout the extent of Step 6. These points are called ext1 and ext2. In Figure 5.8 these points are drawn as respectively the blue and the red marker. Figure 5.8 For now you can’t be sure which one lies on the lateral side and which one lies on the medial side. A simple comparison of their z-values will solve this uncertainty as the extreme point on the lateral side is always situated higher in space than the one on the medial side. In Figure 5.8 the blue marker will represent the extreme point on the lateral side because it has a bigger z-value than the medial extreme point in red. The extreme point on the lateral side is named extLat and the extreme point on the medial side receives the name extMed. Now a rotation around the z-axis is needed which will force extLat into the x-z plane with an x-value that is bigger than 0. Possibly a second rotation is needed if extLat has a negative x-value after the first rotation. As always a rotation of 180 degrees gives the solution. Figure 5.9 shows that extLat (blue) 77 has a negative x-value in 5.10 this is compensated through a rotation over 180 degrees around the z-axis. Figure 5.9 Figure 5.10 As a reminder: the matrix of normals n1 must undergo all the same rotations for it will be used in the curvature calculation. Step 7: Determine whether it is a bone of the right or a bone of the left arm This determination will be made possible by the current orientation of the bone as shown in Figure 5.10 and by observing the medial border of the scapula in this orientation. We isolate a small part of points above the medial extreme point, extMed, the red marker in Figures 5.8-5.11. This set of points, say borderL, must contain for sure all the points on the medial border above extMed. Because we know that the medial border makes a curve towards the anterior side if we follow the border upwards, we can determine the side of the bone by comparing the y-value of extMed with the yvalue of the point with the biggest z-value in borderL, the purple marker in Figure 5.11. For a right scapula the y-value of extMed is smaller because the anterior aspect points to the positive y-axis. So in the case the y-value of extMed is bigger, you can be sure it will be a left scapula. In Figure 5.11 the latter situation takes place, so we can now be sure about the left handed side of the 3D model we used to visualize the procedure. 78 Figure 5.11: extMed in red and the highest point of borderL in purple. Step 8: In case of a scapula of a left arm, transform it to its right sided counterpart The procedure in this step is the same as in Step 9 of 4.1. The specification of the side must be given back as output argument. It is called side and it is a logical. If side equals 1, the processed bone is a left one. In the visualization process it will be used to determine whether the bone should be mirrored before visualization. Comparing Figure 5.11 with Figure 5.12 makes clear the mirroring has taken place with respect to the x-z plane. Figure 5.12 79 The intended orientation (and position) is reached and now this 3D model will be given to the feature extraction functions. This can be in the form of a patch representation of the oriented bone or as a list representation. This depends on the receiving function. At last the finalizing remark on the end of 4.1 must also be taken into account here. 5.2 Extraction of the inferior angle This procedure serves to localize the inferior angle. This is where the medial and the lateral border meet each other. As it would be a tedious task to find it in a random oriented bone, it is an easy procedure in the oriented bone that we have at our disposal. It is nothing more than the origin (0, 0, 0). To keep things uniform we preferred to write a function for this feature too. [infAng] = infAngleScap(L) The output argument is the position of the inferior angle onto the oriented scapula. The input argument is list L of the points of the oriented bone. The function searches for the point in L with the smallest z-value and this is logically the point in the origin. For a visualization the reader is referred to Figure 5.7. The real logic for finding this inferior angle must be found in 5.1. There the inferior angle can be found under the name maxNorm. In 5.1 maxNorm wasn’t associated to the inferior angle yet to not confuse the reader. The position of the inferior angle corresponds in all cases to the definition of the position of the inferior angle in [1]. 5.3 Extraction of the superior angle Here we look for the superior angle of the scapula. This is the angle where the medial and the superior border meet each other. The function developed to do the extraction of the superior angle is called supAngleScap. 80 [supAng] = supAngleScap(L, extMed, extLat) The output argument is the position of the superior angle onto the oriented right scapula. The input argument is list L of the points of the oriented 3D model together with the two extreme points, extMed and extLat, as defined in 5.1. The method uses the relative positions of the points to our disposal, these are extMed and extLat. Through observation it becomes clear that the superior angle in its neighborhood is the point with the biggest orthogonal distance to the line between extMed and extLat. We take advantage of this finding to perform the feature analysis of the superior angle. Step 1: Define the search space in which to look for the superior angle This type of procedure already is commented in Chapter 4. The conditions to which the coordinates of each point in L are tested are: -The x-coordinate has to be smaller than 0.6 * x-value of extMed -> targeting the medial side -The y-coordinate has to be bigger than 0 -> targeting the anterior aspect -The z-coordinate must be bigger than max(all z-coordinates) * 0.5 -> targeting the superior aspect If for one point all these conditions are met, the point becomes a member of search space S. In this space, S, the superior angle is to be searched. Figure 5.13 shows the points of this search space on top of the 3D model of the oriented scapula in yellow. The specification of these conditions is based on observations of the 13 3D models of oriented scapulas and pure logic. 81 Step 2: Calculate for each point in S the orthogonal distance to the line defined by extMed and extLat The result of this step is a list of orthogonal distances to the line between extMed and extLat. In Figure 5.13 the line between extMed and extLat is showed as the blue line between the two extreme points represented by blue dots, extMed and extLat. Figure 5.13 Step 3: Select the point in S that has the biggest distance to the former defined line This point represents the position of the superior angle onto the processed scapula. In Figure 5.13 on the right side, the position of the extracted superior angle is represented by a red marker. 82 5.4 Extraction of the tip of the acromion In [1] the commonly used technique for palpating this feature is defined. It is the most lateral and anterior part of the tip of the acromion as seen from a superior view. Figure 5.14 clarifies. The ‘tip of the acromion’ can have two meanings, or the feature we will extract or the set of points representing the anterior ending part of the acromion, where it articulates with the clavicle. In the following text these two will be used, but the difference will be clear. Figure 5.14: Superior view. The black circle situates the tip of the acromion. In this paragraph the algorithm of the function responsible for localizing this feature is described. The use of the function is as follows: [acrTip] = acromialTipScap(x, y, z, n, extMed) The output argument is the position of the tip of the acromion onto the oriented right scapula, acrTip. The input arguments are firstly the 3 matrices x, y, and z defining the 3D model of the scapula as a patch object, and thus holding the connectivity information. Secondly n, the matrix of the normals to the facets of the patch object of the scapula, is an input argument. The last input argument is extMed, the position of the medial extreme point as defined by the function orientScapula in 5.1. 83 The procedure can be summarized as isolating the acromion and subsequently selecting the most lateral point of the tip (not the feature) of the acromion. Step 1: Locate and isolate a part of the bone that with certainty contains the acromion, this will be referred to as the search space S We chose as boundaries for this part: -the x-coordinates have to be bigger than 0.50 * max(all x-coordinates) -> the lateral side. -the z-coordinate has to be bigger than the z-coordinate of input argument extMed. -> the points have to be localized higher than extMed. The boundaries are chosen by trial and error on the 13 3D models in order to get with certainty the part that contains the acromion. The defined list of points is visualized in Figure 5.15 as the yellow points. Figure 5.15: The list of points that contains the acromion. Of these points the neighbors are defined. Step 2: Define for every vertex of S its neighboring vertices The purpose of this step is its future use in Step 5. The result of this step is a list in which every row holds the position of the point and all the positions of its neighbouring points. The rows in this list can therefore be of different lengths because each vertex doesn’t have the same number of neighbors. This list is called V. 84 As well we could have calculated the neighbors for every point of the 3D model, but this calculation is very time consuming and therefore we preferred to do this only for that part where the acromial tip with certainty resides. Step 3: Select the most superior point of S Practically this is the point of S with the biggest z-value. We call this point p. p is in all cases a point of the acromion. Step 4: Initialize an empty list and add p to it We call this list Lacromion. We want to expand this list with the neighbors of p and with the neighbors of the neighbors of p and so on. This will give us exactly the points of the anterior part of the acromion because the starting point p is a point of the acromion. We have to use this approach as result of the position of the acromion onto the scapula. It is not possible to isolate the acromion just by defining boundaries and making the points lie between those boundaries. In the following step this iterative process of adding neighbors to Lacromion is explained better. Step 5: Cycle through Lacromion in an iterative manner and calculate of every point in Lacromion its neighboring points and append them to Lacromion This continuous adding of new points to Lacromion must stop at a certain point to not include all the points of the bone to Lacromion. Hence a stop condition is defined: if a new candidate point for Lacromion has a z-value smaller than 17/18 max(all zcoordinates), than stop extending the list. The choice for the value 17/18 in the stop condition is the result of doing the trial and error technique for the 13 3D models. This value gives the best result in obtaining the tip of the acromion. The difficulty of defining this value was the varying steepness of the acromion, i.e. the angle between the acromion and the x-y plane. Once the stop condition is met, Lacromion is complete and will contain for sure the most superior and anterior points of the acromion. Figure 5.16 gives the outcome of this step, the set of red points 85 Figure 5.16 This seems a very complex way just to define the acromion, but with this technique you can be sure of the result. We tried for example the following simple approach: take those points that have a z-value bigger than the z-coordinate of extLat. It worked for the majority of scapulas, but in some scapulas it introduced points outside of the acromion to the list, because of the variance in acromion shapes. With our method this uncertainty is avoided and it yields always the correct points for the acromion. Step 6: Look for the part of Lacromion that represents the anterior ending part of the acromion and select of this anterior end the most lateral point. First we refine Lacromion in such a manner only the points real close to the tip of the acromion are chosen. This is done by looking for the most anterior points of Lacromion. Hence we look in Lacromion for points with a big y-value. Of this refined version of Lacromion we look for the most lateral point, hence the point with the biggest x-coordinate. 86 The result of this step is the position of the feature we were looking for, the ‘tip of the acromion’. The refined Lacromion (blue) together with the extracted feature (red spherical marker) are shown in Figure 5.17. Figure 5.17 5.5 Extraction of the acromial angle In this paragraph the extraction of the acromial angle is outlined. The exact definition of the position of this feature is given in [1]. It is the posterior angle between the spine of the scapula and the acromion. Figure 5.18 shows the position of the acromial angle. 87 Figure 5.18: The black circle specifies the position of the acromial angle. The red circle the position of the tip of the coracoid process. The function to select the acromial angle: [acrAngle] = acrAngleScap(x, y, z, n) The output argument is the position of the acromial angle onto the oriented right scapula, acrAngle. The input arguments are firstly the 3 matrices x, y, and z defining the 3D model of the scapula as a patch object, and thus holding the connectivity information. Secondly there is n, the matrix of the normals to the facets of the patch object of the scapula. The procedure comes down to isolating the set of points representing the rough angle of the acromion and examining the mean curvature of all the points of this rough angle. The point with the smallest mean curvature is selected as the acromial angle subsequently. Step 1: Define a search space in which to look for the acromial angle This list is constructed by testing each point of the oriented right scapula to predefined well chosen conditions. 88 -The x-coordinate has to be bigger than 0.5 * max(all x-coordinates). This is in other words the 25% most lateral part of the scapula. -And at the same time the y-coordinate has to be smaller than 0.75 * min(all y-coordinates). This means that we take into account only the 12.5% most posterior part of the bone. The obtained space is called S. Step 2: Calculate for each point in S its mean curvature and select the points with the smallest mean curvatures This step is carried out by handing over the patch representation of S to the function calculateCurvature explained in Chapter 3 and outlined in Appendix D. This yields the combination of each point with its associated mean curvature value. As explained in Chapter 3 negative values of the curvature correspond to convex curvature and positive values to concave curvatures. We are interested in the convex points in S, so we select the 5% of points with the smallest associated mean curvatures. We call this list Smin. Next we calculate of Smin its mean point, Smean. Figure 5.19 visualizes the patch representation of S with on top of it in yellow the points of Smin representing the 5% of points with the smallest mean curvature values. As expected, the points of Smin lie in the blue area which manifests convexity. This can be seen in the colorbar on the right, where the blue color corresponds to negative values for the mean curvature. Step 3: Calculate the point of Smin with the smallest distance to Smean Smean is probably not a point of the list Smin and thus of the model of the bone. To end up with a point of the Smin we select that point with the smallest distance to the above calculated mean point, Smean. This point is the acromial angle as shown in Figure 5.20. 89 Figure 5.19: The representation of the angle together wit the mean curvature information. Figure 5.20 90 It might be odd not to specify that point with the minimal mean curvature value as the acromial angle. However during the testing phase this point was seldom in the proximity of the wanted position. The points of Smin were distributed in the neighborhood of the acromial angle. That is why we decided to take the mean of Smin. 5.6 Extraction of the tip of the coracoid process In this paragraph the extraction of the tip of the coracoid process is described. The exact definition of the position of this feature is given in [1]. It is the very tip of the coracoid process. The red circle in Figure 5.18 marks the lateral end of the coracoid process where the feature resides as the most lateral point. The use of the function carrying out this feature extraction is: [corTip] = corTipScap(x, y, z, n, extMed) The output argument is the position of the tip of the coracoid process onto the oriented right scapula. The input arguments are firstly the 3 matrices x, y, and z defining the 3D model of the scapula as a patch object, and thus holding the connectivity information. Secondly there is n, the matrix of the normals to the facets of 3D model of the scapula. The procedure can be summarized as isolating the entire tip of the coracoid process and subsequently selecting the most lateral point of this tip. Step 1: Locate and isolate a part of the bone that with certainty contains the coracoid process, this will be referred to as the search space S The purpose of this step is to save time in Step 2. We specify S by testing all of the points of the scapula to well chosen conditions. S must contain all of the points of the coracoid process. Hence these conditions are: -The x-coordinate has to be part of the 25% most lateral part -The y-coordinate has to be bigger than 0 If all these conditions are fulfilled by the coordinates of a point, the point will be added to S. 91 Step 2: Calculate for every point of S its neighboring points This step will yield for every point of S a list of its neighboring points. All this information is stocked in V. This will be used in Step 3. Now the purpose of Step 1 becomes clear. As the neighbor calculating process is a time consuming procedure and as the neighbor information is only needed for the roughly approximated coracoid process, we decided to hand over only a selected part of the bone to the neighbor calculating function. Step 3: Select the most anterior point of S This point is denominated p and this is without exception a point of the coracoid process. Step 4: Initialize an empty list, Lcoracoid, and add p to it The purpose of Lcoracoid is to populate it with points belonging to the real coracoid process. If in the end we would depict this list, it will represent the coracoid process. In the following steps we will try to get all the points of the coracoid process by starting with p, a certain point of the coracoid process, and including every neighbor of p to Lcoracoid. Of all these points in Lcoracoid we include also their neighbors to Lcoracoid. We will repeat this including of neighbors until a certain condition is met. This seems a very complex way to just define the coracoid process, but with this technique you can be sure of the correctness of the result, irrespective to the shape of the processed scapula. 92 Step 5: Do the iterative process explained in the previous step until the stop condition is met The stop condition is defined by a trial and error process. Ultimately the stop condition used in our code leads to the involvement of the correct points to Lcoracoid, without adding too many points to it. The stop condition is: if a new candidate point has an y-value smaller than 5/8 of the max(all y-coordinates of S), than stop extending the list. This comes down to the fact that we have reached more or less the base of the coracoid process. By now we have isolated the coracoid process, and its points belong to the list Lcoracoid. In Figure 5.21 S is represented by the yellow points and Lcoracoid by the red points. We see clearly the refining process that S underwent. Figure 5.21 93 Step 6: Look for the most lateral part of the coracoid process and select the midpoint of it In this step of the algorithm all the points of Lcoracoid are sorted with a descending xcoordinate value, from big x-value to small x-value. Of this list only the fifth with biggest x-values will stay for further processing. This will be more or less the lateral end surface of the coracoid process and is called, LcoracoidTip. The points of LcoracoidTip are represented by the blue markers in Figure 5.22. Figure 5.22 Subsequently the mean of all the points of LcoracoidTip is calculated. This mean is probably not a point of the list LcoracoidTip and thus of the 3D model of the bone. To end with a point of the bone we calculate the point with the smallest distance to the above calculated mean point. This point is the tip of the coracoid process, the feature we were looking for. Examining Figure 5.22, this is represented by the red marker. 5.7 Extraction of the root of the spine In this paragraph the extraction of the root of the scapula spine is described. The exact definition of the position of this feature is given in [1]. The root is located next to the medial edge of the scapula. Because of its triangular shape and the fact that we will 94 only extract one position, the root of the spine is defined as the most lateral apex of this triangle The use of the function carrying out this feature extraction is: [trigSpin] = trigSpinScap(x, y, z, n, extMed) The output argument is the position of the root of the spine onto the oriented right scapula. The input arguments are firstly the 3 matrices x, y, and z defining the 3D model of the scapula as a patch object, and thus holding the connectivity information. Secondly there is n, the matrix of the normals to the facets of the patch representation of the scapula. And as last input argument there is extMed, the medial extreme point defined in the function orientScapula, in 5.1. To summarize, the technique used comes down to isolating the right part of the oriented right scapula, calculating the mean curvature of the points of this region and, based on this curvature, selecting in a unified way the root of the spine. Step 1: Locate and isolate a region of the bone that with certainty contains the root of the spine, this will be referred to as the search space S Again this step involves defining boundary conditions and testing each point of the bone to them. Only the points that fulfill the conditions will be added to S. The purpose of this step is to speed up the curvature calculating procedure of step 2. Step 2: Calculate for each point of S the mean curvature The result of this step is a list of points and its associated list of mean curvatures. We are interested in the small mean curvatures, because we want to detect convexity. Thus only 5% of S with the smallest mean curvatures is conserved after this step. We call this thinned out list Smin and it will contain with certainty the root of the spine because it is a convex curved feature. In Figure 5.23 this list of points, Smin, is depicted in yellow. 95 Figure 5.23 Step 3: Calculate the Euclidean distances of each point in Smin to the mean of Smin This mean point is called SminMean. The root of the spine will be located close to this point, because it lies by definition in a region that exhibits a lot of convex curvature. This means that most of the points of Smin are located in this region and hence the mean of Smin. The list of distances is named d. Step 4: Select only the points of Smin with small distances to SminMean as possible candidates for the searched feature For this step we calculate the mean of d, dmean, and we take only into account those points of Smin with a smaller distance to SminMean than dmean. The also become list is referred to as Smin2. A zoom version of Figure 5.23 yields Figure 5.24, SminMean is the purple sphere and the blue dots are the points of Smin2 . 96 Figure 5.24 This step excludes the possibility of picking a point outside the region of the root of the spine as the root of the spine. Step 5: Select the most medial point of Smin2 This point will be the root of the spine. Observing Figure 5.24 makes clear this point is the most medial blue point, i.e. the point on the left (because we are looking at the posterior surface of a right scapula). This last step is based on observation of the set Smin2 throughout the testing process of the function with each model of the 13 scapula. And this step is justified because we are looking for the base of the spine and this is the most medial point of the spine. The spine holds points that exhibit convexity and therefore are contained in Smin2. So if we just picked the point with the biggest convexity, this would have a great probability of being a point of the spine. And we are not looking for a point of the spine, but for its root. In Figure 5.25 the comparison between SminMean in purple and the extracted root of the spine in red, is made. 97 Figure 5.25 5.8 Extraction of the glenoid cavity With ‘extraction of the glenoid cavity’ is defined the representation of the undermost half of the points on the edge of the glenoid cavity by a 3D circle. In this section the algorithm of doing this extraction is described. This feature is not defined in [1]. [glenoidC glenoidR glenoidN] = glenoidScap(x, y, z, n, corTip) The output arguments are the 3 parameters that are needed to define a 3D circle in space. Those are the center, the radius and the normal to the plane of the 3D circle. The input arguments are firstly the 3 matrices x, y, and z defining the oriented 3D model of a scapula as a patch object, and thus holding the connectivity information. Secondly there is n, the matrix of the normals to the facets of the patch representation of the scapula. The last input argument is the representation of the position of the tip of the coracoid process as extracted in 5.6. 98 The followed procedure consists of two least square fittings to a 3D circle. The first is given an input list of points that roughly approximates a part of the points of the glenoid cavity. The second fitting phase will work with a refined list of points that is defined with the help of the results of the first fitting phase. This second input list of points will contain for a regular, well formed scapula only points of the undermost part of the glenoid cavity. Step 1: Locate and isolate the points of the bone that will be given as input arguments to a function that will carry out the first fitting to a 3D circle In this step we will start with a big region of the bone that for sure contains the glenoid cavity. Step by step points will be discarded from this region and ultimately a list of points that roughly approximate the points of the glenoid cavity will survive. The first difficulty is to locate in a uniform manner the position of a point of the glenoid cavity. This is countered by taking all the points of the lateral side of the scapula and calculating for each point the distance to the tip of the coracoid process. The list of distances is called d. If you look to a scapula (that is oriented by the procedure in 5.1) from above (from the direction of the positive z-axis) you will see that the coracoid process is the part where the biggest y-values reside. Below this, if you follow the yaxis, there is a gap before reaching the glenoid cavity. This finding can also be seen in d (if sorted from small to big). First you have a part of small distances that vary very little. These distances are corresponding to points of the coracoid process. Somewhere in d there will be an obvious gap between two distances. This is the manifestation of the gap between the coracoid process and the glenoid cavity. So the biggest distance of the two elements of d defining the gap will be associated to a point of the glenoid cavity. Following this technique gives back unambiguously a correct point of the glenoid cavity. Step 2: Preparing the input list for the first fitting phase Once we have the point, say gPoint, we will use this to isolate a part of the glenoid cavity. For this selected part, say fitL, we will calculate the mean curvature in each point. Because the glenoid labrum is a convex ridge, we than use only the 20% of 99 points of fitL with the smallest mean curvatures. This list of points, say fitL1, is refined once again and than given as input to the least square fitting function. In Figure 5.26 the points of fitL1 are plotted in red. Step 3: Do the fitting of fitL1 to a 3D circle This will produce the center, the radius and the normal to the plane of the fitted 3D circle. If we plot the 3D circle onto the scapula and we also depict the points in fitL1, we see that a reasonable amount of points lies significant far away of the calculated circle and hence does not belong to the undermost part of the glenoid cavity. This means they had a negative influence on the result of the fitting procedure. That’s why we do a second fitting phase with a list of points from which the polluting outliers are removed. The technique of removing these outliers is described in Step 4 of 4.6 about extracting the edge of the trochlea of the humerus. Another condition is that the points must lie under the calculated center of the 3D circle. This improved list is called fitL2. Step 4: Do the fitting of fitL2 to a 3D circle This will produce the center, the radius and the normal to the plane of the fitted 3D circle representing the undermost part of the glenoid cavity. Figure 5.26 illustrates the critical parameters used in the procedure. The big red marker represents gPoint. The red dots are the points contained in fitL1. The blue dots are the points of fitL2 that are used to fit the eventual 3D circle that represents the glenoid cavity. The circle is drawed in blue. 100 Figure 5.26 101 Chapter 6 The clavicle In this chapter the functional processes with respect to the clavicle are explained. These processes are: the function responsible for the unambiguous orientation, and two extraction functions. Orient clavicle Extract most anterior point on the sternoclavicular joint surface Extract most posterior point on the acromioclavicular joint surface The rest of this chapter is constructed in the same manner as the 2 previous chapters. The functions, corresponding to the procedures as stated in the flowchart, are described one by one starting with the description of the orientation procedure. The exact method of working can be found in the end of the introduction of Chapter 4. Throughout the developing and testing process of the 3 functions we had at our disposal a total of 13 3D models of clavicles represented by binary Stl files. This was an even mix of right and left clavicles. In appendix B.4 you can find the code as written in Matlab®. 6.1 The orientation of the clavicle We chose arbitrarily a reference orientation, with which the feature extractions can be done in a uniform way. After the necessary translations and rotations of the bone, its side (left or right) is detected and mirrored, if needed, to its right counterpart. This to 102 facilitate the developing of the subsequent extraction functions as they only have to take into account right bones. For subsequent visualization purposes, this artificial right clavicle has to be mirrored again to its original left state. The chosen orientation depicts the right sided clavicle in its position like in the human body. Its prominent length is aligned along the x-axis. Its real superior surface points towards the positive z-axis. So the ‘plane’ of the clavicle lies more or less in the x-y plane. The anterior aspect is oriented towards the negative y-axis. As a result of all this the medial end will lie onto the positive x-axis. In Figure 6.1 this reference orientation is clarified. Figure 6.1 Above: Superior view of the oriented right clavicle. The bone is looked at from the positive z-axis. Middle: Anterior view of the oriented right clavicle. The bone is looked at from the negative y-axis. Below: Lateral view of the oriented right clavicle. The bone is looked at from the negative x-axis. 103 The function used is called orientClavicle. [x2, y2, z2, n2, side] = orientClavicle(x1, y1, z1, n1) The input and output arguments are almost the same as for the orientation function of the humerus and the scapula. The only difference is the absence of supplementary output arguments for future use. Side will specify in the end whether the processed bone was a right or a left one. Matrix n1 of the normals is also an input argument, because it has to undergo all the same transformations to keep the normals consistent with the facets of the patch object. Figure 6.2 Complete random orientation of a left clavicle In Figure 6.2 the orientation is given of a 3D model that we will use in the following steps to illustrate the orientation to the above stated reference orientation. The 3D model is of a left clavicle and is situated completely random in 3D space. Step 1: Define the prominent direction in the oriented clavicle This is executed by a principal component analysis as explained in previous chapters. The result of this analysis is the principal component axis and the two extreme points on it, ext1 and ext2. By now it is not yet clear which extreme point is situated on the proximal (medial) end and which on the distal (lateral) end. The patch object representation is now also transformed in a list of points, L. 104 Step 2: Define a translation based on the mean of L This mean, meanL, is nothing more than the mean of the x-, y- and z-values of the points in L. Once obtained meanL, a translation of all the points of L must take place. The mean point coincides now with the origin of the global coordinate system. Figure 6.3 In Figure 6.3 the two extremes, ext1 and ext2, are drawed in yellow and meanL is represented by the blue marker. The translation is already carried out, because meanL coincides with the origin of the coordinate system. Step 3: Do a sequence of rotations of the bone in order to get point ext2 to lie onto the positive x-axis The sequence of rotations talked about is one rotation around the z-axis to get ext2 in the x-z plane, and one rotation around the y-axis to get ext2 onto the positive x-axis. The choice of ext2 rather than for ext1 is arbitrary. The resulting orientation of this step is shown in Figure 6.4. As can be noticed, ext2 corresponded to the lateral end of the clavicle, because its position is situated on the positive x-axis by now. 105 Figure 6.4 View from the negative y-axis. The only purpose of this step is to get the principal axis of the clavicle aligned with the x-axis. Nothing more can be said about the orientation by now. The rotations have to be carried out to n1 too. Step 4: Do a rotation around the x-axis in order to get the ‘plane’ of the clavicle to lie in the x-y plane If we talk about the ‘plane’ of the clavicle we reference to the flattened part of the clavicle in the middle part of the bone. Through observation of 13 3D models of clavicles, we picked the middle 40 % of the bone in its longest dimension, i.e. along the x-axis. This part, represented by M, exhibits this flattened structure. If we rotate M around the x-axis in intervals of beta degrees, and calculate for each outcome position the distance between the maximal y-value and the minimal y-value, we can decide over which angle M has to be rotated to get it in the x-y plane. This angle, say alpha, will be determined by the biggest y-distance. Use alpha to carry out the rotation around the xaxis of the entire bone. Figure 6.5 visualizes the outcome position of this step. Figure 6.5 106 Step 5: Determine which end is the medial end and get it onto the positive x-axis The logic behind this step originates again from the observation of the 13 clavicles we worked with. It was obvious that in the orientation we now have reached, the lateral part of the middle part M has a bigger y-distance (maximum y-value – minimal y-value). So we compare the two y-distances of the two parts (points of M with positive x-values and points of M with negative x-values) of M and we denominate the part with the smallest y-distance as being the medial part. If this part is the one of the negative xaxis, a rotation around the z-axis of 180 degrees is needed. In Figure 6.5 it can be seen that the medial part lies onto the negative x-axis, so a rotation will be needed. The outcome of this rotation is depicted below in Figure 6.6. Figure 6.6 Step 6: Look for the curve in the lateral part The clavicle exhibits an S-shape, if we look to it from a superior view, like e.g. in Figure 6.6. So it has also two curves. One curve in the medial part and one curve in the lateral part. We will focus in this step on the lateral part. The curve can lie in the half with positive y-values or in the other half, with negative y-values. Purpose of this step is just determining where the curve is situated. This is done by comparing the maximal and minimal y-value of the middle part of the lateral half (x-coordinate smaller than 0). The logical which will hold this information is latCurv. In step 8 its purpose will be clear. If latCurv equals 1, the curve lies in the positive y-space. If 0, the curve lies in the negative y-space. (respectively above and below the x-z plane with respect to ascending y values). The situation in Figure 6.6 will yield 1 for latCurv, because the curve is positioned above the x-z plane. 107 Step 7: Determine whether the superior surface points upwards It comes down to detecting the conoid tubercle. This tubercle lies on the inferior surface of the clavicle. So if we can detect (more or less) this tubercle and compare its z-value with the mean z-value, meanZ, of the set of points amongst we looked for the conoid tubercle, we can say whether the inferior surface points upwards or downwards. It points downwards if the tubercle has a smaller z-value than meanZ. If the inferior surface points downwards, by logic the superior surface points upwards. The logical that specifies whether the superior surface points upwards is called natural. This name refers to the natural position of the clavicle, that is with its superior surface pointing upwards. Its purpose will be explained in the next step. Figure 6.7 shows for our example the approximation of the conoid tubercle in green. The set of points in which we looked for the conoid tubercle are plotted in purple. Figure 6.7 As will be clear, the approximation of the conoid tubercle drawed in the above figure has a bigger z-coordinate than the mean of all the points in which we looked for the conoid tubercle. These points are depicted in purple. Conclusion: natural equals 0. 108 Step 8: Determine the side of the clavicle and do the necessary transformations to get the orientation as wanted We use the two decision makers of Steps 6 and 7, latCurv and natural, to be sure of the side of the processed bone and to finalize the orientation. We have 2 logicals, that can take on two values so if we combine them we have a total of 4 different combinations. 1 latCurv==1 AND natural==1 The curve in the lateral part lies in the part of the positive y-axis. And the bone has its natural up down orientation. So we can decide that the bone is a right one and it is oriented well. 2 latCurv==1 AND natural==0 The curve in the lateral part lies in the part of the positive y-axis. And the bone’s superior surface points downwards. So we can conclude that the bone is a left one. The only thing to do now is to mirror the entire bone with respect to the x-y plane. By this operation we get a right bone and the up down orientation becomes correct. 3 latCurv==0 AND natural==1 The curve in the lateral part lies in the part of the negative y-axis. And the bone has its natural up down orientation. So we can decide that the bone is a left one. While the up down orientation is good, the only thing to do is to mirror the bone with respect to the x-z plane. We get a right sided bone, and the curve in the lateral part will be situated in its correct position. 4 latCurv==0 AND natural==0 The curve in the lateral part lies in the part of the negative y-axis. And the bone’s up down orientation is incorrect. So we can decide that the bone is a right one and it’s superior surface is faulty oriented. The only thing to do now is to execute 109 a rotation around the x-axis of 180 degrees to get it in its correct place. Mirroring is useless because it already is a right bone. The orientation is ready now. The parameters latCurv and natural for the example are respectively 1 and 0. So as the ultimate transformation the operation of alternative 2 has to be carried out. The outcome orientation will be the intended one like in Figure 5.1. At last the finalizing remark on the end of 4.1 must also be taken into account here. 6.2 Extraction of the anterior sternoclavicular joint This feature is the midpoint on the anterior edge of the sternoclavicular joint surface. We will refer to this feature as the sternoclavicular joint. It will be localized as defined in [1]. The function sternClav will execute the process of locating the mentioned feature. [sternClav] = sternClav(L) The output argument is the position of the point we defined as the sternoclavicular joint. The input argument L is the list of the points of the entire oriented right clavicle. The orientation of the bone as done in 3.1 will make this task straightforward as isolating the medial end of the clavicle and selecting in this set of points the most anterior one. This is carried out by selecting the points with an x-value that is bigger than 95% the maximal x-value in the current orientation, and searching in this selection the point with the smallest y-value. The last step because the anterior aspect of the oriented right clavicle points without exception towards the negative y-axis. In Figure 6.8 this feature is represented by the blue marker. 110 6.3 Extraction of the posterior acromioclavicular joint This feature is the most posterior point on the acromioclavicular joint surface; the surface of the clavicle that will articulate with the acromion of the scapula laterally. The feature will be localized as defined in [1]. We will refer to this feature as the acromioclavicular joint. This feature extraction is executed by: [acrClav] = acrClav(L) The output argument is the position of the point we defined as the acromioclavicular joint. The input argument L is the list of the points of the entire oriented right clavicle. This extraction will come down to selecting the most lateral point in the current orientation of the bone. This is in other words the point with the smallest x-value of all the points of the 3D model of the clavicle. The feature is depicted in Figure 6.8 as the red marker. Figure 6.8: Anterior and slightly lateral view of the oriented right clavicle. In blue the sternoclavicular joint. In red the acromioclavicular joint. 111 Chapter 7 The ulna The ulna is located on the medial aspect of the forearm of the human body. In this chapter the functions associated with the ulna are described and commented. As with the other bones, the first function that has to be executed is the function that will carry out the orientation of the ulna. Subsequently this uniform orientation will help in extracting the features that were mentioned in the end of Chapter 2. The following flowchart gives the sequence of events when extracting features of the ulna. Extract apex of the olecranon Extract coronoid process Orient ulna Extract styloid process Extract head of the ulna The rest of this chapter is constructed in the same manner as the 3 previous chapters. The functions, corresponding to the procedures as stated in the flowchart, are described one by one starting with the description of the orientation procedure. The exact method of working can be found in the end of the introduction of Chapter 4. We had a total of 6 3D models of ulnas at our disposal to make and test the functions outlined in this chapter. The code can be found in Appendix B.5. 112 7.1 The orientation of the ulna The idea is the same as for every orientation for every bone. We chose arbitrarily a reference orientation, with which the feature extractions can be done in a uniform way. After the necessary translations and rotations of the bone, its side is detected and mirrored, if it is a leftsided bone, to its right counterpart. For the subsequent visualization of the bone and its features, all of this has to be mirrored again, with respect to the same plane, in order to get the bone’s original side. The intended final orientation can be seen in Figure 7.1 and 7.2. This is the orientation of the bone resembling to its orientation if the forearm (to which the ulna belongs) is held in its neutral position in the human body. In this orientation the proximal end is situated higher than the distal end. More concretely, the ulna of the right arm is aligned around the z-axis from above to below and the anterior aspect of the bone points towards the negative y-axis Figure 7.1 Superior view of the oriented right ulna. The bone is looked at from the positive z-axis. 113 Figure 7.2 Left: Anterior view of the oriented right ulna. The bone is looked at from the negative y-axis. Right: Lateral view of the oriented right ulna. The bone is looked at from the negative x-axis. The function used is called orientUlna. [x2, y2, z2, n2, side] = orientUlna(x1, y1, z1, n1) As output arguments it will return the patch representation of the oriented right ulna (x2, y2 and z2), the matrix n2 of normals to the facets of the oriented 3D model and the logical operator side, which holds the specification of the side of the bone (left if side == 1 or right if side == 0). The input arguments are the patch representation (x1, y1 and z1) and the matrix of the normals (n1) of the unoriented 3D model of an ulna that serves as input for the orientation process. 114 Step 1: Define the principal component axis of the 3D model The eventual purpose of this step will be the aligning of the bone around the z-axis. As the ulna is a long bone it has a long shape between its proximal and distal end and we will use this property throughout this step. We will determine the best line (axis) around which the data, representing the 3D model, is distributed. This is done by executing a principal component analysis of the data of the bone. We call this principal axis Princ. Because of its long shape, logically Princ will lie along the long side of the ulna. In future steps we do transformations in order to get this line to fall together with the z-axis. The specification of Princ is done by the specification of one point of it and the direction of it. A point that always lies on the principal component axis is the mean of all the points of the data. The direction of Princ is given back by the function carrying out the principal component analysis. In 4.1 this function, princomp is described. So now we have the parameters that specify Princ. With this information we can define the projection of every point of the data onto Princ. If we want Princ to coincide with the zaxis, it is sufficient to get two distinct points of Princ to lie on the z-axis. Arbitrarily we picked the two exteme projections of the data onto Princ to do this aligning. We now have the two extreme points of the principal component axis, ext1 and ext2. Figure 7.3 shows the unoriented bone together with Princ in blue and ext1 and ext2 both in yellow. Figure 7.3 115 On this moment we don’t know where the extreme points are situated with respect to the bone. Does ext1 lies on the distal end or on the proximal end? Figure 7.4 Step 2: Do a linear translation defined by ext1 This step forces ext1 to be the origin of 3D space and the entire bone undergoes the same translation. As a result ext1 yet will be a point of the z-axis. We can see in Figure 7.4 the outcome of the translation. Step 3: Force ext2 onto the z-axis After this step the principal component axis Princ lies aligned with the z-axis, because ext1 and ext2 are now points of the z-axis. See Figure 7.5. This step is carried out by rotating the 3D model first around the z-axis and subsequently around the y-axis. One rotation more around the x-axis or y-axis is possible to get the bone to lie above the x-y plane. For more comments on this issue we refer the reader to 4.1, Step 4 where these sequence of rotations is commented. 116 Figure 7.5 Step 4: Determine the right up-down orientation of the ulna The purpose of this step is making the proximal end of the ulna to lie above the distal end. As a result of step 3, all the points of the model lie above the x-y plane. We now make use of the specific shape of a regular ulna. The proximal end is a broader and bigger part than the distal end, i.e. the maximum distance of a point to the z-axis is bigger in the proximal half than in the distal half. With this information we can determine which extreme point lies on the proximal end and which on the distal end. If the maximal distance to the z-axis is bigger in the half near ext1 (the lowest extreme point), we can conclude that the bone is oriented upside down along the z-axis. In this case we rotate the entire bone over 180 degrees around the y-axis (or the x-axis). Figure 7.6 illustrates the idea. Left we see the projection onto the x-y plane of the points of the uppermost half (near ext2) of the bone depicted in Figure 7.5. Right is shown the projection of the points of the undermost half (near ext1). It is obvious that 117 the biggest x-y distance (the red points in Figure 7.6) can be found in the left image and thus the half corresponding to it will be detected as being the most proximal half, which is the correct conclusion for our example. Figure 7.6: Left: Projection of the uppermost half of the bone onto the x-y plane. Right: Projection of the undermost half of the bone onto the x-y plane. If as a consequence of this rotation the bone lies below the x-y plane, we do a translation in the direction of the z-axis as to force the bone to lie above the x-y plane again. We are now sure which extreme point is near the broad proximal end and which near the thin distal end. The bone is now oriented along the positive z-axis in its correct up down orientation. In our example bone as seen in Figure 7.5 the up down orientation already is a fact so no supplementary transformations were needed here. Step 5: Make the ulna's anterior aspect to point to the negative y-axis To complete this task we made use of the shape of the upper proximal part of the ulna. Through observation of the proximal ends of the 6 models it became clear that the biggest distance between opposing points (with respect to the origin of the 3D space) could be found in the direction defined by the coronoid process and the apex of the olecranon. We used this information to determine the correct angle over which to rotate 118 the 3D model around the z-axis in order to get the anterior aspect of the ulna to point towards the negative z-axis. The technique used to perform this is the same technique as used in Step 4 of 6.1. In the end the direction in which the y-distance (maximal y-coordinate – minimal ycoordinate) is the biggest (more or less the direction defined by the apex of the olecranon and the coronoid process) will be aligned with the y-axis. In this moment it is possible that the anterior aspect points to the positive y-axis for the same reason as with previous rotations – we only do rotations with angles smaller than 90 degrees. If this situation occurs, a supplementary rotation over 180 degrees around the z-axis is needed. The outcome orientation is that of Figure 7.7. Figure 7.7: Anterior view from the negative y-axis. 119 Step 6: Determine whether it is a bone of the right or a bone of the left arm The bone is oriented as wanted, but we are not yet sure upon the side of the bone. To solve this, we base ourselves upon the geometry of the distal half of the ulna. The lowest point of the 3D model of an ulna in the current orientation (we will see in 7.4 this is the styloid process) is always pointing a bit medialwards. For a left bone medialwards in the current orientation means towards the negative x-axis. So if the lowest point has an x-coordinate smaller than 0, the input bone is a left sided ulna. This is the case in Figure 7.7 where the lowest point represented by the purple marker has an xcoordinate smaller than 0. If a left sided bone detected, all the points of the 3D model will be mirrored with respect to the y-z plane and the output argument, side, is set to 1. This mirror process yields for the example bone its right counterpart oriented like intended. This outcome orientation is depicted in Figure 7.1 and 7.2. 7.2 Extraction of the apex of the olecranon The purpose of this procedure is to visualize the apex of the olecranon. The olecranon is the large tuberosity located at the posterior aspect of the proximal end of the ulna. The apex of this tuberosity is the point that has to be extracted. We will extract the position of this point as defined in [1]. The function responsible for this extraction is named olecranonUlna. [olecUlna] = olecranonUlna(L) The output argument is the position of the apex of the olecranon. The input argument L is the list representation of the oriented right ulna. The idea is simple once the ulna is oriented like we described in 7.1. In this orientation the apex of the ulna is the most posterior (biggest y-value) point of the proximal end of the ulna. 120 Step 1: Define the upper part of the ulna in which to look for the apex of the olecranon Through examining the 6 3D models of the ulna, we defined the upper part as the part lying above a z-value defined by 97% of the total length (along the z-axis) of the ulna. This value yielded the best result in all 6 cases. We call this list of points upperL. Figure 7.8: Left: The entire bone with the points of search space upperL plotted in purple. Right: the most posterior point of this search space, the apex of the olecranon, in red. Step 2: Select the most posterior point of upperL This point will be the apex of the olecranon. It is the point in upperL with the biggest ycoordinate. The procedure is illustrated in Figure 7.8. 7.3 Extraction of the coronoid process The coronoid process of the ulna is like the previous feature situated in the most proximal part of the ulna. It is the horizontal part that points anteriorly. We will extract the most anterior point on the coronoid process as defined in [1] and this feature will be referred to as the coronoid process. The function coronoidUlna will carry out this extraction. [corUlna] = coronoidUlna(L) 121 The output argument is the position of the coronoid process onto the oriented right 3D model of an ulna. The input argument L is the list representation of the oriented right ulna. Because the coronoid process is the most anterior part of the proximal end of the ulna, it is sufficient to select the most anterior point of the entire ulna. In the orientation of the bone as described in 7.1 the anterior aspect points to the negative y-axis, therefore the most anterior point is represented by the point with the smallest y-value. See Figure 7.9, where it is very clear this statement holds. In all 6 cases of the 3D models we have studied, this conclusion could be made. Figure 7.9 122 7.4 Extraction of the styloid process The styloid process is a small tuberosity on the distal end of the ulna. The point that we will look for is the very tip of this tuberosity. The tip can be seen as the representation of the entire styloid process and hence the extracted tip will be referred to as the styloid process. The function styloidUlna will execute the process of locating the styloid process. [stylUlna] = styloidUlna(L) The output argument is the position of the point we defined as the styloid process, the tip. The input argument L is the list representation of the oriented right ulna. In [1] the palpation of the styloid process results in localizing not the tip of the styloid process, but a point along the medial edge of the styloid process, just above the tip. This point is chosen because it is impossible to palpate the real tip as it is hidden by the tendon of a muscle (extensor carpi ulnaris) and therefore not reachable. We, on the other hand, do have the possibility to localize the very tip of the styloid process. In the current orientation of the ulna, this tip is the most distal point of the ulna. Consequently it is sufficient to select the point with the smallest z-value of L. In Figure 7.7 the styloid process is represented by the purple marker. 7.5 Extraction of the ulnar head As stated in Chapter 2, the ulnar head is a rounded eminence on the lateral aspect of the distal end of the ulna. We will extract the ulnar head as a sphere and give back the center and the radius of the sphere. The function headUlna will carry out this feature extraction procedure. [center radius] = headUlna(L) center and radius are the parameters that specify the extracted sphere. L is the list representation of all the points of the 3D model of the oriented right ulna. 123 Firstly a region of points is isolated. This set of points, that corresponds to a set belonging to the ulnar head, will be given as input to a function that will do the fitting of these points to a sphere. This fitting process is based upon the least squares fitting technique. Step 1: Define the undermost part of the 3D model in which to look for points of the ulnar head This step comes down to selecting those points lying in the undermost 5% of the ulna with respect to the z-axis. We call this list underL. The points of underL are depicted in Figure 7.10 in yellow. Figure 7.10 Step 2: Make an ultimate list of points that will be handed over to the fitting procedure The purpose of this step is selecting only points belonging to the ulnar head. For this to happen we make use of the position of the ulnar head relative to the undermost part of 124 the ulna. Because it is a surface for articulation with the radius, the ulnar head will lie on the lateral side of the distal end and it is directed anteriorly. For the 3D model that we use in our extraction functions (the outcome of 7.1) this means that the points of the ulnar head are situated in the direction of the negative x-axis and the negative y-axis. Now we will take a point in the center of underL, cent, and we will define a particular vector in it. This vector can be interpreted as the representation of a plane; the plane perpendicular to the vector. So we have created a plane perpendicular to the vector in cent. But what is the vector of which we talked? This vector is (-1, -1, 0) and thus the vertical plane defined by it can be used to select the most lateral and anterior points of underL by taking the points of underL lying on the latero-anterior side of the plane. This refined list of points is called fitL (Figure 7.11, right, the blue dots). This procedure is clarified in Figure 7.11. Step 3: Fit the points of fitL to a sphere The points of fitL are given to a function that does the fitting to a sphere of these points. This sphere will represent the ulnar head. Figure 7.12 shows the ulna with the sphere approximation of the ulnar head in red. 125 Figure 7.11: Left: an inferior view (from the negative z-axis) with in purple the point cent. Right: a slightly inferior view with the plane (purple) drawed through cent perpendicular to the vector (-1,-1,0). We will only take into account the points on the latero-anterior (left) side of it, the list fitL, plotted in blue. Figure 7.12 126 Chapter 8 The radius The radius is located on the lateral aspect of the forearm. In this chapter all of the functions corresponding to the radius are explained and illustrated. The general method of working applied in the previous chapters of the other bones is utilized once again to extract features of the radius. Firstly the 3D model that is read in will be oriented and subsequently this orientation will help in extracting the features relevant to the radius. The features to extract are listed in the end of Chapter 2. The following flow chart shows the sequence of the carried out procedures. Extract the radial head Orient radius Extract the radial head periphery Extract styloid process The rest of this chapter is constructed in the same manner as the 4 previous chapters. The functions, corresponding to the procedures as stated in the flowchart, are described one by one starting with the description of the orientation procedure. The exact method of working can be found in the beginning of Chapter 4. Throughout the writing and testing of the code many ideas originated from the observation of 6 random 3D models of regular radii. The code can be found in Appendix B.6. 8.1 The orientation of the radius The idea is the same as for every orientation for every bone. We chose arbitrarily a reference orientation, with which the feature extractions can be done in a uniform way. After the necessary translations and rotations of the bone, its side is detected and mirrored, if it is a leftsided bone, to its right counterpart. For the subsequent 127 visualization of the bone and its features, all of this has to be mirrored again, with respect to the same plane, in order to get the bone’s original side. The intended final orientation can be seen in Figure 8.1 and 8.2. This is the orientation and positioning of the bone resembling to its orientation if the forearm (to which the radius belongs) is held in its neutral position in the human body. In this orientation the proximal end is situated higher than the distal end. More concretely, the ulna of the right arm is aligned around the z-axis from above downwards and the anterior aspect of the bone points towards the negative y-axis. Figure 8.1 Superior view of the oriented right radius. The bone is looked at from the positive z-axis. The function used is called orientRadius. [x2, y2, z2, n2, side] = orientRadius(x1, y1, z1, n1) As output arguments it will deliver the patch representation of the oriented right radius (x2, y2 and z2), the matrix n2 of normals of the facets of the oriented bone and the logical operator side, which holds the specification of the side of the bone (left if side == 1 or right if side == 0). The input arguments are the patch representation (x1, y1 and z1) and the matrix of the normals (n1) of the unoriented 3D model that serves as input for the orientation process. 128 Figure 8.2 Left: Anterior view of the oriented right radius. The bone is looked at from the negative y-axis. Right: Lateral view of the oriented right radius. The bone is looked at from the negative x-axis. The first 4 steps of the algorithm are completely the same as the first 4 steps in orienting the ulna. For more information on these, the reader is referred to 7.1. Step 1: Define the principal component axis of the 3D model We get two extreme points on the principal axis of the data, ext1 and ext2. Step 2: Do a linear translation defined by ext1 Figure 8.3 shows the result of the 2 first steps. The principal axis is depicted in blue and the two extreme points are yellow. One of the extreme points is positioned in the origin of the global coordinate system. 129 Figure 8.3 Step 3: Force ext2 onto the z-axis Figure 8.4 illustrates that with the two extreme points on the z-axis, the principal axis is aligned along the z-axis. The bone is oriented upside down in this example. 130 Figure 8.4 Step 4: Determine the right up-down orientation of the radius This step exhibits the same logic as in Step 4 of 7.1, but the parameters are different now. Whereas with the ulna the proximal end is broader and bigger than the distal end, the proximal end of the radius is less broad and smaller than the distal end. So we alter the decision making process of doing the 180 degree rotation around the y-axis (or xaxis) as follows. If the maximal distance to the z-axis is smaller in the half near ext1 (the lowest extreme point), we can conclude that the bone is oriented upside down along the z-axis. As a consequence the bone has to be rotated over 180 degrees in order to get its up down orientation as in the forearm of a neutrally positioned arm. 131 Figure 8.5 Left: Projection of the undermost half of the bone onto the x-y plane. Right: Projection of the uppermost half of the bone onto the x-y plane. Figure 8.5 makes clear the norm of the left image is smaller than that of the right image, therefore it can be decided that the narrow part, the head of the radius, lies in the undermost part. As a result the bone has to be rotated 180 degrees over the x-axis or over the y-axis. Figure 8.6 132 The bone is now oriented along the positive z-axis in its correct up down orientation. This is confirmed in Figure 8.6. Step 5: Determine the side of the radius, left or right This step makes use of the relative positioning of the lowest point of the radius in the current orientation, lowest, to the point representing the radial tuberosity, radTub. We utilize these points because they are unambiguously detectable and therefore in every 3D model of any given radius easily and correctly reproducible. Firstly lowest is forced into the x-z plane on the negative x-axis. This is done by rotation around the z-axis. Than radTub is located in the current position of the radius. This is done by looking for the point with the biggest orthogonal distance to the z-axis in the proximal 25% of the bone. In this last reached orientation the radial tuberosity of a radius of the right arm will have a negative y-value. If the y-value of radTub is positive, the processed bone is a left one. Once detected a leftsided radius, a mirror operation will take place to transform the left bone into its right counterpart. Figure 8.7 133 In our example (Figure 8.7) the radial tuberosity in yellow and the lowest point in purple decide that we have processed a left bone. This will be mirrored with respect to the x-z plane to become its right counterpart. Step 6: Let the radial tuberosity point medialwards Medialwards for a rightsided bone means in the direction of the positive x-axis (if the anterior aspect points to the negative y-axis like in our orientation). The task consists of rotating the bone around the z-axis until the radial tuberosity, radTub, lies in the x-z plane on the half of the positive x-axis. If this orientation is reached, the objective of the orientation procedure orientRadius is met. This is shown in Figure 8.8. Figure 8.8 134 8.2 Extraction of the radial head Extraction of the radial head comes down to fitting a sphere to the points of the cup shaped depression on top of the radial head. This is where the radius articulates with the capitulum of the humerus. The function that carries out the fitting of the above defined sphere takes on the following form: [center radius] = headRadius(L) The output arguments are the position of the center and the radius of the fitted sphere. The input argument is the list of the points of the oriented right radius (the list representation of the output of orientRadius). The procedure comes down to locating and isolating the set of points that belongs to this cup shaped form and handing over this list to a function that carries out the least squares fitting procedure to a sphere. Step 1: Locate the center of the cup on the radial head This point, sPoint, will be the first point to add to the list of points that eventually will be given to the fitting function. As the radial head is not exactly centered around the z-axis, we can not select the point of the bone with the biggest z-value as origin. Thus we will define the principal component axis of the superior 20% of the radius and we will select the extreme point with the biggest z-value as origin. Following this method gives certainty about the correct position of sPoint. See Figure 8.9, in red the points of which the blue principal component axis is calculated. The yellow marker represents sPoint. 135 Figure 8.9 Step 2: Calculate the distances to sPoint of the top 5% of the bone, upperL. This is a preparation step for the actual fitting phase of step 3. Purpose is to select the correct points to give to the fitting procedure. The correct points would be those points of the upwards facing cup of the radial head. The term ‘distance’ of the title is meant to be the distance between the points of the upper 5% and sPoint that are all projected onto the x-y plane. We take in other words the x-y distance into account. This is the same technique as in 8.1. We took this decision because with this method the definition of the percentage top part is more robust and not that decisive. We could have taken without having problems instead of 5% a percentage of 10%. This becomes clear by reasoning on the x-y distance. Saying that the x-y distance to a point, say test, must be smaller than a certain value v is nothing more than selecting the points that lie inside the cylinder drawn around the zaxis with a radius of v. If we work with conditions on the distances to sPoint of the points of upperL, this method will be easier to work with. This will become clear in step 3. 136 Step 3: Define the list of data that will be fitted to the sphere This list will be named fitL. This is the list of data points that have a x-y distance to sPoint that is smaller than 40 % the maximal x-y distance. These points will with certainty all be part of the upwards facing cup of the radial head because of the use of the x-y distance. The choice for 40% is the result of the testing of the function with the 6 radii. Figure 8.10 gives an illustration. The blue dots are the projections of the points of upperL onto the x-y plane. The green dot is the projection with the biggest x-y distance and the red dots are the projections of the point that will be involved into the fitting to a sphere, fitL. Those points have an x-y distance smaller or equal to 0.4 * the maximal xy distance, in green. If we had used the Euclidean distance, it would have been difficult to specify a good value for the percentage of the max(distances to sPoint). In the writing and testing of the function, points of the radial head periphery were included into fitX. This had a negative influence on the fitting to the correct sphere. Figure 8.10 137 Step 4: Do the fitting of the data in fitL to the sphere fitL is given to a function carrying out the fitting to a sphere, thus returning the center and the radius of the sphere. This sphere is illustrated in Figure 8.11. Figure 8.11 8.3 Extraction of the radial head periphery In this chapter the fitting of a cylinder to the points of the periphery of the radial head is done. It is a tight ridge that resembles more or less to a cylinder. The function that does this extraction: [radius under upper] = cylRadius(L) The three output arguments are the radius of the cylinder, the uppermost and the undermost point on the axis of the calculated cylinder. The input argument is the list of the points of the oriented right radius (the list representation of the output of orientRadius). 138 The procedure comes down to locating and isolating the set of points that belongs to this periphery and handing over this list to a function that carries out the least squares fitting procedure to a cylinder. Step 1: Locate the position of the uppermost point of the cylinder This step is exactly the same as the second step in 8.2. We call the located point, upper. This will be given as an output argument, but it will also be used in the next steps. See Figure 8.9. Step 2: Calculate the distances to upper of the top 5% of the bone, upperL. See the second step of 8.2. The correct points in this extraction procedure would be those points of the periphery of the radial head. So we have to define a condition with respect to the x-y distance that will yield these points. Step 3: Define the list of data that will be fitted to the cylinder. We call this list fitL. This is the list of data points that have an x-y distance to upper that is bigger than 80% the maximal x-y distance. These points will with certainty all be part of the periphery of the radial head because of the use of the x-y distance. See Figure 8.12 for our example. The above choice for 80% is the result of the testing of the function with the 6 radii. Figure 8.12 Left: The points of fitL marked in red. Right: The eventual fitted cylinder marked in green. 139 Step 4: Do the fitting of the data in fitL to the cylinder fitL is given to a function carrying out the fitting to a cylinder. This function will give back the radius, the upper- and the undermost point on the axis of the fitted cylinder. With these three parameters we are able to visualize the cylinder in the subsequent visualizing procedures. 8.4 Extraction of the styloid process The styloid process of the radius is a tuberosity that lies on the lateral side of the distal end of the radius. The point that we will look for is the very tip of this tuberosity. This point will serve as a representation of the entire tuberosity and will therefore be referred to as the styloid process. The function that does this extraction: [stylRad] = styloidRadius(L) The output argument is the position of the styloid process. The input argument is the list representation of the already oriented right radius. As in the extraction of the styloid process of the ulna we are looking for the most distal point of the entire bone. As the list L represents the already oriented bone, it is sufficient to select the point with the smallest z-value of L. This will be the (tip of the) styloid process of the radius. Figure 8.13 illustrates the position of this feature as the red marker. 140 Figure 8.13 141 Chapter 9 Results This chapter serves to quantify the quality of our automatic feature extraction processes. The problem in doing so is that we do not have data that can serve as reference data to the features we have extracted for all the bones we worked with. And thus it is very difficult to validate the obtained data to a reference set for every processed bone. Although there is not an exact way of validating our functions, we used 3 ways that do this validation process in a heuristic manner. 9.1 Visualization The first validating process is to judge the positions of the extracted features by looking at the resulting position and deciding whether it is the good position for a certain feature. This rudimental technique can not be considered as a scientific significant validation process, but it does give an indication of the correctness of the results. In all cases, with good modeled bones the results were acceptable to good. We have to emphasis that the result is to a great extent dependent on the quality of the input 3D models because we designed our functions for anatomically perfect bones. Any abnormality in a 3D model of a given bone can produce faulty output because the functions only work with the information given by the geometry of the 3D models. The following figures will visualize for each bone type the extracted features. The bone models are those that were made available with the V-Palp software tool (see 9.2). 142 Figure 9.1: Left: anterior view of the humerus (from the negative y-axis). Right: medial view of the humerus (from the positive x-axis) Figure 9.2: Superior view of the humerus (from the positive z-axis) 143 Figure 9.3: Left: posterior view of the scapula (from the negative y-axis). Right: medial view of the scapula (from the positive x-axis) Figure 9.4: Superior view of the scapula (from the positive z-axis) 144 Figure 9.5: Anterior view of the clavicle (from the negative y-axis) Figure 9.6: Superior view of the scapula (from the positive z-axis) 145 Figure 9.7: Left: anterior view of the ulna (from the negative y-axis). Right: medial view of the ulna (from the positive x-axis) Figure 9.8: Superior view of the ulna (from the positive z-axis) 146 Figure 9.9: Left: anterior view of the radius (from the negative y-axis). Right: medial view of the radius (from the positive x-axis) Figure 9.10: Superior view of the radius (from the positive z-axis) 147 9.2 V-Palp On top of this heuristic validation we can work with the bones and the data of the positions of the features of the open source software tool V-Palp [14]. The Virtual Palpation software (V-Palp) is a software tool that allows users to be trained and evaluated on virtual palpation. That is: 3D models of all bones of the human body can be read in and the user can locate the desired landmarks/features by clicking onto the 3D model. Hence it is the virtual counterpart of manual palpation. It is developed as an addition to [1]. Figure 9.1 gives a screenshot of the GUI of V-Palp. Figure 9.11 The tool comes with an example for all the open source 3D models of bones of the body. This is the exact same set of bones we used to visualize all our feature extraction procedures. For each bone of this set the positions of the landmarks are predefined by the developer of the tool and this data can be exported. This is of interest for our work as we worked with the exact same bones. The exported data is given for the unoriented bones. So if we do the same transformations to the exported landmarks/features as we did to the bones in the orientation procedures, we get the positions of the exported landmark data in our reference orientations of the bones and we can compare these positions with the positions we obtained. 148 This procedure is only applicable to those features we extracted that coincide with features processed by V-Palp. For each of the 5 processed bones –humerus, scapula, clavicle, ulna and radius- we will export the features in common with our extracted features and do the necessary transformations to their coordinates representing their positions. These will be compared to the positions we extracted. In 9.4 the tables give the results for each feature of each bone. If this validation technique is used for a particular feature, ‘V-Palp’ will be written under the name of the feature. In the second column come the positions as defined in V-Palp and in the third column come our extractions. The fourth column gives the Euclidean distance between both to give an indication about the correctness of our extractions. 9.3 Manual Some of the features we extracted, such as the head of the humerus (4.2), the capitulum of the humerus (4.5),…, are not localized in V-Palp and therefore can not be compared. For those features we did a manual feature extraction. These features all involve fittings to geometric shapes like spheres, cylinders and 3D circles. For each feature we can manually pick the points that need to be given to a fitting function and carry out the fitting. This approach is the same as we did in our extraction functions, with that difference that in our functions the lists of points to feed the fitting functions are created automatically. In 9.4 the tables give the results for each feature of each bone. If this validation technique is used for a particular feature, ‘manual’ will be written under the name of the feature. In the second column come the results as produced by the manual extraction and in the third column come the results from the automatic extraction. The fourth column will show the difference between the results of the manual and automatic extractions. 149 In case of a sphere fit, the center of the sphere and the radius will be given. In case of a cylinder fit, the radius and the direction of the axis of the cylinder will be given. The directions of the axes are normalized vectors. The difference will be expressed as the angles between the vectors representing the axes of the cylinders. In case of a 3D circle fit, the center, the radius and the vector perpendicular to the plane of the circle will be given. The difference associated with the normals to the planes will be expressed in terms of angles between these normals. 9.4 Results The following tables give for each bone the results. If suitable, remarks will be given. If coordinates are given, they have to be interpreted with respect to the global coordinate system with its origin in (0,0,0). If distances are given, like the radius of a sphere or the difference between two points, they have to be interpreted as millimeters. With difference in direction is meant the angle between the two vectors representing the directions. 9.4.1 Humerus Features Validation Automatic -2.2582 -4.3559 -25.8914 -25.9081 V-Palp 296.9482 299.1959 Greater Tubercle -24.9819 -25.0982 0.0552 -4.5262 V-Palp 305.3458 304.1876 Lateral Epicondyle -32.1017 -32.4460 -2.7128 -2.2030 17.2785 20.2653 31.1213 31.7946 -0.8724 -0.0000 Lesser Tubercle V-Palp Medial Epicondyle Difference 3.0745 4.7269 3.0495 2.9217 V-Palp 150 14.8967 17.6026 Head of humerus 2.3406 2.0054 (Center, radius) 0.5335 -0.1745 301.0679 300.5293 21.8237 22.8569 10.0722 10.0257 -10.4533 -10.5142 11.8674 11.8680 12.1677 12.1996 0.9996 0.9995 -0.0089 -0.0168 -0.0259 -0.0264 Manual (Sphere) Trochlea 0.9506 1.0332 0.0766 (Center, radius, normal) Manual (3D circle) 0.0319 0.4495 (degrees) -20.6330 -22.5090 Capitulum -5.8665 -8.9644 (Center, radius) 19.2525 17.2886 15.1082 11.5859 3.5223 10.7287 11.0881 0.3594 -0.0541 -0.0276 -0.0516 -0.0286 0.9972 0.9992 4.1199 Manual (Sphere) Metaphyseal cylinder (Radius, direction of axis) Manual (Cylinder) 2.0147 (degrees) 151 9.4.2 Scapula Features Validation Automatic 1.1015 0 -3.2008 0 0.1328 0 -53.7605 -54.9436 28.8638 29.3861 135.7329 134.6012 41.9559 39.3312 -30.6843 -31.8560 167.5981 163.8888 45.6540 47.6807 5.7046 3.0465 188.0629 187.3309 43.1938 44.2023 38.4333 34.7641 V-Palp 162.2173 163.8670 Root of Spine -43.1816 -42.5345 -6.7305 -7.5384 107.4537 106.7814 Glenoid Cavity 38.4301 37.4870 (Center, radius, 5.9228 5.9540 139.7882 139.5040 14.8018 15.1726 0.6806 0.6087 -0.0423 -0.0272 0.7314 0.7929 Inferior Angle V-Palp Superior Angle V-Palp Acromial Angle V-Palp Acromial Tip V-Palp Coracoid Tip V-Palp normal) Manual (3D circle) Difference 3.3876 1.7185 4.6927 3.4219 4.1475 1.2343 0.9855 0.3708 5.4910 (degrees) 152 9.4.3 Clavicle Features Validation Automatic Acromioclavicular Joint -78.5317 -80.2292 -6.2244 -1.9944 7.4391 2.3198 Anterior 76.2687 76.0900 Sternoclavicular joint -3.6506 -3.9573 V-Palp 2.5206 -1.1762 Features Validation Automatic Apex of the olecranon 9.7669 3.7962 6.1198 5.4900 262.0236 261.8528 -2.0444 1.4098 -23.3083 -24.4162 240.6457 238.0006 5.8533 5.4375 -0.1225 0.2449 V-Palp 3.3154 0.0000 Ulnar head -1.0597 -0.2380 (Center, radius) -7.3269 -7.8344 10.9629 12.6702 8.8194 9.4121 V-Palp Difference 6.8544 3.7138 9.4.4 Ulna V-Palp Coronoid process V-Palp Styloid process Difference 6.0062 4.4895 3.3615 1.9615 Manual (Sphere) 0.5927 Remark: the difference corresponding to the styloid process is due to the different definitions of its position. See 7.4. 153 9.4.5 Radius Features Validation Automatic -11.1126 -6.2518 -4.2371 -5.8852 V-Palp 6.2712 0.0000 Radial head -2.8305 -0.3822 (Center, radius) -0.5337 1.4061 255.4313 256.8239 10.2944 11.1395 0.8451 9.6693 9.6922 0.0229 Styloid process Difference 8.1038 3.4201 Manual (Sphere) Radial periphery (Radius, direction of axis) 0.0032 Manual (Cylinder) 0.0530 0.9985 0.0049 0.0699 0.9975 1.2384 (degrees) Remark: the difference corresponding to the styloid process is due to the different definitions of its position. See 8.4. As a general remark for the manual validation technique it must be said that it is subjective in its own right. The manual selection of the points can not be the only correct selection of the points, because there is not such thing as the correct selection of points. Therefore the differences between the information of the manual validation and the information of the automatic feature extraction can not be seen as exact differences. It gives only an indication that the automatic feature processing yields good results or very bad results. 154 Chapter 10 Conclusion The objective of doing the automatic extraction of the predefined features of the given bones is met. All features can be extracted and visualized in a correct manner on top of the 3D models of the bones. Although the general task is done, there are some points that have to be looked at in the future. First of all, if the input 3D bone models are of bad quality, the functions will not perform as expected. Requirement for a successful performance are good defined 3D bone models. This issue will always constrain the usefulness of our tool. A very good model can also open the door for implementing new extraction techniques. For example, with a better 3D model, the curvature calculation will yield better usable results which on their turn can be exploited to define new extraction techniques. In this way it could be made possible to segment the bone in its most prominent parts. Another point of discussion is the absence of clear, well defined, unified definitions of the positions of the features. [1] tries to present a solution but yet the given definitions are not very exact in such a manner that only one predefined point can be the wanted feature. On top of this arises the question whether we have to take into account specified positions for a feature or a region of points. For example, the greater tubercle of the humerus is extracted as one point. But strictly spoken, the greater tubercle is an entire bony eminence on the proximal part of the humerus. Shouldn’t the feature ‘greater tubercle’ be defined as a representation of all the points that make up this tubercle? This is certainly an issue to be studied in subsequent implementations of automatic feature extractions. Chapter 9 has given a summary of the validation techniques at hand. It was obvious that this validation is subjective. First of all using with V-Palp, we only have one set of bones at our disposal and secondly the manual process is dependent on the manual selection of the points, which will not yield exact correct results. A possible approach 155 for validating can be done by combining the results of a virtual palpation process by an expert with the results of the automatic feature palpation by our tool. If this can be done with a large number of bones of a great variety, and the statistical analysis yields that there is an accordance between the two sets of extracted features, the conclusion can be made that the automatic feature extraction, as worked out here, is a reliable process that can carry out the task on its own. 156 Appendix A: Anatomical Nomenclature Listed below are general anatomical terms and their meanings that will help understand those parts of the work that deal with the anatomical side. Anatomical Terms Medial Lateral Proximal Distal Inferior Superior Anterior Posterior Dorsal Ventral Direction Towards the midline of the body Away from the midline of the body Towards a reference point (the joint) Away from a reference point (the joint) Lower or below Upper or above Towards the front Towards the back Posterior Anterior Medical professionals often refer to sections of the body in terms of anatomical planes (flat surfaces). These planes are imaginary lines – vertical or horizontal – drawn through an upright body. The terms are used to describe and localize a specific body part. Coronal Plane or Frontal Plane Sagittal Plane or Lateral Plane Axial Plane or Transverse Plane 157 Appendix B Matlab® code First it will be suitable to give some general remarks involving the code. 1. In many occasions we used the Matlab® function find. This function is used to select a part of an array or a matrix that fulfills a certain condition. For example: ind = find(q > 0). q is an array of integers. find will return as ind an array of indices that will refer to the positions in q where the element is bigger than 0. So if q is [1, 0, -1, 10], the function will return as ind [1, 4]. 2. The transformations, linear translations and rotations, are executed on the patch representation of the 3D model. This is done not to lose the connectivity information they hold. It is possible to do the transformations on the list representation of the 3D model, but there is not a unified way to go from a list representation to the patch representation. This method of working will become clear in the orientation functions. After the transformation of the patch representation you have to make sure the list presentation is up to date with the patch representation if you want to do processings with the list representation. In this manner at all times there are two consistent representations of a 3D model, the patch and the list representation. 3. We have used several functions that we downloaded from the Matlab file exchange centre. If this is the case, this will be specified in the code. The code of these downloaded functions is not contained in this Appendix. 4. The rotations used in the orientation functions of the 5 bone types must also be performed on the matrix of the normals to the facets of the 3D model. In this manner the normals keep being consistent with the rotated facets of the 3D model. 5. In the fitting functions we tried to make the number of input points smaller than a given number. This because the functions responsible of carrying out the fittings are very sensitive to the size of the input lists of points. This is done by passing by a constant number of points of the input lists in order to get the size of the inputl list smaller or equal to a given number. 158 6. The code is rendered to black and white. If you open it in Matlab® colors make all more clear. Lines that are preceded by ‘%’ represent in code comments. B.1 The GUI and functions B.1.1 Gui.m We decided to not print the M-file associated with the GUI as a whole, because it takes in 80 pages. As a compensation we will print some parts. The opening function: % --- Executes just before Gui is made visible. function Gui_OpeningFcn(hObject, eventdata, handles, varargin) % This function has no output args, see OutputFcn. % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to Gui (see VARARGIN) %manipulation of the components of the GUI set(handles.pushOpen,'Visible','on'); set(handles.panelBones,'Visible','off'); set(handles.panelHum,'Visible','off'); set(handles.panelScap,'Visible','off'); set(handles.panelClav,'Visible','off'); set(handles.panelUlna,'Visible','off'); set(handles.panelRad,'Visible','off'); set(handles.textLocal,'Visible','off'); set(handles.textGlobal,'Visible','off'); set(handles.panelHumCoord,'Visible','off'); set(handles.panelScapCoord,'Visible','off'); set(handles.panelClavCoord,'Visible','off'); set(handles.panelUlnaCoord,'Visible','off'); set(handles.panelRadCoord,'Visible','off'); set(handles.panelHumCoord2,'Visible','off'); set(handles.panelScapCoord2,'Visible','off'); set(handles.panelClavCoord2,'Visible','off'); set(handles.panelUlnaCoord2,'Visible','off'); set(handles.panelRadCoord2,'Visible','off'); set(handles.panelCurv,'Visible','off'); % Choose default command line output for Gui 159 handles.output = hObject; % Update handles structure A part of the function associated with the push button ‘Read in 3D model’. The other part will carry out manipulation of the GUI -> make all components invisible. … %open a standard dialog box to select an Stl file [filename, pathname] = uigetfile({'*.stl','binary stl files';'*.*','all files'},'Choose your file'); if isequal(filename,0) disp('Read in operation Cancelled'); else %reading in the Stl file in a patch representation of the 3D model %MATLAB FILE EXCHANGE CENTRE [x,y,z,n] = stlread2(filename); %stocking for further use handles.x = x; handles.y = y; handles.z = z; handles.n = n; handles.X = unique([x(:) y(:) z(:)],'rows'); %make the panel 'Bones' invisible set(handles.panelBones,'Visible','on'); end … The variable handles is a structure where one can stock variables to hand over to other callbacks. Every component of the GUI is contained in this structure, so each component is reachable throughout the entire M-file corresponding to the GUI. The following function is the callback of the radiobutton that says ‘Humerus’. The callbacks of the other radiobuttons in the panel ‘Bones’ are similar to this one. % --- Executes on button press in radioHum. function radioHum_Callback(hObject, eventdata, handles) % hObject handle to radioHum (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radioHum %the next part is executed as the radiobutton 'Humerus' is toggled on. if get(hObject,'Value')==get(hObject,'Max') x = handles.x; y = handles.y; z = handles.z; n = handles.n; %orientation of the humerus [x, y, z, n, handles.e1, handles.e2, handles.lowest, side] = ... orientHumerus(x,y,z,n); %switch to the list representation of the read in 3D model X = unique([x(:) y(:) z(:)],'rows'); 160 %extraction of the head of the humerus [handles.c handles.r_head] = headHum(X, handles.e1, handles.e2); %extraction of the greater tubercle [handles.greater] = greaterTubHum(X, handles.c, handles.r_head); %extraction of the lesser tubercle [handles.lesser] = lesserTubHum(X, handles.c, handles.r_head); %extraction of the trochlea [handles.cTroch, handles.rTroch, handles.normal] = ... trochHum(X, handles.lowest, handles.e1); %extraction of the capitulum [handles.cCap handles.rCap] = capitHum(X, handles.e2); %extraction of the metaphyseal cylinder [handles.radiusCylHum, handles.underHum, handles.upperHum] = ... cylHum(X, handles.c, handles.r_head); %extraction of the topmost point [handles.topHum] = topHum(X); handles.x handles.y handles.z handles.n = = = = x; y; z; n; %necessary to do the final transformation to the base proposed in [8] [handles.XDir handles.YDir handles.ZDir] = ... ashum(handles.c,handles.e1,handles.e2); GH = handles.c; if side == 1 %if it is a left bone hFig1 = figure(1); set(hFig1,'DeleteFcn',{@closeProcedure, handles}); set(hFig1,'Name','3D model') figPos = get(hFig1,'Position'); set(hFig1,'Position',[660,161,figPos(3),532]); light('Position',[1,1,300],'Style','infinite'); hp = patch(-x,y,z,'w','EdgeAlpha',0,'FaceAlpha',0.8);axis equal;axis tight; grid on; % mirroring handles.c(1) = -handles.c(1); handles.e1(1) = -handles.e1(1); handles.e2(1) = -handles.e2(1); handles.greater(1) = -handles.greater(1); handles.lesser(1) = -handles.lesser(1); handles.cTroch(1) = -handles.cTroch(1); handles.normal(1) = -handles.normal(1); handles.cCap(1) = -handles.cCap(1); handles.topHum(1) = -handles.topHum(1); handles.underHum(1) = -handles.underHum(1); handles.upperHum(1) = -handles.upperHum(1); handles.XDir(1) = -handles.XDir(1); handles.YDir(1) = -handles.YDir(1); 161 handles.ZDir(1) = -handles.ZDir(1); GH(1) = -GH(1); else hFig1 = figure(1); set(hFig1,'Name','3D model') set(hFig1,'DeleteFcn',{@closeProcedure, handles}); figPos = get(hFig1,'Position'); set(hFig1,'Position',[660,161,figPos(3),532]); light('Position',[1,1,300],'Style','infinite'); hp = patch(x,y,z,'w','EdgeAlpha',0,'FaceAlpha',0.8);axis equal;axis tight; grid on; end %drawing of the arrows making up the local coordinate system hold on xlabel('Xglobal') ylabel('Yglobal') zlabel('Zglobal') endpoints = [GH + 2 * handles.r_head * handles.XDir;... GH + 2 * handles.r_head * handles.YDir;GH + 2 * handles.r_head ... * handles.ZDir]; arrow3([GH;GH;GH],endpoints, 'b', .5,1,.5) text(GH(1), GH(2),GH(3)- 10,'\bfGH') text(endpoints(1,1),endpoints(1,2),endpoints(1,3),'\bfXh','color','b') text(endpoints(2,1),endpoints(2,2),endpoints(2,3),'\bfYh','color','b') text(endpoints(3,1),endpoints(3,2),endpoints(3,3),'\bfZh','color','b') set(hp, 'EdgeColor','none') axis equal; axis tight; axis auto hold off %To calculate the definition of the global coordinate system in terms %of the new coordinate system. handles.Transf = ... [handles.XDir; handles.YDir; handles.ZDir]'\[1 0 0;0 1 0;0 0 1]; %Manipulate the GUI set(handles.panelBones,'Visible','off'); set(handles.panelHum,'Visible','on'); set(handles.panelHumCoord,'Visible','on'); set(handles.panelHumCoord2,'Visible','on'); set(handles.panelScap,'Visible','off'); set(handles.panelClav,'Visible','off'); set(handles.panelUlna,'Visible','off'); set(handles.panelRad,'Visible','off'); set(handles.textLocal,'Visible','on'); 162 set(handles.textGlobal,'Visible','on'); set(handles.panelCurv,'Visible','on'); end handles.output = hObject; guidata(hObject, handles); …. The following function is the callback of the radiobutton that says ‘Capitulum’. The callbacks of the other radiobuttons in the panel ‘Features humerus’ are similar to this one. function radioHumCap_Callback(hObject, eventdata, handles) % hObject handle to radioHumCent (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of radioHumCent %The center and the radius of the sphere estimation of the capitulum. Those %were calculated in the callback associated with the humerus. cCap = handles.cCap; rCap = handles.rCap; %if the radiobutton toggled on if get(hObject,'Value')==get(hObject,'Max') figure(1) hold on %the drawing %drawSphere -> Matlab file exchange centre. %used to draw a sphere with a given center and a given radius. handles.hCapit = drawSphere(cCap(1),cCap(2),cCap(3), rCap, ... 'FaceColor','r','EdgeAlpha',0,'FaceAlpha',1); %calculating the coordinates in the local coordinate system. c_coord = handles.Transf*handles.c'; cap_coord = handles.Transf*cCap' - c_coord; %manipulation of the textfields referring to the local coordinate %system set(handles.textcaphumx,'String',num2str(cap_coord(1),'%11.4f')); set(handles.textcaphumy,'String',num2str(cap_coord(2),'%11.4f')); set(handles.textcaphumz,'String',num2str(cap_coord(3),'%11.4f')); set(handles.textcaprad,'String',num2str(rCap,'%11.4f')); %manipulation of the textfields referring to the global coordinate %system set(handles.textcaphumx2,'String',num2str(cCap(1),'%11.4f')); set(handles.textcaphumy2,'String',num2str(cCap(2),'%11.4f')); set(handles.textcaphumz2,'String',num2str(cCap(3),'%11.4f')); set(handles.textcaprad2,'String',num2str(rCap,'%11.4f')); 163 %if the radiobutton toggled off else figure(1) %the deletion of the drawing of the sphere delete(handles.hCapit); %manipulation of the textfields referring to the local coordinate %system set(handles.textcaphumx,'String',''); set(handles.textcaphumy,'String',''); set(handles.textcaphumz,'String',''); set(handles.textcaprad,'String',''); %manipulation of the textfields referring to the global coordinate %system set(handles.textcaphumx2,'String',''); set(handles.textcaphumy2,'String',''); set(handles.textcaphumz2,'String',''); set(handles.textcaprad2,'String',''); end handles.output = hObject; guidata(hObject, handles); The following code is associated with the popupmenu where you can define the neighborhood with which to calculate the mean curvature. % --- Executes on selection change in popupCurv. function popupCurv_Callback(hObject, eventdata, handles) % hObject handle to popupCurv (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: contents = get(hObject,'String') returns popupCurv contents as cell array % contents{get(hObject,'Value')} returns selected item from popupCurv % --- Executes during object creation, after setting all properties. %get the value chosen in the popupmenu -> neighborhood contents = get(hObject,'String'); neighborhood = str2num(contents{get(hObject,'Value')}); %calculate the mean curvature -> appendix D handles.cfinal = calculateCurvature(handles.x,handles.y,handles.z,handles.n,neighborhood); %open a second window hFig2 = figure(2); set(hFig2,'Name','Curvature information') set(hFig2,'DeleteFcn',{@closeProcedure, handles}); %visualization of the 3D model as a function of the curvature info. hpc = patch(handles.x, handles.y, handles.z, handles.cfinal,'FaceColor','flat');axis equal; axis tight; 164 set(hpc, 'Edgecolor','none'); %visualization of the colorbar colorbar B.1.2 The functions responsible for drawing the local coordinate systems Like in the previous paragraph we will not show each function for each bone type to save space. The method is the same, only the variables are different. 3.3 gives for each bone type the construction of its local coordinate system. The next part of code corresponds to the humerus (arbitrarily chosen). function [xh yh zh] = ashum(GH,EM,EL) %ASHUM CALCULATES THE 3 COMPONENTS THAT SPECIFY THE LOCAL COORDINATE SYSTEM %FOR THE HUMERUS % %OUTPUT: % xh is a 1-by-3 array of coordinates of the first component of the local % coordinate system % % yh is a 1-by-3 array of coordinates of the second component of the local % coordinate system % % zh is a 1-by-3 array of coordinates of the third component of the local % coordinate system % %INPUT: % GH is a 1-by-3 array of the coordinates of the center of the head of the % oriented right humerus. % % EM is a 1-by-3 array of the coordinates of the medial epicondyle of the % oriented right humerus humerus. % % EL is a 1-by-3 array of the coordinates of the lateral epicondyle of the % oriented right humerus. % --------% % author : Bart Coppieters % % %Calculation of the midpoint between the epicondyles H_mid=(EM+EL)/2; %Definition of axis yh, the 2nd component of the local coordinate system. yh = (GH-H_mid) / norm(GH-H_mid); %Definition of axis xh, the 1st component of the local coordinate system. xh = cross(yh,EL-EM); xh = xh/norm(xh); %Definition of axis zh, the 3rd component of the local coordinate system. zh = cross(xh,yh); The helping point for the ulna coordinate system construction. 165 function [auxUlna] = auxUlna(X) %AUXULNA CALCULATES THE POSITION OF MOST ANTERIOR POINT OF THE OLECRANON. % %OUTPUT: % % auxUlna is a 1-by-3 array of the coordinates of the most anterior % point of the olecranon of an oriented right ulna. % %INPUT: % % X is a m-by-3 array of the coordinates of the m datapoints that % constitute to the right oriented ulna. % --------% % author : Bart Coppieters %the most proximal 5% of the bone with respect to the length of it. upperX = X( find( X(:,3) > min(X(:,3)) + ... 0.95 * (max(X(:,3)) - min(X(:,3))) ),:); %auxUlna is the point of upperX with the smallest y-value. auxUlna = upperX( find( upperX(:,2) == min(upperX(:,2))),:); The helping point for the radius coordinate system construction. function [notchRad] = ulnarNotchRadius(X) %ULNARNOTCHRADIUS CALCULATES THE POSITION OF THE CENTER OF THE ULNAR NOTCH %OF THE ORIENTED RIGHT RADIUS. %OUTPUT: % % notchRad is a 1-by-3 array of the coordinates of center of the ulnar % notch of the oriented right radius. % %INPUT: % % X is a m-by-3 array of the coordinates of the m datapoints that % constitute the right oriented radius. % --------% % author : Bart Coppieters underXposy = X( find( X(:,3) < min(X(:,3)) + ... 0.1 * (max(X(:,3)) - min(X(:,3))) & X(:,2) > 0),:); underXnegy = X( find( X(:,3) < min(X(:,3)) + ... 0.1 * (max(X(:,3)) - min(X(:,3))) & X(:,2) < 0),:); notchM = underXposy( find( underXposy(:,1) == max(underXposy(:,1))),:); notchA = underXnegy( find( underXnegy(:,1) == max(underXnegy(:,1))),:); notchRad = (notchM + notchA)/2; 166 B.2 Humerus The functions associated with the humerus. B.2.1 orientHumerus.m function [x, y, z, n, e1, e2, lowest, side] = orientHumerus(x,y,z,n) %ORIENTHUMERUS WILL ORIENT A 3D-MODEL OF A HUMERUS FOR FURTHER PROCESSING, %I.E. FEATURE EXTRACTION. % %OUTPUT: % x, y and z are 3-by-N matrices that together form the oriented patch % object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the oriented patch object. % % e1 is a 1-by-3 array of the coordinates of the medial epicondyle. % % e2 is a 1-by-3 array of the coordinates of the lateral epicondyle. % % lowest is a 1-by-3 array of the coordinates of point with the smallest % z-value. Therefore this is the lowest point of the 3D model. % % side is a scalar that serves as a logical to state whether it is a left % (side == 1) or a right (side == 0) bone. % %INPUT: % x, y and z are 3-by-N matrices that together form the original patch % object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the original patch object. % % % % % % % % % X with N the number of facets of the 3D bone model. --------author : Bart Coppieters Step 1 the list representation of the input patch representation of the entire humerus. = unique([x(:) y(:) z(:)],'rows'); % Step 2 [a b] = findRefP(X); %selfwritten function. Look there for more info. % Step 3 %Translation [x,y,z,bb] = translation(x,y,z,a,b); %selfwritten function. % Does a translation so that the point a will go to the point (0,0,0). 167 a = [0 0 0]; % Step 4 %The following lines are written to do a sequence of rotations so to %rotate point bb on to the z-axis. This will result in the fact that the %line ab will coincide with the z-axis. % First rotation hoekz = -atan(bb(2)/bb(1)); %angle over which to rotate around the z-axis. [x,y,z, rotz] = rotation_Z_axe(x,y,z,hoekz); %selfwritten function. n = rotz * n; %important to get n corresponding to the rotated patch % representation bb = rotz * bb'; %rotated version of bb. % Second rotation hoeky = -atan(bb(1)/bb(3)); %angle [x, y, z, roty] = rotation_Y_axe(x, y, z, hoeky); n = roty * n; bb = roty * bb; if bb(3) < 0 %if the point that corresponds to the head of the humerus is %below the z=0 plane, i.e. z-coordinate < 0, then rotate around the %X-axis for 180 degrees. [x,y,z,rotx] = rotation_X_axe(x,y,z,pi); %Does a rotation around the x-axis of 180 degrees. n = rotx * n; end %list representation of the current orientied humerus. X = unique([x(:) y(:) z(:)],'rows'); % Step 5 %selfwritten function. To calculate the positions of the 2 epicondyles of %the distal part of the humerus. [e1 e2] = epicondylesHum(X); % Step 6 %The following 3 lines look for the distances between the epicondyles and %the lowest (where the z-coordinate is the smallest) point of the bone. The %epicondyle the closest to this lowest point is also on the medial side of %the bone. %calculate lowest point of the bone lowest = X(find(X(:,3) == min(X(:,3))),:); n1_1 = norm(e1 - lowest); %the distance (norm) between e1 and lowest n2_1 = norm(e2 - lowest); %the distance (norm) between e2 and lowest % Step 7 %In the following code we will rotate the whole bone around the z-axis so %that the medial epicondyle will lie on the positive part of the x-axis. %This regardless to the fact whether it is a right or a left bone. rot_z2 = 1; % definition of a rotation matrix for further purpose. if (n1_1 < n2_1) % if this condition holds, e1 is the medial epicondyle hoekz = -atan(e1(2)/e1(1)); %use e1 to get the necessary rotation-angle. % The rotation 168 [x,y,z,rot_z1] = rotation_Z_axe(x,y,z,hoekz); n = rot_z1 * n; e = rot_z1 * e1'; if e(1) < 0 %If its x-coordinate is negative, rotate around the z-axis %over an angle of pi rad. [x,y,z,rot_z2] = rotation_Z_axe(x,y,z,pi); n = rot_z2 * n; end else %in this alternative e2 is the medial epicondyle, the following %procedure is the same as above for e1 hoekz=-atan(e2(2)/e2(1)); [x,y,z,rot_z1] = rotation_Z_axe(x,y,z,hoekz); n = rot_z1 * n; e = rot_z1 * e2'; if e(1) < 0 [x,y,z,rot_z2] = rotation_Z_axe(x,y,z,pi); n = rot_z2 * n; end % this is to rename the medial epicondyle e1 and the lateral e2. A % question of uniformity. epi = e2; e2 = e1; e1 = epi; end %The following rotations use the created rotation matrices to rotate points %that will be visualized in the plot of the bone, i.e. e1(medial %epicondyle), e2(lateral epicondyle) and the lowest point of the bone. e1 = (rot_z2 * rot_z1 * e1')'; e2 = (rot_z2 * rot_z1 * e2')'; lowest = (rot_z2 * rot_z1 * lowest')'; %The following lines will decide upon the fact that the bone is of the %right or the left arm. vector = cross([e1(1) e1(2) 0],[lowest(1) lowest(2) 0]); if vector(3)<0 side = 0; %side = right side. else side = 1; %side = left side. y = -y; e1(2) = -e1(2); e2(2) = -e2(2); lowest(2) = -lowest(2); n(2,:) = -n(2,:); end 169 B.2.2 headHum.m function [c r] = headHum(X, e1, e2) %HEADHUM CALCULATES THE POSITION OF THE CENTER OF THE HEAD OF THE HUMERUS %AND ITS RADIUS. % %OUTPUT: % c is a 1-by-3 array of the coordinates of the center of the head. % % r (scalar) is the radius of the fitted head of the humerus. % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the right oriented humerus. % % e1 and e2 are 1-by-3 array's of the coordinates of respectively the % lateral and the medial epicondyle. (as defined by orientHumerus) % % % --------% % author : Bart Coppieters % % % Step 1 % Define the uppermost proximal part of the humerus max_z = max(X(:,3)); min_z = min(X(:,3)); searchX_rough = X(find(X(:,3) > 0.87 * (max_z - min_z) + min_z), :); meanX = mean(searchX_rough); % Step 2 % Do a fitting to sphere of the list searchX_rough. % The method to restrict the length of the input list for the fitting % function to 2000 is to pass over in a controlled, uniform way a fixed % amount of points in the original list searchX_rough. The number of points % to pass over is calculated and given by int_rough. int_rough = ceil(length(searchX_rough)/2000); searchX_rough = searchX_rough(1:int_rough:end,:); %MATLAB FILE EXCHANGE CENTRE %It takes a list of points and some %initialization parameters as input and produces the center and the radius %of the sphere that is the result of the least squares fitting method %executed to the input list of points, searchX_rough. More info is found in %the function itself. [x0n, rn] = lssphere(searchX_rough, meanX', 20, 0.1, 0.1); c_rough = x0n'; %rough center of the head r_rough = rn; %rough radius of the head % Step 3 170 % In the following we will select the points needed in a second least % squares fitting for the fitting of the real head of the humerus. %Definition of the transepicondylar transepis = e1 - e2; transepis = transepis/norm(transepis); %Definition of dir, the direction of the line P. %rotated 2 times, one time around the y-axis and %to respectively present the inclination and the %for the angles are slightly different from [13] rot_z = rotation_Z_axe(deg2rad(18)); rot_y = rotation_Y_axe(deg2rad(-30)); dir = (rot_z* rot_y * transepis')'; The transepicondylar is one time around the z-axis retroversion. The values to optimalize the result. %the orthogonal direction to the plane that will cut the head %to select input points for the second call to lssphere. dir = dir/norm(dir); % Step 4 (and part of step 3) % iterative process of finding the center. c_old = c_rough; r_old = r_rough; flag = 1; count = 0; while flag == 1; % threshold is the point in 3D that is at a distance of 30% the old % radius away from the old center of the head in the direction of % dirvect. threshold = c_old + 0.30 * r_old * dir; % in the following will be seen at the points above (in the direction % of dir) of the plane, these will be used as input for lssphere. %Create vectors for each point in searchX_rough with start point %threshold and ending point that particular point that is considered. pCent = repmat(threshold, [length(searchX_rough),1]); V = searchX_rough - pCent; searchX_fine = []; %initialize empty list for i = 1:size(V,1) proj = dot(V(i,:), dir); if proj > 0 %if this condition holds, the angle between dir drawn in %threshold and the vector associated with a point of %searchX_rough is smaller than 90 degrees, therefore the point %lies on the same side of dir drawn in threshold and thus the %point lies above the defined plane in threshold searchX_fine = [searchX_fine; searchX_rough(i,:)]; end end % The method to restrict the length of the input list for the fitting % function to 2000 is to pass over in a controlled, uniform way a fixed % amount of points in the original list searchX_fine. The number of 171 % points to pass over is calculated and given by int_fine. int_fine = ceil(length(searchX_fine)/2000); searchX_fine = searchX_fine(1:int_fine:length(searchX_fine),:); %MATLAB FILE EXCHANGE CENTRE %the function doing the fitting to sphere of the points in searchX_fine. [x0n2, rn2] = lssphere(searchX_fine, x0n, rn, 0.1, 0.1); c_new = x0n2'; %the real center r_new = rn2; %the real radius diff_new = norm(c_new - c_old); %Check whether the newly defined center has a distance to the old %center of more than 0.25, if this holds do another fitting process. if diff_new > 0.25 && count < 30 flag = 1; c_old = c_new; r_old = r_new; count = count + 1; else flag = 0; end end c = c_new; %the real center r = r_new; %the real radius B.2.3 epicondylesHum.m function [e1 e2] = epicondylesHum(X) %EPICONDYLES GIVES BACK THE 2 EPICONDYLES OF A HUMERUS ORIENTED ALONG THE %POSITIVE Z-AXIS % %OUTPUT: % e1 and e2 are 1-by-3 array's of the coordinates of the epicondyles. %INPUT: % X is a m-by-3 matrix of the coordinates of the m points that constitute % the humerus. % % --------% % author : Bart Coppieters % % max_z = max(X(:,3)); % highest point of oriented humerus min_z = min(X(:,3)); % lowest point of oriented humerus threshold = 0.15 * (max_z - min_z) + min_z; % a value of z-coordinate that will be the upper boundary for the points to % involve in the following step. 172 searchX = X(find(X(:,3) < threshold), :); %points below 'threshold' [coeff, score] = princomp(searchX); %principal component analysis e1 = searchX(find(min(score(:,1)) == score(:,1)),:); % the point with the % smallest score (projection) on the principal axis of the data ('searchX') e2 = searchX(find(max(score(:,1)) == score(:,1)),:); % the point with the % biggest score (projection) on the principal axis of the data ('searchX') B.2.4 greaterTubHum.m function [greater] = greaterTubHum(X, c, r) %GREATERTUBHUM CALCULATES THE POSITION OF THE GREATER TUBERCLE OF THE %ORIENTED HUMERUS. % %OUTPUT: % greater is a 1-by-3 array of the coordinates of the greater tubercle % % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute oriented right humerus. % % c is a 1-by-3 array of the coordinates of the center of the real % head of the humerus (output of headHum.m) % % r represents the radius of the real head of the humerus (output of % headHum.m) % --------% % author : Bart Coppieters % Step 1: define a searchspace for the greater tubercle. max_z = max(X(:,3)); min_z = min(X(:,3)); max_y = max(X(:,2)); min_y = min(X(:,2)); searchX = X(find( (X(:,1) < 0) & (X(:,2) < max_y*0.3 ) & ... (X(:,2) > min_y*0.3) & (X(:,3) > 0.93 * (max_z - min_z) + min_z) ), :); %Step 2 %select out of the list of points searchX those points with a distance %to the middlepoint of the real head of the humerus that is bigger than %the radius of this head. Make a new list of these points and name it %list1. list1 = []; %initialize empty list for k=1:length(searchX) if norm(searchX(k,:) - c) > r list1 = [list1; searchX(k,:)]; end end %Step 3 %construct a list of dimension (length(list1), 1) with for each point of 173 %'list1' the orthogonal distance of this point to the sphere representing %the real head. list2 = []; %initialize empty list for k=1:length(list1) distance = norm(list1(k,:) - c) - r; %calculates the orthogonal distance of a point lying outside of the %sphere to the sphere. list2 = [list2; distance]; end %Step 4 %to get %to the greater %of the the greater tubercle: look at the point with the biggest distance sphere. = list1(find(list2(:,1) == max(list2(:,1))), :); %the coordinates point with the biggest distance to the sphere. B.2.5 lesserTubHum function [lesser] = lesserTubHum(X, c, r) %LESSERTUBHUM CALCULATES THE POSITION OF THE LESSER TUBERCLE OF THE %ORIENTED RIGHT HUMERUS. % %OUTPUT: % lesser is a 1-by-3 array of the coordinates of the greater tubercle % % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute humerus. % % c is a 1-by-3 array of the coordinates of the center of the real % head of the humerus (output of headHum.m) % % r represents the radius of the real head of the humerus (output of % headHum.m) % % % --------% % author : Bart Coppieters %Step 1 % define a searchspace for the lesser tubercle. max_z = max(X(:,3)); min_z = min(X(:,3)); max_x = max(X(:,1)); min_x = min(X(:,1)); searchX = X(find( (X(:,1) < max_x * 0.3) & (X(:,1) > min_x * 0.4 ) & ... (X(:,2) < 0) & (X(:,3) > 0.90 * (max_z - min_z) + min_z) ), :); %Step 2 %select out of the list of points searchX those points with a distance %to the middlepoint of the real head of the humerus that is bigger than 174 %the radius of this head. Make a new list of these points and name it %list1. list1 = []; %initialize empty list for k=1:length(searchX) if norm(searchX(k,:) - c) > r list1 = [list1; searchX(k,:)]; end end %Step 3 %construct a list of dimension (length(list1), 1) with for each point of %'list1' the orthogonal distance of this point to the sphere representing %the real head. list2 = []; %initialize empty list for k=1:length(list1) distance = norm(list1(k,:) - c) - r; %calculates the orthogonal distance of a point lying outside of the %sphere to the sphere. list2 = [list2; distance]; end %Step 4 %to get the greater tubercle: look at the point with the biggest distance %to the sphere. lesser = list1(find(list2(:,1) == max(list2(:,1))), :); %the coordinates of %the point with the biggest distance to the sphere. B.2.6 trochHum.m function [c,radius,normal] = trochHum(X, lowest, med_e) %TROCHHUM DEFINES THE 3D CIRCLE APPROXIMATING THE TROCHLEA OF THE ORIENTED %RIGHT HUMERUS. % %OUTPUT: % c is a 1-by-3 array of the coordinates of the center of the 3D circle % representing the trochlea % % radius represents the radius of the calculated 3D circle % % normal is a 1-by-3 array that represents the vector perpendicular to % the plane of the 3D circle %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the humerus. % % lowest is a 1-by-3 array of the coordinates of the most distal point of % the humerus. (output of orientHumerus.m) % % med_e is a 1-by-3 array of the coordinates of the medial epicondyle % (output of orientHumerus.m) % --------% % author : Bart Coppieters 175 %Step 1 %Construct a searchspace of points to hand over to a function carrying out %the fitting of these points to the searched 3D circle. searchX = []; for i = 1:length(X) if (X(i,3) < med_e(3) ) && (X(i,1) > lowest(1) - 1) && (X(i,1) <... lowest(1) + 1) searchX = [searchX; X(i,:)]; end end %Step 2 %Do the first fitting process. %Initializations of the input arguments for the fitting function %ls3dcircle. x0 = lowest; x0(3) = med_e(3)/2; r0 = x0(3)/2; a0 = [1 0 0]; tolp = 0.1; tolg = 0.1; %MATLAB FILE EXCHANGE CENTRE %It takes a list of points and some %initialization parameters as input and produces the center, the radius and %the normal of the 3D circle that is the result of the least squares %fitting method of the input list of points. [x0n, an, rn] = ls3dcircle(searchX, x0', a0', r0, tolp, tolg); c = x0n'; radius = rn; normal = an'; %Step 3 %Compute the euclidean distance between every point of searchX and the %center of the circle of the first fitting process. d_searchX_c = sqrt(sum((searchX - repmat(c,length(searchX),1)).^2,2)); %Step 4 %Remove the initial outliers of the list searchX. %Select only those points of SearchX that met certain conditions involving %the standard deviation of the set of distances. mu = mean(d_searchX_c); sigma = std(d_searchX_c); ind = []; for i = 1:length(searchX) if d_searchX_c(i) > radius - 3*sigma && d_searchX_c(i) < ... radius + 3*sigma && searchX(i,3) < c(3) ind = [ind; i]; end end searchX = searchX(ind,:); %Step 5 176 %Second fitting process. %Use the reduced list searchX as the input list to the function carrying %out the fitting to a 3D circle, ls3dcircle. x0 = mean(searchX); a0 = [1 0 0]; tolp = 0.1; tolg = 0.1; %MATLAB FILE EXCHANGE CENTRE [x0n, an, rn] = ls3dcircle(searchX, x0', a0', r0, tolp, tolg); c = x0n'; radius = rn; normal = an'; B.2.7 capitHum.m function [c r] = capitHum(X, lat_e) %CAPITHUM DEFINES THE SPHERE THAT APPROXIMATES THE CAPITULUM OF THE %ORIENTED RIGHT HUMERUS. % %OUTPUT: % c is a 1-by-3 array of the coordinates of the center of the sphere % representing the approximated capitulum. % % r represents the radius of the calculated sphere representing the % approximated capitulum %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the humerus. % % lat_e is a 1-by-3 array of the coordinates of the lateral epicondyle % (output of orientHumerus.m) % --------% % author : Bart Coppieters %Step 1 %Construct a searchspace of points to hand over to a function carrying out %the fitting of this points to the searched sphere. X = X(find(X(:,1) < lat_e(1)/2 & X(:,3) < lat_e(3)),:);%initial selection %refinement of the initial selection proj = zeros(length(X),1); for i = 1:length(X) proj(i) = dot(X(i,:), [0; -1; -1]); end pFit = X(find(proj == max(proj)),:); %the point with the biggest %projection value %Pick only those points that lie a small distance away from p_low. This %small distance is defined relatively to the size of the bone. 177 fitX = []; for i = 1:length(X) if norm(pFit - X(i,:)) < norm(pFit - lat_e)/4 fitX = [fitX; X(i,:)]; end end %Step 2 %Do the fitting process. %Make sure the input list for lssphere is smaller than or equal to 1000. int_fit = ceil(length(fitX)/1000); fitX = fitX(1:int_fit:end,:); %MATLAB FILE EXCHANGE CENTRE %It takes a list of points and some %initialization parameters as input and produces the center and the radius %of the sphere that is the result of the least squares fitting method %of the input list of points. [x0n, rn] = lssphere(fitX, mean(fitX)', 10, 0.1, 0.1); c = x0n'; r = rn; B.2.8 topHum.m function [top] = topHum(X) %TOPHUM CALCULATES THE POSITION OF THE TOPMOST POINT OF THE ORIENTED %HUMERUS % %OUTPUT: % top is a 1-by-3 array of the coordinates of the uppermost point on % the 3D model of the oriented humerus. % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the oriented humerus. % --------% % author : Bart Coppieters % % top = X(find(X(:,3) == max(X(:,3))),:); B.2.9 cylHum.m function [radius, underP, upperP] = cylHum(X, cHead, rHead) %CYLHUM CALCULATES THE RADIUS, THE UNDERMOST AND THE UPPERMOST POINT THAT %DEFINE THE METAPHYSEAL CYLINDER % %OUTPUT: % radius represents the radius of the cylinder. % 178 % underP is a 1-by-3 array of the coordinates of the undermost point on % the axis of the determined cylinder % % upperP is a 1-by-3 array of the coordinates of the uppermost point on % the axis of the determined cylinder %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the oriented right humerus. % % cHead is a 1-by-3 array of the coordinates of the center of the real % head of the humerus. (output of headHum.m) % % rHead represents the radius of the real head of the humerus (output of % headHum.m) % --------% % author : Bart Coppieters % %Step 1 %Define the under boundary for z-values. %look to the bone in the coronal plane, i.e. the x-z-plane. Thus setting %each y coordinate to 0. points_in_y_plane = [X(:,1) , zeros(length(X),1), X(:,3)]; %conserve only those points of the lateral half of the bone. points_in_y_plane = points_in_y_plane(find(points_in_y_plane(:,1) < 0),:); meanX = mean(X); %discard the distal end of the list points_in_y_plane. points_in_y_plane = points_in_y_plane(find(points_in_y_plane(:,3) >... meanX(3)*0.9),:); %Sort these points with respect to ascending z-value. points_in_y_plane = sortrows(points_in_y_plane,3); %In the following step we will look at the points of points_in_y_plane that %have a z-value between begin and eind. We will distribute the points in %function of their z-value in intervals of length 4 starting from begin. %Of each interval we take that point with the biggest negative %x-offset(smallest x-value). This will give us the contour of the lateral %edge in the x-z plane. begin = floor(meanX(3)*0.9); eind = floor(max(points_in_y_plane(:,3))); list_of_max = []; for k = begin:8:eind list_temp = []; for j = 1:length(points_in_y_plane) if (points_in_y_plane(j,3) >= k) && (points_in_y_plane(j,3)... < k + 8) list_temp = [list_temp; points_in_y_plane(j,:)]; %the set of points in an interval of length 8. end end maxi = min(list_temp); list_of_max = [list_of_max;k maxi]; %list of minimal x-offsets 179 end %We had to use this technique, because the 3D model is not a continuum of %points. %move a window of size 4 over the list and look when the 4 values of the %window meet a descending_ascending condition. If this condicion is met -> %curve of shaft -> under boundary for the z-values for i = 1:length(list_of_max) - 3 values = zeros(1, 4); values(1) = list_of_max(i, 2); values(2) = list_of_max(i + 1, 2); values(3) = list_of_max(i + 2, 2); values(4) = list_of_max(i + 3, 2); if (values(1) > values(2)) && (values(2) > values(3)) && ... (values(3) < values(4)) under = list_of_max(i+2, 1); %the under boundary value break end end %Step 2 %Define the upper boundary for z-values. upper = cHead(3) - rHead; %the upper boundary value %Step 3 %The fitting to a cylinder. %The points that will be fitted to a cylinder have a z-value between under %and upper. fitX = X(find((X(:,3) > under) & (X(:,3) < upper)),:); %The following function, lscylinder, is downloaded from the Matlab file % exchange centre on the official Matlab site. It takes a list of points % (fitX) and some initialization parameters as input and produces the % midpoint on the axis, the direction and the radius of the fitted % cylinder. %initializations for lscylinder x0 = [0 0 under/2+upper/2]'; a0 = [0 0 1]'; %MATLAB FILE EXCHANGE CENTRE [x0n, an, rn] = lscylinder(fitX, x0, a0, abs(list_of_max(1,2)), 0.1, 0.1); %The axis of the fitted cylinder is defined by its midpoint x0n and its %direction, a0. The radius of the cylinder is rn. radius = rn; % To force underP, an output argument, on the axis of the fitted cylinder. % We can construct the undermost point on the axis of the fitted cylinder % with a z-value of under, the under boundary for z-values. 180 maal1 = (under - x0n(3))/an(3); underP = (x0n + maal1 * an)'; % To force upperP, an output argument, on the axis of the fitted cylinder. % We can construct the uppermost point on the axis of the fitted cylinder % with a z-value of upper, the upper boundary for z-values. maal2 = (upper - x0n(3))/an(3); upperP = (x0n + maal2 * an)'; B.2.10 findRefP.m function [under, upper] = findRefP(X) %FINDREFP GIVES BACK THE 2 EXTREME POINTS ALONG THE %PRINCIPAL AXIS OF THE HUMERUS % %OUTPUT: % under is a 1-by-3 array with the coordinates of the undermost point % upper is a 1-by-3 array with the coordinates of the uppermost point %INPUT: % X is a m-by-3 array of the coordinates of the m datapoints. % % --------% % author : Bart Coppieters % % %Predefined function of the Matlab-library to carry out the principal %component analysis of the data points of the bone, thus X. %coeff will give the 3 principal components, score will give %the coordinates of each point in X with respect to the base formed by the %3 principal components. [coeff,score] = princomp(X); meanX = mean(X,1); dirVect = coeff(:,1); %The principal component axis of the data. a = min(score(:,1)); %The smallest coordinate (projection) on the dirVect b = max(score(:,1)); %The biggest coordinate (projection) on the dirVect % a and b are the 2 extreme points but in the case of the humerus I want a % to be the distal end and b to be the proximal end. Therefore I see which % part is the closest to a sphere, and this will be the proximal end. indices_a = find(score(:,1)< a + 0.05 * (b-a)); indices_b = find(score(:,1)> b - 0.05 * (b-a)); a_list = X(indices_a,:); b_list = X(indices_b,:); sizeA = length(a_list); sizeB = length(b_list); 181 %calculate the intervals so the inputs for the lssphere-functions %have reasonable lengths with a maximum of 1000. int_a = ceil(sizeA/1000); int_b = ceil(sizeB/1000); mean_a = mean(a_list); mean_b = mean(b_list); %lssphere is a predefined function to approximate a sphere given a bunch of %datapoints using the 'least square fitting'-algorithm. [x0n_a, rn_a, d_a] = lssphere(a_list(1:int_a:end,:), mean_a', 10, 1, 1); [x0n_b, rn_b, d_b] = lssphere(b_list(1:int_b:end,:), mean_b', 10, 1, 1); normA = norm(d_a); normB = norm(d_b); %definition of the points under and upper making use of dirVect and meanX. if normA>normB under = min(score(:,1))*dirVect' + meanX; upper = max(score(:,1))*dirVect' + meanX; else under = max(score(:,1))*dirVect' + meanX; upper = min(score(:,1))*dirVect' + meanX; end 182 B.3 Scapula B.3.1 orientScapula.m function [x, y, z, n, extLat, extMed, side] = orientScapula(x,y,z,n) %ORIENTSCAPULA WILL ORIENT A 3D-MODEL OF A SCAPULA FOR FURTHER PROCESSING, %I.E. FEATURE EXTRACTION. % %OUTPUT: % x, y and z are 3-by-N matrices that together form the oriented patch % object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the oriented patch object. % % ext1 is a 1-by-3 array of the coordinates of the medial extreme point % of the superior part of the scapula. This point is used in subsequent % functions. % % ext2 is a 1-by-3 array of the coordinates of the lateral extreme point % of the scapula. This point is used in subsequent % functions. % % side is a scalar that serves as a logical to state whether it is a left % (side == 1) or a right (side == 0) bone. % %INPUT: % x, y and z are 3-by-N matrices that together form the original patch % object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the original patch object. % % with N the number of facets of the 3D model % --------% % author : Bart Coppieters % %Step 1 % The list representation of the 3D model. X = unique([x(:) y(:) z(:)],'rows'); %Step 2 %The mean of X will define a translation in such a manner that meanX is put %in the origin (0,0,0). %Do this translation for the whole bone meanX = mean(X); [x,y,z,zero] = translation(x,y,z,meanX,meanX); X = unique([x(:) y(:) z(:)],'rows'); %Step 3 183 %Calculate for each point of X the euclidean distance to meanX, thus %(0,0,0), and stock this together with the point in the length(X)-by-4 %matrix normX. normX = zeros(length(X),4); for i = 1:length(X) normX(i,1:3) = X(i,:); normX(i,4) = norm(X(i,:)); end %Sorted list of points and corresponding norms. %The norms are in descending order. normX = sortrows(normX,-4); %point with biggest euclidean distance to meanX maxNorm = normX(1,1:3); %Step 4 %Do a sequence of rotations of the bone in order to get point maxNorm to %lie on the positive z-axis. %A first rotation around the z-axis to force maxNorm into the x-z plane. %angle over which to rotate around the z-axis hoekz = -atan(maxNorm(2)/maxNorm(1)); [x,y,z, rotz] = rotation_Z_axe(x,y,z,hoekz); n = rotz * n; maxNorm = (rotz * maxNorm')'; %A second rotation around the y-axis to force maxNorm onto the z-axis. %angle over which to rotate around the y-axis. hoeky = -atan(maxNorm(1)/maxNorm(3)); [x,y,z, roty] = rotation_Y_axe(x,y,z,hoeky); n = roty*n; %rotated version of maxNorm. maxNorm = (roty * maxNorm')' ; %Step 5 %Do subsequent transformations to get the already oriented bone in the %z>0-space, i.e. above the x-y plane. %A first translation defined by maxNorm. [x,y,z,o] = translation(x,y,z,maxNorm,maxNorm); X = unique([x(:) y(:) z(:)],'rows'); %We do a 180 degrees-rotation if the the scapula lies upside down. %This means that maxNorm has the highest z-value, thus 0. if max(X(:,3)) <= 0 [x, y, z, rotx] = rotation_X_axe(x, y, z, pi); n = rotx * n; end % List representation of the currently oriented bone. 184 X = unique([x(:) y(:) z(:)],'rows'); %Step 6 %Getting the triangular plane of the scapula to concede with the x-z plane. %By now the bone is oriented along the z-axis. %'upperX' will consist of the points that lie in the uppermost half of the %the so far oriented 3D model of the scapula. upperX = X(find(X(:,3)> 0.5*(max(X(:,3))-min(X(:,3)))),:); %We do a pricipal component analysis of this data. [coeff,score] = princomp(upperX); a = min(score(:,1)); %The smallest coordinate on dirVect b = max(score(:,1)); %The biggest coordinate on dirVect % the point with the smallest score (projection) on the principal axis of % the data ('upperX'). ext1 = upperX(find(score(:,1) == a),:); % the point with the highest score (projection) on the principal axis of % the data ('upperX'). ext2 = upperX(find(score(:,1) == b),:); %Which extreme point has the biggest z-value? This point we will use in the %further orientation of the bone, in such a way that this point (and all %the other points of the bone) will be rotated around the z-axis on to the %x-z plane. if ext1(3) > ext2(3) %ext1 has the highest location. %rotation with respect to ext1 %angle over which to rotate around the Z-axe. hoekz = -atan(ext1(2)/ext1(1)); [x, y, z, rotz] = rotation_Z_axe(x, y, z, hoekz); n = rotz*n; extLat = (rotz * ext1')'; extMed = (rotz * ext2')'; else %ext2 has the highest location. %rotation with respect to ext2 %angle over which to rotate around the Z-axe. hoekz = -atan(ext2(2)/ext2(1)); [x, y, z, rotz] = rotation_Z_axe(x, y, z, hoekz); n = rotz*n; extMed = (rotz * ext1')'; extLat = (rotz * ext2')'; end %Make sure extLat lies in the (X>0)-part of the space. if extLat(1) < 0 %rotation around the z-axis over 180 degrees. [x, y, z, rotz] = rotation_Z_axe(x, y, z, pi); n = rotz*n; extLat = (rotz * extLat')'; extMed = (rotz * extMed')'; 185 end % List representation of the currently oriented bone. X = unique([x(:) y(:) z(:)],'rows'); %Step 7 %Looking if the bone is a right or a left one. decideX = X(find(X(:,3) > extMed(3) & X(:,1) < ... extMed(1) + 0.10*abs(extMed(1))),:); %The highest (with respect to the z-axis) point of deciderP. deciderP = decideX(find(decideX(:,3)==max(decideX(:,3))),:); %Step 8 %Take a mirror image with respect to the x-z plane to transform a left bone %into its right counterpart. Give back the side as an output argument. %the bone is from a left arm if extMed(2) > deciderP(2) y = -y; n(2,:) = -n(2,:); extLat(2) = - extLat(2); extMed(2) = - extMed(2); side = 1; %right else %we don't do nothing as we will work on the right scapula side = 0; end B.3.2 infAngleScap.m function [infAngle] = infAngleScap(X) %INFANGLESCAP CALCULATES THE POSITION OF THE INFERIOR ANGLE OF THE ORIENTED %RIGHT SCAPULA. % %OUTPUT: % infAngle is a 1-by-3 array of the coordinates of the inferior angle % of the oriented right scapula % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the oriented right scapula. % % --------% % author : Bart Coppieters % % infAngle = X(find(X(:,3) == min(X(:,3))),:); 186 B.3.3 supAngleScap.m function [supAngle] = supAngleScap(X, extMed, extLat) %SUPANGLESCAP CALCULATES THE POSITION OF THE SUPERIOR ANGLE OF THE ORIENTED % RIGHT SCAPULA. % %OUTPUT: % supAngle is a 1-by-3 array of the coordinates of the superior angle % of the right oriented scapula % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the oriented right scapula. % % extMed is a 1-by-3 array of the coordinates of the extreme on the % medial side of the oriented right scapula(defined in orientScapula) % % extLat is a 1-by-3 array of the coordinates of the extreme on the % lateral side of the oriented right scapula(defined in orientScapula) % % --------% % author : Bart Coppieters % % %Step 1 %Define the initial search space as a selection out of all of the points of %the bone. searchX = X(find(X(:,1) < 0 & X(:,2) > 0 & X(:,3) > max(X(:,3)) * 0.5),:); %Step 2 %Calculate for each point in searchX the orthogonal distance to the line %defined by extMed and extLat. dir = extMed - extLat; for i = 1:length(searchX) d(i) = distancePointLine3d(searchX(i,:), [extMed(1) ... extMed(2) extMed(3) dir(1) dir(2) dir(3)]); %The above function is downloaded from the Matlab file exchange center %and calculates for a given point and line the othogonal distance %between the point and the line. end %Step 3 %The superior angle is the point with the biggest orthogonal distance to %the former defined line. supAngle = searchX(find(d == max(d)),:); 187 B.3.4 acromialTipScap.m function [acrTip] = acromialTipScap(x, y, z, n, extMed) %ACROMIALTIPSCAP CALCULATES THE POSITION OF THE TIP OF THE ACROMION OF A %RIGHT ORIENTED SCAPULA. % %OUTPUT: % acrTip is a 1-by-3 array of the coordinates of the tip of the acromion % of the right oriented scapula. % %INPUT: % x, y and z are 3-by-N matrices that together form the patch % object of the oriented right scapula. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the patch object of the oriented right scapula. % % with N the number of facets of the 3D model of the scapula. % % extMed is a 1-by-3 array of the coordinates of the extreme on the % medial side of the scapula(defined in function orientScapula). % % --------% % author : Bart Coppieters % % %Step 1 %Construct a list-representation of x, y and z(patch representation). X = unique([x(:) y(:) z(:)],'rows'); %Define for saving time more or less that part of the bone where the %feature with certainty resides, the rough acromion, we will refer to it as %the search space. %We provide the patch representation and the corresponding %list representation of the mentioned search space. [x2 y2 z2 n2] = localPatchLimit(x, y, z, n, max(X(:,1)), max(X(:,1))/2, ... max(X(:,2)), min(X(:,2)), max(X(:,3))+1,... extMed(3) + (max(X(:,3)) - extMed(3))/2); X2 = unique([x2(:) y2(:) z2(:)],'rows'); % Step 2 %Define for each vertex in the patch representation of the roughly selected %acromion its neighboring vertices. This info will be stocked in V. %Look to which facets each vertex belongs. A = vertexInFaces(x2, y2, z2); %Appendix D %Look for the neighboring points of each point of the search space. This %yields V, a cell array. This will be used in step 4. V = findPointNeigh(x2, y2, z2, A, 1); %Appendix D %Step 3 %Initialize Macromion with p as only element. 188 p = X(find(X(:,3) == max(X(:,3))),:); % Step 4 Macromion = p; %Step 5 %Do the following iterative process: go repeatedly through Macromion and %calculate of every point in Macromion its neighboring points and append %them to Macromion. Do this until a certain stop condition is met. lengths = 0; stop = 0; while stop == 0 lengths = [lengths, size(Macromion,1)]; for i = lengths(end-1)+1:lengths(end) %to be sure to calculate just %once the neighbours of a point. point = Macromion(i,:); ind = find(X2(:,1) == point(1) & X2(:,2) == point(2)... & X2(:,3) == point(3)); M = cell2mat(V{ind}); %The list of neigboring points of the i-th point in Macromion. M = reshape(M,3,length(M)/3)'; for j = 2:2:length(M) %we pass by 1 point to save time. if M(j,3) < max(X(:,3)) * 17/18 %the stop condition: if a newly added point has a z-value %smaller than 17/18 of the max(all z-coordinates), than %stop extending the list. stop = 1; break end %if the newly calculated neighbor, M(j,:) already is contained %in Macromion, it will not be added to Macromion. if abs(prod(Macromion(:,1) - M(j,1))) < 0.00001 && ... abs(prod(Macromion(:,2) - M(j,2))) < 0.00001 && ... abs(prod(Macromion(:,3) - M(j,3))) < 0.00001 %if both of the above conditions aren't fulfilled add the %newly calculated point to Macromion. else Macromion = [Macromion;M(j,:)]; end end end end %Step 6 %Look for the tip end of the acromion and select of this tip the most %lateral point. yDist = norm(max(Macromion(:,2)) - min(X(:,2))); zDist = norm(max(Macromion(:,3)) - min(Macromion(:,3))); %Definition of the tip of the acromion. The 189 %parameters were set through trial and error to get the most suitable %result. searchTip = Macromion(find(Macromion(:,2)>... max(Macromion(:,2))-1/5 * yDist),:); %Selection of the most lateral point of the tip of searchTip. acrTip = searchTip(find(searchTip(:,1) == max(searchTip(:,1))),:); B.3.5 acrAngleScap.m function [acrAngle] = acrAngleScap(x, y, z, n) %ACRANGLESCAP CALCULATES THE POSITION OF THE ACROMIAL ANGLE OF A RIGHT %ORIENTED SCAPULA. % %OUTPUT: % acrAngle is a 1-by-3 array of the coordinates of the acromial angle % of the right oriented scapula % %INPUT: % x, y and z are 3-by-N matrices that together form the patch % object of the oriented right scapula. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the patch object of the oriented right scapula. % % with N the number of facets of the 3D model of the oriented scapula. % --------% % author : Bart Coppieters % % % Step 1 % Define a search space in which to look for the acromial angle. X = unique([x(:) y(:) z(:)],'rows'); %The list-representation of the search space searchAng = X(find(X(:,1) > 0.5*max(X(:,1))& X(:,2) < 0.75*min(X(:,2))),:); % The patch-representation of the search space, needed for step 2. [x2 y2 z2 n2] = localPatchBunch(x,y,z, n, searchAng); % Step 2 % Calculate for each point in this search space its mean curvature. Sort % the points of the search space in order of ascending mean % curvature. Select the 10 points with the smallest mean curvature. cfinalang = globalCurvature(x2,y2,z2,n2,1); %Appendix D curv = unique([x2(:) y2(:) z2(:) cfinalang(:)],'rows'); curv = sortrows(curv, 4); % Select the points with the smallest mean curvature. Smin = curv(1:10,1:3); % Calculate the mean of these Smin. Smean = mean(Smin); 190 % Step 3 % Calculate the point with the smallest distance to Smean. This will be % the acromial angle. % List of distances to Smean. d = sqrt(sum((Smin - repmat(Smean,length(Smin),1)).^2,2)); % The output argument acrAngle = Smin( find(d == min(d)),: ); B.3.6 corTipScap.m function [corTip] = corTipScap(x, y, z, n, extMed) %CORTIPSCAP CALCULATES THE POSITION OF THE TIP OF THE CORACOID PROCESS OF A %RIGHT ORIENTED SCAPULA. % %OUTPUT: % corTip is a 1-by-3 array of the coordinates of the tip of the coracoid % process of the right oriented scapula % %INPUT: % x, y and z are 3-by-N matrices that together form the patch % object of the oriented right scapula. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the patch object of the oriented right scapula. % % extMed is a 1-by-3 array of the coordinates of the extreme on the % medial side of the scapula(defined in function orientScapula). % % with N the number of facets of the oriented 3D model of the scapula % --------% % author : Bart Coppieters % % % Step 1 % Locate and isolate a part of the bone that with certainty contains the % coracoid process, this will be referred to as the search space. % We will provide the patch representation and the corresponding % list representation of the mentioned search space. % The boundaries are chosen by trial and error on the 13 scapulas in order % to get a good starting search space. The patch representation of the % search space will be the input for the neighbor defining function of Step % 2. X = unique([x(:) y(:) z(:)],'rows'); X = sortrows(X, -2); % patch representation [x2 y2 z2 n2] = localPatchLimit(x, y, z, n, max(X(:,1)), max(X(:,1))/2,... max(X(:,2)), 0, max(X(:,3)), 0); % list representation X2 = unique([x2(:) y2(:) z2(:)],'rows'); 191 % Step 2 % Define for each vertex in the patch representation of the search space % its neighboring vertices. This info will be stocked in V. %Look to which facets each vertex belongs. A = vertexInFaces(x2, y2, z2); %Appendix D %Look for the neighboring points of each point of the search space. This %yields V, a cell array. This will be used in step 5. V = findPointNeigh(x2, y2, z2, A, 1); %Appendix D % Step 3 % Select the most anterior point of the search space, p. p = X2(find(X2(:,2) == max(X2(:,2))),:); %Step 4 %Initialize Mcoracoid with p as only element. Mcoracoid = p; % Step 5 % Do the following iterative process: go repeatedly through Mcoracoid and % calculate of every point in Mcoracoid its neighboring points and append % them to Mcoracoid. Do this until a certain stop condition is met. lengths = [0]; stop = 0; while stop == 0 lengths = [lengths, size(Mcoracoid,1)]; for i = lengths(end-1)+1:lengths(end)%to be sure to calculate just %one time the neighbors of a point. point = Mcoracoid(i,:); ind = find(X2(:,1) == point(1) & X2(:,2) == point(2) & ... X2(:,3) == point(3)); M = cell2mat(V{ind}); %The list of neigboring points of the i-th point in Mcoracoid. M = reshape(M,3,length(M)/3)'; for j = 2:2:length(M) %we pass by 1 point to save time. if M(j,2) < max(X2(:,2)) * 5/8 %the stop condition: if a newly added point has an x-value %smaller than 3/4 of the max(all x-coordinates of the %search space), than stop extending the list. Mcoracoid = [Mcoracoid;M(j,:)]; stop = stop + 1; end %if the newly calculated neighbor, M(j,:), already is contained %in Macromion, it will not be added to Mcoracoid. if abs(prod(Mcoracoid(:,1) - M(j,1))) < 0.00001 ... && abs(prod(Mcoracoid(:,2) - M(j,2))) < 0.00001 ... && abs(prod(Mcoracoid(:,3) - M(j,3))) < 0.00001 %if both of the above conditions aren't fulfilled, add the %newly calculated point to Mcoracoid. 192 else Mcoracoid = [Mcoracoid;M(j,:)]; end if stop == 1 disp('STOP') break end end if stop == 1 break end end end % Step 6 % Look for the lateral end part of the coracoid process and select of this % end the most lateral and centic point. % Sort the rows with descending x-value. Mcoracoid = sortrows(Mcoracoid, -1); % Select the first fifth part of points of this sorted list. This will % represent the lateral end of the coracoid process McoracoidTip = Mcoracoid(1:round(length(Mcoracoid)*0.2),:); meanTip = mean(McoracoidTip); % Make a list of euclidean distances to meanTip d = sqrt(sum((McoracoidTip-repmat(meanTip,length(McoracoidTip),1)).^2,2)); % The output argument, the point with the smallest distance to meanTip. corTip = McoracoidTip( find(d == min(d)),: ); B.3.7 trigSpinScap.m function [trigSpin] = trigSpinScap(x, y, z, n, extMed) %TRIGSPINSCAP CALCULATES THE POSITION OF THE ROOT OF THE SPINE OF A %RIGHT ORIENTED SCAPULA. % %OUTPUT: % trigSpin is a 1-by-3 array of the coordinates of root of the spine % of the right oriented scapula. % %INPUT: % x, y and z are 3-by-N matrices that together form the patch % object of the oriented right scapula. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the patch object of the oriented right scapula. % % with N the number of facets of the 3D model % % extMed is a 1-by-3 array of the coordinates of the extreme on the % medial side of the scapula(defined in function orientScapula). % % --------% % author : Bart Coppieters % 193 % % Step 1 % Locate and isolate a part of the bone that with certainty contains the % root of the spine, this will be referred to as the search space. % The list representation of the input scapula. X = unique([x(:) y(:) z(:)],'rows'); % Calculation of the midpoint along the z-axis. mid = (max(X(:,3))-min(X(:,3)))/2; % Specification of the list representation of the search space. This will % depend on the original location of extMed. Dependent on extMed the % boundaries are defined differently. if extMed(2) > 0 searchX = X(find(X(:,1)<min(X(:,1))/2 & X(:,2)<extMed(2)/2 & ... X(:,3)>mid),:); else searchX = X(find(X(:,1)<min(X(:,1))/2 & X(:,2)<extMed(2) & ... X(:,3)>mid),:); end % The patch representation of the search space [x2 y2 z2 n2] = localPatchBunch(x, y, z, n, searchX); % Step 2 % Calculate for each point of searchX the mean curvature. % cfinal is a 3-by-N matrix of the curvatures of each point in searchX cfinal = calculateCurvature(x2,y2,z2,n2,1); %Appendix D % Add cfinal as a column to the list representation of the search space, % searchX. searchX will now have 4 columns and is sorted with an ascending % 4th column, being the mean curvatures. searchX = sortrows(unique([x2(:) y2(:) z2(:) cfinal(:)],'rows'),4); % Select the first 5% of the sorted searchX. This will be a list of the 5% % points of the search space with the smallest mean curvatures. thr = ceil(length(searchX)*0.02);%determining the bounding index searchXmin = searchX(1:thr,:);%selecting the first thr rows of searchX % Step 3 % Calculate the euclidean distances of the points in searchXmin to the mean % of the points in searchXmin. meanSpin = mean(searchXmin(:,1:3)); d = sqrt(sum((searchXmin(:,1:3) - ... repmat(meanSpin,length(searchXmin),1)).^2,2)); % Step 4 % Take only those points of searchXmin with a distance smaller than the % mean distance to the mean of searchXmin. searchX2 = searchXmin(find(d < mean(d)),:); % Step 5 % Of this refined list of points, searchX2, take the most medial one. This % is the output argument. trigSpin = searchX2(find(searchX2(:,1) == min(searchX2(:,1))),1:3); 194 B.3.8 glenoidScap.m function [glenoidC glenoidR glenoidNormal]= glenoidScap(x, y, z, n, corTip) %INFANGLESCAP CALCULATES THE 3D CIRCLE THAT BEST FITS THE POINTS OF THE %GLENOID LABRUM. % %OUTPUT: % glenoidC is a 1-by-3 array of the coordinates of the center of the 3D % circle represEnting the glenoid cavity. % % glenoidR represents the radius of the calculated 3D circle. % % glenoidNormal is a 1-by-3 array that represents the vector % perpendicular to the plane of the calculated 3D circle. % %INPUT: % x, y and z are 3-by-N matrices that together form the patch % object of the oriented right scapula. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the patch object of the oriented right scapula. % % corTip is a 1-by-3 array of the coordinates of the tip of the coracoid % process. (defined in function corTipScap) % % with N the number of facets of the oriented 3D model. % % --------% % author : Bart Coppieters % % % Step 1 % Locate and isolate the points of the bone that will be given as input % arguments to a function that will carry out the fitting to a 3D circle. % This step will start with a search space and this space will be refined % throughout this step. % List representation of the entire input scapula. X = unique([x(:) y(:) z(:)],'rows'); % All the points of the lateral side that have a smaller z-value than the % z-value of the tip of the coracoid process. searchX = X( find(X(:,1)> corTip(1) - 10 & X(:,3) <= corTip(3)),:); % Making a list of euclidean distances to the tip of the coracoid process. d = sqrt(sum((searchX - repmat(corTip,length(searchX),1)).^2,2)); % % M % % M % % M is the matrix with in each row a point of the search space and in the last column the distance to the tip of the coracoid process. = [searchX, d]; Sort M with respect to ascending distance to the tip of the coracoid process. = sortrows(M, 4); Look which point is the closest to the tip of the coracoid process and which is not a point of the coracoid process. This is a point of the 195 % glenoid labrum. We call this point gPoint. for i = 2:length(M) %Go through the sorted list and stop (and save the index) if the %distance between two subsequent points in the list is bigger than 2. %This represents the gap between the coracoid process and the glenoid %labrum. if M(i,4) - M( i-1 ,4) > 2 break end end gPoint = M(i,1:3); % Step 2 % Use gPoint to define a patch representation of the rough approximation of % the glenoid cavity. [x2 y2 z2 n2] = localPatchLimit(x,y,z, n, max(X(:,1)), gPoint(1), ... gPoint(2), min(M(:,2)), gPoint(3), 0); % Calculate for each point of this approximation the mean curvature. cfinal = globalCurvature(x2,y2,z2,n2,1); % Isolate the fifth of the points with the smallest mean curvatures, fitX1. C = unique([x2(:) y2(:) z2(:) cfinal(:)],'rows'); C = sortrows(C, 4); L = ceil(length(C) * 0.2); fitX1 = C(1:L,1:3); % Refine fitX1 for a better approximation of the points of the glenoid % cavity, fitX2. fPoint is the most lateral point and because the glenoid % cavity is oriented a bit upward, points below fpoint should not be % involved in fitX2. fPoint = fitX1(find(fitX1(:,1) == max(fitX1(:,1))),:); fitX2 = fitX1(find(fitX1(:,3) > fPoint(3)),:); % Step 3 % First fitting phase. x0 = mean(fitX2); r0 = max(fitX2(:,2)) - min(fitX2(:,2)); a0 = [1 0 0]; tolp = 0.1; tolg = 0.1; %MATLAB FILE EXCHANGE CENTRE %It carries out the least squares fitting %method to a 3d circle. It takes a list of points and some initialization %parameters as input and produces the center, the radius and the normal of %the fitted 3D circle. [x0n, an, rn] = ls3dcircle(fitX2, x0', a0', r0, tolp, tolg); glenoidC_rough = x0n'; glenoidR_rough = rn; glenoidNormal_rough = an'; % Step 4 % Second, more refined fitting phase. % The list of euclidean distances of the points of fitX2 to the center of 196 % the 3d circle fitted in step 3. d_fitX_glenoidC = sqrt(sum((fitX2 - repmat(glenoidC_rough,... length(fitX2),1)).^2,2)); % the mean distance and the standard deviation of the distances mu = mean(d_fitX_glenoidC); sigma = std(d_fitX_glenoidC); % Discard the outliers from fitX2. Outliers are those points with a % distance of sigma or bigger different of the radius of the 3d circle of % step 3. ind = []; for i = 1:length(fitX2) % contain only the points of fitX2 that lie closer than sigma to the % radius of step 3 AND that lie lower than the center of step 3. if d_fitX_glenoidC(i) > glenoidR_rough - sigma && d_fitX_glenoidC(i)... < glenoidR_rough + sigma && fitX2(i,3) < glenoidC_rough(3) ind = [ind; i]; end end % the refined list of points to give to the fitting function ls3dcircle. fitX3 = fitX2(ind,:); x0 = mean(fitX3); r0 = max(fitX3(:,2)) - min(fitX3(:,2)); a0 = [1 0 0]; tolp = 0.1; tolg = 0.1; %MATLAB FILE EXCHANGE CENTRE [x0n, an, rn] = ls3dcircle(fitX3, x0', a0', r0, tolp, tolg); % The output arguments that define the 3d circle representing the lower % part of the glenoid cavity. glenoidC = x0n'; glenoidR = rn; glenoidNormal = an'; 197 B.4 Clavicle B.4.1 orientClavicle.m function [x, y, z, n, side] = orientClavicle(x,y,z,n) %ORIENTCLAVICLE WILL ORIENT A 3D-MODEL OF A CLAVICLE FOR FURTHER %PROCESSING, I.E. FEATURE EXTRACTION. % %OUTPUT: % x, y and z are 3-by-N matrices that together form the oriented patch % object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the oriented patch object. % % side is a scalar that serves as a logical to state whether it is a left % (side == 1) or a right (side == 0) bone. % %INPUT: % x, y and z are 3-by-N matrices that together form the original patch % object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the original patch object. % % with N the total number of facets of the 3D model of the clavicle. % % --------% % author : Bart Coppieters % % %Step 1 %Define the prominent direction. X = unique([x(:) y(:) z(:)],'rows'); [coeff,score] = princomp(X); %Predefined Matlab-function to carry out the %principal component analysis. coeff will give the principal components %directions, score will give the scores of the points with respect to %the principal component space. meanX = mean(X,1); dirVect = coeff(:,1); %The principal component axis of the data. a = min(score(:,1)); %The smallest coordinate on dirVect b = max(score(:,1)); %The biggest coordinate on dirVect %the two extreme points on the principal axis. ext1 = a*dirVect' + meanX; ext2 = b*dirVect' + meanX; %Step 2 %First translation defined by meanX. 198 [x,y,z,o] = translation(x,y,z,meanX,meanX); ext1 = ext1 - meanX; ext2 = ext2 - meanX; %Step 3 %Do a sequence of rotations to get ext2 onto the positive x-axis. %Rotation around z-axis to get ext2 into the x-z plane on the half of the %positive x-axis. hoekz = -atan(ext2(2)/ext2(1)); %angle to rotate around the z-axis. [x, y, z, rotz] = rotation_Z_axe(x,y,z,hoekz); n = rotz*n; ext2 = (rotz*ext2')'; ext1 = (rotz*ext1')'; % Rotate over 180 degrees if ext2 lies in the half of negative x-axis. if ext2(1) < 0 [x, y, z, rotz] = rotation_Z_axe(x,y,z,pi); n = rotz*n; ext2 = (rotz*ext2')'; ext1 = (rotz*ext1')'; end %Rotation around y-axis to get ext2 onto the positive x-axis. hoeky = atan(ext2(3)/ext2(1));%angle over which to rotate around the y-axis [x, y, z, roty] = rotation_Y_axe(x,y,z,hoeky); n = roty*n; ext2 = (roty*ext2')'; ext1 = (roty*ext1')'; %Step 4 %Do a rotation around the x-axis in order to get the same position as in %the human body, that is the 'plane' lies in the x-y plane. %Select along the prominent axis only the middle part. %We need the patch object of the bone, rather than the list of points, %because we have to do rotations of this part of the bone. X = unique([x(:) y(:) z(:)],'rows'); [xresult yresult zresult nresult] = localPatchLimit(x, y, z, n, ... max(X(:,1))* 0.4, min(X(:,1)) * 0.4, max(X(:,2)), min(X(:,2)),... max(X(:,3)),min(X(:,3))); %Do rotations in intervals of pi/36 rad and calculate in each end %position the distance between the maximal y-value and the minimal y-value. %The rotation that yields the biggest y-distance will be the wanted %rotation. j = 1; for i = 0:pi/36:35/36*pi [xb, yb, zb, rotx] = rotation_X_axe(xresult,yresult,zresult,i); dist(j) = max(yb(:)) - min(yb(:)); %calculating the maximal y-distance j = j+1; end hoekx = (find(dist == max(dist)) - 1) * pi/36; %calculating the angle %associated with the biggest y-distance [x, y, z, rotx] = rotation_X_axe(x,y,z,hoekx); 199 n =rotx*n; X = unique([x(:) y(:) z(:)],'rows'); %Step 5 %Look which half has the biggest y-distance in this orientation. With this %step we determine that the medial end lies onto the positive x-axis. %Because the biggest part should be the lateral half. leftpart = X(find( X(:,1) > 0.60 * min(X(:,1)) & X(:,1) < 0 ),:); leftdist = max(leftpart(:,2)) - min(leftpart(:,2)); rightpart = X(find( X(:,1) < 0.60 * max(X(:,1)) & X(:,1) > 0 ),:); rightdist = max(rightpart(:,2)) - min(rightpart(:,2)); %if this condition holds, the lateral half lies onto the positive x-axis, %therefore we need a rotation of 180 degrees around the z-axis. if rightdist > leftdist [x, y, z, rotz] = rotation_Z_axe(x,y,z,pi); n = rotz*n; end X = unique([x(:) y(:) z(:)],'rows'); %Step 6 %look where lies the posterior convexity this should lie in the half of %the positive y-axis. %Isolate the part to look. leftpart = X(find( X(:,1) > 0.75 * min(X(:,1))&X(:,1)<0.25*min(X(:,1))),:); leftmax = max(leftpart(:,2)); leftmin = min(leftpart(:,2)); %If this condition holds, the curve lies in the part of positive y-axis. %This is specified by the logical latcurv. if abs(leftmax) > abs(leftmin) p = leftpart(find(leftpart(:,2) == max(leftpart(:,2))),:);%will be used %to determine a search space in the next step. latcurv = 1; %In this case the curve lies in the part of the negative y-axis. else p = leftpart(find(leftpart(:,2) == min(leftpart(:,2))),:); latcurv = 0; end %Step 7 %Look whether the bone lies in its natural position, i.e. the superior %surface points upwards. We use the info provided by the curvature %function to do this. On the inferior surface of the clavicle lies the %conoid tubercle so determining more or less this tubercle will determine %the inferior surface. xdist = max(X(:,1))-min(X(:,1)); %maximal x-distance. 200 [x2, y2, z2, n2] = localPatchLimit(x, y, z, n, p(1) + xdist * 0.2, ... 0.85 * min(X(:,1)), p(2) + 10, p(2) - xdist * 0.1, max(X(:,3)), ... min(X(:,3))); %the search space. Has to be a patch %representation of the bone to give it to the curvature function. cfinal = globalCurvature(x2, y2, z2, n2,1); %calculate the mean curvature %Look for the 10 points that show the biggest convex mean curvature, i.e. %the smallest values of the calculated mean curvature. The conoid tubercle %exhibits namely convexity. The 10 point with smallest mean curvature are %stocked in curveMatrix. curveMatrix = sortrows(unique([x2(:) y2(:) z2(:) cfinal(:)],'rows'),4); curveMatrix = curveMatrix(1:10,1:3); meanSelect = mean(curveMatrix(:,:)); %the mean of these 10 points. meanAll = mean(leftpart(:,3)); %the mean of the entire left part. % a logical that specifies whether the bone lies with its superior surface % upwards. natural = meanSelect(3) < meanAll; %Step 8 %Determine the side of the clavicle, left or right. %This property can be determined by examining the 2 variables latcurv and %meanSelect. Each combination of these will yield a side. if latcurv if natural side = 0; %right side else side = 1; % left side z = -z; end else if ~natural side = 0; %right side [x, y, z, rotx] = rotation_X_axe(x,y,z,pi); n = rotx*n; else side = 1; %left side y = -y; end end B.4.2 sternClav.m function sternClav = sternClav(X) %STERNCLAV CALCULATES THE POSITION OF THE MOST ANTERIOR POINT ON THE %STERNOCLAVICULAR JOINT SURFACE OF THE ORIENTED RIGHT CLAVICLE. % %OUTPUT: % sternClav is a 1-by-3 array of the coordinates of the most anterior % point of the sternal joint surface of the oriented right clavicle. % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that 201 % constitute the oriented right clavicle. % --------% % author : Bart Coppieters % % %Locate the most medial part of the bone. medX = X( find( X(:,1) > 0.95 * max(X(:,1))) , :); %The most anterior point. The point in medX with the smallest y-value in %the current orientation. sternClav = medX( find( medX(:,2) == min(medX(:,2))),:); B.4.3 acrClav.m function acrClav = acrClav(X) %ACRCLAV CALCULATES THE POSITION OF THE MOST POSTERIOR POINT ON THE %ACROMIOCLAVICULAR JOINT SURFACE OF THE ORIENTED RIGHT CLAVICLE. % %OUTPUT: % acrClav is a 1-by-3 array of the coordinates of the most dorsal % point of the acromial joint surface of the oriented right clavicle. % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the oriented right clavicle. % --------% % author : Bart Coppieters % % % The most lateral point. The point with the smalles x-value in the current % orientation. acrClav = X( find( X(:,1) == min(X(:,1))),:); 202 B.5 Ulna B.5.1 orientUlna.m function [x, y, z, n, side] = orientUlna(x,y,z,n) %ORIENTULNA WILL ORIENT A 3D-MODEL OF AN ULNA FOR FURTHER PROCESSING, %I.E. FEATURE EXTRACTION. % %OUTPUT: % x, y and z are 3-by-N matrices that together form the oriented patch % object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the oriented patch object. % % side is a scalar that serves as a logical to state whether it is a left % (side == 1) or a right (side == 0) bone. % %INPUT: % x, y and z are 3-by-N matrices that together form the original input % patch object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the original input patch object. % with N the number of facets of the 3D model. % --------% % author : Bart Coppieters % % Step 1 % Define the principal component axis of the 3D model. This axis will be the % best line around which the data of the 3D model is distributed. % the list representation of the input patch representation of the entire % ulna. X = unique([x(:) y(:) z(:)],'rows'); %Predefined function of the Matlab-library to carry out the principal %component analysis of the data points of the bone. %coeff will give the main 3 principal components, score will give %the coordinates of each point in X with respect to the base formed by the %3 principal components. [coeff,score] = princomp(X); meanX = mean(X,1); dirVect = coeff(:,1); %The principal component axis of the data. a = min(score(:,1)); %The smallest coordinate (projection) on the dirVect b = max(score(:,1)); %The biggest coordinate (projection) on the dirVect temp11 = a*dirVect' + meanX; %an extreme point on the principal axis. 203 temp2 = b*dirVect' + meanX; %an extreme point on the principal axis. % Step 2 % Do a translation defined by temp1. [x,y,z,temp2] = translation(x,y,z,temp11,temp2); temp1 = [0 0 0]; % Step 3 %Do a sequence of rotations of the 3D model of the ulna in order to get %point temp2 to lie on the z-axis. %First rotation around the z-axis to force temp2 into the x-z plane. %Define the angle over which to rotate around the z-axis and carry out the %rotation. hoekz = -atan(temp2(2)/temp2(1)); [x, y, z, rotz] = rotation_Z_axe(x,y,z,hoekz); %of the patch representation n = rotz * n; %of the matrix of normals. temp2 = (rotz * temp2')'; %of temp2 %Second rotation around the y-axis to force temp2 on the z-axis. %Define the angle over which to rotate around the y-axis and carry out the %rotation. hoeky = -atan(temp2(1)/temp2(3)); [x, y, z, roty] = rotation_Y_axe(x,y,z,hoeky); %of the patch representation n = roty * n;%of the matrix of normals. temp2 = (roty * temp2')';%of temp2 %Possibly third rotation around the y-axis if temp2 lies on the negative %z-axis. if temp2(3) < 0 [x, y, z, roty] = rotation_Y_axe(x,y,z,pi); %of the patch representation n = roty * n;%of the matrix of normals. temp2 = (roty * temp2')';%of temp2 end X = unique([x(:) y(:) z(:)],'rows'); % Step 4 % Determine the right up-down orientation of the ulna, thus the orientation % as in the forearm in a neutral vertical position. % List representations of the two halves of the bone. % On this moment it is not clear if upperX represents the distal or the % proximal half of the bone. The same holds for underX. midZ = max(X(:,3)) / 2; upperX = X( find( X(:,3) > midZ ),:); underX = X( find( X(:,3) < midZ ),:); % Do for upperX and underX the projection onto the x-y plane and % subsequently calculate for each point of both 2 dimensional lists of % points its euclidean distance to the origin. Thus this is the same as % calculating their x-y norm. Than select the biggest norm in both sets of % points. ->maxUpperDist and maxUnderDist upperX2 = upperX(:,1:2); upperDist = sqrt(sum(upperX2.^2,2)); 204 maxUpperDist = max(upperDist); %biggest norm in the x-y plane of the uppermost half underX2 = underX(:,1:2); underDist = sqrt(sum(underX2.^2,2)); maxUnderDist = max(underDist); %biggest norm in the x-y plane of the undermost half %The proximal part (natural highest part) has a bigger norm in the x-y %plane, so if we detect the biggest norm, we know that this corresponds to %the proximal half of the ulna. And logically this half has to lie above %the distal half. if maxUpperDist < maxUnderDist %If the above condition holds, the defined undermost half, underX, %corresponds to the proximal part. Therefore a rotation around the y-axis %of 180 degrees is executed. %Rotation [x, y, z, roty] = rotation_Y_axe(x,y,z,pi);%of the patch representation n = roty * n;%of the matrix of normals. temp1 = (roty * temp1')';%of temp 1. temp2 = (roty * temp2')';%of temp 2. end %The correct specification of the two extreme points on the principal axis. %proximal represents the most proximal point on this axis and distal the %most distal. The current up down orientation of the bone is correct, thus %we can say that the extreme point with the biggest z-value is proximal. if temp1(3) > temp2(3) proximal = temp1; distal = temp2; else proximal = temp2; distal = temp1; end %Do a translation defined by the most distal point, distal, in order to get %the bone to lie in the half defined by the positive z-axis. This %translation will be in the direction of the z-axis. [x,y,z,proximal] = translation(x,y,z,distal,proximal); %list representation of the currently oriented ulna. X = unique([x(:) y(:) z(:)],'rows'); % Step 5 % Make the ulna's anterior aspect to point to the negative y-axis. %list representation of the points with a z-value bigger than 80 % %of the height of the bone. This is a set of the points of the proximal %epiphysis. upperX = X( find( X(:,3) > max(X(:,3)) * 0.85 ),:); %Do rotations around the z-axis with well chosen angles and calculate for %each outcome orientation the maximal y-distance of the proximal epiphysis. %Store these y-distances in dist. 205 % the patch representation of list representation upperX [x2, y2, z2, n2] = localPatchBunch(x, y, z, n, upperX); j = 1; for i = 0: pi/36: 35/36*pi %We take steps of pi/36 and go not further than pi because this would %produce double work. The y-distance for pi/36 and for 37 * pi/36 is the %same. %Do the rotation around the z-axis of i * (180/pi) degrees. [xb, yb, zb, rotz] = rotation_Z_axe(x2,y2,z2,i); %the patch %representation of upperX dist(j) = max(yb(:)) - min(yb(:)); %the maximal y-distance in this %orientation. j = j+1; end %Look for the angle which yields the biggest y-distance with the help of %dist and use this angle to rotate around the z-axis once more. hoekz = (find(dist == max(dist)) - 1) * pi/36; [x, y, z, rotz] = rotation_Z_axe(x,y,z,hoekz); n =rotz*n; if max(y(:)) > abs(min(y(:))) %The coronoid process lies in the part of space defined by the positive %y-axis, therefore the ulna's anterior aspect points towards the positive %y-axis. -> Do a rotation over 180 degrees around the z-axis.; [x, y, z, rotz] = rotation_Z_axe(x,y,z,pi); n =rotz*n; end % Step 6 %Look whether it is a right or a leftsided bone. %list representation of the currently oriented ulna. X = unique([x(:) y(:) z(:)],'rows'); lowest = X(find(X(:,3) == min(X(:,3))),:); if lowest(1)< 0 %left bone x = -x; side = 1; else side = 0; end B.5.2 olecranonUlna.m function [olecUlna] = olecranonUlna(X) %OLECRANONULNA CALCULATES THE POSITION OF THE APEX OF THE OLECRANON OF THE %ORIENTED RIGHT ULNA. % %OUTPUT: % olecUlna is a 1-by-3 array of the coordinates of the apex of the % olecranon. % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that 206 % % % % % % constitute the oriented right ulna. --------author : Bart Coppieters % Step 1 % List representation of the highest 3% of the oriented ulna. upperX = X( find( X(:,3) > min(X(:,3)) + 0.98 * (max(X(:,3)) min(X(:,3)))),:); % Step 2 % The apex of the olecranon is the point of upperX with the biggest % y-value. olecUlna = upperX( find( upperX(:,2) == max(upperX(:,2))),:); B.5.3 coronoidUlna.m function [corUlna] = coronoidUlna(X) %OLECRANONULNA CALCULATES THE POSITION OF THE APEX OF THE OLECRANON OF THE %ORIENTED RIGHT ULNA. % %OUTPUT: % corUlna is a 1-by-3 array of the coordinates of the coronoid process % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the oriented right ulna. % --------% % author : Bart Coppieters % % % Step 1 % The apex of the olecranon is the point of upperX with the smallest % y-value. corUlna = X( find( X(:,2) == min(X(:,2))),:); B.5.4 styloidUlna.m aaaa function [stylUlna] = styloidUlna(X) %STYLOIDULNA CALCULATES THE POSITION OF THE TIP OF THE STYLOID PROCESS OF %THE ORIENTED RIGHT ULNA. % %OUTPUT: % stylUlna is a 1-by-3 array of the coordinates of the tip of the styloid % process of the ulna. % %INPUT: 207 % % % % % % % X is a m-by-3 matrix of the coordinates of the m datapoints that constitute the oriented right ulna. --------author : Bart Coppieters %The most distal point or in other words the point with the smallest %z-value. stylUlna = X( find( X(:,3) == min(X(:,3))),:); B.5.5 headUlna.m function [sphCentUlna sphRadUlna] = headUlna(X) %HEADULNA DEFINES THE SPHERE THAT APPROXIMATES THE ULNAR HEAD OF THE %ORIENTED RIGHT ULNA. % %OUTPUT: % sphCentUlna is a 1-by-3 array of the coordinates of the center of the % sphere representing the approximated ulnar head. % % sphRadUlna represents the radius of the calculated sphere representing % the approximated ulnar head. %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % together make up the humerus. % % --------% % author : Bart Coppieters % Step 1 % Define a initial search space that contain with certainty the points of % the ulnar head. This is the distalmost part of the ulna. underX1 = X( find( X(:,3) < min(X(:,3)) + ... 0.05 * (max(X(:,3)) - min(X(:,3)))),:); %The maximal y-distance between points in underX1. yDist = max(underX1(:,2)) - min(underX1(:,2)); % Step 2 %Make a list of points that have to be involved in the sphere fitting. vector = [-1 -1 0]/norm([-1 -1 0]); %The lowest point -> styloid process lowest = X( find( X(:,3) == min(X(:,3))),:); %For each point of underX1, make a vector that starts in (lowest + %yDist/2*vector) in the direction defined by the line between the point and %(lowest + 2*yDist/3*vector). The point (lowest + 2*yDist/3*vector) will %have the same z-value as lowest and will be situated more to the center. 208 underV = underX1 - repmat(lowest + vector * 2*yDist/3,length(underX1),1); for i = 1:length(underV) proj(i) = dot(vector, underV(i,:)); end underX2 = underX1(find(proj > 0),:); %Step 3 %The fitting of underX2 to a sphere. %pass over a number of interval points in underX to get a list with less %than 1000 points. ->fitX interval = ceil(length(underX2)/1000); fitX = underX2(1:interval:end,:); meanX = mean(fitX); %MATLAB FILE EXCHANGE CENTRE %Does a sphere fit of the points contained in fitX. %x0n is the center of the fitted sphere. %rn is the radius of the fitted sphere. [x0n, rn] = lssphere(fitX, meanX', 0.01 * ... (max(X(:,3)) - min(X(:,3))), 0.01, 0.01); %the output arguments. sphCentUlna = x0n'; sphRadUlna = rn; 209 B.6 Radius B.6.1 orientRadius.m function [x, y, z, n, side] = orientRadius(x,y,z,n) %ORIENTRADIUS WILL ORIENT A 3D-MODEL OF A RADIUS FOR FURTHER PROCESSING, %I.E. FEATURE EXTRACTION. % %OUTPUT: % x, y and z are 3-by-N matrices that together form the oriented patch % object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the oriented patch object. % % side is a scalar that serves as a logical to state whether it is a left % (side == 1) or a right (side == 0) bone. % %INPUT: % x, y and z are 3-by-N matrices that together form the original input % patch object. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the original input patch object. % % with N the number of facets of the 3D model. % --------% % author : Bart Coppieters % % Step 1 % Define the principal component axis of the 3D model. This axis will be % the best line around which the data of the 3D model is distributed. % the list representation of the input patch representation of the entire % radius. X = unique([x(:) y(:) z(:)],'rows'); %Predefined function of the Matlab-library to carry out the principal %component analysis of the data points of the bone. %coeff will give the main 3 principal components, score will give %the coordinates of each point in X with respect to the base formed by the %3 principal components. [coeff,score] = princomp(X); meanX = mean(X,1); dirVect = coeff(:,1); %The principal component axis of the data. a = min(score(:,1)); %The smallest coordinate (projection) on the dirVect b = max(score(:,1)); %The biggest coordinate (projection) on the dirVect 210 temp11 = b*dirVect' + meanX; %an extreme point on the principal axis. temp2 = a*dirVect' + meanX; %an extreme point on the principal axis. % Step 2 % Do a translation defined by temp1. [x,y,z,temp2] = translation(x,y,z,temp11,temp2); temp1 = [0 0 0]; % Step 3 %Do a sequence of rotations of the 3D model of the radius in order to get %point temp2 to lie on the z-axis. %First rotation around the z-axis to force temp2 into the x-z plane. %Define the angle over which to rotate around the z-axis and carry out the %rotation. hoekz = -atan(temp2(2)/temp2(1)); [x, y, z, rotz] = rotation_Z_axe(x,y,z,hoekz); %of the patch representation n = rotz * n; %of the matrix of normals. temp2 = (rotz * temp2')'; %of temp2 %Second rotation around the y-axis to force temp2 on the z-axis. %Define the angle over which to rotate around the y-axis and carry out the %rotation. hoeky = -atan(temp2(1)/temp2(3)); [x, y, z, roty] = rotation_Y_axe(x,y,z,hoeky); %of the patch representation n = roty * n;%of the matrix of normals. temp2 = (roty * temp2')';%of temp2 %Possibly third rotation around the y-axis if temp2 lies on the negative %z-axis. if temp2(3) < 0 [x, y, z, roty] = rotation_Y_axe(x,y,z,pi);%of the patch representation n = roty * n;%of the matrix of normals. temp2 = (roty * temp2')';%of temp2 end X = unique([x(:) y(:) z(:)],'rows'); % Step 4 % Determine the right up-down orientation of the radius, thus the % orientation as in the forearm in a neutral vertical position. % List representations of the two halves of the bone. % On this moment it is not clear if upperX represents the distal or the % proximal half of the bone. The same holds for underX. midZ = max(X(:,3)) / 2; upperX = X( find( X(:,3) > midZ),:); underX = X( find( X(:,3) < midZ),:); % Do for upperX and underX the projection onto the x-y plane and % subsequently calculate for each point of both 2 dimensional lists of % points its euclidean distance to the origin. Thus this is the same as % calculating their x-y norm. Than select the biggest norm in both sets of % points. -> maxUpperDist and maxUnderDist upperX2 = upperX(:,1:2); 211 upperDist = sqrt(sum(upperX2.^2,2)); maxUpperDist = max(upperDist); underX2 = underX(:,1:2); underDist = sqrt(sum(underX2.^2,2)); maxUnderDist = max(underDist); % The proximal part (natural highest part) has a smaller norm in the x-y % plane, so if we detect the smallest norm, we know that this corresponds to % the proximal half of the radius. if maxUpperDist > maxUnderDist % The above condition states that the undermost half, underX, % corresponds to the proximal part. Therefore a rotation around the y-axis % of 180 degrees is executed. [x, y, z, roty] = rotation_Y_axe(x,y,z,pi); n = roty * n; temp1 = (roty * temp1')'; temp2 = (roty * temp2')'; end %The correct specification of the two extreme points on the principal axis. %proximal represents the most proximal point on this axis and distal the %most distal. The current updown orientation of the bone is correct, thus %we can say that the extreme point with the biggest z-value is proximal. if temp1(3) > temp2(3) proximal = temp1; distal = temp2; else proximal = temp2; distal = temp1; end %Do a translation defined by the most distal point, distal. This %translation will be in the direction of the z-axis. [x,y,z,proximal] = translation(x,y,z,distal,proximal); %list representation of the currently oriented radius. X = unique([x(:) y(:) z(:)],'rows'); %Step 5 %Determine whether it is a right or a left radius. % The point with the smallest z-coordinate, this value will be 0 in the % current orientation. lowest = X( find( X(:,3) == min(X(:,3))),:); %Do a rotation around the z-axis in order to get lowest in the x-z plane. hoekz = -atan(lowest(2)/lowest(1)); %the angle [x, y, z, rotz] = rotation_Z_axe(x,y,z,hoekz); n =rotz*n; lowest = (rotz * lowest')'; % We want lowest to have a negative x-value, so if its x-value is positive % -> rotate the model over 180 degrees around the z-axis. 212 if lowest(1) > 0 [x, y, z, rotz] = rotation_Z_axe(x,y,z,pi); n =rotz*n; lowest = (rotz * lowest')'; end %list representation of the currently oriented radius. X = unique([x(:) y(:) z(:)],'rows'); %Search for the point with the biggest x-y norm in the proximal fourth of %the bone. This point is situated on the radial tuberosity. The y-value of %this point will give us the info to decide upon the side of the bone. upperX = X( find( X(:,3) < max(X(:,3)) & X(:,3) > 0.75 * max(X(:,3)) ),:); upperX2 = upperX(:,1:2); upperDist = sqrt(sum(upperX2.^2,2)); radTub = upperX( find( upperDist == max(upperDist)),:); %In the current orientation, with lowest on the negative x-axis, the radial %tuberosity, radTub, should have a negative y-value. Thus if the y-value of %radTub is positive, we can decide that the bone is a leftsided one. In %this case we do a mirror operation of the entire bone with respect to the %x-z plane. if radTub(2) > 0 %a left bone side =1; y = -y; radTub(2) = -radTub(2); else % a right bone side = 0; end % %Step 6 %Last orientation fact: make the orientation equal to the one in the body. %That is: the radial tuberosity is pointing medialwards. For this to happen %we force the radial tuberosity into the x-z plane on the side of the %positive x-axis. This done by a rotation around the z-axis. hoekz = -atan(radTub(2)/radTub(1)); [x, y, z, rotz] = rotation_Z_axe(x,y,z,hoekz); n =rotz*n; radTub = (rotz * radTub')'; %If after the rotation the radial tuberosity lies on the negative x-axis, %that means it points lateralwards. Another rotation of 180 degrees %around the z-axis is needed in this case. if radTub(1) < 0 [x, y, z, rotz] = rotation_Z_axe(x,y,z,pi); n =rotz*n; end B.6.2 headRadius.m function [headRad radiusHeadRad] = headRadius(X) %HEADRADIUS CALCULATES THE POSITION OF THE CENTER AND THE RADIUS OF THE %SPHERE THAT IS FITTED WITH THE POINTS OF THE SUPERIOR SURFACE OF THE %RADIAL HEAD. 213 % %OUTPUT: % headRad is a 1-by-3 array of the coordinates of the center of the % fitted sphere. % % radiusHeadRad represents the radius of the fitted sphere. % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the right oriented radius. % % --------% % author : Bart Coppieters % % % Step 1 % Defining the center point of the cup, orig. %List representation of the highest 20% of the oriented radius. upperX = X(find(X(:,3) > max(X(:,3)) * 0.8) , :); [coeff,score] = princomp(upperX); %Predefined function of the Matlab-library to carry out the principal %component analysis of the data points in upperX. %coeff will give the main 3 principal components, score will give %the coordinates of each point in upperX with respect to the base formed by %the 3 principal components, saved in coeff. meanX = mean(upperX,1); dirVect = coeff(:,1); %The principal axis of the data. a = min(score(:,1)); %The smallest coordinate on dirVect b = max(score(:,1)); %The biggest coordinate on dirVect %The two extreme points on the principal axis. temp1 = b*dirVect' + meanX; temp2 = a*dirVect' + meanX; %orig has to be the extreme point with the biggest z-value. if temp1(3) > temp2(3) orig = temp1; else orig = temp2; end % Step 2 % list representation of the top 5% of the oriented radius. upperX = X(find(X(:,3) > max(X(:,3)) * 0.95) , :); % Calculate for each point of upperDist its orthogonal distance to the axis % parallel to the z-axis that contains orig. This is the same as the % distance to orig in the x-y plane. upperX2 = upperX(:,1:2); %two dimensional equivalent of upperX in the x-y %plane % upperDist is the list with distances to orig in the x-y plane. origM = repmat(orig(1:2),length(upperX2),1); upperDist = sqrt(sum((upperX2-origM).^2,2)); %Step 3 214 % Define the list of data that will be fitted to the sphere % fitX will be the list that is given to the function carrying out the % least square fitting to a sphere. % Select only those points of upperX, that have a x-y distance to % orig of less than 30 % the maximal horizontal distance to orig. fitX = upperX( find( upperDist < max(upperDist) * 0.3),:); % Step 4 %The fitting phase %MATLAB FILE EXCHANG CENTRE %The following function is downloaded from the Matlab file exchange centre %on the official Matlab site. It takes a list of points, in this case fitX %and some initialization parameters as input and produces the center and %the radius of the sphere that best fits the input data. [x0n, rn] = lssphere(fitX, mean(fitX)', 10, 0.1, 0.1); headRad = x0n'; %the position of the center of the fitted sphere radiusHeadRad = rn; %the radius of the fitted sphere. B.6.3 cylRadius.m function [radius under upper] = cylRadius(X) %CYLRADIUS CALCULATES THE RADIUS, THE UNDERMOST AND THE UPPERMOST POINT %OF THE CYLINDER THAT APPROXIMATES THE RADIAL HEAD PERIPHERY. % %OUTPUT: % under and upper are 1-by-3 arrays of the coordinates of respectively % the under- and uppermost point on the axis of the fitted cylinder. % % radius represents the radius of the fitted cylinder % %INPUT: % X is a m-by-3 array of the coordinates of the m datapoints that % constitute the oriented right radius. % --------% % author : Bart Coppieters % % % Step 1 % Defining the center point of the cup, orig. %List representation of the highest 20% of the oriented radius. upperX = X(find(X(:,3) > max(X(:,3)) * 0.8) , :); [coeff,score] = princomp(upperX); %Predefined function of the Matlab-library to carry out the principal %component analysis of the data points in upperX. %coeff will give the main 3 principal components, score will give %the coordinates of each point in upperX with respect to the base formed by %the 3 principal components, saved in coeff. meanX = mean(upperX,1); dirVect = coeff(:,1); %The principal axis of the data. 215 a = min(score(:,1)); %The smallest coordinate on dirVect b = max(score(:,1)); %The biggest coordinate on dirVect %The two extreme points on the principal axis. temp1 = b*dirVect' + meanX; temp2 = a*dirVect' + meanX; %orig has to be the extreme point with the biggest z-value. if temp1(3) > temp2(3) upper = temp1; else upper = temp2; end % Step 2 % list representation of the top 5% of the oriented radius. upperX = X(find(X(:,3) > max(X(:,3)) * 0.95) , :); % Calculate for each point of upperDist its orthogonal distance to the axis % parallel to the z-axis that contains orig. This is the same as the % distance to orig in the x-y plane. upperX2 = upperX(:,1:2); %two dimensional equivalent of upperX in the x-y %plane % upperDist is the list with distances to orig in the x-y plane. origM = repmat(upper(1:2),length(upperX2),1); upperDist = sqrt(sum((upperX2-origM).^2,2)); % Step 3 % Define the list of data that will be fitted to the cylinder. % fitX will be the list that is given to the function carrying out the % least square fitting to a cylinder. % Select only those points of upperX, that have a x-y distance to % upper of more than 80 % the maximal horizontal distance to upper. fitX = upperX( find( upperDist > max(upperDist) * 0.8),:); % Step 4 %MATLAB FILE EXCHANGE CENTRE %The fitting phase %The following function is downloaded from the Matlab file exchange centre %on the official Matlab site. It takes a list of points, in this case fitX %and some initialization parameters as input and produces the center, the %radius and the direction of the axis of the cylinder that best fits the %input data. x0 = upper'; a0 = [0 0 1]'; [x0n, an, rn] = lscylinder(fitX, x0, a0, max(upperDist), 0.1, 0.1); % Definitions of the output arguments. radius = rn; %Assuring the direction has an upwards pointing orientation. %Only important in defining under. if dirVect(3) < 0 dirVect = -dirVect; end % under lies 5% of the total length of the radius lower than upper while % following the direction of dirVect of step 1. under = upper - 0.05 * max(X(:,3)) * dirVect'; 216 B.6.4 styloidRadius.m function [stylRad] = styloidRadius(X) %STYLOIDRADIUS CALCULATES THE POSITION OF THE TIP OF THE STYLOID PROCESS OF %THE 3D MODEL OF THE ORIENTED RIGHT RADIUS. % %OUTPUT: % stylRad is a 1-by-3 array of the coordinates of the tip of the styloid % process of the oriented right radius. % %INPUT: % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the oriented right radius. % --------% % author : Bart Coppieters % % %The most distal point or in other words the point with the smallest %z-value. stylRad = X(find( X(:,3) == min(X(:,3))),:); 217 B.7 General functions In this section come some functions used by all the functional procedures of the 5 bone types. B.7.1 rotation_X_axe This is the function responsible for a rotation around the x-axis. The functions of the rotations around the y-axis and z-axis are the same apart from some parameters. function varargout = rotation_X_axe(varargin) %ROTATION_X_AXE DOES A ROTATION AROUND THE X-AXis OF ALL THE POINTS IN 3D % % 2 forms of use: % 1 input for 1 output: % rot_x = rotation_X_axe(hoekx) % 4 inputs for 4 outputs: % [xx, yy, zz, rot_x] = rotation_X_axe(x, y, z, hoekx) % %OUTPUT: variable % 1 output: rot_x is a 3-by-3 rotation matrix % % 4 outputs: % xx, yy and zz are 3-by-m matrices of respectively the x-, y- and % z-coordinates of the points that constitute the facets of the rotated % 3D model. % rot_x is a 3-by-3 rotation matrix defined by the angle hoekx. % %INPUT: variable % 1 input: hoekx is the angle in rad over which will be rotated. % % 4 inputs: % x, y and z are 3-by-m matrices of respectively the x-, y- and % z-coordinates of the points that constitute the facets of the % unrotated 3d-model. % hoekx is the angle in rad over which will be rotated. % % % --------% % author : Bart Coppieters % % %input if length(varargin)==1 hoekx = varargin{1}; x = 0; y = 0; z = 0; elseif length(varargin)==4 x = varargin{1}; y = varargin{2}; z = varargin{3}; 218 hoekx = varargin{4}; end [hx bx] = size(x); [hy by] = size(y); [hz bz] = size(z); xx = zeros(hx,bx); yy = zeros(hy,by); zz = zeros(hz,bz); rot_x = [1 0 0;0 cos(hoekx) sin(hoekx); 0 -sin(hoekx) cos(hoekx)]; % creation of the rotation matrix defined by hoekx % carry out the rotation for every point if(hx==hy && hx==hz && bx==by && bx==bz) for i=1:hx for j=1:bx gedraaid = rot_x * [x(i,j); y(i,j); z(i,j)]; xx(i,j) = gedraaid(1); yy(i,j) = gedraaid(2); zz(i,j) = gedraaid(3); end end end %output if nargout == 1 varargout{1} = rot_x; elseif nargout == 4 varargout{1} = xx; varargout{2} = yy; varargout{3} = zz; varargout{4} = rot_x; end B.7.2 translation.m function [xx,yy,zz,bb] = translation(x,y,z,a,b) %TRANSLATION DOES A TRANSLATION OF ALL THE POINTS IN 3D DEFINED BY INPUT %ARGUMENT A. % %OUTPUT: % xx, yy and zz are 3-by-m matrices of respectively the x-, y- and % z-coordinates of the points that constitute the facets of the translated % 3D model, thus the patch representation. % bb is the translated 1-by-3 array of a chosen point of interest. % %INPUT: % x, y and z are 3-by-m matrices of respectively the x-, y- and % z-coordinates of the points that constitute the facets of the untranslated % 3D model, thus the patch representation. % a is a 1-by-3 array of the coordinates of the point that will define 219 % % % % % % % % % the translation. b is a 1-by-3 array of the coordinates of a chosen point of interest. --------author : Bart Coppieters [hx bx] = size(x); [hy by] = size(y); [hz bz] = size(z); % the output variables. xx = zeros(hx,bx); yy = zeros(hy,by); zz = zeros(hz,bz); % the linear translation, defined by a. xx = x - a(1) * ones(size(x)); yy = y - a(2) * ones(size(x)); zz = z - a(3) * ones(size(x)); % the translation of b, a point of interest. bb = b - a; B.7.3 localPatchLimit function [xresult yresult zresult nresult] = ... localPatchLimit(x, y, z, n, xmax, xmin, ymax, ymin, zmax, zmin) %FUNCTION TO CONSTRUCT A PATCH OBJECT CONSTRAINED BY LIMITS. biggestx = max(x); smallestx = min(x); biggesty = max(y); smallesty = min(y); biggestz = max(z); smallestz = min(z); logicbigx = logicsmallx logicbigy = logicsmally logicbigz = logicsmallz biggestx <= = smallestx biggesty <= = smallesty biggestz <= = smallestz xmax; >= xmin; ymax; >= ymin; zmax; >= zmin; M = [logicbigx;logicsmallx;logicbigy;logicsmally ;logicbigz ;logicsmallz]; Mresult = M(1,:) & M(2,:) & M(3,:) & M(4,:) & M(5,:) & M(6,:); find(Mresult == 1) xresult = x(:,find(Mresult == 1)); yresult = y(:,find(Mresult == 1)); 220 zresult = z(:,find(Mresult == 1)); nresult = n(:,find(Mresult == 1)); B.7.4 localPatchBunch function [xresult yresult zresult nresult] = localPatchBunch(x,y,z,n, X) %FUNCTION TO CONSTRUCT A PATCH OBJECT DEFINED BY A SET OF POINTS. nVertices = length(X); setFaces = []; %Look for each vertex to which faces it belongs and store this information %in a cell array. for i = 1:nVertices xVertex = X(i,1); yVertex = X(i,2); zVertex = X(i,3); ind1 = find(x == xVertex); ind2 = find(y == yVertex); ind3 = find(z == zVertex); %if the 3 index-matrices are the same, just use one of them to specify %to which faces the vertex belongs if length(ind1) == length(ind2) && length(ind1) == length(ind3) faces = floor((ind1-1)/3)+1; %if they are different in length, the shortest one will determine the %indices of the faces they are member of. else l1 = length(ind1); l2 = length(ind2); l3 = length(ind3); l = [l1 l2 l3]; lmin = find(l == min(l)); switch lmin(1) case 1 faces = floor((ind1-1)/3)+1; case 2 faces = floor((ind2-1)/3)+1; case 3 faces = floor((ind3-1)/3)+1; end end setFaces = [setFaces; faces]; end setFaces = unique(setFaces, 'rows'); xresult = x(:,setFaces); yresult = y(:,setFaces); zresult = z(:,setFaces); nresult = n(:,setFaces); 221 Appendix C: The Stl format Based on [15]. C.1 Format Specifications An StL file consists of a list of facet data. Each facet is uniquely identified by a unit normal and by three vertices. The normal and each vertex are specified by three coordinates each, so there is a total of 12 numbers stored for each facet. Facet orientation. The facets define the surface of a 3-dimensional object. As such, each facet is part of the boundary between the interior and the exterior of the object. The orientation of the facets (which way is “out” and which way is “in”) is specified redundantly in two ways which must be consistent. First, the direction of the normal is outward. Second, the vertices are listed in counterclockwise order when looking at the object from the outside (right-hand rule). These rules are illustrated in Figure C.1. 1 3 2 Figure C.1: A facet with its outward pointing normal. 222 Vertex-to-vertex rule. Each triangle must share two vertices with each of its adjacent triangles. In other words, a vertex of one triangle cannot lie on the side of another. This is illustrated in Figure C.2. Figure C.2: Left a violation of the rule. Right the same corrected structure. C.2 Format Specifications The StL standard includes two data formats, ASCII and binary. Only binary will be described, because that is the only format we have used. The binary format uses the IEEE integer and floating point numerical representation. The syntax for a binary StL file is as follows: Bytes Data type Description 80 ASCII Header. No data significance 4 Unsigned long integer Number of facets in file. 12 Float i j for normal k 12 Float 223 12 Float x y for vertex 2 z 12 Float x y for vertex 3 z 2 Unsigned integer Attribute byte count ….. The above in blue corresponds to one facet and therefore has to be repeated more times for each facet of the model. 224 Appendix D: Curvature D.1 Matlab implementation The used algorithm can be found in [12]. Executing this algorithm calculates the mean curvature as well as the gaussian curvature. In our work we will only use the mean curvature. In D.1.1 the general framework of invoked functions is written. In the subsequent paragraphs the functions used in D.1.1 are outlined. A thorough explanation is not given because the curvature calculation is not a real objective of the work. D.1.1 The general framework: calculateCurvature.m function cfinal = calculateCurvature(x,y,z,n,neighbors) %CALCULATECURVATURE WILL CARRY OUT THE MEAN CURVATURE CALCULATION FOR A %TRIANGULAR 3D MODEL % %OUTPUT: % cfinal is a 3-by-N matrix that contains for each vertex (point) its % mean curvature value. %INPUT: % x, y and z are 3-by-N matrices that together form the original input % patch object of the 3D model. % % n is a 3-by-N matrix representing the normalized normals to the facets % of the original input patch object of the 3D model. % % with N the number of facets of the 3D model. % % neighbors represents the neighborhood with which the calculation is % executed. % --------% % author : Bart Coppieters % %list representation of the 3D model X = unique([x(:) y(:) z(:)],'rows'); %function: see vertexInFaces A = vertexInFaces(x, y, z); %function: see pointNormal N = pointNormal(x, y, z, A, n); %function: see findPointNeigh 225 V = findPointNeigh(x, y, z, A, neighbors); %function: see curvature C = curvature(N, V, X); %function: see patchCurv c = patchCurv(x,y,z,C); %function: see removeOutliers cfinal = removeOutliers(c); D.1.2 vertexInFaces.m function A = vertexInFaces(x, y, z) %VERTEXINFACES CALCULATES FOR EACH VERTEX THE FACETS IT BELONGS TO. % %OUTPUT: % A is a cell array that contains in each row the facets of the vertex % the row corresponds to. %INPUT: % x, y and z are 3-by-N matrices that together form the original input % patch object of the 3D model. % % with N the number of facets of the 3D model. % % --------% % author : Bart Coppieters % %list representation of the 3D model X = unique([x(:) y(:) z(:)],'rows'); %the number of vertices nVertices = length(X); A = cell(nVertices,1); h = waitbar(0, 'Please wait...'); set(h,'Name','vertex in faces (1/6)'); %Look for each vertex to which faces it belongs and store this information %in a cell array. for i = 1:nVertices xVertex = X(i,1); yVertex = X(i,2); zVertex = X(i,3); ind1 = find(x == xVertex); ind2 = find(y == yVertex); ind3 = find(z == zVertex); %if the 3 index-matrices are the same, just use one of them to specify %to which faces the vertex belongs if length(ind1) == length(ind2) && length(ind1) == length(ind3) faces = floor((ind1-1)/3)+1; 226 %if they are different in length, the shortest one will determine the %indices of the faces the vertex is a member of. else l1 = length(ind1); l2 = length(ind2); l3 = length(ind3); l = [l1 l2 l3]; lmin = find(l == min(l)); switch lmin(1) case 1 faces = floor((ind1-1)/3)+1; case 2 faces = floor((ind2-1)/3)+1; case 3 faces = floor((ind3-1)/3)+1; end end % populate the cell array A. A{i} = faces'; waitbar(i/nVertices, h); end close(h); D.1.3 pointNormal.m function N = pointNormal(x, y, z, A, n) % POINTNORMAL computes the normal in every point of the triangular model % as a weighted sum of the normals of its neighbouring faces. % %OUTPUT: % N is a cell array that contains in each row the 3 coordinates of % a point and and the 3 coordinates of its normal. %INPUT: % x, y and z are 3-by-N matrices that together form the original input % patch object of the 3D model. % % n is a 3-by-M matrix representing the normalized normals to the facets % of the original input patch object of the 3D model. % % with M the number of facets of the 3D model. % % % % % % A is the output cell array of function vertexInFaces. --------author : Bart Coppieters X = unique([x(:) y(:) z(:)],'rows'); 227 nVertices = length(X); N = cell(nVertices, 2); h = waitbar(0, 'Please wait...'); set(h,'Name','pointNormal (2/5)'); for i = 1:nVertices faces = A{i}; p = X(i,:); % normals = zeros(3,length(faces)); normals = n(:,faces); W = zeros(1,length(faces)); %make a vector of which each element is a weight that will be used to %determine the normal in the point. for k = 1:length(faces) g = sum([x(:,faces(k)) y(:,faces(k)) z(:,faces(k))])/3; W(k) = 1/norm(g - p); end W = repmat(W,3,1); N{i,1} = p; N{i,2} = sum((W .* normals),2)'/norm(sum((W .* normals),2)); waitbar(i/nVertices, h); end close(h) D.1.4 findPointNeigh.m function V = findPointNeigh(x, y, z, A, buren) % FINDPOINTNEIGH serves to find for each point in the triangular mesh its % neighbouring points. % %OUTPUT: % V is a cell array of cell arrays with in each cell array (the row) the % coordinates of the point of which you want to find the neighbours and the % coordinates of the neighbours of the point. %INPUT: % x, y and z are 3-by-N matrices that together form the original input % patch object of the 3D model. % % with N the number of facets of the 3D model. % % A is the output cell array of function vertexInFaces. % % % buren is a number that will define the neighborhood in which to find % for neighbors. For example, a neighborhood of 1 defines all the % vertices connected directly to the point, a neighborhood of 2 defines 228 % % % % % the vertices connected to the point by 1 or 2 edges.... --------author : Bart Coppieters X = unique([x(:) y(:) z(:)],'rows'); nVertices = length(X); V = cell(nVertices,1); h = waitbar(0, 'Please wait...'); set(h,'Name','findPointNeigh (3/5)'); % For each point p in the mesh: % -reveil its neighbouring faces. % -look for the points the neighbouring triangles consist of. % -make a unique list of these points with p the first element. % -convert each list (matrix) point for point in a cell array. for i = 1:nVertices faces = A{i}; p = X(i,:); Vi = cell(1, length(faces) + 1); vList = zeros(length(faces)*3,3); for k = 1:length(faces) vList(3*k-2 : 3*k, :) = [x(:,faces(k)) y(:,faces(k)) z(:,faces(k))]; end vList = unique(vList, 'rows'); ind = find(vList(:,1) == p(1,1) & vList(:,2) == p(1,2) & vList(:,3) == p(1,3)); vList = [vList(ind:end,:);vList(1:ind-1,:)]; for l = 1:length(vList) Vi{1, l} = vList(l,:); end V{i} = Vi; waitbar(i/nVertices, h); end close(h) if buren > 1 %function: see extendList V = extendList(V, X, buren); end 229 D.1.4.1 extendList function VV = extendList(V, X, buren) %EXTENDLIST EXTENDS FOR EACH POINT ITS ORIGINAL 1_NEIGHBORHOOD LIST TO A %BUREN_NEIGHORHOOD LIST % %OUTPUT: % VV is a cell array of cell arrays with in each row the % buren_neighborhood of the point associated to the row. %INPUT: % V is a cell array of cell arrays with in each row the 1_neighborhood of % the point associated to the row. % % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the 3D model. % % buren is a number that will define the neighborhood in which to find % for neighbors. For example, a neighborhood of 1 defines all the % vertices connected directly to the point, a neighborhood of 2 defines % the vertices connected to the point by 1 or 2 edges.... % --------% % author : Bart Coppieters % nVertices = length(V); h = waitbar(0, 'Please wait...'); set(h,'Name','extendList (3/6)'); for i = 1:nVertices vActual = V{i}; count = buren; while count - 1 > 0 n1buren = length(vActual) - 1; nextburen = [vActual{1}]; for j = 1:n1buren ind = find(X(:,1) == vActual{j+1}(1) & ... X(:,2) == vActual{j+1}(2) & X(:,3) == vActual{j+1}(3)); nextburen = [nextburen; V{ind}']; end nextburenM = cell2mat(nextburen); nextburenM = unique(nextburenM,'rows'); ind = find(nextburenM(:,1) == vActual{1}(1) & nextburenM(:,2) ... == vActual{1}(2) & nextburenM(:,3) == vActual{1}(3)); nextburenM = [nextburenM(ind:end,:);nextburenM(1:ind-1,:)]; vActual = mat2cell(nextburenM, ... [repmat(1,length(nextburenM),1)],[3])'; VV{i,1} = vActual; count = count -1; end waitbar(i/nVertices); end 230 close(h) D.1.5 curvature.m function C = curvature(N, V, X) %VERTEXINFACES serves to find for each point in the triangular mesh its % neighbouring points. % %OUTPUT: % C is a m-by-5 matrix that contains in the first 3 columns the coordinates % of the points, in the 4th column the gaussian curvature and in the 5th % column the mean curvature. %INPUT: % N is a cell array that contains in each row the 3 coordinates of % a point and and the 3 coordinates of its normal. % % V is a cell array of cell arrays with in each row the selected % neighborhood of the point associated to the row. % % X is a m-by-3 matrix of the coordinates of the m datapoints that % constitute the 3D model. % --------% % author : Bart Coppieters % nVertices = length(N); C = zeros(nVertices, 5); h = waitbar(0, 'Please wait...'); set(h,'Name','curvature (4/5)'); % for each point p: % -look for its neighbouring points pi -> pList % -compute for each point pi the unit length projection of pi-p % onto the tangent plane: pi -> ti % -compute the normal curvature in direction ti. % -look at the maximum normal curvature. The corresponding % direction is the first estimate of a principal direction. for i = 1:nVertices pList = V{i}; p = pList{1}; %the central point for which we calculate the curvature n = N{i, 2}; %its normal max = -inf; normal_curvatures = zeros(1, length(pList) - 1); tDirections = cell(1, length(pList) - 1); for k = 1:length(pList)-1 pi = pList{k+1}; ti = ((pi-p) - dot(pi - p, n)*n)/norm(((pi-p) - dot(pi - p, n)*n)); 231 tDirections{1, k} = ti; ind = find(X(:,1) == pi(1,1) & X(:,2) == pi(1,2) & X(:,3) == pi(1,3)); ni = N{ind,2}; %normal in point pi normal_curvature_ti = - dot(pi - p, ni - n)/dot(pi - p, pi - p); normal_curvatures(k) = normal_curvature_ti; % looking for the maximum if normal_curvature_ti > max max = normal_curvature_ti; e1 = ti; index = k; end end normal_curvatures(1, index) = 0; normal_curvatures = nonzeros(normal_curvatures)'; a = max; e2 = cross(e1, n)/norm(cross(e1, n)); thetas = zeros(1, length(tDirections)-1); plek = 1;%% for l = 1:length(tDirections) t = tDirections{1, l}; theta = acos(dot(e1, t)); if norm(theta) > power(10,-6) thetas(plek) = theta; plek = plek+1; end end a11 = sum(cos(thetas).^2 .* sin(thetas).^2); a12 = sum(cos(thetas) .* sin(thetas).^3); a21 = a12; a22 = sum(sin(thetas).^4); a13 = sum((normal_curvatures a*cos(thetas).^2).*cos(thetas).*sin(thetas)); a23 = sum((normal_curvatures - a*cos(thetas).^2).*sin(thetas).^2); b = (a13*a22 - a23*a12)/(a11*a22 - a12^2); c = (a11*a23 - a12*a13)/(a11*a22 - a12^2); Kg = (a*c - b*b)/4; H = (a + c) / 2; C(i, 1:3) = p; C(i, 4) = Kg; 232 C(i, 5) =H; waitbar(i/nVertices, h); end close(h) D.1.6 patchCurv.m function c = patchCurv(x,y,z,C) % PATCHCURV makes the information of C (output of function curvature) % consistent with the patch representation of the 3D model. % %OUTPUT: % c is a 3-by-N matrix of the mean curvatures for each point of the model % as structured in the patch representation. %INPUT: % x, y and z are 3-by-N matrices that together form the original input % patch object of the 3D model. % % C is a m-by-5 matrix that contains in the first 3 columns the coordinates % of the points of the 3D model, in the 4th column the gaussian % curvature and in the 5th column the mean curvature. % --------% % author : Bart Coppieters % X = [x(:) y(:) z(:)]; len = length(X); c = zeros(len, 1); h = waitbar(0, 'Please wait...'); set(h,'Name','patchCurv(5/5)'); for i=1:len ind = find(C(:,1) == X(i,1) & C(:,2) == X(i,2) & C(:,3) == X(i,3)); c(i) = C(ind,5); %kolom 5 = mean curvature waitbar(i/len, h); end c = reshape(c,3, length(c)/3); close(h) D.1.7 removeOutliers.m function c_update = removeOutliers(c) % REMOVEOUTLIERS removes the ouliers of c, the input matrix of mean % curvature data. % %OUTPUT: 233 % c_update is a 3-by-N matrix of the mean curvatures after removing the % outliers. %INPUT: % c is a 3-by-N matrix of the mean curvatures for each point of the model % as structured in the patch representation. % % % % % with N the number of facets of the 3D model. --------author : Bart Coppieters c_update = c(:); c_update(find(isnan(c_update) == 1)) = 3; c_update(find(c_update > 3)) = 3; c_update(find(c_update < -3)) = 3; mu = mean(c_update,1); sigma = std(c_update,1); ind1 = find(c_update > mu ind2 = find(c_update < mu + 3*sigma); - 3*sigma); c_update(ind1) = mu + 3*sigma; c_update(ind2) = mu - 3*sigma; c_update = reshape(c_update,3, length(c_update)/3); 234 Bibliography [1] S.Van Sint Jan, Color Atlas of Skeltal landmark Definitions, Edition 1, Elsevier Health Sciences, Amsterdam, (2007), 208 p. [2] F.H. Netter, S. Colacino, Atlas of Human Anatomy, Edition 2, ICON Learning Systems (1998), 525 p. [3] J.W. Rohen, C. Yokochi, E. Lutjen-Drecoll, Color Atlas of Anatomy: A Photographic Study of the Human Body, Edition 6, Lippincott Williams & Wilkins (2006), 528 p. [4] H. Gray, Anatomy of the Human Body, Lea & Febiger, Philadelfia(1918) [5] B.L. Kaptein, F.C.T. van der Helm, Estimating muscle attachment contours by transforming geometrical bone models, Journal of Biomechanics 37(2003), 263–273. [6] C.M.L. Werner, P. Favrea, C. Gerbera, The role of the subscapularis in preventing anterior glenohumeral subluxation in the abducted, externally rotated position of the arm, Clinical Biomechanics, Volume 22, Issue 5 (2006), 495-501 [7] Ryan T. Bicknell et al, Early experience with computer-assisted shoulder hemiarthroplasty for fractures of the proximal humerus: Development of a novel technique and an in vitro comparison with traditional methods, Journal of Shoulder and Elbow Surgery, Volume 16, Issue 3, Supplement 1 (2007), Pages S117-S125 [8] S Gupta, FCT van der Helm, Load transfer across the scapula during humeral abduction, Journal of Biomechanics 37, Issue 7 (2004), 1001–1009 [9] L. A. Murphy, P. J.Prendergast, Acromion-fixation of glenoid components in total shoulder arthroplasty, Journal of Biomechanics 38, Issue 8 (2004), 1702-1711 [10 ] C.G.M.Meskers et al, In vivo estimation of the glenohumeral joint rotation center from scapular bony landmarks by linear regression, Journal of Biomechanics, 31 (1998), 93 – 96 [11] G.Wu et al, ISB recommendation on definitions of joint coordinate systems of various joints for the reporting of human joint motion – Part II: shoulder, elbow , wrist and hand, Journal of Biomechanics, 38 (2005), 981 – 992 [12] Dong et al, Curvatures estimation on triangular mesh, Journal of Zhejiang University Science, 6A (Suppl I) (2005), 128 – 136 235 [13] P. Boileau, G.Walch, The three-dimensional geometry of the proximal humerus, The Journal of Bone and Joint Surgery, vol 79-B , 5 (1997), 857 – 865 [14] Biomed: The free community for biomedical research and technology. http://www.biomedtown.org/biomed_town/B3C_Building/products/VPalp/ [15] StereoLithography Interface Specification, 3D Systems, Inc., October 1989 In general: I.A. Kapandji, Physiology of the Joints (Upper Extremities), edition 5, Churchill Livingstone (1982) , 208 p Matlab® – The language of Technical Computing. http://www.mathworks.com/products/matlab/ 236 237