├── CMakeLists.txt ├── README ├── SupplyMap.h ├── OxygenationMap.h ├── VascularTree.h ├── NodeTable.h ├── TreeDrawer.h ├── SupplyMap.cpp ├── OxygenationMap.cpp ├── TreeDrawer.cpp ├── NodeTable.cpp ├── MersenneTwister.h ├── VascularTree.cpp └── VascuSynth.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT( VascuSynth ) 2 | 3 | FIND_PACKAGE( ITK ) 4 | IF( ITK_FOUND ) 5 | INCLUDE( ${ITK_USE_FILE} ) 6 | ENDIF (ITK_FOUND ) 7 | 8 | ADD_EXECUTABLE(VascuSynth VascuSynth.cpp SupplyMap.cpp OxygenationMap.cpp NodeTable.cpp VascularTree.cpp TreeDrawer.cpp) 9 | 10 | TARGET_LINK_LIBRARIES( VascuSynth ITKCommon ITKIO) -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | VascuSynth - Vascular Tree Synthesis Software 2 | Preet S. Jassi, May 5 2011 3 | 4 | Please visit the Insight Journal and read the paper here http://www.insight-journal.org/browse/publication/794 for proper instructions on how to compile and execute VascuSynth. VascuSynth requires CMake and ITK. Instructions on how to install CMake and ITK are in the paper above. 5 | 6 | After you have installed CMake and ITK, use CMake to create a Makefile by selecting the source in this directory. Using either Visual Studio or the command line, compile VascuSynth. 7 | 8 | Once VascuSynth has compiled, you can issue a command similar to the one below to execute VascuSynth: 9 | 10 | ./VascuSynth paramFiles.txt imageNames.txt 0.04 testNoise.txt 11 | 12 | Please read the Insight Journal paper for any additional information or feel free to contact me! 13 | 14 | 15 | -------------------------------------------------------------------------------- /SupplyMap.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: SupplyMap.h,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #ifndef _supplymap_h 51 | #define _supplymap_h 52 | 53 | #include 54 | #include 55 | 56 | 57 | using namespace std; 58 | 59 | /** \class SupplyMap 60 | * \brief Class that contains info about oxygen supply. 61 | * 62 | * Map that contains info about the oxygen supply in the volume. 63 | * Used to update the oxygenation map when a new candidate node is applied. 64 | */ 65 | class SupplyMap { 66 | public: 67 | double *map_d; 68 | int dim[4]; 69 | //load a file describing a supply map 70 | void loadMap(string filename); 71 | //calculate the reduction to target if supplied becomes a terminal node 72 | double reduction(int supplied[], int target[]); 73 | }; 74 | 75 | #endif -------------------------------------------------------------------------------- /OxygenationMap.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: OxygenationMap.h,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #ifndef _oxygenationmap_h 51 | #define _oxygenationmap_h 52 | 53 | #include 54 | #include 55 | 56 | #include "SupplyMap.h" 57 | #include "MersenneTwister.h" 58 | 59 | using namespace std; 60 | 61 | /** \class OxygenationMap 62 | * \brief Class for the Oxygenation Demand Map 63 | * 64 | * Contains a map of information about the demand for oxygen in the volume 65 | * as well as methods to update the map based on the supply map 66 | */ 67 | class OxygenationMap { 68 | public: 69 | SupplyMap *supply; 70 | 71 | double * map_d; 72 | double * effectiveMap_d; 73 | MTRand rand; 74 | 75 | int dim[3]; 76 | 77 | OxygenationMap(SupplyMap *sMap, int randSeed); 78 | 79 | //load a file containing the description of an oxygenation map 80 | void loadMap(string filename); 81 | //calculate the sum of the current effective map 82 | double sum(); 83 | //selecte a candidate terminal node 84 | void candidate(double sum, int *cand); 85 | //update the effective map based on using cand as a new terminal node 86 | void applyCandidate(int cand[]); 87 | //determine if source is visible from target with respect to the oxygenation map (uses original map - not effective map) 88 | bool visible(double source[], double target[]); 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /VascularTree.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: VascularTree.h,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #ifndef _vasculartree_h 51 | #define _vasculartree_h 52 | 53 | #include "NodeTable.h" 54 | #include "OxygenationMap.h" 55 | 56 | 57 | /** \class VascularTree 58 | * \brief Class that iteratively builds the vascular tree 59 | * 60 | * Iteratively builds the vascular structure by selecting a new candidate 61 | * node and connecting it to an existing edge, creating a bifurcation location. 62 | * Updates the node table with the new candidate node. 63 | */ 64 | class VascularTree{ 65 | public: 66 | NodeTable nt; 67 | OxygenationMap *oxMap; 68 | 69 | double Pperf; 70 | double Pterm; 71 | double Qperf; 72 | double Qterm; 73 | 74 | double rho; 75 | double gamma; 76 | double lambda; 77 | double mu; 78 | 79 | double minDistance; 80 | int numNodes; 81 | 82 | double* perf; 83 | 84 | int closestNeighbours; 85 | 86 | double mapVoxelWidth; //in cm 87 | 88 | VascularTree(OxygenationMap * oxMap, double* perf, double Pperf, double Pterm, double Qperf, double rho, double gamma, double lambda, double mu, double minDistance, int numNodes, double voxelWidth, int closestNeighbours); 89 | 90 | //calculate the distance between to nodes in the node table 91 | double distance(int from, int to); 92 | //calculate the reduced resistence of segment @ id 93 | void calculateReducedResistence(int id); 94 | //calculates the ratio of radii of the segment @ id 95 | void calculateRatios(int id); 96 | //update the tree @ the bifurication point @ id 97 | void updateAtBifurication(int id, int newChild); 98 | 99 | //calculate the radii throughout the tree 100 | void calculateRadius(); 101 | void calculateRadius(int id); 102 | 103 | //calculate the fitness function 104 | double calculateFitness(); 105 | 106 | //When used by local optimization, ignored is the segment to connect to 107 | //otherwise it should be -1; 108 | bool validateCandidate(double* x0, int ignored); 109 | 110 | //connect point to segment through bifPoint 111 | void connectPoint(double* point, int segment, double* bifPoint); 112 | //update the flow throughout the tree 113 | void incrementFlow(int parent, double Qterm); 114 | //the distance between a point and a segment 115 | double pointSegmentDistance(double* x0, int segment); 116 | //optimize the location of a bifurication point for terminal node point and segment 'segment' 117 | double* localOptimization(double * point, int segment, int steps); 118 | //determine if point is in the volume 119 | bool inVolume(double * point); 120 | //try to attach point to the tree 121 | bool connectCandidate(double * point, int steps); 122 | //build the tree 123 | void buildTree(); 124 | }; 125 | 126 | #endif -------------------------------------------------------------------------------- /NodeTable.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: NodeTable.h,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | 51 | #ifndef _nodetable_h 52 | #define _nodetable_h 53 | 54 | #include 55 | #include 56 | 57 | using namespace std; 58 | 59 | /** \class NodeTable 60 | * \brief Class that contains node references. 61 | * 62 | * Class that contains references to all of the nodes in the vascular structure 63 | * as well as the relationship between the nodes (the nodes ancestor and siblings). 64 | * It also contains information about the flow, radius, and reduced resistance. 65 | * It essentially describes the generated vascular structure. 66 | * 67 | * The NodeTable is essentially a vector of arrays. The arrays are a series of doubles 68 | * that correspond to various properties of the nodes in the vascular structure. 69 | * The values that are stored include the node type, position, parent node, leftRatio, 70 | * rightRatio, flow, leftChild, rightChild in that order. 71 | * 72 | */ 73 | class NodeTable { 74 | 75 | public: 76 | //constants used for the type field of a node 77 | static int TERM; 78 | static int ROOT; 79 | static int BIF; 80 | static int FIELDS; 81 | 82 | //copy of entries in the node table before they were modified (used for undos) 83 | map originalEntries; 84 | int length; 85 | bool prepareUndo; 86 | 87 | //current set of nodes 88 | vector nodes; 89 | 90 | NodeTable(); 91 | 92 | //undo utilities 93 | void startUndo(); 94 | void stopUndo(); 95 | void clearUndo(); 96 | void applyUndo(); 97 | 98 | 99 | //setters/getters 100 | int getType(int index); 101 | void setType(int index, int type); 102 | double* getPos(int index); 103 | void setPos(int index, double* source); 104 | int getParent(int index); 105 | void setParent(int index, int parent); 106 | double getLeftRatio(int index); 107 | void setLeftRatio(int index, double ratio); 108 | double getRightRatio(int index); 109 | void setRightRatio(int index, double ratio); 110 | double getFlow(int index); 111 | void setFlow(int index, double flow); 112 | int getLeftChild(int index); 113 | void setLeftChild(int index, int id); 114 | int getRightChild(int index); 115 | void setRightChild(int index, int id); 116 | double getRadius(int index); 117 | void setRadius(int index, double radius); 118 | double getReducedResistance(int index); 119 | void setReducedResistence(int index, double resistance); 120 | 121 | //adds a new node to the node table (makes a copy of *node 122 | void addNode(double* node); 123 | //sets the node specified by index to *node 124 | void setNode(int index, double* node); 125 | //adds a new node to the node table 126 | void addNode(int type, double* pos, int parent, double leftRatio, double rightRatio, double flow, int leftChild, int rightChild); 127 | 128 | //makes a copy of the current node table 129 | NodeTable copy(); 130 | }; 131 | #endif 132 | -------------------------------------------------------------------------------- /TreeDrawer.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: TreeDrawer.h,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #ifndef _treedrawer_h 51 | #define _treedrawer_h 52 | 53 | #include "VascularTree.h" 54 | 55 | #include 56 | 57 | using namespace std; 58 | 59 | /** \class TreeDrawer 60 | * \brief Constructs a volume of the vasculature that can be printed to an image 61 | * 62 | * Converts the vascular structure from the node table into a 3d matrix that can then 63 | * be saved as a 3d image. Also adds noise to the matrix before it is saved as an image. 64 | */ 65 | class TreeDrawer { 66 | public: 67 | double imageVoxelWidth; 68 | double mapVoxelWidth; 69 | VascularTree * vt; 70 | double *c1, *c2; 71 | unsigned char *image; 72 | int dim[3]; 73 | 74 | TreeDrawer(VascularTree * vt, double width, double *corner1, double *corner2); 75 | 76 | /** 77 | * maps a voxel to a point 78 | * 8 subsections in a voxel 79 | * if a voxel has corners (0,0,0) and (1,1,1) 80 | * section 0 has corners (0.0, 0.0, 0.0) and (0.5, 0.5, 0.5) => center = voxel center + (-0.25, -0.25, -0.25) 81 | * section 1 has corners (0.5, 0.0, 0.0) and (1.0, 0.5, 0.5) => center = voxel center + (+0.25, -0.25, -0.25) 82 | * section 2 has corners (0.0, 0.5, 0.0) and (0.5, 1.0, 0.5) => center = voxel center + (-0.25, +0.25, -0.25) 83 | * section 3 has corners (0.5, 0.5, 0.0) and (1.0, 1.0, 0.5) => center = voxel center + (+0.25, +0.25, -0.25) 84 | * section 4 has corners (0.0, 0.0, 0.5) and (0.5, 0.5, 1.0) => center = voxel center + (-0.25, -0.25, +0.25) 85 | * section 5 has corners (0.5, 0.0, 0.5) and (1.0, 0.5, 1.0) => center = voxel center + (+0.25, -0.25, +0.25) 86 | * section 6 has corners (0.0, 0.5, 0.5) and (0.5, 1.0, 1.0) => center = voxel center + (-0.25, +0.25, +0.25) 87 | * section 7 has corners (0.5, 0.5, 0.5) and (1.0, 1.0, 1.0) => center = voxel center + (+0.25, +0.25, +0.25) 88 | */ 89 | void voxelToPoint(int* voxel, int subSection, double*ret); 90 | //checks if a point is inside a given tube 91 | bool inTube(double * point, double * p1, double * p2, double radius); 92 | //checks if a point is in the end of a tube 93 | bool inEnd(double *point, double *p1, double radius); 94 | //checks if a point is in the same region as a tube (used to improve performance) 95 | bool checkTube(double *point, double *to, double *from, double radius); 96 | //determines the value at a specific voxel 97 | unsigned char valueAtVoxel(int* voxel); 98 | 99 | //add some uniform noise to a voxel 100 | unsigned char addNoise_Uniform(unsigned char c, double lb, double ub); 101 | //add some salt&pepper noise to a voxel 102 | unsigned char addNoise_saltPepper(unsigned char c, unsigned char valSalt, double probSalt, unsigned char valPepper, double probPepper); 103 | //add some gaussian noise to a voxel 104 | unsigned char addNoise_gaussian(unsigned char c, double median, double sigma); 105 | 106 | //draw the tree to a matrix 107 | void drawImage(); 108 | //add uniform noise to the matrix 109 | void addNoise_Uniform(double lb, double ub); 110 | //add salt&pepper noise to the matirx 111 | void addNoise_saltPepper(unsigned char valSalt, double probSalt, unsigned char valPepper, double probPepper); 112 | //add gaussian noise to the matrix 113 | void addNoise_gaussian(double median, double sigma); 114 | //add a single shadow @ (x,y,z) 115 | void addShadow(double x, double y, double z, double centerRatio, double r); 116 | //add 'numShadows' shadows to the matix 117 | void addShadows(int numShadows); 118 | //get the value of the matrix at (x,y,z) 119 | unsigned char imageAt(int x, int y, int z); 120 | //copy the matrix 121 | TreeDrawer* copy(); 122 | }; 123 | 124 | #endif -------------------------------------------------------------------------------- /SupplyMap.cpp: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: SupplyMap.cpp,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | #include "SupplyMap.h" 59 | 60 | using namespace std; 61 | 62 | // A parametric macro to allow the use of dynamic multi-dimensional arrays 63 | #define arr(arr,w,x,y,z,dim) *(arr + (z + dim[3]*(y + dim[2]*(x + dim[1]*(w))))) 64 | 65 | 66 | /** 67 | * calculate the reduction to target if supplied becomes a terminal node 68 | */ 69 | double SupplyMap::reduction(int supplied[], int target[]){ 70 | if(supplied[0] == target[0] && supplied[1] == target[1] && supplied[2] == target[2]) 71 | return 0; 72 | 73 | double dist = sqrt(pow((double)(supplied[0] - target[0]), 2) + pow((double)(supplied[1] - target[1]),2 )+ pow((double)(supplied[2] - target[2]), 2)); 74 | double acc = 0; 75 | 76 | if(arr(map_d, target[0], target[1], target[2], dim[3]-1, dim) <= dist) 77 | return 1; 78 | 79 | for(int i = 0; i < dim[3]; i++){ 80 | acc += pow(dist, i)*arr(map_d, target[0], target[1], target[2], i, dim); 81 | } 82 | 83 | acc = 1.0 - (1.0/acc); 84 | acc = (acc < 0 ? 0 : acc); 85 | 86 | 87 | return acc; 88 | } 89 | 90 | /** 91 | * load a file containing the description of an supply map 92 | */ 93 | void SupplyMap::loadMap(string filename){ 94 | 95 | char * tempFilename; 96 | 97 | tempFilename = new char[filename.size()+1]; 98 | strcpy(tempFilename, filename.c_str()); 99 | 100 | int lastIndex = strlen(tempFilename)-1; 101 | 102 | while ((tempFilename[lastIndex] == '\r') || (tempFilename[lastIndex] == '\n') || (tempFilename[lastIndex] == ' ')) { 103 | tempFilename[lastIndex] = '\0'; 104 | lastIndex--; 105 | } 106 | 107 | ifstream mapFile; 108 | mapFile.open(tempFilename, ios::in); 109 | string line; 110 | 111 | if (mapFile.is_open()) { 112 | 113 | // defining i,j,k,l outside of the for stament 114 | // so this is compatable with both VC++ 6.0 and later versions 115 | int i; 116 | int j; 117 | int k; 118 | int l; 119 | 120 | getline(mapFile, line); 121 | char * tok = new char[line.size()+1]; 122 | strcpy(tok, line.c_str()); 123 | 124 | for(i = 0; i < 4; i++){ 125 | char * temp = strtok((i == 0? tok : NULL), " "); 126 | if(temp == NULL) { 127 | break; 128 | } 129 | dim[i] = atoi(temp); 130 | } 131 | 132 | //get the dimensions 133 | map_d = new double[dim[0]*dim[1]*dim[2]*dim[3]]; 134 | 135 | for(i = 0; i < dim[0]; i++) { 136 | for(j = 0; j < dim[1]; j++) { 137 | for(k = 0; k < dim[2]; k++) { 138 | arr(map_d,i,j,k,dim[3]-1,dim) = 0; 139 | } 140 | } 141 | } 142 | 143 | //get the regions 144 | 145 | while(!mapFile.eof()){ 146 | 147 | getline(mapFile, line); 148 | tok = new char[line.size()+1]; 149 | strcpy(tok, line.c_str()); 150 | 151 | int region[6]; 152 | for(i = 0; i < 6; i++){ 153 | char * temp = strtok((i == 0? tok : NULL), " "); 154 | if(temp == NULL) { 155 | break; 156 | } 157 | region[i] = atoi(temp); 158 | } 159 | 160 | if(mapFile.eof()) { 161 | break; 162 | } 163 | 164 | getline(mapFile, line); 165 | tok = new char[line.size()+1]; 166 | strcpy(tok, line.c_str()); 167 | 168 | double *values = new double[dim[3]]; 169 | for(i = 0; i < dim[3]; i++) { 170 | values[i] = atof(strtok((i == 0? tok : NULL), " ")); 171 | } 172 | 173 | for(i = region[0]; i < region[3]; i++) { 174 | for(j = region[1]; j< region[4]; j++) { 175 | for(k = region[2]; k < region[5]; k++) { 176 | for(l = 0; l < dim[3]; l++) { 177 | arr(map_d, i, j, k, l, dim) = values[l]; 178 | } 179 | } 180 | } 181 | } 182 | 183 | } 184 | 185 | 186 | mapFile.close(); 187 | 188 | } else { 189 | 190 | throw "Could not read the SupplyMap file"; 191 | 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /OxygenationMap.cpp: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: OxygenationMap.cpp,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | #include "MersenneTwister.h" 59 | #include "OxygenationMap.h" 60 | #include "SupplyMap.h" 61 | 62 | using namespace std; 63 | 64 | // A parametric macro to allow the use of dynamic multi-dimensional arrays 65 | #define arr(arr,x,y,z,dim) *(arr + (z + dim[2]*(y + dim[1]*(x)))) 66 | 67 | /** 68 | * constructor, takes the supply map and random seed 69 | */ 70 | OxygenationMap::OxygenationMap(SupplyMap *sMap, int randSeed):rand(){ 71 | 72 | supply = sMap; 73 | 74 | //if the user specifies a random seed, then seed the mersenne twister 75 | //random number generator with the seed they have specified. Otherwise 76 | //the seed will be somewhat random (the CPU time). 77 | if (randSeed > 0) { 78 | long unsigned int a = (long unsigned int) randSeed; 79 | rand.seed(a); 80 | } 81 | 82 | 83 | } 84 | 85 | /** 86 | * calculate the sum of the current effective map. This is used to select 87 | * a candidate node that has high demand for oxygen. 88 | */ 89 | double OxygenationMap::sum(){ 90 | double acc = 0; 91 | 92 | for(int i = 0; i < dim[0]; i++){ 93 | for(int j = 0; j < dim[1]; j++){ 94 | for(int k = 0; k < dim[2]; k++) 95 | acc += arr(effectiveMap_d, i, j, k, dim); 96 | } 97 | } 98 | 99 | return acc; 100 | } 101 | 102 | /** 103 | * select a candidate terminal node 104 | */ 105 | void OxygenationMap::candidate(double sum, int *cand){ 106 | //MTRand rand; 107 | double r = rand.rand()*sum; 108 | 109 | double acc = 0; 110 | for(int i = 0; i < dim[0]; i++){ 111 | for(int j = 0; j < dim[1]; j++){ 112 | for(int k = 0; k < dim[2]; k++){ 113 | acc += arr(effectiveMap_d, i, j, k, dim); 114 | if(acc >= r){ 115 | cand[0] = i; 116 | cand[1] = j; 117 | cand[2] = k; 118 | return; 119 | } 120 | } 121 | } 122 | } 123 | 124 | return; 125 | } 126 | 127 | /** 128 | * update the effective map using cand (candidate as a new terminal node 129 | */ 130 | void OxygenationMap::applyCandidate(int cand[]){ 131 | int temp[3]; 132 | for(int i = 0; i < dim[0]; i++){ 133 | for(int j = 0; j < dim[1]; j++){ 134 | for(int k = 0; k < dim[2]; k++){ 135 | temp[0] = i; temp[1] = j; temp[2] = k; 136 | arr(effectiveMap_d, i, j, k, dim) *= supply->reduction(cand, temp); 137 | } 138 | } 139 | } 140 | } 141 | 142 | 143 | /** 144 | * determine if source is visible from target with respect to 145 | * the oxygenation map (uses original map - not effective map) 146 | */ 147 | bool OxygenationMap::visible(double source[], double target[]){ 148 | 149 | 150 | double vect[3]; 151 | vect[0] = target[0] - source[0]; 152 | vect[1] = target[1] - source[1]; 153 | vect[2] = target[2] - source[2]; 154 | 155 | double pos[3]; 156 | pos[0] = source[0]; pos[1] = source[1]; pos[2] = source[2]; 157 | 158 | int voxel[3]; 159 | voxel[0] = (int)(source[0]+0.5); voxel[1] = (int)(source[1]+0.5); voxel[2] = (int)(source[2]+0.5); 160 | 161 | int targetVoxel[3]; 162 | targetVoxel[0] =(int)(target[0]+0.5); targetVoxel[1] =(int)(target[1]+0.5); targetVoxel[2] =(int)(target[2]+0.5); 163 | 164 | int i; 165 | 166 | while( !(voxel[0] == targetVoxel[0] && voxel[1] == targetVoxel[1] && voxel[2] == targetVoxel[2]) && 167 | !((fabs(pos[0] - target[0]) < 1e-10) && (fabs(pos[1] - target[1]) < 1e-10) && (fabs(pos[2] - target[2]) < 1e-10))){ 168 | 169 | double mult = 1e50; 170 | double dir = 0.5; 171 | 172 | for(i = 0; i < 3; i++){ 173 | 174 | if(vect[i] < 0) { 175 | dir = -0.5; 176 | } else { 177 | dir = 0.5; 178 | } 179 | 180 | double singleMult = fabs((voxel[i]-pos[i] + dir)/vect[i]); 181 | 182 | 183 | while (singleMult == 0) { 184 | 185 | // singleMult should always be > 0, this will only present itself as a problem 186 | // when vect[i] < 0 - where pos[i] moves to x.5 - when it should move to x.5 - c 187 | // where c is some small positive number 188 | 189 | dir *= 1.000000001; 190 | singleMult = fabs((voxel[i]-pos[i] + dir)/vect[i]); 191 | 192 | } 193 | 194 | if(singleMult < mult) { 195 | mult = singleMult; 196 | } 197 | 198 | } 199 | 200 | for(i = 0; i < 3; i++){ 201 | pos[i] += mult*vect[i]; 202 | voxel[i] = (int)(pos[i]+0.5); 203 | } 204 | 205 | if(arr(map_d, voxel[0], voxel[1], voxel[2], dim) == 0) { 206 | return false; 207 | } 208 | 209 | } 210 | 211 | return true; 212 | 213 | } 214 | 215 | 216 | /** 217 | * load a file containing the description of an oxygenation map 218 | */ 219 | void OxygenationMap::loadMap(string filename){ 220 | 221 | char * tempFilename; 222 | 223 | tempFilename = new char[filename.size()+1]; 224 | strcpy(tempFilename, filename.c_str()); 225 | 226 | int lastIndex = strlen(tempFilename)-1; 227 | 228 | while ((tempFilename[lastIndex] == '\r') || (tempFilename[lastIndex] == '\n') || (tempFilename[lastIndex] == ' ')) { 229 | tempFilename[lastIndex] = '\0'; 230 | lastIndex--; 231 | } 232 | 233 | ifstream mapFile; 234 | mapFile.open(tempFilename, ios::in); 235 | string line; 236 | 237 | 238 | if(mapFile.is_open()){ 239 | 240 | // defining i,j,k,l outside of the for stament 241 | // so this is compatable with both VC++ 6.0 and later versions 242 | int i; 243 | int j; 244 | int k; 245 | 246 | getline(mapFile, line); 247 | char* tok = new char[line.size()]; 248 | strcpy(tok, line.c_str()); 249 | 250 | for(i = 0; i < 3; i++){ 251 | char * temp = strtok((i == 0? tok : NULL), " "); 252 | if(temp == NULL) { 253 | break; 254 | } 255 | dim[i] = atoi(temp); 256 | } 257 | 258 | map_d = new double[dim[0]*dim[1]*dim[2]]; 259 | effectiveMap_d = new double[dim[0]*dim[1]*dim[2]]; 260 | 261 | for(i = 0; i < dim[0]; i++) { 262 | for(j = 0; j < dim[1]; j++) { 263 | for(k = 0; k < dim[2]; k++){ 264 | arr(map_d, i, j, k, dim) = 0; 265 | arr(effectiveMap_d, i, j, k, dim) = 0; 266 | } 267 | } 268 | } 269 | 270 | while(!mapFile.eof()){ 271 | getline(mapFile, line); 272 | 273 | tok = new char[line.size()]; 274 | strcpy(tok, line.c_str()); 275 | 276 | int region[6]; 277 | for(i = 0; i < 6; i++){ 278 | char * temp = strtok((i == 0? tok : NULL), " "); 279 | if(temp == NULL) { 280 | break; 281 | } 282 | region[i] = atoi(temp); 283 | } 284 | 285 | if(mapFile.eof()) { 286 | break; 287 | } 288 | 289 | getline(mapFile, line); 290 | tok = new char[line.size()]; 291 | strcpy(tok, line.c_str()); 292 | 293 | double value = atof(line.c_str()); 294 | 295 | for(i = region[0]; i < region[3]; i++) { 296 | for(j = region[1]; j< region[4]; j++) { 297 | for(k = region[2]; k < region[5]; k++){ 298 | arr(map_d, i, j,k, dim) = value; 299 | arr(effectiveMap_d, i, j, k, dim) = value; 300 | } 301 | } 302 | } 303 | 304 | } 305 | 306 | mapFile.close(); 307 | 308 | } else { 309 | 310 | //error reading the mapfile 311 | throw "Could not read the OxygenationMap file"; 312 | 313 | 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /TreeDrawer.cpp: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: TreeDrawer.cpp,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #include "TreeDrawer.h" 51 | #include "VascularTree.h" 52 | #include "MersenneTwister.h" 53 | 54 | #include 55 | #include 56 | 57 | using namespace std; 58 | 59 | // A parametric macro to allow the use of dynamic multi-dimensional arrays 60 | #define arr(arr,x,y,z,dim) *(arr + (z + dim[2]*(y + dim[1]*(x)))) 61 | 62 | /** 63 | * Constructor 64 | */ 65 | TreeDrawer::TreeDrawer(VascularTree * _vt, double width, double * corner1, double * corner2){ 66 | vt = _vt; 67 | 68 | imageVoxelWidth = width; 69 | mapVoxelWidth = vt->mapVoxelWidth; 70 | 71 | c1 = new double[3]; 72 | c1[0] = corner1[0]; c1[1] = corner1[1]; c1[2]= corner1[2]; 73 | 74 | c2 = new double[3]; 75 | c2[0] = corner2[0]; c2[1] = corner2[1]; c2[2]= corner2[2]; 76 | 77 | dim[2] = (int) ((fabs(corner2[2] - corner1[2]) * mapVoxelWidth / width) + 1); 78 | dim[1] = (int) ((fabs(corner2[1] - corner1[1]) * mapVoxelWidth / width) + 1); 79 | dim[0] = (int) ((fabs(corner2[0] - corner1[0]) * mapVoxelWidth / width) + 1); 80 | 81 | image = new unsigned char[dim[2]*dim[1]*dim[0]]; 82 | 83 | } 84 | 85 | /** 86 | * maps a voxel to a point 87 | * 8 subsections in a voxel 88 | * if a voxel has corners (0,0,0) and (1,1,1) 89 | * section 0 has corners (0.0, 0.0, 0.0) and (0.5, 0.5, 0.5) => center = voxel center + (-0.25, -0.25, -0.25) 90 | * section 1 has corners (0.5, 0.0, 0.0) and (1.0, 0.5, 0.5) => center = voxel center + (+0.25, -0.25, -0.25) 91 | * section 2 has corners (0.0, 0.5, 0.0) and (0.5, 1.0, 0.5) => center = voxel center + (-0.25, +0.25, -0.25) 92 | * section 3 has corners (0.5, 0.5, 0.0) and (1.0, 1.0, 0.5) => center = voxel center + (+0.25, +0.25, -0.25) 93 | * section 4 has corners (0.0, 0.0, 0.5) and (0.5, 0.5, 1.0) => center = voxel center + (-0.25, -0.25, +0.25) 94 | * section 5 has corners (0.5, 0.0, 0.5) and (1.0, 0.5, 1.0) => center = voxel center + (+0.25, -0.25, +0.25) 95 | * section 6 has corners (0.0, 0.5, 0.5) and (0.5, 1.0, 1.0) => center = voxel center + (-0.25, +0.25, +0.25) 96 | * section 7 has corners (0.5, 0.5, 0.5) and (1.0, 1.0, 1.0) => center = voxel center + (+0.25, +0.25, +0.25) 97 | */ 98 | void TreeDrawer::voxelToPoint(int* voxel, int subSection, double *ret){ 99 | ret[0] = ((double)voxel[0])*imageVoxelWidth / mapVoxelWidth; 100 | ret[1] = ((double)voxel[1])*imageVoxelWidth / mapVoxelWidth; 101 | ret[2] = ((double)voxel[2])*imageVoxelWidth / mapVoxelWidth; 102 | 103 | if(subSection == 0 || subSection == 2 || subSection == 4 || subSection == 6) 104 | ret[0] -= 0.25*imageVoxelWidth/mapVoxelWidth; 105 | if(subSection == 1 || subSection == 3 || subSection == 5 || subSection == 7) 106 | ret[0] += 0.25*imageVoxelWidth/mapVoxelWidth; 107 | if(subSection == 0 || subSection == 1 || subSection == 4 || subSection == 5) 108 | ret[1] -= 0.25*imageVoxelWidth/mapVoxelWidth; 109 | if(subSection == 2 || subSection == 3 || subSection == 6 || subSection == 7) 110 | ret[1] += 0.25*imageVoxelWidth/mapVoxelWidth; 111 | if(subSection == 0 || subSection == 1 || subSection == 2 || subSection == 3) 112 | ret[2] -= 0.25*imageVoxelWidth/mapVoxelWidth; 113 | if(subSection == 4 || subSection == 5 || subSection == 6 || subSection == 7) 114 | ret[2] += 0.25*imageVoxelWidth/mapVoxelWidth; 115 | } 116 | 117 | /** 118 | * checks if a point is inside a given tube 119 | */ 120 | bool TreeDrawer::inTube(double * point, double * p1, double * p2, double radius){ 121 | if(inEnd(point, p1, radius)) 122 | return true; 123 | 124 | double t = -((p2[0] - p1[0])*(p1[0] - point[0]) + 125 | (p2[1] - p1[1])*(p1[1] - point[1]) + 126 | (p2[2] - p1[2])*(p1[2] - point[2])) / 127 | ((p2[0] - p1[0])*(p2[0] - p1[0]) + 128 | (p2[1] - p1[1])*(p2[1] - p1[1]) + 129 | (p2[2] - p1[2])*(p2[2] - p1[2])); 130 | 131 | if (t < 0 || t > 1) { 132 | return false; 133 | } else { 134 | double distance = pow(pow((((p2[0] - p1[0])*t + p1[0]) - point[0]), 2) + 135 | pow((((p2[1] - p1[1])*t + p1[1]) - point[1]), 2) + 136 | pow((((p2[2] - p1[2])*t + p1[2]) - point[2]), 2), 0.5); 137 | distance *= mapVoxelWidth; 138 | 139 | // this should be return (distance <= radius); 140 | if (distance <= radius) { 141 | return true; 142 | } else { 143 | return false; 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * checks if a point is in the end of a tube 150 | */ 151 | bool TreeDrawer::inEnd(double * point, double * p1, double radius){ 152 | 153 | //same as above, no need for if, just return 154 | if(pow(pow(p1[0] - point[0], 2) + 155 | pow(p1[1] - point[1], 2) + 156 | pow(p1[2] - point[2], 2), 0.5)*mapVoxelWidth < radius) 157 | return true; 158 | 159 | return false; 160 | } 161 | 162 | /** 163 | * checks if a point is in the same region as a tube (used to improve performance) 164 | */ 165 | bool TreeDrawer::checkTube(double *point, double *to, double *from, double radius){ 166 | double c1[3]; 167 | c1[0] = (to[0] < from[0]? to[0] : from[0]) - radius/mapVoxelWidth; 168 | c1[1] = (to[1] < from[1]? to[1] : from[1]) - radius/mapVoxelWidth; 169 | c1[2] = (to[2] < from[2]? to[2] : from[2]) - radius/mapVoxelWidth; 170 | 171 | double c2[3]; 172 | c2[0] = (to[0] > from[0]? to[0] : from[0]) + radius/mapVoxelWidth; 173 | c2[1] = (to[1] > from[1]? to[1] : from[1]) + radius/mapVoxelWidth; 174 | c2[2] = (to[2] > from[2]? to[2] : from[2]) + radius/mapVoxelWidth; 175 | 176 | //can do the same thing, return blah 177 | if(point[0] >= c1[0] && point[0] <= c2[0] && 178 | point[1] >= c1[1] && point[1] <= c2[1] && 179 | point[2] >= c1[2] && point[2] <= c2[2]) 180 | return true; 181 | return false; 182 | 183 | } 184 | 185 | /** 186 | * determines the intensity value at a specific voxel 187 | * in the range of 255 to 0 188 | */ 189 | unsigned char TreeDrawer::valueAtVoxel(int* voxel){ 190 | 191 | int value = 0; 192 | int segments[] = {0,1,2,3,4,5,6,7}; 193 | double *point = new double[3]; 194 | int size = (int) vt->nt.nodes.size(); 195 | 196 | for(int j = 1; j < size; j++){ 197 | double * p1 = vt->nt.getPos(j); 198 | int parent = vt->nt.getParent(j); 199 | double * p2 = vt->nt.getPos(parent); 200 | double radius = vt->nt.getRadius(j); 201 | 202 | voxelToPoint(voxel, -1, point); 203 | if(checkTube(point, p1, p2, radius)){ 204 | //for each of the 8 offsets of a point - check if the point is in a tube 205 | for(int i = 0; i < 7; i++){ 206 | if(segments[i] == -1) 207 | continue; 208 | 209 | voxelToPoint(voxel, segments[i], point); 210 | if(inTube(point, p1, p2, radius)){ 211 | value += 32; 212 | segments[i] = -1; 213 | } 214 | } 215 | } 216 | if(value == 256) 217 | break; 218 | } 219 | 220 | value--; 221 | 222 | if (value >= 256) { 223 | value = 255; 224 | } 225 | 226 | if (value < 0) { 227 | value = 0; 228 | } 229 | 230 | delete point; 231 | return (unsigned char)value; 232 | } 233 | 234 | /** 235 | * add some uniform noise to a voxel 236 | */ 237 | unsigned char TreeDrawer::addNoise_Uniform(unsigned char c, double lb, double ub){ 238 | 239 | MTRand rand; 240 | int x = c + (int) (rand.rand()*(ub-lb) + lb); 241 | if (x > 255) { 242 | return (unsigned char)255; 243 | } else if (x < 0) { 244 | return (unsigned char)0; 245 | } else { 246 | return (unsigned char) x; 247 | } 248 | } 249 | 250 | /** 251 | * add some salt&pepper noise to a voxel 252 | */ 253 | unsigned char TreeDrawer::addNoise_saltPepper(unsigned char c, unsigned char valSalt, double probSalt, unsigned char valPepper, double probPepper){ 254 | 255 | MTRand rand; 256 | double r = rand.rand(); 257 | if(r <= probSalt) { 258 | return valSalt; 259 | } else if(r <= probPepper+ probSalt) { 260 | return valPepper; 261 | } else { 262 | return c; 263 | } 264 | } 265 | 266 | /** 267 | * add some gaussian noise to a voxel 268 | */ 269 | unsigned char TreeDrawer::addNoise_gaussian(unsigned char c, double median, double sigma){ 270 | 271 | MTRand rand; 272 | int x = c + (int)(rand.randNorm(median, sigma)); 273 | if(x > 255) { 274 | return (unsigned char)255; 275 | } else if(x < 0) { 276 | return (unsigned char)0; 277 | } else { 278 | return (unsigned char) x; 279 | } 280 | } 281 | 282 | /** 283 | * draw the tree to a matrix 284 | */ 285 | void TreeDrawer::drawImage(){ 286 | int voxel[3]; 287 | for(int i = 0; i < dim[0]; i++){ 288 | for(int j = 0; j < dim[1]; j++) { 289 | for(int k = 0; k < dim[2]; k++){ 290 | voxel[0] = i; voxel[1] = j; voxel[2] = k; 291 | arr(image, i, j, k, dim) = valueAtVoxel(voxel); 292 | } 293 | } 294 | } 295 | } 296 | 297 | /** 298 | * add uniform noise to the matrix 299 | */ 300 | void TreeDrawer::addNoise_Uniform(double lb, double ub){ 301 | 302 | for(int i = 0; i < dim[0]; i++) { 303 | for(int j = 0; j < dim[1]; j++) { 304 | for(int k = 0; k < dim[2]; k++) { 305 | arr(image, i, j, k, dim) = this->addNoise_Uniform(arr(image, i, j, k, dim), lb, ub); 306 | } 307 | } 308 | } 309 | } 310 | 311 | /** 312 | * add salt&pepper noise to the matrix 313 | */ 314 | void TreeDrawer::addNoise_saltPepper(unsigned char valSalt, double probSalt, unsigned char valPepper, double probPepper){ 315 | 316 | for(int i = 0; i < dim[0]; i++) { 317 | for(int j = 0; j < dim[1]; j++) { 318 | for(int k = 0; k < dim[2]; k++) { 319 | arr(image, i, j, k, dim) = this->addNoise_saltPepper(arr(image, i, j, k, dim), valSalt, probSalt, valPepper, probPepper); 320 | } 321 | } 322 | } 323 | 324 | } 325 | 326 | /** 327 | * add gaussian noise to the matrix 328 | */ 329 | void TreeDrawer::addNoise_gaussian(double median, double sigma){ 330 | 331 | for(int i = 0; i < dim[0]; i++) { 332 | for(int j = 0; j < dim[1]; j++) { 333 | for(int k = 0; k < dim[2]; k++) { 334 | arr(image, i, j, k, dim) = this->addNoise_gaussian(arr(image, i, j, k, dim), median, sigma); 335 | } 336 | } 337 | } 338 | 339 | } 340 | 341 | 342 | /** 343 | * add a single shadow @ (x,y,z) 344 | */ 345 | void TreeDrawer::addShadow(double x, double y, double z, double centerRatio, double r){ 346 | 347 | for(int i = 0; i < dim[0]; i++) { 348 | for(int j = 0; j < dim[1]; j++) { 349 | for(int k = 0; k < dim[2]; k++){ 350 | 351 | int voxel[3]; voxel[0] = i; voxel[1] = j; voxel[2] = k; 352 | double point[3]; voxelToPoint(voxel, -1, point); 353 | double dist = sqrt(pow((point[0]-x), 2) + pow((point[1]-y), 2) + pow((point[2]-z), 2)); 354 | 355 | if(dist > r) { 356 | dist = r; 357 | } 358 | 359 | arr(image, i, j, k, dim) *= (dist/r) + (1- dist/r)*centerRatio; 360 | 361 | } 362 | } 363 | } 364 | 365 | } 366 | 367 | /** 368 | * add 'numShadows' shadows to the matrix 369 | */ 370 | void TreeDrawer::addShadows(int numShadows){ 371 | 372 | MTRand rand; 373 | for(int i = 0; i < numShadows; i++){ 374 | 375 | int segment = (int)(rand.rand()*(vt->nt.nodes.size()-1))+1; 376 | double * endPoint = vt->nt.getPos(segment); 377 | double * startPoint = vt->nt.getPos(vt->nt.getParent(segment)); 378 | double length = sqrt(pow((startPoint[0]-endPoint[0]), 2) + pow((startPoint[1]-endPoint[1]), 2) + pow((startPoint[2]-endPoint[2]), 2)); 379 | 380 | double strength = rand.rand(); 381 | this->addShadow((startPoint[0]+endPoint[0])/2, (startPoint[1]+endPoint[1])/2, (startPoint[2]+endPoint[2])/2, strength, length); 382 | } 383 | 384 | } 385 | 386 | /** 387 | * get the value of the matrix at (x,y,z) 388 | */ 389 | unsigned char TreeDrawer::imageAt(int x, int y, int z){ 390 | return arr(image, x, y, z, dim); 391 | } 392 | 393 | /** 394 | * copy the matrix 395 | */ 396 | TreeDrawer* TreeDrawer::copy(){ 397 | TreeDrawer * copy = new TreeDrawer(vt, imageVoxelWidth, c1, c2); 398 | 399 | for(int i = 0; i < dim[0]*dim[1]*dim[2]; i++) { 400 | copy->image[i] = image[i]; 401 | } 402 | 403 | return copy; 404 | } -------------------------------------------------------------------------------- /NodeTable.cpp: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: NodeTable.cpp,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #include "NodeTable.h" 51 | 52 | #include 53 | #include 54 | #include 55 | 56 | using namespace std; 57 | 58 | int NodeTable::TERM = 0; 59 | int NodeTable::ROOT = 1; 60 | int NodeTable::BIF = 2; 61 | int NodeTable::FIELDS = 12; 62 | 63 | /** 64 | * constructor 65 | */ 66 | NodeTable::NodeTable(){ 67 | prepareUndo = false; 68 | } 69 | 70 | /** 71 | * start recording entries for undos 72 | */ 73 | void NodeTable::startUndo(){ 74 | prepareUndo = true; 75 | length = nodes.size(); 76 | } 77 | 78 | /** 79 | * stop recording entries for undo 80 | */ 81 | void NodeTable::stopUndo(){ 82 | prepareUndo = false; 83 | } 84 | 85 | /** 86 | * clear the undo table 87 | */ 88 | void NodeTable::clearUndo(){ 89 | 90 | int size = (int) originalEntries.size(); 91 | for(int i = 0; i < size; i++) { 92 | delete originalEntries[i]; 93 | } 94 | 95 | originalEntries.clear(); 96 | } 97 | 98 | /** 99 | * apply all accumulate undos 100 | */ 101 | void NodeTable::applyUndo(){ 102 | stopUndo(); 103 | map::iterator itr; 104 | for(itr = originalEntries.begin(); itr != originalEntries.end(); itr++){ 105 | double *newNode = new double[FIELDS]; 106 | for(int i = 0; i < FIELDS; i++) 107 | newNode[i] = itr->second[i]; 108 | setNode(itr->first, newNode); 109 | } 110 | 111 | if( (int) nodes.size() > length) { 112 | nodes.resize(length); 113 | } 114 | 115 | clearUndo(); 116 | startUndo(); 117 | } 118 | 119 | /** 120 | * return the type of the node at index 121 | */ 122 | int NodeTable::getType(int index){ 123 | if(index > (int) nodes.size()) { 124 | return -1; 125 | } 126 | return (int)nodes[index][0]; 127 | } 128 | 129 | /** 130 | * set the type of a node at an index 131 | */ 132 | void NodeTable::setType(int index, int type){ 133 | 134 | if(index < (int) nodes.size()) { 135 | 136 | //if this is the first time this entry has been modified - adds its original state to the undo table 137 | if(prepareUndo == true && originalEntries[index] == NULL){ 138 | double * d = new double[FIELDS]; 139 | double * ent = nodes[index]; 140 | for(int i = 0 ; i < FIELDS; i++) 141 | d[i] = ent[i]; 142 | originalEntries[index] = d; 143 | } 144 | 145 | nodes[index][0] = type; 146 | 147 | } 148 | } 149 | 150 | /** 151 | * get the position 152 | */ 153 | double* NodeTable::getPos(int index){ 154 | 155 | if(index > (int) nodes.size()) { 156 | return NULL; 157 | } 158 | 159 | return nodes[index]+1; 160 | } 161 | 162 | /** 163 | * set the position 164 | */ 165 | void NodeTable::setPos(int index, double* source){ 166 | 167 | if (index < (int) nodes.size()) { 168 | 169 | //if this is the first time this entry has been modified - adds its original state to the undo table 170 | if(prepareUndo == true && originalEntries[index] == NULL){ 171 | double * d = new double[FIELDS]; 172 | double * ent = nodes[index]; 173 | for(int i = 0 ; i < FIELDS; i++) 174 | d[i] = ent[i]; 175 | originalEntries[index] = d; 176 | } 177 | 178 | nodes[index][1] = source[0]; 179 | nodes[index][2] = source[1]; 180 | nodes[index][3] = source[2]; 181 | 182 | } 183 | } 184 | 185 | /** 186 | * get the parent of the node at index 187 | */ 188 | int NodeTable::getParent(int index){ 189 | 190 | if(index > (int) nodes.size()) { 191 | return NULL; 192 | } 193 | 194 | return (int) nodes[index][4]; 195 | } 196 | 197 | /** 198 | * set the parent of the node at index 199 | */ 200 | void NodeTable::setParent(int index, int parent){ 201 | 202 | if (index < (int) nodes.size()) { 203 | 204 | //if this is the first time this entry has been modified - adds its original state to the undo table 205 | if(prepareUndo == true && originalEntries[index] == NULL){ 206 | double * d = new double[FIELDS]; 207 | double * ent = nodes[index]; 208 | for(int i = 0 ; i < FIELDS; i++) 209 | d[i] = ent[i]; 210 | originalEntries[index] = d; 211 | } 212 | 213 | nodes[index][4] = parent; 214 | 215 | } 216 | } 217 | 218 | /** 219 | * get the ratio which is used to determine the radii of the branches 220 | * for the left segment 221 | */ 222 | double NodeTable::getLeftRatio(int index){ 223 | 224 | if (index > (int) nodes.size()) { 225 | return NULL; 226 | } 227 | 228 | return nodes[index][5]; 229 | } 230 | 231 | /** 232 | * set the left ratio for a node, ratio used for radii calculations 233 | */ 234 | void NodeTable::setLeftRatio(int index, double ratio){ 235 | 236 | if (index < (int) nodes.size()) { 237 | 238 | //if this is the first time this entry has been modified - adds its original state to the undo table 239 | if(prepareUndo == true && originalEntries[index] == NULL){ 240 | double * d = new double[FIELDS]; 241 | double * ent = nodes[index]; 242 | for(int i = 0 ; i < FIELDS; i++) 243 | d[i] = ent[i]; 244 | originalEntries[index] = d; 245 | } 246 | nodes[index][5] = ratio; 247 | 248 | } 249 | } 250 | 251 | /** 252 | * get the ratio which is used to determine the radii of the branches 253 | * for the right segment 254 | */ 255 | double NodeTable::getRightRatio(int index){ 256 | 257 | if (index > (int) nodes.size()) { 258 | return NULL; 259 | } 260 | 261 | return nodes[index][6]; 262 | } 263 | 264 | /** 265 | * set the right ratio for a node, ratio used for radii calculations 266 | */ 267 | void NodeTable::setRightRatio(int index, double ratio){ 268 | 269 | if (index < (int) nodes.size()) { 270 | 271 | //if this is the first time this entry has been modified - adds its original state to the undo table 272 | if(prepareUndo == true && originalEntries[index] == NULL){ 273 | double * d = new double[FIELDS]; 274 | double * ent = nodes[index]; 275 | for(int i = 0 ; i < FIELDS; i++) 276 | d[i] = ent[i]; 277 | originalEntries[index] = d; 278 | } 279 | nodes[index][6] = ratio; 280 | 281 | } 282 | } 283 | 284 | /** 285 | * get the flow at the node position 286 | */ 287 | double NodeTable::getFlow(int index){ 288 | 289 | if (index > (int) nodes.size()) { 290 | return NULL; 291 | } 292 | 293 | return nodes[index][7]; 294 | } 295 | 296 | /** 297 | * set the flow at the position 298 | */ 299 | void NodeTable::setFlow(int index, double flow){ 300 | 301 | if (index < (int) nodes.size()) { 302 | 303 | //if this is the first time this entry has been modified - adds its original state to the undo table 304 | if(prepareUndo == true && originalEntries[index] == NULL){ 305 | double * d = new double[FIELDS]; 306 | double * ent = nodes[index]; 307 | for(int i = 0 ; i < FIELDS; i++) 308 | d[i] = ent[i]; 309 | originalEntries[index] = d; 310 | } 311 | nodes[index][7] = flow; 312 | 313 | } 314 | } 315 | 316 | /** 317 | * get the left child at a node 318 | */ 319 | int NodeTable::getLeftChild(int index){ 320 | 321 | if(index > (int) nodes.size()) { 322 | return NULL; 323 | } 324 | 325 | return (int) nodes[index][8]; 326 | } 327 | 328 | /** 329 | * set the left child at a node 330 | */ 331 | void NodeTable::setLeftChild(int index, int id){ 332 | 333 | if (index < (int) nodes.size()) { 334 | 335 | //if this is the first time this entry has been modified - adds its original state to the undo table 336 | if(prepareUndo == true && originalEntries[index] == NULL){ 337 | double * d = new double[FIELDS]; 338 | double * ent = nodes[index]; 339 | for(int i = 0 ; i < FIELDS; i++) 340 | d[i] = ent[i]; 341 | originalEntries[index] = d; 342 | } 343 | nodes[index][8] = id; 344 | 345 | } 346 | 347 | } 348 | 349 | /** 350 | * get the right child at a node 351 | */ 352 | int NodeTable::getRightChild(int index){ 353 | 354 | if (index > (int) nodes.size()) { 355 | return NULL; 356 | } 357 | 358 | return (int) nodes[index][9]; 359 | } 360 | 361 | /** 362 | * set the right child at a node 363 | */ 364 | void NodeTable::setRightChild(int index, int id){ 365 | 366 | if(index < (int) nodes.size()) { 367 | 368 | //if this is the first time this entry has been modified - adds its original state to the undo table 369 | if(prepareUndo == true && originalEntries[index] == NULL){ 370 | double * d = new double[FIELDS]; 371 | double * ent = nodes[index]; 372 | for(int i = 0 ; i < FIELDS; i++) 373 | d[i] = ent[i]; 374 | originalEntries[index] = d; 375 | } 376 | nodes[index][9] = id; 377 | 378 | } 379 | } 380 | 381 | /** 382 | * get the radius at a node position 383 | */ 384 | double NodeTable::getRadius(int index){ 385 | 386 | if(index > (int) nodes.size()) { 387 | return NULL; 388 | } 389 | 390 | return nodes[index][11]; 391 | 392 | } 393 | 394 | /** 395 | * set the radius at a node position 396 | */ 397 | void NodeTable::setRadius(int index, double radius){ 398 | 399 | if (index < (int) nodes.size()) { 400 | 401 | //if this is the first time this entry has been modified - adds its original state to the undo table 402 | if(prepareUndo == true && originalEntries[index] == NULL){ 403 | double * d = new double[FIELDS]; 404 | double * ent = nodes[index]; 405 | for(int i = 0 ; i < FIELDS; i++) 406 | d[i] = ent[i]; 407 | originalEntries[index] = d; 408 | } 409 | nodes[index][11] = radius; 410 | 411 | } 412 | } 413 | 414 | /** 415 | * get the reduced resistence for a node at the index 416 | */ 417 | double NodeTable::getReducedResistance(int index){ 418 | 419 | if (index > (int) nodes.size()) { 420 | return NULL; 421 | } 422 | 423 | return nodes[index][10]; 424 | 425 | } 426 | 427 | /** 428 | * set the reduced resistence for a node at the index 429 | */ 430 | void NodeTable::setReducedResistence(int index, double resistance){ 431 | 432 | if (index < (int) nodes.size()) { 433 | 434 | //if this is the first time this entry has been modified - adds its original state to the undo table 435 | if(prepareUndo == true && originalEntries[index] == NULL){ 436 | double * d = new double[FIELDS]; 437 | double * ent = nodes[index]; 438 | for(int i = 0 ; i < FIELDS; i++) { 439 | d[i] = ent[i]; 440 | } 441 | originalEntries[index] = d; 442 | } 443 | nodes[index][10] = resistance; 444 | 445 | } 446 | 447 | } 448 | 449 | /** 450 | * add a new node to the node table 451 | */ 452 | void NodeTable::addNode(double* node){ 453 | double *row = new double[FIELDS]; 454 | for(int i = 0; i < FIELDS; i++) 455 | row[i] = node[i]; 456 | 457 | nodes.push_back(row); 458 | } 459 | 460 | /** 461 | * set a node in the node table 462 | */ 463 | void NodeTable::setNode(int index, double* node){ 464 | //if this is the first time this entry has been modified - adds its original state to the undo table 465 | if(prepareUndo == true && originalEntries[index] == NULL){ 466 | double * d = new double[FIELDS]; 467 | double * ent = nodes[index]; 468 | for(int i = 0 ; i < FIELDS; i++) 469 | d[i] = ent[i]; 470 | originalEntries[index] = d; 471 | } 472 | 473 | if(index >= (int) nodes.size()) { 474 | nodes.resize(index+1); 475 | } 476 | 477 | delete nodes[index]; 478 | nodes[index] = node; 479 | } 480 | 481 | /** 482 | * add a node to the node table and set the values of that node as well 483 | */ 484 | void NodeTable::addNode(int type, double* pos, int parent, double leftRatio, double rightRatio, double flow, int leftChild, int rightChild){ 485 | double * row = new double[FIELDS]; 486 | row[0] = type; 487 | row[1] = pos[0]; 488 | row[2] = pos[1]; 489 | row[3] = pos[2]; 490 | row[4] = parent; 491 | row[5] = leftRatio; 492 | row[6] = rightRatio; 493 | row[7] = flow; 494 | row[8] = leftChild; 495 | row[9] = rightChild; 496 | 497 | nodes.push_back(row); 498 | } 499 | 500 | /** 501 | * copy the node table 502 | */ 503 | NodeTable NodeTable::copy(){ 504 | NodeTable * nt = new NodeTable(); 505 | vector::iterator itr; 506 | 507 | for(itr = nodes.begin(); itr != nodes.end(); itr++) 508 | nt->addNode(*itr); 509 | 510 | return *nt; 511 | } 512 | -------------------------------------------------------------------------------- /MersenneTwister.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: MersenneTwister.h,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | 51 | // MersenneTwister.h 52 | // Mersenne Twister random number generator -- a C++ class MTRand 53 | // Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus 54 | // Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com 55 | 56 | // The Mersenne Twister is an algorithm for generating random numbers. It 57 | // was designed with consideration of the flaws in various other generators. 58 | // The period, 2^19937-1, and the order of equidistribution, 623 dimensions, 59 | // are far greater. The generator is also fast; it avoids multiplication and 60 | // division, and it benefits from caches and pipelines. For more information 61 | // see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html 62 | 63 | // Reference 64 | // M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally 65 | // Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on 66 | // Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. 67 | 68 | // Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 69 | // Copyright (C) 2000 - 2003, Richard J. Wagner 70 | // All rights reserved. 71 | // 72 | // Redistribution and use in source and binary forms, with or without 73 | // modification, are permitted provided that the following conditions 74 | // are met: 75 | // 76 | // 1. Redistributions of source code must retain the above copyright 77 | // notice, this list of conditions and the following disclaimer. 78 | // 79 | // 2. Redistributions in binary form must reproduce the above copyright 80 | // notice, this list of conditions and the following disclaimer in the 81 | // documentation and/or other materials provided with the distribution. 82 | // 83 | // 3. The names of its contributors may not be used to endorse or promote 84 | // products derived from this software without specific prior written 85 | // permission. 86 | // 87 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 88 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 89 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 90 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 91 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 92 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 93 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 94 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 95 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 96 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 97 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 98 | 99 | // The original code included the following notice: 100 | // 101 | // When you use this, send an email to: matumoto@math.keio.ac.jp 102 | // with an appropriate reference to your work. 103 | // 104 | // It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu 105 | // when you write. 106 | 107 | #ifndef MERSENNETWISTER_H 108 | #define MERSENNETWISTER_H 109 | 110 | // Not thread safe (unless auto-initialization is avoided and each thread has 111 | // its own MTRand object) 112 | 113 | #include 114 | #include 115 | #include 116 | #include 117 | #include 118 | 119 | 120 | /** \class MTRand 121 | * \brief Random Number Generator 122 | * 123 | * Random Number Generator 124 | */ 125 | class MTRand { 126 | // Data 127 | public: 128 | typedef unsigned long uint32; // unsigned integer type, at least 32 bits 129 | 130 | enum { N = 624 }; // length of state vector 131 | enum { SAVE = N + 1 }; // length of array for save() 132 | enum { M = 397 }; // period parameter 133 | 134 | uint32 state[N]; // internal state 135 | uint32 *pNext; // next value to get from state 136 | int left; // number of values left before reload needed 137 | 138 | 139 | //Methods 140 | MTRand( const uint32& oneSeed ); // initialize with a simple uint32 141 | MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array 142 | MTRand(); // auto-initialize with /dev/urandom or time() and clock() 143 | 144 | // Do NOT use for CRYPTOGRAPHY without securely hashing several returned 145 | // values together, otherwise the generator state can be learned after 146 | // reading 624 consecutive values. 147 | 148 | // Access to 32-bit random numbers 149 | double rand(); // real number in [0,1] 150 | double rand( const double& n ); // real number in [0,n] 151 | double randExc(); // real number in [0,1) 152 | double randExc( const double& n ); // real number in [0,n) 153 | double randDblExc(); // real number in (0,1) 154 | double randDblExc( const double& n ); // real number in (0,n) 155 | uint32 randInt(); // integer in [0,2^32-1] 156 | uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32 157 | double operator()() { return rand(); } // same as rand() 158 | 159 | // Access to 53-bit random numbers (capacity of IEEE double precision) 160 | double rand53(); // real number in [0,1) 161 | 162 | // Access to nonuniform random number distributions 163 | double randNorm( const double& mean = 0.0, const double& variance = 0.0 ); 164 | 165 | // Re-seeding functions with same behavior as initializers 166 | void seed( const uint32 oneSeed ); 167 | void seed( uint32 *const bigSeed, const uint32 seedLength = N ); 168 | void seed(); 169 | 170 | // Saving and loading generator state 171 | void save( uint32* saveArray ) const; // to array of size SAVE 172 | void load( uint32 *const loadArray ); // from such array 173 | friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); 174 | friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); 175 | 176 | void initialize( const uint32 oneSeed ); 177 | void reload(); 178 | uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; } 179 | uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; } 180 | uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; } 181 | uint32 mixBits( const uint32& u, const uint32& v ) const 182 | { return hiBit(u) | loBits(v); } 183 | uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const 184 | { return m ^ (mixBits(s0,s1)>>1) ^ (-loBit(s1) & 0x9908b0dfUL); } 185 | static uint32 hash( time_t t, clock_t c ); 186 | }; 187 | 188 | inline MTRand::MTRand( const uint32& oneSeed ) 189 | { seed(oneSeed); } 190 | 191 | inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) 192 | { seed(bigSeed,seedLength); } 193 | 194 | inline MTRand::MTRand() 195 | { seed(); } 196 | 197 | inline double MTRand::rand() 198 | { return double(randInt()) * (1.0/4294967295.0); } 199 | 200 | inline double MTRand::rand( const double& n ) 201 | { return rand() * n; } 202 | 203 | inline double MTRand::randExc() 204 | { return double(randInt()) * (1.0/4294967296.0); } 205 | 206 | inline double MTRand::randExc( const double& n ) 207 | { return randExc() * n; } 208 | 209 | inline double MTRand::randDblExc() 210 | { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } 211 | 212 | inline double MTRand::randDblExc( const double& n ) 213 | { return randDblExc() * n; } 214 | 215 | inline double MTRand::rand53() 216 | { 217 | uint32 a = randInt() >> 5, b = randInt() >> 6; 218 | return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada 219 | } 220 | 221 | inline double MTRand::randNorm( const double& mean, const double& variance ) 222 | { 223 | // Return a real number from a normal (Gaussian) distribution with given 224 | // mean and variance by Box-Muller method 225 | double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance; 226 | double phi = 2.0 * 3.14159265358979323846264338328 * randExc(); 227 | return mean + r * cos(phi); 228 | } 229 | 230 | inline MTRand::uint32 MTRand::randInt() 231 | { 232 | // Pull a 32-bit integer from the generator state 233 | // Every other access function simply transforms the numbers extracted here 234 | 235 | if( left == 0 ) reload(); 236 | --left; 237 | 238 | register uint32 s1; 239 | s1 = *pNext++; 240 | s1 ^= (s1 >> 11); 241 | s1 ^= (s1 << 7) & 0x9d2c5680UL; 242 | s1 ^= (s1 << 15) & 0xefc60000UL; 243 | return ( s1 ^ (s1 >> 18) ); 244 | } 245 | 246 | inline MTRand::uint32 MTRand::randInt( const uint32& n ) 247 | { 248 | // Find which bits are used in n 249 | // Optimized by Magnus Jonsson (magnus@smartelectronix.com) 250 | uint32 used = n; 251 | used |= used >> 1; 252 | used |= used >> 2; 253 | used |= used >> 4; 254 | used |= used >> 8; 255 | used |= used >> 16; 256 | 257 | // Draw numbers until one is found in [0,n] 258 | uint32 i; 259 | do 260 | i = randInt() & used; // toss unused bits to shorten search 261 | while( i > n ); 262 | return i; 263 | } 264 | 265 | 266 | inline void MTRand::seed( const uint32 oneSeed ) 267 | { 268 | // Seed the generator with a simple uint32 269 | initialize(oneSeed); 270 | reload(); 271 | } 272 | 273 | 274 | inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) 275 | { 276 | // Seed the generator with an array of uint32's 277 | // There are 2^19937-1 possible initial states. This function allows 278 | // all of those to be accessed by providing at least 19937 bits (with a 279 | // default seed length of N = 624 uint32's). Any bits above the lower 32 280 | // in each element are discarded. 281 | // Just call seed() if you want to get array from /dev/urandom 282 | initialize(19650218UL); 283 | register int i = 1; 284 | register uint32 j = 0; 285 | register int k = ( N > seedLength ? N : seedLength ); 286 | for( ; k; --k ) 287 | { 288 | state[i] = 289 | state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); 290 | state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; 291 | state[i] &= 0xffffffffUL; 292 | ++i; ++j; 293 | if( i >= N ) { state[0] = state[N-1]; i = 1; } 294 | if( j >= seedLength ) j = 0; 295 | } 296 | for( k = N - 1; k; --k ) 297 | { 298 | state[i] = 299 | state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); 300 | state[i] -= i; 301 | state[i] &= 0xffffffffUL; 302 | ++i; 303 | if( i >= N ) { state[0] = state[N-1]; i = 1; } 304 | } 305 | state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array 306 | reload(); 307 | } 308 | 309 | 310 | inline void MTRand::seed() 311 | { 312 | // Seed the generator with an array from /dev/urandom if available 313 | // Otherwise use a hash of time() and clock() values 314 | 315 | // First try getting an array from /dev/urandom 316 | FILE* urandom = fopen( "/dev/urandom", "rb" ); 317 | if( urandom ) 318 | { 319 | uint32 bigSeed[N]; 320 | register uint32 *s = bigSeed; 321 | register int i = N; 322 | register bool success = true; 323 | while( success && i-- ) 324 | success = fread( s++, sizeof(uint32), 1, urandom ); 325 | fclose(urandom); 326 | if( success ) { seed( bigSeed, N ); return; } 327 | } 328 | 329 | // Was not successful, so use time() and clock() instead 330 | seed( hash( time(NULL), clock() ) ); 331 | } 332 | 333 | 334 | inline void MTRand::initialize( const uint32 seed ) 335 | { 336 | // Initialize generator state with seed 337 | // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. 338 | // In previous versions, most significant bits (MSBs) of the seed affect 339 | // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. 340 | register uint32 *s = state; 341 | register uint32 *r = state; 342 | register int i = 1; 343 | *s++ = seed & 0xffffffffUL; 344 | for( ; i < N; ++i ) 345 | { 346 | *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; 347 | r++; 348 | } 349 | } 350 | 351 | 352 | inline void MTRand::reload() 353 | { 354 | // Generate N new values in state 355 | // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) 356 | register uint32 *p = state; 357 | register int i; 358 | for( i = N - M; i--; ++p ) 359 | *p = twist( p[M], p[0], p[1] ); 360 | for( i = M; --i; ++p ) 361 | *p = twist( p[M-N], p[0], p[1] ); 362 | *p = twist( p[M-N], p[0], state[0] ); 363 | 364 | left = N, pNext = state; 365 | } 366 | 367 | 368 | inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) 369 | { 370 | // Get a uint32 from t and c 371 | // Better than uint32(x) in case x is floating point in [0,1] 372 | // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) 373 | 374 | static uint32 differ = 0; // guarantee time-based seeds will change 375 | 376 | uint32 h1 = 0; 377 | unsigned char *p = (unsigned char *) &t; 378 | for( size_t i = 0; i < sizeof(t); ++i ) 379 | { 380 | h1 *= UCHAR_MAX + 2U; 381 | h1 += p[i]; 382 | } 383 | uint32 h2 = 0; 384 | p = (unsigned char *) &c; 385 | for( size_t j = 0; j < sizeof(c); ++j ) 386 | { 387 | h2 *= UCHAR_MAX + 2U; 388 | h2 += p[j]; 389 | } 390 | return ( h1 + differ++ ) ^ h2; 391 | } 392 | 393 | 394 | inline void MTRand::save( uint32* saveArray ) const 395 | { 396 | register uint32 *sa = saveArray; 397 | register const uint32 *s = state; 398 | register int i = N; 399 | for( ; i--; *sa++ = *s++ ) {} 400 | *sa = left; 401 | } 402 | 403 | 404 | inline void MTRand::load( uint32 *const loadArray ) 405 | { 406 | register uint32 *s = state; 407 | register uint32 *la = loadArray; 408 | register int i = N; 409 | for( ; i--; *s++ = *la++ ) {} 410 | left = *la; 411 | pNext = &state[N-left]; 412 | } 413 | 414 | 415 | inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) 416 | { 417 | register const MTRand::uint32 *s = mtrand.state; 418 | register int i = mtrand.N; 419 | for( ; i--; os << *s++ << "\t" ) {} 420 | return os << mtrand.left; 421 | } 422 | 423 | 424 | inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) 425 | { 426 | register MTRand::uint32 *s = mtrand.state; 427 | register int i = mtrand.N; 428 | for( ; i--; is >> *s++ ) {} 429 | is >> mtrand.left; 430 | mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; 431 | return is; 432 | } 433 | 434 | #endif // MERSENNETWISTER_H 435 | 436 | // Change log: 437 | // 438 | // v0.1 - First release on 15 May 2000 439 | // - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus 440 | // - Translated from C to C++ 441 | // - Made completely ANSI compliant 442 | // - Designed convenient interface for initialization, seeding, and 443 | // obtaining numbers in default or user-defined ranges 444 | // - Added automatic seeding from /dev/urandom or time() and clock() 445 | // - Provided functions for saving and loading generator state 446 | // 447 | // v0.2 - Fixed bug which reloaded generator one step too late 448 | // 449 | // v0.3 - Switched to clearer, faster reload() code from Matthew Bellew 450 | // 451 | // v0.4 - Removed trailing newline in saved generator format to be consistent 452 | // with output format of built-in types 453 | // 454 | // v0.5 - Improved portability by replacing static const int's with enum's and 455 | // clarifying return values in seed(); suggested by Eric Heimburg 456 | // - Removed MAXINT constant; use 0xffffffffUL instead 457 | // 458 | // v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits 459 | // - Changed integer [0,n] generator to give better uniformity 460 | // 461 | // v0.7 - Fixed operator precedence ambiguity in reload() 462 | // - Added access for real numbers in (0,1) and (0,n) 463 | // 464 | // v0.8 - Included time.h header to properly support time_t and clock_t 465 | // 466 | // v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto 467 | // - Allowed for seeding with arrays of any length 468 | // - Added access for real numbers in [0,1) with 53-bit resolution 469 | // - Added access for real numbers from normal (Gaussian) distributions 470 | // - Increased overall speed by optimizing twist() 471 | // - Doubled speed of integer [0,n] generation 472 | // - Fixed out-of-range number generation on 64-bit machines 473 | // - Improved portability by substituting literal constants for long enum's 474 | // - Changed license from GNU LGPL to BSD 475 | -------------------------------------------------------------------------------- /VascularTree.cpp: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: VascularTree.cpp,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | #include 51 | #include 52 | 53 | #include "VascularTree.h" 54 | #include "OxygenationMap.h" 55 | #include "NodeTable.h" 56 | 57 | #define PI 3.1415926535897 58 | 59 | using namespace std; 60 | 61 | /** 62 | * Constructor 63 | */ 64 | VascularTree::VascularTree(OxygenationMap * _oxMap, double* _perf, double _Pperf, double _Pterm, double _Qperf, double _rho, double _gamma, double _lambda, double _mu, double _minDistance, int _numNodes, double _voxelWidth, int _closestNeighbours){ 65 | oxMap = _oxMap; 66 | perf = new double[3]; perf[0] = _perf[0]; perf[1] = _perf[1];perf[2] = _perf[2]; 67 | Pperf = _Pperf; 68 | Pterm = _Pterm; 69 | Qperf = _Qperf; 70 | rho = _rho; 71 | gamma = _gamma; 72 | lambda = _lambda; 73 | mu = _mu; 74 | minDistance = _minDistance; 75 | mapVoxelWidth = _voxelWidth; 76 | Qterm = _Qperf/_numNodes; 77 | numNodes = _numNodes; 78 | 79 | closestNeighbours = _closestNeighbours; 80 | 81 | nt.addNode(NodeTable::ROOT, perf, -1, 1, 1, Qperf, -1, -1); 82 | } 83 | 84 | /** 85 | * Calculates the distance between to nodes in the node table. 86 | */ 87 | double VascularTree::distance(int from, int to){ 88 | double* fromPos = nt.getPos(from); 89 | double* toPos = nt.getPos(to); 90 | 91 | return sqrt( 92 | pow(fromPos[0] - toPos[0], 2) + 93 | pow(fromPos[1] - toPos[1], 2) + 94 | pow(fromPos[2] - toPos[2], 2))*mapVoxelWidth; 95 | } 96 | 97 | /** 98 | * Calculates the reduced resistence of segment at id. 99 | */ 100 | void VascularTree::calculateReducedResistence(int id){ 101 | if(nt.getType(id) == NodeTable::TERM){ 102 | double acc = (8.0 * rho * distance(id, nt.getParent(id))/PI); 103 | nt.setReducedResistence(id, acc); 104 | } else { 105 | double acc = 0; 106 | acc += pow(nt.getLeftRatio(id), 4) / nt.getReducedResistance(nt.getLeftChild(id)); 107 | acc += pow(nt.getRightRatio(id), 4) / nt.getReducedResistance(nt.getRightChild(id)); 108 | acc = 1.0 / acc; 109 | acc += (8.0 * rho * distance(id, nt.getParent(id))/PI); 110 | nt.setReducedResistence(id, acc); 111 | } 112 | } 113 | 114 | /** 115 | * Calculates the ratio of radii of the segment at id. 116 | */ 117 | void VascularTree::calculateRatios(int id){ 118 | int left = nt.getLeftChild(id); 119 | int right = nt.getRightChild(id); 120 | 121 | double left_over_right = (nt.getFlow(left)*nt.getReducedResistance(left)) / (nt.getFlow(right) * nt.getReducedResistance(right)); 122 | left_over_right = pow(left_over_right, 0.25); 123 | 124 | nt.setLeftRatio(id, pow((1 + pow(left_over_right, -gamma)), (-1.0)/gamma)); 125 | nt.setRightRatio(id, pow((1 + pow(left_over_right, gamma)), (-1.0)/gamma)); 126 | } 127 | 128 | /** 129 | * Updates the tree at the bifurication point at id. 130 | */ 131 | void VascularTree::updateAtBifurication(int id, int newChild){ 132 | if(nt.getType(id) != NodeTable::ROOT){ 133 | calculateReducedResistence(newChild); 134 | calculateRatios(id); 135 | 136 | updateAtBifurication(nt.getParent(id), id); 137 | } else { 138 | calculateReducedResistence(newChild); 139 | } 140 | } 141 | 142 | /** 143 | * Calculates the radii throughout the tree. 144 | */ 145 | void VascularTree::calculateRadius(){ 146 | //the child of the root (perferation) node defines the root segment 147 | int rootChild = nt.getLeftChild(0); 148 | 149 | double radius = (nt.getFlow(rootChild) * nt.getReducedResistance(rootChild)) / (Pperf - Pterm); 150 | radius = pow(radius, 0.25); 151 | nt.setRadius(rootChild, radius); 152 | 153 | calculateRadius(rootChild); 154 | } 155 | 156 | /** 157 | * Calculates the radius at id. 158 | */ 159 | void VascularTree::calculateRadius(int id){ 160 | if(nt.getType(id) == NodeTable::TERM) 161 | return; 162 | 163 | int left = nt.getLeftChild(id); 164 | int right = nt.getRightChild(id); 165 | 166 | nt.setRadius(left, nt.getRadius(id)*nt.getLeftRatio(id)); 167 | nt.setRadius(right, nt.getRadius(id)*nt.getRightRatio(id)); 168 | 169 | calculateRadius(left); 170 | calculateRadius(right); 171 | } 172 | 173 | /** 174 | * Calculates the fitness. 175 | */ 176 | double VascularTree::calculateFitness(){ 177 | 178 | //stupid but you have to cast it otherwise it will throw a warning 179 | int size = (int) nt.nodes.size(); 180 | double acc = 0; 181 | 182 | for(int i = 1; i < size; i++){ 183 | acc += pow(distance(i, nt.getParent(i)), mu) * pow(nt.getRadius(i), lambda); 184 | } 185 | 186 | return acc; 187 | } 188 | 189 | /** 190 | * When used by local optimization, ignored is the segment to connect to 191 | * otherwise it should be -1; 192 | */ 193 | bool VascularTree::validateCandidate(double* x0, int ignored){ 194 | 195 | //stupid but you have to cast it otherwise it will throw a warning 196 | int size = (int) nt.nodes.size(); 197 | 198 | for(int i = 1; i < size; i++){ 199 | if(i != ignored){ 200 | double distance = pointSegmentDistance(x0, i); 201 | if(distance < minDistance) 202 | return false; 203 | } 204 | } 205 | 206 | return true; 207 | } 208 | 209 | /** 210 | * Connects the candidate node to a segment through the 211 | * bifurcation point bifPoint. 212 | */ 213 | void VascularTree::connectPoint(double* point, int segment, double* bifPoint){ 214 | if(nt.getType(segment) == NodeTable::ROOT){ 215 | nt.addNode(NodeTable::TERM, point, segment, 1, 1, Qterm, -1, -1); 216 | nt.setLeftChild(0, 1); 217 | nt.setRightChild(0, 1); 218 | calculateReducedResistence(1); 219 | } else { 220 | /* *--- --- * 221 | // 222 | // becomes 223 | // 224 | // *--- --- * ---- < I_con > --- * 225 | // \ 226 | // \ 227 | // 228 | // \ 229 | // \ 230 | // * point[] 231 | // 232 | // 233 | // where I_sec is replaced with I_con 234 | // 235 | // 236 | // I_con = I_seg with the exception of parent (which is set to I_biff 237 | // I_seg's parent updates its child to be I_biff 238 | // I_new = is a new segment wich is trivially built 239 | // I_biff is built using I_new and I_con */ 240 | 241 | int biffId = nt.nodes.size(); 242 | int newId = biffId + 1; 243 | 244 | int oldParent = nt.getParent(segment); 245 | 246 | nt.setParent(segment, biffId); 247 | if(nt.getLeftChild(oldParent) == segment) 248 | nt.setLeftChild(oldParent, biffId); 249 | if(nt.getRightChild(oldParent) == segment) 250 | nt.setRightChild(oldParent, biffId); 251 | 252 | if(oldParent > 0) 253 | incrementFlow(oldParent, Qterm); 254 | 255 | nt.addNode(NodeTable::BIF, bifPoint, oldParent, 1, 1, nt.getFlow(segment) + Qterm, segment, newId); 256 | nt.addNode(NodeTable::TERM, point, biffId, 1, 1, Qterm, -1, -1); 257 | 258 | calculateReducedResistence(segment); 259 | updateAtBifurication(biffId, newId); 260 | } 261 | } 262 | 263 | /** 264 | * Updates the flow throughout the tree. 265 | */ 266 | void VascularTree::incrementFlow(int parent, double Qterm){ 267 | nt.setFlow(parent, nt.getFlow(parent)+Qterm); 268 | if(nt.getParent(parent) > 0) 269 | incrementFlow(nt.getParent(parent), Qterm); 270 | } 271 | 272 | /** 273 | * Returns the distance between a point and a segment. 274 | */ 275 | double VascularTree::pointSegmentDistance(double* x0, int segment){ 276 | double *pos1 = nt.getPos(segment); 277 | double *pos2 = nt.getPos(nt.getParent(segment)); 278 | 279 | double t = -((pos2[0] - pos1[0])*(pos1[0] - x0[0]) + 280 | (pos2[1] - pos1[1])*(pos1[1] - x0[1]) + 281 | (pos2[2] - pos1[2])*(pos1[2] - x0[2])) / 282 | ((pos2[0] - pos1[0])*(pos2[0] - pos1[0]) + 283 | (pos2[1] - pos1[1])*(pos2[1] - pos1[1]) + 284 | (pos2[2] - pos1[2])*(pos2[2] - pos1[2])); 285 | 286 | if(t < 0 || t > 1){ 287 | double d1 = pow(pos1[0] - x0[0], 2) + pow(pos1[1] - x0[1], 2) + pow(pos1[2] - x0[2], 2); 288 | double d2 = pow(pos2[0] - x0[0], 2) + pow(pos2[1] - x0[1], 2) + pow(pos2[2] - x0[2], 2); 289 | 290 | if(d1 < d2) 291 | return pow(d1, 0.5); 292 | else 293 | return pow(d2, 0.5); 294 | } 295 | else{ 296 | return pow(pow((((pos2[0] - pos1[0])*t + pos1[0]) - x0[0]), 2) + 297 | pow((((pos2[1] - pos1[1])*t + pos1[1]) - x0[1]), 2) + 298 | pow((((pos2[2] - pos1[2])*t + pos1[2]) - x0[2]), 2), 0.5); 299 | } 300 | } 301 | 302 | /** 303 | * Optimizes the location of a bifurication point for terminal node 304 | * point and segment 'segment' 305 | */ 306 | double* VascularTree::localOptimization(double * point, int segment, int steps){ 307 | double bestFitness = 1e200; 308 | 309 | double bif[3]; 310 | double perf[3]; 311 | perf[0] = nt.getPos(nt.getParent(segment))[0]; 312 | perf[1] = nt.getPos(nt.getParent(segment))[1]; 313 | perf[2] = nt.getPos(nt.getParent(segment))[2]; 314 | double con[3]; 315 | con[0] = nt.getPos(segment)[0]; 316 | con[1] = nt.getPos(segment)[1]; 317 | con[2] = nt.getPos(segment)[2]; 318 | 319 | bif[0] = ((con[0] - perf[0])/2.0 + perf[0]); 320 | bif[1] = ((con[1] - perf[1])/2.0 + perf[1]); 321 | bif[2] = ((con[2] - perf[2])/2.0 + perf[2]); 322 | 323 | double stepSize = (((perf[0]+con[0]+point[0])/3.0) - bif[0]+ 324 | ((perf[1]+con[1]+point[1])/3.0) - bif[1] + 325 | ((perf[2]+con[2]+point[2])/3.0) - bif[2])* 2.0 / steps; 326 | 327 | 328 | //makesure point is visible from bif - otherwise return null 329 | if(!oxMap->visible(bif, point) || !inVolume(bif)) 330 | return NULL; 331 | 332 | nt.startUndo(); 333 | 334 | connectPoint(point, segment, bif); 335 | nt.applyUndo(); 336 | 337 | double localBest[3]; 338 | double test[3]; 339 | 340 | for(int i = 0; i < steps; i++){ 341 | localBest[0] = bif[0]; localBest[1] = bif[1]; localBest[2] = bif[2]; 342 | //try the neighbours of bif (6 connected) as possible new bif points 343 | // for a bif point to be valid: 344 | // - perf must be visible from bif 345 | // - con must be visible from bif 346 | // - point must be visilbe from bif 347 | // - bif must be a valid candidate (using segment as the ignored) 348 | // 349 | // if the current bif is ever a local optima -search is terminated 350 | 351 | //up 352 | test[0] = bif[0]+stepSize; test[1] = bif[1]; test[2] = bif[2]; 353 | if(inVolume(test) &&oxMap->visible(perf, test) && oxMap->visible(con, test) && oxMap->visible(point, test) && validateCandidate(test, segment)){ 354 | connectPoint(point, segment, test); 355 | calculateRadius(); 356 | double fitness = calculateFitness(); 357 | 358 | if(fitness < bestFitness){ 359 | localBest[0] = test[0]; localBest[1] = test[1]; localBest[2] = test[2]; 360 | bestFitness = fitness; 361 | } 362 | } 363 | nt.applyUndo(); 364 | 365 | //down 366 | test[0] = bif[0]-stepSize; test[1] = bif[1]; test[2] = bif[2]; 367 | if(inVolume(test) &&oxMap->visible(perf, test) && oxMap->visible(con, test) && oxMap->visible(point, test) && validateCandidate(test, segment)){ 368 | connectPoint(point, segment, test); 369 | calculateRadius(); 370 | double fitness = calculateFitness(); 371 | 372 | if(fitness < bestFitness){ 373 | localBest[0] = test[0]; localBest[1] = test[1]; localBest[2] = test[2]; 374 | bestFitness = fitness; 375 | } 376 | } 377 | nt.applyUndo(); 378 | 379 | //left 380 | test[0] = bif[0]; test[1] = bif[1]+stepSize; test[2] = bif[2]; 381 | if(inVolume(test) &&oxMap->visible(perf, test) && oxMap->visible(con, test) && oxMap->visible(point, test) && validateCandidate(test, segment)){ 382 | connectPoint(point, segment, test); 383 | calculateRadius(); 384 | double fitness = calculateFitness(); 385 | 386 | if(fitness < bestFitness){ 387 | localBest[0] = test[0]; localBest[1] = test[1]; localBest[2] = test[2]; 388 | bestFitness = fitness; 389 | } 390 | } 391 | nt.applyUndo(); 392 | 393 | //right 394 | test[0] = bif[0]; test[1] = bif[1]-stepSize; test[2] = bif[2]; 395 | if(inVolume(test) &&oxMap->visible(perf, test) && oxMap->visible(con, test) && oxMap->visible(point, test) && validateCandidate(test, segment)){ 396 | connectPoint(point, segment, test); 397 | calculateRadius(); 398 | double fitness = calculateFitness(); 399 | 400 | if(fitness < bestFitness){ 401 | localBest[0] = test[0]; localBest[1] = test[1]; localBest[2] = test[2]; 402 | bestFitness = fitness; 403 | } 404 | } 405 | nt.applyUndo(); 406 | 407 | //forward 408 | test[0] = bif[0]; test[1] = bif[1]; test[2] = bif[2]+stepSize; 409 | if(inVolume(test) &&oxMap->visible(perf, test) && oxMap->visible(con, test) && oxMap->visible(point, test) && validateCandidate(test, segment)){ 410 | connectPoint(point, segment, test); 411 | calculateRadius(); 412 | double fitness = calculateFitness(); 413 | 414 | if(fitness < bestFitness){ 415 | localBest[0] = test[0]; localBest[1] = test[1]; localBest[2] = test[2]; 416 | bestFitness = fitness; 417 | } 418 | } 419 | nt.applyUndo(); 420 | 421 | //back 422 | test[0] = bif[0]; test[1] = bif[1]; test[2] = bif[2]-stepSize; 423 | if(inVolume(test) &&oxMap->visible(perf, test) && oxMap->visible(con, test) && oxMap->visible(point, test) && validateCandidate(test, segment)){ 424 | connectPoint(point, segment, test); 425 | calculateRadius(); 426 | double fitness = calculateFitness(); 427 | 428 | if(fitness < bestFitness){ 429 | localBest[0] = test[0]; localBest[1] = test[1]; localBest[2] = test[2]; 430 | bestFitness = fitness; 431 | } 432 | } 433 | nt.applyUndo(); 434 | 435 | if(localBest[0] != bif[0] || localBest[1] != bif[1] || localBest[2] != bif[2]){ 436 | bif[0] = localBest[0]; bif[1] = localBest[1]; bif[2] = localBest[2]; 437 | } else { 438 | break; 439 | } 440 | } 441 | nt.clearUndo(); 442 | nt.stopUndo(); 443 | 444 | double *ret = new double[4]; 445 | ret[0] = bif[0]; ret[1] = bif[1]; ret[2] = bif[2]; 446 | ret[3] = bestFitness; 447 | return ret; 448 | } 449 | 450 | /** 451 | * Determines if point is in the volume. 452 | */ 453 | bool VascularTree::inVolume(double* point){ 454 | if(point[0] < 0 || point[0] >= oxMap->dim[0]) 455 | return false; 456 | if(point[1] < 0 || point[1] >= oxMap->dim[1]) 457 | return false; 458 | if(point[2] < 0 || point[2] >= oxMap->dim[2]) 459 | return false; 460 | 461 | return true; 462 | } 463 | 464 | /** 465 | * Connects the candidate node to the closestNeighbour segments 466 | * with an optimized bifurcation location (originally the midpoint of the segment. 467 | * The conceptually connected segment that yields the smallest objective 468 | * function is elected. 469 | */ 470 | bool VascularTree::connectCandidate(double* point, int steps){ 471 | 472 | //cout << "connect candidate" << endl; 473 | 474 | if(!validateCandidate(point, -1)) { 475 | return false; //candiate is too close to an existing segment 476 | } 477 | 478 | if(nt.nodes.size() == 1){ 479 | 480 | if(!oxMap->visible(nt.getPos(0), point)) { 481 | return false; 482 | } 483 | 484 | connectPoint(point, 0, NULL); 485 | return true; 486 | } 487 | 488 | double best[3]; 489 | bool foundSolution = false; 490 | double bestFitness = 1e200; 491 | int bestSegment = 0; 492 | 493 | map distances; 494 | int i; 495 | int j; 496 | 497 | //determine the distance between the point and each segment 498 | int size = (int) nt.nodes.size(); 499 | for(i = 1; i < size; i++){ 500 | double d = pointSegmentDistance(point, i); 501 | distances[i] = d; 502 | } 503 | 504 | int numCandidates = distances.size(); 505 | int *candidateSegments = new int[numCandidates]; 506 | 507 | //sort the segments by distance 508 | for(j = 0; j < numCandidates; j++){ 509 | int closest = -1; 510 | double distance = 1e200; 511 | 512 | map::iterator itr; 513 | for(itr = distances.begin(); itr != distances.end(); itr++){ 514 | double d = itr->second; 515 | if(d < distance){ 516 | distance = d; 517 | closest = itr->first; 518 | } 519 | } 520 | 521 | if(closest >= 1) 522 | distances.erase(closest); 523 | if(closest == -1) 524 | return false; 525 | candidateSegments[j] = closest; 526 | } 527 | 528 | //try the first 'closestNeighbours' segments 529 | int count = 0; 530 | double* test; 531 | for(j = 0; j < numCandidates && count < closestNeighbours; j++){ 532 | test = localOptimization(point, candidateSegments[j], steps); 533 | if(test != NULL){ 534 | count++; 535 | if(test[3] < bestFitness){ 536 | best[0] = test[0]; best[1] = test[1]; best[2] = test[2]; 537 | bestSegment = candidateSegments[j]; 538 | bestFitness = test[3]; 539 | foundSolution = true; 540 | } 541 | } 542 | delete test; 543 | } 544 | 545 | delete candidateSegments; 546 | 547 | if(!foundSolution) 548 | return false; //could not connect candidate to ANY segment 549 | 550 | connectPoint(point, bestSegment, best); 551 | 552 | return true; 553 | } 554 | 555 | /** 556 | * iteratively builds a tree by selecting candidate nodes based on an oxygenation demand map 557 | * and then connecting that candidate node to a series of closest segments at the midpoint of the 558 | * segment. The resulting new segment that yields the smallest objective function is selected and 559 | * added to the tree. The oxygenation map is updated - reducing values proximal to the candidate node. 560 | * 561 | * finally the radii are calcualted recursively by calculating the ratio of the radius of the child over parent, 562 | * and multiplying that by the radius of the parent to ascertain the radius. 563 | */ 564 | void VascularTree::buildTree(){ 565 | 566 | int count = 0; 567 | double cand[3]; 568 | int term[3]; 569 | while(count < numNodes){ 570 | 571 | double sum = oxMap->sum(); 572 | oxMap->candidate(sum, term); 573 | cand[0] = term[0]; cand[1] = term[1]; cand[2] = term[2]; 574 | if(connectCandidate(cand, 20)){ 575 | count++; 576 | oxMap->applyCandidate(term); 577 | } 578 | 579 | } 580 | 581 | calculateRadius(); 582 | 583 | } -------------------------------------------------------------------------------- /VascuSynth.cpp: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | 3 | Program: VascuSynth 4 | Module: $RCSfile: VascuSynth.cpp,v $ 5 | Language: C++ 6 | Date: $Date: 2011/02/08 10:43:00 $ 7 | Version: $Revision: 1.0 $ 8 | 9 | Copyright (c) 2011 Medical Imaging Analysis Lab, Simon Fraser University, 10 | British Columbia, Canada. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimer in the documentation 21 | and/or other materials provided with the distribution. 22 | 23 | * The name of the Insight Consortium, nor the names of any consortium members, 24 | nor of any contributors, may be used to endorse or promote products derived 25 | from this software without specific prior written permission. 26 | 27 | * Modified source versions must be plainly marked as such, and must not be 28 | misrepresented as being the original software. 29 | 30 | * Free for non-commercial use only. For commercial use, explicit approval 31 | must be requested by contacting the Authors. 32 | 33 | * If you use the code in your work, you must acknowledge it 34 | 35 | * Modifications of the source code must also be released as open source 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 38 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 39 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 40 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 41 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 42 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 43 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 44 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 45 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | 48 | =========================================================================*/ 49 | 50 | //commands for mkdir 51 | #ifdef _WIN32 52 | #include "direct.h" 53 | #else 54 | #include 55 | #include 56 | #endif 57 | 58 | #include "itkImage.h" 59 | #include "itkImageFileReader.h" 60 | #include "itkImageSeriesReader.h" 61 | #include "itkImageFileWriter.h" 62 | #include "itkImageSeriesWriter.h" 63 | #include "itkNumericSeriesFileNames.h" 64 | 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | using namespace std; 73 | 74 | /** 75 | * Utility function to read a text file and store the lines into a vector 76 | * that is returned. This way we do not have to have a bunch of files open at 77 | * the same time. 78 | * @param const char* filename the filename to read 79 | * @return vector a vector of file lines from the file 80 | * @throws string exception if the file cannot be read 81 | */ 82 | vector * readFileLines(const char * filename){ 83 | 84 | ifstream oFile; 85 | oFile.open(filename, ios::in); 86 | vector * lines = new vector; 87 | string line; 88 | 89 | if(oFile.is_open()){ 90 | 91 | while(!oFile.eof()){ 92 | getline(oFile, line); 93 | string copy = line; 94 | lines->push_back(copy); 95 | } 96 | 97 | oFile.close(); 98 | 99 | } else { 100 | throw "Could not open file " + ( (string) filename); 101 | } 102 | 103 | return lines; 104 | 105 | } 106 | 107 | 108 | #include "OxygenationMap.h" 109 | #include "SupplyMap.h" 110 | #include "TreeDrawer.h" 111 | #include "VascularTree.h" 112 | 113 | 114 | 115 | /** 116 | * make dir that is cross platform 117 | */ 118 | int mmkdir(const char * dirname) { 119 | #ifdef _WIN32 120 | return mkdir(dirname); 121 | #else 122 | return mkdir(dirname, 0777); 123 | #endif 124 | } 125 | 126 | /** 127 | * itoa is non standard so define it and use it, 128 | * converts an integer to a string 129 | */ 130 | string itoa(int value, int base) { 131 | 132 | string buf; 133 | 134 | // check that the base if valid 135 | if (base < 2 || base > 16) return buf; 136 | 137 | enum { kMaxDigits = 35 }; 138 | buf.reserve( kMaxDigits ); // Pre-allocate enough space. 139 | 140 | int quotient = value; 141 | 142 | // Translating number to string with base: 143 | do { 144 | buf += "0123456789abcdef"[ std::abs( quotient % base ) ]; 145 | quotient /= base; 146 | } while ( quotient ); 147 | 148 | // Append the negative sign 149 | if ( value < 0) buf += '-'; 150 | 151 | reverse( buf.begin(), buf.end() ); 152 | return buf; 153 | } 154 | 155 | /** 156 | * Reads the parameters from the parameter file and then builds 157 | * the vascular structure in the form of a tree 158 | * 159 | * Parameter File Entries: 160 | * 161 | * SUPPLY_MAP: supply map file 162 | * OXYGENATION_MAP: oxygenation map file 163 | * PERF_POINT: perf_x perf_y perf_z 164 | * PERF_PRESSURE: perfussion pressure 165 | * TERM_PRESSURE: termination pressure 166 | * PERF_FLOW: perfusion flow 167 | * RHO: rho 168 | * GAMMA: gamma 169 | * LAMBDA: lambda 170 | * MU: mu 171 | * MIN_DISTANCE: minDistance 172 | * NUM_NODES: numNodes 173 | * VOXEL_WIDTH: voxelWidth 174 | * CLOSEST_NEIGHBOURS: closestNeighbours 175 | */ 176 | VascularTree * buildTree(const char * filename){ 177 | 178 | SupplyMap * sm = NULL; 179 | OxygenationMap * om = NULL; 180 | 181 | double* perf = new double[3]; 182 | double pperf; 183 | double pterm; 184 | double qperf; 185 | double rho; 186 | double gamma; 187 | double lambda; 188 | double mu; 189 | double minDistance; 190 | int numNodes; 191 | double voxelWidth; 192 | int closestNeighbours; 193 | int randomSeed = -1; 194 | string line; 195 | string supplyMapFileName; 196 | string oxygenMapFileName; 197 | 198 | //c++ doesn't allow us to check for undefined variables 199 | //so we need a boolean so that we know that the variables 200 | //are defined. 201 | bool perfSet = false; 202 | bool pperfSet = false; 203 | bool ptermSet = false; 204 | bool qperfSet = false; 205 | bool rhoSet = false; 206 | bool gammaSet = false; 207 | bool lambdaSet = false; 208 | bool muSet = false; 209 | bool minDistanceSet = false; 210 | bool numNodesSet = false; 211 | bool voxelWidthSet = false; 212 | bool closestNeighboursSet = false; 213 | bool supplyMapFileNameSet = false; 214 | bool oxygenMapFileNameSet = false; 215 | 216 | vector *mapFilesLines = readFileLines(filename); 217 | int size = (int) mapFilesLines->size(); 218 | 219 | for (int i=0; i < size; i++) { 220 | 221 | line = mapFilesLines->at(i); 222 | 223 | if (line.compare("") == 0) { 224 | break; 225 | } 226 | 227 | int colonPosition = line.find(":"); 228 | string name = line.substr(0, colonPosition); 229 | string value = line.substr(colonPosition+2); 230 | 231 | if (name.compare("SUPPLY_MAP") == 0) { 232 | 233 | //store the supplyMapFileName for later 234 | //the ordering of things matters so just save the file name 235 | //then initialize the map later so that the user can type the fields 236 | //in whatever order 237 | supplyMapFileName = value; 238 | supplyMapFileNameSet = true; 239 | 240 | } else if (name.compare("OXYGENATION_MAP") == 0) { 241 | 242 | //same as supply map since we need random seed before 243 | //we init the oxygenation map 244 | oxygenMapFileName = value; 245 | oxygenMapFileNameSet = true; 246 | 247 | } else if (name.compare("PERF_POINT") == 0) { 248 | 249 | int spacePosition = value.find(" "); 250 | string pointvalue = value.substr(0, spacePosition); 251 | perf[0] = atof(pointvalue.c_str()); 252 | 253 | int spacePosition2 = value.find(" ", spacePosition+1); 254 | pointvalue = value.substr(spacePosition+1, spacePosition2); 255 | perf[1] = atof(pointvalue.c_str()); 256 | 257 | pointvalue = value.substr(spacePosition2+1); 258 | perf[2] = atof(pointvalue.c_str()); 259 | 260 | perfSet = true; 261 | 262 | } else if (name.compare("PERF_PRESSURE") == 0){ 263 | 264 | pperf = atof(value.c_str()); 265 | pperfSet = true; 266 | 267 | } else if (name.compare("TERM_PRESSURE") == 0){ 268 | 269 | pterm = atof(value.c_str()); 270 | ptermSet = true; 271 | 272 | } else if (name.compare("PERF_FLOW") == 0){ 273 | 274 | qperf = atof(value.c_str()); 275 | qperfSet = true; 276 | 277 | } else if (name.compare("RHO") == 0){ 278 | 279 | rho = atof(value.c_str()); 280 | rhoSet = true; 281 | 282 | } else if (name.compare("GAMMA") == 0){ 283 | 284 | gamma = atof(value.c_str()); 285 | gammaSet = true; 286 | 287 | } else if (name.compare("LAMBDA") == 0){ 288 | 289 | lambda = atof(value.c_str()); 290 | lambdaSet = true; 291 | 292 | } else if (name.compare( "MU") == 0){ 293 | 294 | mu = atof(value.c_str()); 295 | muSet = true; 296 | 297 | } else if (name.compare("MIN_DISTANCE") == 0){ 298 | 299 | minDistance = atof(value.c_str()); 300 | minDistanceSet = true; 301 | 302 | } else if (name.compare("NUM_NODES") == 0){ 303 | 304 | numNodes = atoi(value.c_str()); 305 | numNodesSet = true; 306 | 307 | } else if (name.compare("VOXEL_WIDTH") == 0){ 308 | 309 | voxelWidth = atof(value.c_str()); 310 | voxelWidthSet = true; 311 | 312 | } else if (name.compare("CLOSEST_NEIGHBOURS") == 0){ 313 | 314 | closestNeighbours = atoi(value.c_str()); 315 | closestNeighboursSet = true; 316 | 317 | } else if (name.compare("RANDOM_SEED") == 0){ 318 | 319 | randomSeed = atoi(value.c_str()); 320 | 321 | } else { 322 | 323 | } 324 | 325 | 326 | } 327 | 328 | //make sure that we have everything defined 329 | if (perfSet && pperfSet && ptermSet && qperfSet && rhoSet && gammaSet && lambdaSet && muSet && minDistanceSet && numNodesSet && voxelWidthSet && closestNeighboursSet && supplyMapFileNameSet && oxygenMapFileNameSet) { 330 | 331 | //load the supply map 332 | sm = new SupplyMap(); 333 | 334 | try { 335 | sm->loadMap(supplyMapFileName); 336 | } catch (char * str) { 337 | throw (string) str; 338 | } 339 | 340 | //load the oxygenation map, rand seed will be -1 if there 341 | //is no randomSeed specified or it will be whatever the user specifies 342 | om = new OxygenationMap(sm, randomSeed); 343 | 344 | try { 345 | om->loadMap(oxygenMapFileName); 346 | } catch (char * str) { 347 | throw (string) str; 348 | } 349 | om->supply = sm; 350 | 351 | //TODO: should probably check and make sure everything is defined 352 | //and throw an error if it is not (and catch the error in the main function) 353 | VascularTree *vt = new VascularTree(om, perf, pperf, pterm, qperf, rho, gamma, lambda, mu, minDistance, numNodes, voxelWidth, closestNeighbours); 354 | vt->buildTree(); 355 | 356 | return vt; 357 | 358 | } else { 359 | 360 | string errorStr = "Error while parsing the parameter file, not all parameters have been defined."; 361 | throw errorStr; 362 | 363 | } 364 | } 365 | 366 | /** 367 | * draws the tree to a matrix 368 | */ 369 | TreeDrawer *drawTree(VascularTree * vt, double * corner1, double * corner2, double imageVoxelWidth){ 370 | TreeDrawer * td = new TreeDrawer(vt, imageVoxelWidth, corner1, corner2); 371 | td->drawImage(); 372 | return td; 373 | } 374 | 375 | /** 376 | * draws the tree from the TreeDrawer into a volumetric 3D 377 | * image as a series of 2D png slices 378 | */ 379 | void drawImage(TreeDrawer * td, const char* rootName){ 380 | typedef unsigned char PixelType; 381 | const unsigned int Dimension = 3; 382 | typedef itk::Image< PixelType, Dimension > ImageType; 383 | 384 | ImageType::Pointer image = ImageType::New(); 385 | 386 | ImageType::SizeType size; 387 | size[0] = td->dim[0]; // size along X 388 | size[1] = td->dim[1]; // size along Y 389 | size[2] = td->dim[2]; // size along Z 390 | 391 | ImageType::IndexType start; 392 | start[0] = 0; // first index on X 393 | start[1] = 0; // first index on Y 394 | start[2] = 0; // first index on Z 395 | 396 | ImageType::RegionType region; 397 | region.SetSize( size ); 398 | region.SetIndex( start ); 399 | 400 | image->SetRegions( region ); 401 | image->Allocate(); 402 | 403 | ImageType::IndexType pixelIndex; 404 | pixelIndex[0] = 0; // x position 405 | pixelIndex[1] = 0; // y position 406 | pixelIndex[2] = 0; // z position 407 | 408 | for(int i = 0; i < td->dim[0]; i++){ 409 | for(int j = 0; j < td->dim[1]; j++){ 410 | for(int k = 0 ; k < td->dim[2]; k++){ 411 | pixelIndex[0] = i; 412 | pixelIndex[1] = j; 413 | pixelIndex[2] = k; 414 | 415 | image->SetPixel(pixelIndex, td->imageAt(i, j, k)); 416 | } 417 | } 418 | } 419 | 420 | 421 | typedef itk::Image< unsigned char, 2 > Image2DType; 422 | typedef itk::ImageSeriesWriter< ImageType, Image2DType > WriterType; 423 | WriterType::Pointer writer = WriterType::New(); 424 | writer->SetInput( image); 425 | 426 | typedef itk::NumericSeriesFileNames NameGeneratorType; 427 | NameGeneratorType::Pointer nameGenerator = NameGeneratorType::New(); 428 | 429 | std::string format = rootName; 430 | format += "%03d"; 431 | format += ".jpg"; 432 | nameGenerator->SetSeriesFormat( format.c_str() ); 433 | 434 | const unsigned int firstSlice = start[2]; 435 | const unsigned int lastSlice = start[2] + size[2] - 1; 436 | nameGenerator->SetStartIndex( firstSlice ); 437 | nameGenerator->SetEndIndex( lastSlice ); 438 | nameGenerator->SetIncrementIndex( 1 ); 439 | 440 | writer->SetFileNames( nameGenerator->GetFileNames() ); 441 | 442 | try{ 443 | writer->Update(); 444 | }catch( itk::ExceptionObject & excp ){ 445 | 446 | throw "Exception thrown while reading the image"; 447 | 448 | } 449 | 450 | return; 451 | } 452 | 453 | /** 454 | * applies noise to the volumetric image 455 | * 456 | * noise.txt format: 457 | * 458 | * SHADOW: numShadows 459 | * GAUSSIAN: median sigma 460 | * UNIFORM: lb ub 461 | * SALTPEPER: valSalt probsalt valpepper probpepper 462 | * 463 | * noise will be added in the order specified by the file 464 | * 465 | * one image for each noise file will be generated 466 | */ 467 | void applyNoise(TreeDrawer *td, const char* noiseFile){ 468 | 469 | ifstream mapFile; 470 | mapFile.open(noiseFile); 471 | string line; 472 | 473 | double lb, ub, median, sigma, probSalt, probPepper; 474 | int numShadows; 475 | char valSalt, valPepper; 476 | 477 | if(mapFile.is_open()){ 478 | while(!mapFile.eof()){ 479 | getline(mapFile, line); 480 | char* tok = new char[line.size()]; 481 | strcpy(tok, line.c_str()); 482 | 483 | if(line.length() == 0) 484 | continue; 485 | 486 | char * field = strtok(tok, ":"); 487 | 488 | if(strcmp(field, "SHADOW") == 0){ 489 | 490 | //apply shadow noise 491 | numShadows = atoi(strtok(NULL, " ")); 492 | 493 | td->addShadows(numShadows); 494 | 495 | } else if(strcmp(field, "GAUSSIAN") == 0){ 496 | 497 | //apply gaussian noise 498 | median = atof(strtok(NULL, " ")); 499 | sigma = atof(strtok(NULL, " ")); 500 | 501 | td->addNoise_gaussian(median, sigma); 502 | 503 | } else if(strcmp(field, "UNIFORM") == 0){ 504 | 505 | //applying uniform noise 506 | lb = atof(strtok(NULL, " ")); 507 | ub = atof(strtok(NULL, " ")); 508 | 509 | td->addNoise_Uniform(lb, ub); 510 | 511 | } else { 512 | 513 | if (strcmp(field, "SALTPEPPER") == 0) { 514 | 515 | //apply salt and pepper noise 516 | valSalt = (char)atoi(strtok(NULL, " ")); 517 | probSalt = atof(strtok(NULL, " ")); 518 | valPepper = (char)atoi(strtok(NULL, " ")); 519 | probPepper = atof(strtok(NULL, " ")); 520 | valPepper = (char)valPepper; 521 | 522 | td->addNoise_saltPepper(valSalt, probSalt, valPepper, probPepper); 523 | 524 | } 525 | 526 | } 527 | } 528 | 529 | } else { 530 | 531 | throw "Could not read the noise file"; 532 | 533 | } 534 | 535 | mapFile.close(); 536 | } 537 | 538 | 539 | /** 540 | * prints a node into XML/GXL format from the NodeTable 541 | */ 542 | void subPrint_node(NodeTable *nt, int segment, ofstream &os){ 543 | os<<" "<"<getType(segment) == NodeTable::ROOT){ 546 | os<<" root node "<getType(segment) == NodeTable::TERM){ 548 | os<<" terminal node "<getType(segment) == NodeTable::BIF){ 550 | os<<" bifurication "< unknown type "<"<"<"<getPos(segment); 559 | os<<" "<"<"<"<"<"<"<"<"<getType(segment) != NodeTable::TERM){ 567 | subPrint_node(nt, nt->getLeftChild(segment), os); 568 | if(nt->getType(segment) != NodeTable::ROOT) 569 | subPrint_node(nt, nt->getRightChild(segment), os); 570 | } 571 | } 572 | 573 | /** 574 | * prints an edge into XML/GXL format from a node table 575 | */ 576 | void subPrint_edge(NodeTable *nt, int segment, ofstream &os){ 577 | 578 | if(nt->getType(segment) != NodeTable::ROOT){ 579 | os<<" getParent(segment)<<"\">"<"<"<getFlow(segment)<<""<"<"<"<getRadius(segment)<<""<"<"<getType(segment) != NodeTable::TERM){ 592 | subPrint_edge(nt, nt->getLeftChild(segment), os); 593 | if(nt->getType(segment) != NodeTable::ROOT) 594 | subPrint_edge(nt, nt->getRightChild(segment), os); 595 | } 596 | } 597 | 598 | /** 599 | * Writes the tree structure to a GXL file and stores information 600 | * about the nodes, edges, their heirarchy, radii and bifurcation locations 601 | */ 602 | void printTreeStructure(VascularTree * vt, const char * filePath){ 603 | 604 | ofstream output; 605 | 606 | //writing the tree structure as GXL to the filePath specified 607 | output.open(filePath); 608 | output<<""<nt.length; i++){ 616 | if(vt->nt.getType(i) == NodeTable::ROOT){ 617 | subPrint_node(&vt->nt, i, output); 618 | subPrint_edge(&vt->nt, i, output); 619 | output<<""< *paramFiles = readFileLines(argv[1]); 657 | vector *imageNameFiles = readFileLines(argv[2]); 658 | 659 | //voxel widths 660 | string voxelWidth = argv[3]; 661 | string *noiseFiles = new string[argc-4]; 662 | 663 | int paramFilesSize = (int) paramFiles->size(); 664 | 665 | //go through each param file and build tree an spit it out 666 | for(int m = 0; m < paramFilesSize; m++){ 667 | 668 | string paramFile = paramFiles->at(m); 669 | string rootDirectory = imageNameFiles->at(m); 670 | 671 | for(int i = 4; i < argc; i++) { 672 | noiseFiles[i-4] = argv[i]; 673 | } 674 | 675 | int numNoise = argc-4; 676 | 677 | cout << "Reading parameters and building the tree..." << endl; 678 | 679 | //build the tree 680 | VascularTree * vt = buildTree(paramFile.c_str()); 681 | 682 | cout << "The vascular tree has been built sucessfully..." << endl; 683 | 684 | //filter out the /r that appears at times and messes up the directory name 685 | if (rootDirectory[ rootDirectory.length() - 1 ] == '\r') { 686 | rootDirectory = rootDirectory.substr(0, rootDirectory.length()-1); 687 | } 688 | 689 | //create the directory for the output 690 | rootDirectory = "./"+rootDirectory; 691 | mmkdir(rootDirectory.c_str()); 692 | 693 | //create the GXL file 694 | string treeStructure = rootDirectory+"/tree_structure.xml"; 695 | printTreeStructure(vt, treeStructure.c_str()); 696 | 697 | cout << "The directory for the image has been created..." << endl; 698 | cout << "Information about the vascular structure has been saved in the gxl file tree_structure.xml..." << endl; 699 | 700 | double corner1[] = {0,0,0}; 701 | double corner2[] = {vt->oxMap->dim[0], vt->oxMap->dim[1], vt->oxMap->dim[2]}; 702 | TreeDrawer * td = drawTree(vt, corner1, corner2, atof(voxelWidth.c_str())); 703 | 704 | //create the subdirectory for the images 705 | string imageName = rootDirectory+"/original_image"; 706 | mmkdir(imageName.c_str()); 707 | 708 | //output the images 709 | imageName = imageName + "/image"; 710 | drawImage(td, imageName.c_str()); 711 | 712 | cout << "The volumetric image has been saved..." << endl; 713 | 714 | char *buff = new char[20]; 715 | 716 | if (numNoise > 0) { 717 | cout << "The images are being degraded by noise..." << endl; 718 | } 719 | 720 | //apply noise to the images - creating niose_images 721 | for(int i = 0; i < numNoise; i++){ 722 | 723 | TreeDrawer * td_c = td->copy(); 724 | applyNoise(td_c, noiseFiles[i].c_str()); 725 | 726 | string noiseImage = rootDirectory+"/noise_image_"+itoa(i, 10); 727 | mmkdir(noiseImage.c_str()); 728 | 729 | noiseImage = noiseImage+"/image"; 730 | drawImage(td_c, noiseImage.c_str()); 731 | 732 | } 733 | 734 | if (numNoise > 0) { 735 | cout << "Images have been succesfully degraded by noise and saved..." << endl; 736 | } 737 | 738 | //clean up 739 | delete buff; 740 | delete td; 741 | delete vt; 742 | } 743 | 744 | } catch (string str) { 745 | cout << "ERROR: " << str << endl; 746 | cout << "Exiting VascuSynth" << endl; 747 | } 748 | 749 | return 0; 750 | } 751 | 752 | --------------------------------------------------------------------------------