Commit 005d864444e2473d3ec97242228eb41dddc89017
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," "); | ... | ... |