Blame view

main.cpp 6.3 KB
f51866f3   David Mayerich   initial commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  #include <iostream>
  #include <fstream>
  #include <vector>
  #include <string>
  #include <fstream>
  #include <stim/math/vector.h>
  #include <stim/parser/arguments.h>
  #include <stim/parser/filename.h>
  #include <stim/image/image.h>
  #include <ANN/ANN.h>
  
  //load a set of points from an ASCII file
  std::vector< stim::vec<float> > points_from_list(std::string filename){
  
  	std::vector< stim::vec<float> > result;		//create an array to store the result
  
  	std::string str;
  
  	//load the ground truth
  	std::ifstream gfile(filename.c_str());
  	while(std::getline(gfile, str)){
  		stim::vec<float> v(str);
  		result.push_back(v);
  	}
  
  	return result;			//return the list of points
  }
  
  //load a set of points from an image (nonzero values are point positions)
  std::vector< stim::vec<float> > points_from_image(std::string filename){
  
  	stim::image<unsigned char> I;		//create an image object
  
  	I.load(filename);					//load the image file
  
  	std::vector<unsigned long> idx = I.sparse_idx();		//get the 1D indices for each nonzero pixel
  
  	std::vector< stim::vec<float> > result;					//generate an array to store the 2D pixel positions
  	result.resize(idx.size());								//allocate the space to save time
  
  	unsigned long w = I.width();							//get the image width (used to resolve the 2D pixel position from the 1D index)
  
  	unsigned long x, y;
  	for(unsigned long i = 0; i < idx.size(); i++){
  		y = idx[i] / w;
  		x = idx[i] - y * w;
  
  		result[i] = stim::vec<float>(x, y);
  	}
  
  
  	return result;											//return the array of points
  
  }
  
  /// This function calculates the number of hits (
  void calc_hitlist(std::vector< unsigned int >& hits,
  					std::vector< float >& dists,
  				    std::vector< stim::vec<float> > data,
  				    std::vector< stim::vec<float> > query){
  
  	int n_data = data.size();
  	int n_query = query.size();
  
  	//allocate space in the hit lists
  	hits.resize(n_query);
  	dists.resize(n_query);
  
  	//calculate the spatial dimension
  	unsigned int dim = 1;
  	for(unsigned int i = 0; i < data.size(); i++)
  		if(data[i].size() > dim) dim = data[i].size();
  
  										//number of data points
  	ANNpointArray dataPts = annAllocPts(n_data, dim);				//array of data points
  	ANNpoint queryPt = annAllocPt(dim);							//query point
  	ANNidxArray nnIdx = new ANNidx[1];							//indices of nearest neighbors
  	ANNdistArray sq_dist = new ANNdist[1];						//array of squared distances to nearest neighbors
  
  	//load the ground truth points into the ANN point array
  	for(unsigned int i = 0; i < data.size(); i++){
  		for(unsigned int d = 0; d < dim; d++){
  			dataPts[i][d] = data[i][d];
  		}
  	}
  
  	//create a KD-tree
  	ANNkd_tree* kdTree;											//KD tree search structure
  	kdTree = new ANNkd_tree(dataPts, n_data, dim);				//create the KD tree search structure
  
  
  	//submit each query point to the KD tree to find the nearest neighbor
  	for(unsigned int q = 0; q < n_query; q++){
  
  		for(unsigned int d = 0; d < dim; d++){
  			queryPt[d] = query[q][d];
  		}
  
  		//query the KD-tree
  		kdTree->annkSearch(queryPt, 1, nnIdx, sq_dist);
  
  		//put the result in the hit array
  		hits[q] = nnIdx[0];
  		dists[q] = sqrt(sq_dist[0]);
  	}
  }
  
  int main(int argc, char** argv){
  
7aacb90f   David Mayerich   updated examples,...
110
111
112
113
114
115
116
117
  	//output advertisement

  	std::cout<<std::endl<<std::endl;

  	std::cout<<"========================================================================="<<std::endl;

  	std::cout<<"Thank you for using the PointMets validation tool!"<<std::endl;

  	std::cout<<"Scalable Tissue Imaging and Modeling (STIM) Lab, University of Houston"<<std::endl;

  	std::cout<<"Developers: David Mayerich"<<std::endl;

  	std::cout<<"Source: https://git.stim.ee.uh.edu/validation/pointmets"<<std::endl;

  	std::cout<<"========================================================================="<<std::endl<<std::endl;

f51866f3   David Mayerich   initial commit
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  
  
  	stim::arglist args;
  
  #ifdef _WIN32
  	args.set_ansi(false);
  #endif
  
  	//add arguments
  	args.add("help", "prints this help");
  	args.add("r", "maximum possible distance to consider a point 'detected'", "10", "[positive value]");
  
  	//parse the command line arguments
  	args.parse(argc, argv);
  
  	//display the help text if requested
7aacb90f   David Mayerich   updated examples,...
134
135
136
137
138
139
140
141
  	if(args["help"].is_set()){				

  		std::cout<<std::endl<<"usage: pointmets ground_truth test_case --option [A B C ...]"<<std::endl;

  		std::cout<<std::endl<<std::endl

  				  << "examples: pointmets blue_gt.txt blue_t.txt"<<std::endl;

  		std::cout << "          pointmets blue_gt.bmp blue_t.bmp" << std::endl;

  		std::cout<<std::endl<<std::endl;

  		std::cout<<args.str()<<std::endl;

  		exit(1);

f51866f3   David Mayerich   initial commit
142
143
  	}
  
7aacb90f   David Mayerich   updated examples,...
144
145
146
147
  	//if the input and output files aren't specified, throw an error and exit

  	if(args.nargs() < 2){

  		std::cout<<"ERROR: Two files must be specified for comparison, enter pointmets --help for options."<<std::endl<<std::endl;

  		exit(1);

f51866f3   David Mayerich   initial commit
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  	}
  
  	//get the ground truth and test file
  	stim::filename gfilename(args.arg(0));
  	stim::filename tfilename(args.arg(1));
  
  	//set the radius
  	float r = args["r"].as_float();
  
  	std::vector< stim::vec<float> > G;
  	std::vector< stim::vec<float> > T;
  	
  
  	//if an ASCII list of points is specified
  	if(gfilename.get_extension() == "bmp")
  		G = points_from_image(gfilename.str());
  	else
  		G = points_from_list(gfilename.str());
  
  	if(tfilename.get_extension() == "bmp")
  		T = points_from_image(gfilename.str());
  	else
  		T = points_from_list(tfilename.str());
  
  	//calculate the hit list and distances for the test array
  	std::vector< unsigned int > T_hits;
  	std::vector< float > T_dists;
  	calc_hitlist(T_hits, T_dists, G, T);
  
  	//calculate the hit list and distances for the ground truth array
  	std::vector< unsigned int > G_hits;
  	std::vector< float > G_dists;
  	calc_hitlist(G_hits, G_dists, T, G);
  
  	//count up metrics for each test point
  	std::vector< unsigned int > TP;
  	std::vector< unsigned int > FP;
  	unsigned int g, t;
  	for(t = 0; t < T.size(); t++){
  
  		//a true positive has a matching hit in both hit lists, with a distance less than the radius
  		g = T_hits[t];
  		if(G_hits[g] == t && T_dists[t] <= r) TP.push_back(t);
  		else FP.push_back(t);
  	}
  
  	std::vector< unsigned int > FN;
  	for(g = 0; g < G.size(); g++){
  		t = G_hits[g];
  		if(T_hits[t] != g || G_dists[g] > r) FN.push_back(g);
  	}
  
  	std::cout<<"N: "<<G.size()<<std::endl;
  	std::cout<<"TP: "<<TP.size()<<"   ( "<<(double)TP.size() / (double)G.size() * 100<<"% detected)"<<std::endl;
  	std::cout<<"FN: "<<FN.size()<<"   ( "<<(double)FN.size() / (double)G.size() * 100<<"% undetected)"<<std::endl;
  	std::cout<<"FP: "<<FP.size()<<"   ( "<<(double)FP.size() / (double)T.size() * 100<<"% incorrectly detected)"<<std::endl;
  
  
  
  }