This project was created to analyze 3D MRI root data.
config.xmlcontains meta-information about raw data filesmain.pytakes raw data, upsamples it and calculates vesselness measurewurzel/*helper classes formain.pyandvis.py
dijkstra/grid.cpptransforms raw data and vesselness measure to root treedijkstra/treeinfo.cppprint info about a root tree, translate to other formats
wurzel/viewer.pydefines some default views on 3D data for conveniencevis.pyvisualizes results of all other components, including c++ components usingviewer.py
For each raw data file, put a section like this in the config file:
<datafile read-dtype="float32">
<base-name>../data/GersteLA_96x96x410</base-name>
<read-shape x="410" y="96" z="96"/>
<shape x="436" y="96" z="96"/>
<stem-plane>5</stem-plane>
<stem-axis>0</stem-axis>
<has-rohr>false</has-rohr>
<scale>0.94</scale>
<noise-cutoff>0.02</noise-cutoff>
</datafile>
read-dtypeis the datatype of the elementsbase-nameis the basename (with prepended../) as used lateron in BASE, which is used as a keyread-shapethe size of the data as stored in the raw.datfileshapethe size of the data after upsampling (should be isotropic then!)stem-planeindex of plane in which to look for stemstem-axisnumber of axis the plane index refers tohas-rohrtrue/false, whether the dataset contains a measuring tubescalehow to get fromread-shaperesololution to millimeters (factor)noise-cutoffaverage noise level after normalization
-
Makefilecontains targets to run all steps separately.make BASE=GersteLA_96x96x410_normal satoupsamples
data/GersteLA_96x96x410_normaland computes vesselnessmake BASE=GersteLA_96x96x410_normal gridgiven vesselness and upsampled raw data from previous step, find the root tree (in the graph-theoretical sense ^^)
make BASE=GersteLA_96x96x410_normal treeinfooutput some stats about the found root tree
-
Makefilehas targets for running a dataset through the whole process, e.g.make BASE=GersteLA_96x96x410_normaldoes all of the above steps.
-
Note that when running everything through
makecommands as suggested above, you need to adjust thedijkstra/gridparameters in theMakefile. -
vesselness measure: You can mainly configure the scales at which the measure is calculated. Have a look at
main.pyfor that purpose. -
root tree extraction: The
gridprogram is configurable using command line parameters. Try runninggrid --helpto find out more, e.g.Allowed options: --help produce help message --base the base name of the dataset -c [ --cfg ] arg (=config.xml) the config-file containing dataset descriptions (XML) --force force recomputation of dijkstra algorithm --stem-plane arg plane index in which to search for stem (read from config-file if not given) --stem-axis arg the axis of stem-plane (read from config-file if not given) -v [ --max-void-dist ] arg (=5) maximum consecutive distance (in mm) traveled through below-noiselevel data (use this to get rid of weed not connected to root) -r [ --max-radius ] arg (=1.8) maximum root radius (in mm) -s [ --start-threshold ] arg (=0.1) minimum raw value to start tracking is start-threshold times noise (from XML) -t [ --total-path-cost ] arg (=1000000000) maximal path cost -d [ --dijkstra-stop-val ] arg (=1000000000) stop Dijkstra when paths costs higher than this (decreases Dijkstr runtime) -l [ --leaf-select-method ] arg (=median_raw) method for selecting leaf candidates [`edge_detect`,`median_raw`,`subtree_weight`] -f [ --leaf-select-param ] arg (=0.0001) parameter of the leaf selection --no-gauss-fit save some time --no-subpix-pos save some time -
Note to some of the parameters:
force: after Dijkstra algorithm has been executed, the result is written to filesd_map.datandp_map.dat. When you re-rungrid, these files will be loaded instead of re-running Dijkstra. After changing scales in the vesselness measure ordijkstra-stop-val, you have to use this parameter to ensure that the distance/predecessor maps are updated.dijskstra-stop-val: Most of the MRI image volume does not belong to the root. You can avoid determining its connectivity (which takes a lot of time) by choosing a maximum distance from the root you want to explore.max-void-dist: Cut off branches where you need to cross this many millimeters of below-noise-level space. E.g. if you have underground weed that is not connected to the root, but the root is largely connected, you can use this parameter to remove the weed.leaf-select-method: a voxel is considered to be a leaf node if it is abovestart-thresholdand its distance to the root node is less thantotal-len-threshandedge_detectis the method used in the VISAPP paper for maize. it is true if the upstream/downstream ratio is abovemin-flow-thresh.subtree_weight: In the subtree defined by the current node, the sum of the weights above threshold must be larger thanmin-flow-thresh.median_rawIn a breadth first search towards the root, and away from it (with same number of steps, respectively) the median of the visited raw-values must at least bemin-flow-thresh*noiselevel.
All steps save intermediate outputs in the same directory as the original raw data. Subsequent runs will make use of (at least some) cached results:
- upsampling is not repeated if avoidable
- dijkstra is only run if no d_map/p_map file exists
- treeinfo is very fast and operates only on serialized root tree
If in doubt, delete intermediate files and re-run everything.
Care has been taken to parallelize as many operations as possible:
- Hessian computation of a single scale is parallelized with OpenMP using weave in Python. It will make use of all available cores.
- Optionally, you can distribute the Hessian computation of different scales
over multiple computers using a running ipcluster engine and the
-pparameter tomain.py - Dijkstra is not distributed, the parallel implementation for
dijkstra_shortest_pathalgorithm is currently only available for adjacency list graphs, not the newgrid_graphs. - Most loops over vertices in
grid.cpphave been parallelized using Intel Thread Building blocks. It automatically makes use of all available cores. The side-effect free action objects are implemented using C++0X lambda functions, which are available ingccversion >=4.5.
If you use this code, please cite our publication
Hannes Schulz, Johannes Postma, Dagmar van Dusschoten, Hanno Scharr, and Sven Behnke:
3D Reconstruction of Plant Roots from MRI Images
In Proceedings of International Conference on Computer Vision Theory and Applications (VISAPP), Rome, February 2012.

