Commit 005d864444e2473d3ec97242228eb41dddc89017

Authored by pranathivemuri
1 parent 018bda33

added all the functions to compute netmets metric ie. objtonetwork conversion,kd…

… tree generation, comparison of network and kdtree
Showing 1 changed file with 279 additions and 32 deletions   Show diff stats
stim/visualization/network.h
1 1 #ifndef STIM_NETWORK_H
2 2 #define STIM_NETWORK_H
3 3  
  4 +#include <list>
  5 +#include <stdlib.h>
  6 +#include <sstream>
  7 +#include <fstream>
  8 +#include <algorithm>
  9 +#include <string.h>
  10 +#include <math.h>
4 11 #include <stim/math/vector.h>
5 12 #include <stim/visualization/obj.h>
6   -#include <list>
  13 +#include <stim/visualization/fiber.h>
7 14 #include <ANN/ANN.h>
8   -#include "fiber.h"
9   -#include <sstream>
  15 +#include <boost/tuple/tuple.hpp>
10 16  
  17 +#define multfactor 2.51
11 18  
12 19 namespace stim{
13 20 /** This is the a class that interfaces with gl_spider in order to store the currently
... ... @@ -77,10 +84,7 @@ class network{
77 84 setEndpoints(int id1, int id2)
78 85 {
79 86 Nodes[1] = id1; Nodes[2] = id2;
80   - }
81   -
82   -
83   -
  87 + }
84 88  
85 89 };
86 90  
... ... @@ -480,40 +484,74 @@ class network{
480 484  
481 485 ///@param pos: std::vector of stim vecs with the positions of the point in the fiber.
482 486 ///@param radii: std::vector of floats with the radii of the fiber at positions in pos.
483   - ///adds an edge from two std::vectors with positions and radii.
484   - void
485   - addEdge(std::vector< stim::vec<T> > pos, std::vector<T> radii)
  487 + ///returns the forest as a std::string. For testing only.
  488 + std::string
  489 + edges_to_str()
486 490 {
487   - edge *a = new edge(pos,radii);
488   - E.push_back(a);
  491 + std::stringstream ss;
  492 + for(unsigned int i = 0; i < E.size(); i++)
  493 + {
  494 + ss << i << ": " << E[i]->str() << std::endl;
  495 + }
  496 + return(ss.str());
489 497 }
490   - void
491   - addNode(stim::vec<T> nodes)
  498 + // total number of points on all edges!=fibers in the network
  499 + int
  500 + totalEdges()
492 501 {
493   - V.push_back(nodes);
  502 + int totalEdgeVertices=0;int N=0;
  503 + for (int i=0; i < sizeE(); i ++)
  504 + {// FOR N points on the fiber N-1 edges are possible
  505 + N = E[i]->n_pts();
  506 + totalEdgeVertices = totalEdgeVertices + N- 1;
  507 + }
  508 + return totalEdgeVertices;
494 509 }
495   -
496   - ///adds an edge from two std::lists with positions and radii.
497   - /// NOT NECESSARY.
498   - void
499   - addEdge(std::list< stim::vec<T> > pos, std::list<T> radii)
  510 + // sum of all the fiber lengths
  511 + float
  512 + lengthOfNetwork()
500 513 {
501   - edge a = edge(pos,radii);
502   - E.push_back(a);
  514 + float networkLength=0;float N=0;
  515 + for (int i=0; i < sizeE(); i ++)
  516 + {
  517 + N = E[i]->length();
  518 + networkLength = networkLength + N;
  519 + }
  520 + return networkLength;
503 521 }
504   -
505   - ///returns the forest as a std::string. For testing only.
  522 + /// get index of a node on a fiber
  523 + // by matching the node on fiber to already set vertices (both strings)
  524 + // used in obj file conversion
  525 + int
  526 + getIndexes(std::string* input, std::string searched, int sizeV)
  527 + {
  528 + int result = 0;
  529 + for (int i = 0; i < sizeV; i++)
  530 + {
  531 + if (input[i] == searched)
  532 + {
  533 + result = i + 1;
  534 + }
  535 + }
  536 + return result;
  537 + }
  538 + // strObj returns a string of fiber indices corresponding to vectors of positions in the fiber including intermediate nodes
506 539 std::string
507   - edges_to_str()
  540 + strObj(std::string* strArray, int sizeV)
508 541 {
509 542 std::stringstream ss;
510   - for(unsigned int i = 0; i < E.size(); i++)
  543 + std::stringstream oss;
  544 + for(unsigned int i = 0; i < N; i++)
511 545 {
512   - ss << i << ": " << E[i]->str() << std::endl;
  546 + ss.str(std::string());
  547 + for(unsigned int d = 0; d < 3; d++)
  548 + {
  549 + ss<<c[i][d];
  550 + }
  551 + oss<<getIndexes(strArray, ss.str(), sizeV)<<" ";
513 552 }
514   - return(ss.str());
  553 + return oss.str();
515 554 }
516   -
517 555 ///@param i: index of the required fiber.
518 556 ///Returns an std::vector with the centerline of the ith fiber in the edgelist.
519 557 std::vector< stim::vec<T> >
... ... @@ -541,8 +579,158 @@ class network{
541 579 ss << "[from Node " << E[i] -> Nodes[1] << " to " << E[i] -> Nodes[2] << "]";
542 580 return ss.str();
543 581 }
544   -
  582 + //load an obj file into a network class
  583 + stim::network<T>
  584 + objToNetwork(stim::obj<T> objInput)
  585 + {
  586 + stim::network<T> nwc;
  587 + // function to convert an obj file loaded using stim/visualization/obj.h
  588 + // to a 3D network class using network.h methods.
  589 + std::vector< stim::vec<T> > fiberPositions; //initialize positions on the fiber to be added to network
  590 + objInput.getLine(1, fiberPositions); int numDim = fiberPositions[0].size();//find dimensions of the vertices in obj file
  591 + // to verify if the nodes are already pushed into node list
  592 + std::vector<bool> validList;
  593 + validList.assign(objInput.numV(), false);
  594 + // go through each of the lines "l followed by indices in obj and add all start and end vertices as the nodes
  595 + // using addNode function for adding nodes
  596 + // and the line as an edge(belongs to fiber class) using addEdge function
  597 + std::vector<unsigned> vertexIndices(objInput.numV());
  598 + std::vector< stim::vec<T> > allVerticesList = objInput.getAllV(vertexIndices);
  599 + for (unsigned i =1; i< objInput.numL() + 1; i++)
  600 + {
  601 + // edges/fibers could be added to the network class
  602 + std::vector< stim::vec<T> > fiberPositions;
  603 +
  604 + objInput.getLine(i, fiberPositions);
  605 + // finding size to allocate radii
  606 + int numPointsOnFiber = fiberPositions.size();
  607 + // to extend it to a 3D postion if it is a 1D/2D vertex in space
  608 + std::vector< stim::vec<T> > fiberPositions1(numPointsOnFiber);
  609 + // 2D case append and assign the last dimension to zero
  610 + if (numDim == 2)
  611 + {// 2D case append and assign the last dimension to zero repeating it for all the points on fiber
  612 + for (int j = 0; j < numPointsOnFiber; j ++)
  613 + {
  614 + fiberPositions1[j][numDim - 2] = fiberPositions[j][0];
  615 + fiberPositions1[j][numDim -1 ] = fiberPositions[j][1];
  616 + fiberPositions1[j][numDim] = 0;
  617 + }
  618 + }
  619 + // 1D case append and assign last two dimensions to zero
  620 + else if (numDim == 1)
  621 + {
  622 + for (int j = 0; j < numPointsOnFiber; j ++)
  623 + {
  624 + fiberPositions1[j][numDim - 2] = fiberPositions[j][0];
  625 + fiberPositions1[j][numDim -1 ] = 0;
  626 + fiberPositions1[j][numDim] = 0;
  627 + }
  628 + }
  629 + else
  630 + {
  631 + fiberPositions1 = fiberPositions;
  632 + }
  633 + std::vector<T> radii(numPointsOnFiber); // allocating radii to zero
  634 + // then add edge
  635 + // edge* a = new edge(fiberPositions1,radii);
  636 + //E.push_back(a);
  637 + nwc.addEdge(fiberPositions1, radii);
  638 + // add nodes
  639 + stim::vec<T> v0(3);stim::vec<T> vN(3);
  640 + int endId = numPointsOnFiber -1;
  641 + v0[0] = fiberPositions1[0][0];v0[1] = fiberPositions1[0][1];v0[2] = fiberPositions1[0][2];
  642 + vN[0] = fiberPositions1[endId][0];vN[1] = fiberPositions1[endId][1];vN[2] = fiberPositions1[endId][2];
  643 + // VISITED INDEXES OF the nodes are set to true
  644 + if(!validList[objInput.getIndex(allVerticesList, v0)])
  645 + {
  646 + //V.push_back(node(v0));
  647 + nwc.addNode(v0);
  648 + validList[objInput.getIndex(allVerticesList, v0)] = true;
  649 + }
  650 + if(!validList[objInput.getIndex(allVerticesList, vN)])
  651 + {
  652 + //V.push_back(node(vN));
  653 + nwc.addNode(vN);
  654 + validList[objInput.getIndex(allVerticesList, vN)] = true;
  655 + }
  656 + }
  657 + return nwc;
  658 + }
  659 + // convert ground and test case to network ,kd trees
  660 + boost::tuple< ANNkd_tree*, ANNkd_tree*, stim::network<T>, stim::network<T> >
  661 + LoadNetworks(std::string gold_filename, std::string test_filename)
  662 + {
  663 + ANNkd_tree* kdTree1;ANNkd_tree* kdTree2;
  664 + using namespace stim;
  665 + network network1;network network2;
  666 + stim::obj<T> objFile1(gold_filename);
  667 + network1 = objToNetwork(objFile1);
  668 + kdTree1 = generateKdTreeFromNetwork(network1);
  669 + std::cout<<"Gold Standard:network and kdtree generated"<<std::endl;
  670 + stim::obj<T> objFile2(test_filename);
  671 + network2 = objToNetwork(objFile2);
  672 + kdTree2 = generateKdTreeFromNetwork(network2);
  673 + std::cout<<"Test Network:network and kdtree generated"<<std::endl;
  674 + return boost::make_tuple(kdTree1, kdTree2, network1, network2);
  675 + }
  676 + //distance between two points
  677 + double dist(std::vector<T> p0, std::vector<T> p1)
  678 + {
545 679  
  680 + double sum = 0;
  681 + for(unsigned int d = 0; d < 3; d++)
  682 + sum += p0[d] * p1[d];
  683 + return sqrt(sum);
  684 + }
  685 + // sum of elements in a vector
  686 + double sum(stim::vec<T> metricList)
  687 + {
  688 + float sumMetricList = 0;
  689 + for (int count=0; count<metricList.size(); count++)
  690 + { sumMetricList += metricList[count];}
  691 + return sumMetricList;
  692 + }
  693 + //generate a kd tree from network
  694 + ANNkd_tree*
  695 + generateKdTreeFromNetwork(stim::network<T> network) //kd-tree stores all points in the fiber for fast searching
  696 + {
  697 + ANNkd_tree* kdt;
  698 + double **c; //centerline (array of double pointers)
  699 + float* r; // array of fiber radii
  700 + unsigned int n_data = network.sizeV(); //set the number of points
  701 + c = (double**) malloc(sizeof(double*) * n_data); //allocate the array pointer
  702 + for(unsigned int i = 0; i < n_data; i++) //allocate space for each point
  703 + {c[i] = (double*) malloc(sizeof(double) * 3);}
  704 + r = (float*) malloc(sizeof(float) * n_data); //allocate space for the radii
  705 + stim::vec<T> node;
  706 + for(int i = 0; i < n_data; i++)
  707 + {
  708 + node = network.V[i].getPosition();
  709 + //convert_to_double
  710 + for(unsigned int d = 0; d < 3; d++){ //for each dimension
  711 + c[i][d] = double(node[d]); //copy the coordinate
  712 + }
  713 + r[i] = r[i]; //copy the radius
  714 + }
  715 + ANNpointArray pts = (ANNpointArray)c; //create an array of data points
  716 + kdt = new ANNkd_tree(pts, n_data, 3); //build a KD tree
  717 + return kdt;
  718 + }
  719 +
  720 + ///@param pos: std::vector of stim vecs with the positions of the point in the fiber.
  721 + ///@param radii: std::vector of floats with the radii of the fiber at positions in pos.
  722 + ///adds an edge from two std::vectors with positions and radii.
  723 + void
  724 + addEdge(std::vector< stim::vec<T> > pos, std::vector<T> radii)
  725 + {
  726 + edge *a = new edge(pos,radii);
  727 + E.push_back(a);
  728 + }
  729 + void
  730 + addNode(stim::vec<T> nodes)
  731 + {
  732 + V.push_back(nodes);
  733 + }
546 734 ///exports the graph.
547 735 void
548 736 to_csv()
... ... @@ -575,6 +763,65 @@ class network{
575 763 }
576 764 ofs.close();
577 765 }
  766 + // gaussian function
  767 + float gaussianFunction(float x, float std=25)
  768 + {
  769 + float normalization = multfactor * std;
  770 + float evaluate = 1.0 - (exp(-x/(2*std*std)));
  771 + return evaluate;
  772 + }
  773 + boost::tuple< float, float >
  774 + compareSkeletons(boost::tuple< ANNkd_tree*, ANNkd_tree*, stim::network<int>, stim::network<int> > networkKdtree)
  775 + {
  776 + float gFPR, gFNR;
  777 + gFPR = CompareNetKD(networkKdtree.get<0>(), networkKdtree.get<3>());
  778 + gFNR = CompareNetKD(networkKdtree.get<1>(), networkKdtree.get<2>());
  779 + return boost::make_tuple(gFPR, gFNR);
  780 + }
  781 + // gaussian distance of points on network to Kdtree
  782 + float
  783 + CompareNetKD(ANNkd_tree* kdTreeGroundtruth, stim::network<T> networkTruth)
  784 + {
  785 + std::vector< stim::vec<T> > fiberPoints;
  786 + float netmetsMetric=0;
  787 + double eps = 0; // error bound
  788 + ANNdistArray dists1;dists1 = new ANNdist[1]; // near neighbor distances
  789 + ANNdistArray dists2; dists2 = new ANNdist[1];// near neighbor distances
  790 + ANNidxArray nnIdx1; nnIdx1 = new ANNidx[1]; // near neighbor indices // allocate near neigh indices
  791 + ANNidxArray nnIdx2; nnIdx2 = new ANNidx[1]; // near neighbor indices // allocate near neigh indices
  792 + int N; int numQueryPoints = networkTruth.totalEdges();
  793 + float totalNetworkLength = networkTruth.lengthOfNetwork();
  794 + stim::vec<float> fiberMetric(networkTruth.sizeE());
  795 + //for each fiber
  796 + for (unsigned i=0; i < networkTruth.sizeE(); i ++)
  797 + {
  798 + std::vector<T> p1(3); std::vector<T> p2(3);//temporary variables to store point positions
  799 + fiberPoints = networkTruth.E[i]->centerline();
  800 + N = networkTruth.E[i]->n_pts();
  801 + stim::vec<float> segmentMetric(N-1);
  802 + // for each segment in the fiber
  803 + for (unsigned j = 0; j < N - 1; j++)
  804 + {
  805 + ANNpoint queryPt1; queryPt1 = annAllocPt(3);
  806 + ANNpoint queryPt2; queryPt2 = annAllocPt(3);
  807 + //for each dimension of the point
  808 + for(unsigned int d = 0; d < 3; d++)
  809 + {
  810 + queryPt1[d] = double(fiberPoints[j][d]);
  811 + p1[d] = double(fiberPoints[j][d]);
  812 + queryPt2[d] = double(fiberPoints[j + 1][d]);
  813 + p2[d] = double(fiberPoints[j + 1][d]);// for each dimension
  814 + }
  815 + kdTreeGroundtruth->annkSearch( queryPt1, 1, nnIdx1, dists1, eps); // error bound
  816 + kdTreeGroundtruth->annkSearch( queryPt2, 1, nnIdx2, dists2, eps); // error bound
  817 + float dist1 = gaussianFunction(dists1[0]);float dist2 = gaussianFunction(dists2[0]);
  818 + segmentMetric[j] = (((dist1 + dist2) / 2) * dist(p1, p2)) ;
  819 + }
  820 + fiberMetric[i] = sum(segmentMetric);
  821 + }
  822 + netmetsMetric = sum(fiberMetric)/totalNetworkLength ;
  823 + return netmetsMetric;
  824 + }
578 825 void removeCharsFromString(std::string &str, char* charsToRemove ) {
579 826 for ( unsigned int i = 0; i < strlen(charsToRemove); ++i ) {
580 827 str.erase( remove(str.begin(), str.end(), charsToRemove[i]), str.end() );
... ... @@ -588,10 +835,10 @@ class network{
588 835 ofs.open("Graph.obj", std::ofstream::out | std::ofstream::app);
589 836 int num = V.size();
590 837 string *strArray = new string[num];
591   - for(int i = 0; i < V.size(); i++)
  838 + for(int i = 0; i < allVerticesList.size(); i++)
592 839 {
593 840 std::string str;
594   - str = V[i].str();
  841 + str = allVerticesList[i].str();
595 842 removeCharsFromString(str, "[],");
596 843 ofs << "v " << str << "\n";
597 844 removeCharsFromString(str," ");
... ...