1 #include <algorithm>
2 #include <cmath>
3 #include <fstream>
4 #include <iostream>
5 #include <set>
6 #include <sstream>
7 
8 #include "acal_match_graph.h"
9 #include "vul/vul_file.h"
10 
11 
12 // --------------------
13 // match_vertex
14 // --------------------
15 
16 // edge ids connected to vertex
17 std::vector<size_t>
edge_ids() const18 match_vertex::edge_ids() const
19 {
20   std::vector<size_t> ids;
21   for (auto edge : edges_) {
22     ids.push_back(edge->id_);
23   }
24   return ids;
25 }
26 
27 
28 // equality operator
29 bool
operator ==(match_vertex const & other) const30 match_vertex::operator==(match_vertex const& other) const
31 {
32   return this->cam_id_ == other.cam_id_ &&
33          this->edge_ids() == other.edge_ids();
34 }
35 
36 
37 // streaming operator
38 std::ostream&
operator <<(std::ostream & os,match_vertex const & vertex)39 operator<<(std::ostream& os, match_vertex const& vertex)
40 {
41   os << "vertex " << vertex.cam_id_ << " with edges [";
42 
43   std::string separator;
44   for (auto edge_id : vertex.edge_ids()) {
45     os << separator << edge_id;
46     separator = ",";
47   }
48   os << "]";
49 
50   return os;
51 }
52 
53 
54 // --------------------
55 // match_edge
56 // --------------------
57 
58 // vertex ids connected to edge
59 std::vector<size_t>
vertex_ids() const60 match_edge::vertex_ids() const
61 {
62   std::vector<size_t> ids = {v0_->cam_id_, v1_->cam_id_};
63   return ids;
64 }
65 
66 
67 // equality operator
68 bool
operator ==(match_edge const & other) const69 match_edge::operator==(match_edge const& other) const
70 {
71   return this->id_ == other.id_ &&
72          this->vertex_ids() == other.vertex_ids() &&
73          this->matches_ == other.matches_;
74 }
75 
76 
77 // streaming operator
78 std::ostream&
operator <<(std::ostream & os,match_edge const & edge)79 operator<<(std::ostream& os, match_edge const& edge)
80 {
81   os << "edge " << edge.id_ << " connecting vertices ["
82      << edge.v0_->cam_id_ << "," << edge.v1_->cam_id_
83      << "] with " << edge.matches_.size() << " matches";
84   return os;
85 }
86 
87 
88 // --------------------
89 // acal_match_graph
90 // --------------------
91 
acal_match_graph(std::map<size_t,std::map<size_t,std::vector<acal_match_pair>>> const & incidence_matrix)92 acal_match_graph::acal_match_graph(
93     std::map<size_t, std::map<size_t, std::vector<acal_match_pair> > > const& incidence_matrix)
94 {
95   bool success = this->load_incidence_matrix(incidence_matrix);
96 }
97 
98 
99 bool
load_incidence_matrix(std::map<size_t,std::map<size_t,std::vector<acal_match_pair>>> const & incidence_matrix)100 acal_match_graph::load_incidence_matrix(
101     //       cam id i         cam id j            matches (i, j)
102     std::map<size_t, std::map<size_t, std::vector<acal_match_pair> > > const& incidence_matrix)
103 {
104   // construct vertices
105   for (std::map<size_t, std::map<size_t, std::vector<acal_match_pair> > >::const_iterator iit = incidence_matrix.begin();
106       iit != incidence_matrix.end(); ++iit) {
107     size_t i = iit->first;
108     if (!match_vertices_[i]) {
109       match_vertices_[i] = std::make_shared<match_vertex>(i);
110     }
111     const std::map<size_t, std::vector<acal_match_pair> >& temp = iit->second;
112     for (std::map<size_t, std::vector<acal_match_pair> >::const_iterator jit = temp.begin();
113         jit != temp.end(); ++jit) {
114       size_t j = jit->first;
115       if (!match_vertices_[j]) {
116         match_vertices_[j] = std::make_shared<match_vertex>(j);
117       }
118     }
119   }
120 
121   // construct edges
122   size_t edge_id = 0;
123   for (std::map<size_t, std::map<size_t, std::vector<acal_match_pair> > >::const_iterator iit = incidence_matrix.begin();
124       iit != incidence_matrix.end(); ++iit) {
125     size_t i = iit->first;
126     std::shared_ptr<match_vertex> v0 = match_vertices_[i];
127     const std::map<size_t, std::vector<acal_match_pair> >& temp = iit->second;
128     for (std::map<size_t, std::vector<acal_match_pair> >::const_iterator jit = temp.begin();
129         jit != temp.end(); ++jit) {
130       size_t j = jit->first;
131       const std::vector<acal_match_pair>& matches = jit->second;
132       if (matches.size() == 0)
133         continue;
134       std::shared_ptr<match_vertex> v1 = match_vertices_[j];
135       match_edges_.push_back(std::make_shared<match_edge>(v0, v1, matches, edge_id));
136       edge_id++;
137     }
138   }
139 
140   return true;
141 }
142 
143 
144 bool
load_from_fmatches(std::string const & fmatches_path)145 acal_match_graph::load_from_fmatches(std::string const& fmatches_path)
146 {
147   std::map<size_t, std::map<size_t, std::vector<acal_match_pair> > > fmatches;
148   std::map<size_t, std::string> image_paths;
149   bool good = acal_f_utils::read_f_matches(fmatches_path, fmatches, image_paths);
150   if (!good)
151     return false;
152 
153   if (!this->load_incidence_matrix(fmatches))
154     return false;
155 
156   image_paths_ = image_paths;
157   return true;
158 }
159 
160 
161 bool
load_affine_cams(std::string const & affine_cam_path)162 acal_match_graph::load_affine_cams(std::string const& affine_cam_path)
163 {
164   return acal_f_utils::read_affine_cameras(affine_cam_path, all_acams_);
165 }
166 
167 
168 void
clear_vertex_marks()169 acal_match_graph::clear_vertex_marks()
170 {
171   for (std::map<size_t, std::shared_ptr<match_vertex> >::iterator vit = match_vertices_.begin();
172       vit != match_vertices_.end(); ++vit)
173     (*vit).second->mark_ = false;
174 }
175 
176 
177 std::vector<std::shared_ptr<match_vertex> >
adjacent_verts(std::shared_ptr<match_vertex> const & v)178 acal_match_graph::adjacent_verts(std::shared_ptr<match_vertex> const& v)
179 {
180   std::vector<std::shared_ptr<match_vertex> > ret;
181   for (std::vector<match_edge*>::iterator eit = v->edges_.begin();
182       eit != v->edges_.end(); ++eit) {
183 
184     if ((*eit)->v0_ != v)
185       ret.push_back((*eit)->v0_);
186 
187     if ((*eit)->v1_ != v)
188      ret.push_back((*eit)->v1_);
189   }
190   return ret;
191 }
192 
193 
194 void
visit(std::shared_ptr<match_vertex> & v,std::vector<std::shared_ptr<match_vertex>> & comp)195 acal_match_graph::visit(
196     std::shared_ptr<match_vertex>& v,
197     std::vector<std::shared_ptr<match_vertex> >& comp)
198 {
199   v->mark_ = true;
200   comp.push_back(v);
201   std::vector<std::shared_ptr<match_vertex> > adj_verts = adjacent_verts(v);
202   for (std::vector<std::shared_ptr<match_vertex> >::iterator vit = adj_verts.begin();
203       vit != adj_verts.end(); ++vit) {
204     if ((*vit)->mark_)
205       continue;
206     visit(*vit,comp);
207   }
208 }
209 
210 
211 void
find_connected_components()212 acal_match_graph::find_connected_components()
213 {
214   this->clear_vertex_marks();
215   for (std::map<size_t, std::shared_ptr<match_vertex> >::iterator vit = match_vertices_.begin();
216       vit != match_vertices_.end(); ++vit) {
217     if ((*vit).second->mark_)
218       continue;
219     std::vector<std::shared_ptr<match_vertex> > comp;
220     visit((*vit).second, comp);
221     if (comp.size())
222       conn_comps_.push_back(comp);
223   }
224 }
225 
226 
227 void
set_intersect_match_edge(std::shared_ptr<match_vertex> const & focus_vert,match_edge * e,std::vector<acal_corr> & focus_corrs,std::map<size_t,std::vector<acal_corr>> & other_corrs)228 acal_match_graph::set_intersect_match_edge(
229     std::shared_ptr<match_vertex> const& focus_vert,
230     match_edge* e,
231     std::vector<acal_corr>& focus_corrs,
232     std::map<size_t, std::vector<acal_corr> >& other_corrs)
233 {
234   size_t fid = focus_vert->cam_id_; // focus cam id
235   size_t oid = -1;   //other cam id
236   std::shared_ptr<match_vertex> v0 = e->v0_, v1 = e->v1_; //edge verts
237   size_t nm = e->matches_.size();
238   if (nm == 0)
239     return; //e has no matches - shouldn't happen
240 
241   // initialization step for first call
242   if (focus_corrs.size() == 0 && other_corrs.size() == 0) {
243     focus_corrs.resize(nm);
244     if (v0->cam_id_ == fid) {  // is v0 the focus vertex?
245       oid = v1->cam_id_;
246       other_corrs[oid].resize(nm);
247       for (size_t m = 0; m<nm; ++m) { // idx1, uv1 are focus corrs
248         const acal_match_pair& pr = e->matches_[m];
249         focus_corrs[m] = pr.corr1_;
250         other_corrs[oid][m] = pr.corr2_;
251       }
252     } else {  // v1 is the focus vertex
253       oid = v0->cam_id_;
254       other_corrs[oid].resize(nm);
255       for (size_t m = 0; m<nm; ++m) {  // idx2, uv2 are focus corrs
256         const acal_match_pair& pr = e->matches_[m];
257         focus_corrs[m] = pr.corr2_;
258         other_corrs[oid][m] = pr.corr1_;
259       }
260     }
261     return;
262   }
263 
264   // intersect correspondences of e with current correspondence sets
265   std::vector<acal_corr> inter_focus_corrs; //focus corrs after intersection
266   std::map<size_t, std::vector<acal_corr> > inter_other_corrs; //other camera corrs after intersection
267   if (v0->cam_id_ == fid) { // idx1, uv1 are assigned to focus
268     oid = v1->cam_id_;
269     for (size_t m = 0; m<nm; ++m) {
270         const acal_match_pair& pr = e->matches_[m];
271         size_t idf = pr.corr1_.id_, ido = pr.corr2_.id_;
272 
273         bool found = false; // find idf in focus_corrs
274         for (std::vector<acal_corr>::iterator fit = focus_corrs.begin();
275             fit != focus_corrs.end()&&!found; ++fit)
276           found = (*fit).id_ == idf;
277 
278         if (found) {
279           inter_focus_corrs.push_back(pr.corr1_);
280           inter_other_corrs[oid].push_back(pr.corr2_);
281         }
282     }
283   } else {  // idx2, uv2 are assigned to focus
284     oid = v0->cam_id_;
285     for (size_t m = 0; m<nm; ++m) {
286       const acal_match_pair& pr = e->matches_[m];
287       size_t idf = pr.corr2_.id_, ido = pr.corr1_.id_;
288 
289         bool found = false; // find idf in focus_corrs
290         for (std::vector<acal_corr>::iterator fit = focus_corrs.begin();
291             fit != focus_corrs.end()&&!found; ++fit)
292           found = (*fit).id_ == idf;
293 
294       if (found) {
295         inter_focus_corrs.push_back(pr.corr2_);
296         inter_other_corrs[oid].push_back(pr.corr1_);
297       }
298     }
299   }
300 
301   // other cam, oid, is up to date but need to remove correspondences from other cams
302   size_t nif = inter_focus_corrs.size();
303   for (std::map<size_t, std::vector<acal_corr > >::iterator cit = other_corrs.begin();
304       cit != other_corrs.end(); ++cit) {
305     size_t ocam_id = cit->first;
306     std::vector<acal_corr> pruned_other_corrs;
307     const std::vector<acal_corr>& cur_ocorrs = cit->second;
308     size_t noc = cur_ocorrs.size();
309 
310     std::vector<acal_corr> otemp;
311 
312     size_t nf = focus_corrs.size();
313     size_t nco = cur_ocorrs.size();
314     if (nf != nco) {
315       std::cout << "Mismatch in correspondences - fatal" << std::endl;
316       focus_corrs.clear();
317       other_corrs.clear();
318       return;
319     }
320     // search through original focus corrs to find vector index of
321     // the other image corr in the set of other correspondences before intersection
322     for (size_t f = 0; f<nif; ++f) {
323       const acal_corr& ifc = inter_focus_corrs[f];
324       size_t fidx = ifc.id_;
325       bool found = false;
326       size_t found_j = -1;
327       for (size_t j = 0; j<nf&&!found; ++j) {
328         found = focus_corrs[j].id_ == fidx;
329         found_j = j;
330       }
331       if (found)
332         pruned_other_corrs.push_back(cur_ocorrs[found_j]);
333     }
334     other_corrs[ocam_id] = pruned_other_corrs;
335   }
336   other_corrs[oid] = inter_other_corrs[oid];
337   focus_corrs = inter_focus_corrs;
338 }
339 
340 
341 bool
find_joint_tracks(std::shared_ptr<match_vertex> const & focus_vert,std::vector<std::map<size_t,vgl_point_2d<double>>> & joint_tracks,size_t min_n_tracks)342 acal_match_graph::find_joint_tracks(
343     std::shared_ptr<match_vertex> const& focus_vert,
344     //   track            cam_id  correspondence
345     std::vector< std::map<size_t, vgl_point_2d<double> > >& joint_tracks,
346     size_t min_n_tracks)
347 {
348 
349   //    tracks for focus vert
350   std::vector<acal_corr> focus_corrs;
351   //   other cam id      tracks on other cam
352   std::map<size_t, std::vector<acal_corr> > other_corrs;
353 
354   size_t fid = focus_vert->cam_id_;
355 
356   for (std::vector<match_edge*>::iterator eit = focus_vert->edges_.begin();
357       eit != focus_vert->edges_.end(); ++eit) {
358     // temporary cache in case intersection produces no tracks
359     std::vector<acal_corr> tempf = focus_corrs;
360     std::map<size_t, std::vector<acal_corr> > tempo = other_corrs;
361 
362     // intersect matches of *eit with current set of tracks
363     set_intersect_match_edge(focus_vert, *eit, tempf, tempo);
364 
365     // if min number of tracks survive update the current intersection set
366     // otherwise skip the edge
367     if (tempf.size() >= min_n_tracks) {
368       focus_corrs = tempf;
369       other_corrs = tempo;
370     }
371   }
372   // check for failure
373   size_t nf = focus_corrs.size();
374   if (nf < min_n_tracks)
375     return false;
376 
377   // convert to form suitable for ray intersection (track form) to construct 3-d points
378   joint_tracks.resize(nf);
379   size_t t = 0;
380   for (std::vector<acal_corr >::iterator fit = focus_corrs.begin();
381       fit != focus_corrs.end(); ++fit, t++)
382     joint_tracks[t][fid] = (*fit).pt_;
383 
384   for (std::map<size_t, std::vector<acal_corr> >::iterator oit = other_corrs.begin();
385       oit != other_corrs.end(); ++oit) {
386     size_t cid = oit->first;
387     const std::vector<acal_corr>& ocorrs = oit->second;
388     if (ocorrs.size() != nf) {
389       std::cout << "inconsistent number of correspondences" << std::endl;
390       return false;
391     }
392     t = 0;
393     for (; t<nf; ++t)
394       joint_tracks[t][cid] = ocorrs[t].pt_;
395   }
396   return true;
397 }
398 
399 
400 void
compute_focus_tracks()401 acal_match_graph::compute_focus_tracks()
402 {
403   size_t nc = conn_comps_.size();
404   focus_track_metric_.resize(nc, 0.0);
405 
406   size_t V = 0;
407   for (size_t c = 0; c < nc; ++c) {
408     V += conn_comps_[c].size();
409   }
410 
411   for (size_t c = 0; c < nc; ++c) {
412     size_t nv = conn_comps_[c].size();
413     if (nv == 0) {
414       continue;
415     }
416     double metric = 0.0;
417     //   focus cam id      track          cam_id   correspondence location
418     std::map<size_t, std::vector< std::map<size_t, vgl_point_2d<double> > > > c_tracks;
419     for (size_t i = 0; i < nv; ++i) {
420       std::shared_ptr<match_vertex>  focus_v = conn_comps_[c][i];
421       if (!focus_v)
422         continue;
423 
424       //    track          cam_id   correspondence location
425       std::vector< std::map<size_t, vgl_point_2d<double> > > joint_tracks;
426       if (!find_joint_tracks(focus_v, joint_tracks, params_.min_n_tracks_))
427         continue;
428       size_t ntrks = joint_tracks.size();
429       if (ntrks == 0) {
430         metric = -10.0;
431         continue;
432       }
433       size_t n_connected_cams = joint_tracks[0].size();
434       if (n_connected_cams < params_.min_n_cams_)
435         continue;
436       // update metric
437       metric += static_cast<double>((n_connected_cams - 2) * (n_connected_cams - 2) * ntrks);
438       c_tracks[focus_v->cam_id_] = joint_tracks;
439       }
440     focus_track_metric_[c] = metric * (static_cast<double>(nv) / V);
441     focus_tracks_[c] = c_tracks;
442   }
443 }
444 
445 
446 void
compute_match_trees()447 acal_match_graph::compute_match_trees()
448 {
449   size_t nc = conn_comps_.size();
450   // iterate over all the connected components
451   for (size_t c = 0; c < nc; ++c) {
452     size_t nv = conn_comps_[c].size();
453     if (nv == 0)
454       continue;
455     // iterate over all vertices in connected component c
456     for (size_t v = 0; v<nv; ++v) {
457       std::shared_ptr<match_vertex> f_vert = conn_comps_[c][v];
458       size_t fidx = f_vert->cam_id_;
459       auto mt_ptr = std::make_shared<acal_match_tree>(fidx);
460       std::map<size_t, std::shared_ptr<match_vertex> > active_verts;
461       active_verts[fidx] = f_vert;
462       std::set<size_t> used_verts;     // vertices already explored for a given focus vertex
463       std::set<match_edge*> used_edges;// graph edges already explored for a given focus vertex
464       bool first = true;
465       // construct the match tree from all reachable vertices
466       // from f_vert in connected component c
467       while (active_verts.size() > 0) {
468         size_t aidx = (active_verts.begin())->first;
469         // has active vertex aidx already been explored?
470         std::set<size_t>::iterator uit;
471         uit = std::find(used_verts.begin(), used_verts.end(), aidx);
472         if (uit != used_verts.end()) {
473           active_verts.erase(*uit);
474           continue;
475         }
476         // no, so try to extend the tree
477         used_verts.insert(aidx);
478         std::shared_ptr<match_vertex> a_vert = (active_verts.begin())->second;
479         std::vector<match_edge*>& edges = a_vert->edges_;
480         size_t ne = edges.size();
481         // iterate over the graph edges for the active vertex
482         for (size_t e = 0; e<ne; ++e) {
483           match_edge* edge = edges[e];
484           // has the edge already been explored?
485           std::set<match_edge*>::iterator eit;
486           eit = std::find(used_edges.begin(), used_edges.end(), edge);
487           if (eit != used_edges.end())
488             continue;
489           // no so try to extend the tree
490           used_edges.insert(edge);
491           size_t id0 = edge->v0_->cam_id_;
492           size_t id1 = edge->v1_->cam_id_;
493           std::vector<acal_match_pair>  match_pairs = edge->matches_;
494           size_t parent_id = id0, child_id = id1;
495           if (id1 == aidx) {
496             parent_id = id1; child_id = id0;
497             acal_match_utils::reverse(match_pairs);
498           }
499           // try to add the child to a node of the current tree
500           if (mt_ptr->add_child_node(parent_id, child_id, match_pairs))
501             if (id1 == aidx)  // if success then add child node as an active vertex
502               active_verts[id0] = edge->v0_;
503             else
504               active_verts[id1] = edge->v1_;
505         }// end vert edges
506         active_verts.erase(aidx);
507       } // end active verts
508       mt_ptr->update_tree_size();
509       match_trees_[c][fidx] = mt_ptr;
510     }// end focus v
511   } // end conn-comp
512 }
513 
514 
515 bool
valid_tree(std::shared_ptr<acal_match_tree> const & mtree)516 acal_match_graph::valid_tree(std::shared_ptr<acal_match_tree> const& mtree)
517 {
518   // valid trees must have at least 2 nodes
519   if (mtree->size() < 2)
520     return false;
521 
522   std::vector< std::map<size_t, vgl_point_2d<double> > > tracks = mtree->tracks();
523   std::map<size_t, std::map<size_t, vgl_point_2d<double> > > proj_tracks;
524   std::map<size_t, vpgl_affine_camera<double> > tree_acams;
525   std::vector<size_t> tree_cam_ids = mtree->cam_ids();
526 
527   for (std::vector<size_t>::iterator cit = tree_cam_ids.begin();
528        cit != tree_cam_ids.end(); ++cit)
529   {
530     if (all_acams_.count(*cit) == 0) {
531       std::cout << "affine camera " << *cit << " not in match graph - fatal" << std::endl;
532       return false;
533     }
534     tree_acams[*cit] = all_acams_[*cit];
535   }
536 
537   std::map<size_t, vgl_point_3d<double> > inter_pts;
538   if (! acal_f_utils::intersect_tracks_with_3d(tree_acams, tracks, inter_pts, proj_tracks))
539     return false;
540 
541   double max_proj_error = 0.0;
542   size_t max_track = -1, max_cam_id = -1;
543   for (std::map<size_t, std::map<size_t, vgl_point_2d<double> > >::iterator pit = proj_tracks.begin();
544        pit != proj_tracks.end(); ++pit)
545   {
546     size_t tidx = pit->first; //track index
547     std::map<size_t, vgl_point_2d<double> > temp = pit->second;
548     for (std::map<size_t, vgl_point_2d<double> >::iterator cit = temp.begin();
549          cit != temp.end(); ++cit)
550     {
551       size_t cam_id = cit->first;
552       vgl_point_2d<double>& pt      = tracks[tidx][cam_id];
553       vgl_point_2d<double>& proj_pt = cit->second;
554       double proj_error = (pt-proj_pt).length();
555       if (proj_error > max_proj_error) {
556         max_proj_error = proj_error;
557         max_track = tidx;
558         max_cam_id = cam_id;
559       }
560     }
561   }
562 
563   //std::cout << max_proj_error << ' ';
564   if (max_proj_error > params_.max_uncal_proj_error_) {
565     //std::cout << max_proj_error << " error exceeds limit for cam " << max_cam_id << " on track " << max_track << std::endl;
566     bad_track_camera_ids_[max_cam_id]++;
567     return false;
568   }
569   return true;
570 }
571 
572 
573 void
print_bad_camera_ids()574 acal_match_graph::print_bad_camera_ids()
575 {
576   std::cout << "cameras that have excessive projection error in a track" << std::endl;
577   for(std::map<size_t, size_t>::iterator bit = bad_track_camera_ids_.begin();
578       bit != bad_track_camera_ids_.end(); ++bit) {
579         size_t nbad = bit->second;
580         if (nbad == 0) continue;
581         std::cout << "cam id " << bit->first << " number of tracks invalidated " << bit->second << std::endl;
582   }
583 }
584 
585 
586 void
validate_match_trees_and_set_metric()587 acal_match_graph::validate_match_trees_and_set_metric()
588 {
589   size_t ncc = conn_comps_.size();
590   match_tree_metric_.clear();
591   match_tree_metric_.resize(ncc, 0);
592 
593   for (size_t cc = 0; cc<ncc; ++cc)
594   {
595     std::map<size_t, std::shared_ptr<acal_match_tree> >& mtrees = match_trees_[cc];
596     std::map<size_t, std::shared_ptr<acal_match_tree> > temp, repaired_trees;
597     size_t  n_trees = mtrees.size();
598     if (n_trees==0) {
599       std::cout << "no match trees for connected component " << cc << std::endl;
600       continue;
601     }
602 
603     // initialize bad camera map
604     for (std::map<size_t, std::shared_ptr<acal_match_tree> >::iterator trit = mtrees.begin();
605          trit != mtrees.end(); ++trit)
606     {
607       std::vector<size_t> cam_ids =  trit->second->cam_ids();
608       for(size_t i = 0; i<cam_ids.size(); ++i)
609         bad_track_camera_ids_[cam_ids[i]] = 0;//initialize bad in track count to 0
610     }
611 
612     for (std::map<size_t, std::shared_ptr<acal_match_tree> >::iterator trit = mtrees.begin();
613          trit != mtrees.end(); ++trit)
614     {
615       if (valid_tree(trit->second))//updates bad track camera count
616         temp[trit->first] = trit->second;
617     }
618 
619     // no valid match trees
620     if (temp.empty())
621     {
622       std::cout << "all match trees failed for connected component " << cc
623                 << " listing cams with excessive projection error" << std::endl;
624       std::vector<size_t> bad_ids;
625       for (std::map<size_t, size_t>::iterator bit = bad_track_camera_ids_.begin();
626            bit != bad_track_camera_ids_.end(); ++bit)
627       {
628         size_t nbad = bit->second;
629         if (nbad > 0) {
630           bad_ids.push_back(bit->first);
631           std::cout << "cam id " << bit->first << ", n_bad " << nbad
632                     << ", n_trees " << n_trees << std::endl;
633         }
634       }
635 
636       std::cout << "remove bad cameras from match trees and retry to find valid trees" << std::endl;
637       std::map<size_t, std::shared_ptr<acal_match_tree> > repaired_trees;
638       for(std::map<size_t, std::shared_ptr<acal_match_tree> >::iterator mit = mtrees.begin();
639           mit != mtrees.end(); ++mit)
640       {
641         size_t fid = mit->first;
642         if (bad_track_camera_ids_[fid] > 0) // skip entire tree if root is bad
643           continue;
644         std::shared_ptr<acal_match_tree> repaired(new acal_match_tree(*(mit->second), bad_ids));
645         repaired_trees[mit->first] = repaired;
646       }
647 
648       // initialize bad camera map
649       bad_track_camera_ids_.clear();
650       for (std::map<size_t, std::shared_ptr<acal_match_tree> >::iterator trit = repaired_trees.begin();
651            trit != repaired_trees.end(); ++trit)
652       {
653         std::vector<size_t> cam_ids =  trit->second->cam_ids();
654         for(size_t i = 0; i<cam_ids.size(); ++i)
655           bad_track_camera_ids_[cam_ids[i]] = 0;//initialize bad in track count to 0
656       }
657 
658       for (std::map<size_t, std::shared_ptr<acal_match_tree> >::iterator trit = repaired_trees.begin();
659            trit != repaired_trees.end(); ++trit)
660       {
661         if (valid_tree(trit->second))//updates bad track camera count
662           temp[trit->first] = trit->second;
663       }
664     }
665 
666     if (temp.size() > 0)
667     {
668       match_trees_[cc] = temp;
669       std::shared_ptr<acal_match_tree> best_tree = this->largest_tree(cc);
670       if (!best_tree) {
671         match_tree_metric_[cc] = 0;
672       } else {
673         match_tree_metric_[cc] = best_tree->size();
674       }
675     }
676   }
677 }
678 
679 
680 // the predicate that orders based on number of cameras in the tree
681 static bool
tree_size_greater(std::pair<size_t,std::shared_ptr<acal_match_tree>> const a,std::pair<size_t,std::shared_ptr<acal_match_tree>> const b)682 tree_size_greater(std::pair<size_t, std::shared_ptr<acal_match_tree> > const a,
683                   std::pair<size_t, std::shared_ptr<acal_match_tree> > const b)
684 {
685   return a.second->n_ > b.second->n_;
686 }
687 
688 
689 // sort the trees on number of cameras and return the first tree in the sorted list
690 std::shared_ptr<acal_match_tree>
largest_tree(size_t conn_comp_index)691 acal_match_graph::largest_tree(size_t conn_comp_index)
692 {
693   if (conn_comp_index == -1)
694     return std::shared_ptr<acal_match_tree>();
695   std::map<size_t, std::shared_ptr<acal_match_tree> > & mtrees = match_trees_[conn_comp_index];
696   if (mtrees.size()==0) {
697     std::cout << "no match trees for connected component " << conn_comp_index << std::endl;
698     return std::shared_ptr<acal_match_tree>();
699   }
700   std::vector<std::pair<size_t, std::shared_ptr<acal_match_tree> > > trees;
701   for (std::map<size_t, std::shared_ptr<acal_match_tree> >::iterator mit = mtrees.begin();
702 	  mit != mtrees.end(); ++mit) {
703     std::pair<size_t, std::shared_ptr<acal_match_tree> > pr(mit->first, mit->second);
704     trees.push_back(pr);
705   }
706   std::sort(trees.begin(), trees.end(), tree_size_greater);
707   return trees[0].second;
708 }
709 
710 
711 void
print_connected_components()712 acal_match_graph::print_connected_components()
713 {
714   size_t nc = conn_comps_.size();
715   for (size_t c =0; c<nc; ++c) {
716     std::cout << "\nconnected component " << c <<
717       "\n( ";
718     size_t nv = conn_comps_[c].size();
719     for (size_t i = 0; i<nv; ++i)
720       std::cout << conn_comps_[c][i]->cam_id_ << ' ';
721     std::cout << ")" << std::endl;
722     for (size_t i = 0; i<nv; ++i) {
723       std::string img_path = image_paths_[conn_comps_[c][i]->cam_id_];
724       std::string img_name = vul_file::strip_directory(img_path);
725       img_name = vul_file::strip_extension(img_name);
726       std::cout << conn_comps_[c][i]->cam_id_ << ' ' << img_name << std::endl;
727     }
728     std::cout << std::endl;
729   }
730 }
731 
732 
733 void
print_n_tracks_for_conn_comp()734 acal_match_graph::print_n_tracks_for_conn_comp()
735 {
736     size_t nc = conn_comps_.size();
737   for (size_t c =0; c<nc; ++c) {
738     std::cout << "\nconnected component " << c <<  std::endl;
739     size_t nv = conn_comps_[c].size();
740     for (size_t i = 0; i<nv; ++i) {
741       const std::shared_ptr<match_vertex>& v = conn_comps_[c][i];
742       std::cout << "vertex " << v->cam_id_ << "\n( ";
743       for (std::vector<match_edge*>::iterator eit = v->edges_.begin();
744       eit != v->edges_.end(); ++eit)
745         std::cout << (*eit)->v0_->cam_id_ << ":" << (*eit)->v1_->cam_id_ << ' ' << (*eit)->matches_.size() << ' ';
746       std::cout << " )" << std::endl;
747     }
748   }
749 }
750 
751 
752 void
print_focus_tracks()753 acal_match_graph::print_focus_tracks()
754 {
755   std::cout << "\n======focus vertex tracks=======" << std::endl;
756   //         conn comp        focus vert      tracks          cam id     track point
757   for (std::map<size_t, std::map<size_t, std::vector< std::map<size_t, vgl_point_2d<double> > > > >::iterator cit = focus_tracks_.begin();
758       cit != focus_tracks_.end(); ++cit) {
759     size_t cc = cit->first;
760     std::cout << "\nconnected component " << cc << std::endl;
761     const std::map<size_t, std::vector< std::map<size_t, vgl_point_2d<double> > > >& ftemp = cit->second;
762     for (std::map<size_t, std::vector< std::map<size_t, vgl_point_2d<double> > > >::const_iterator fit = ftemp.begin();
763         fit != ftemp.end(); ++fit) {
764       size_t focus_id = fit->first;
765       std::cout << "----- focus camera id " << focus_id << std::endl;
766       const std::vector< std::map<size_t, vgl_point_2d<double> > >& tr_temp = fit->second;
767       size_t t = 0;
768       for (std::vector< std::map<size_t, vgl_point_2d<double> > >::const_iterator trit = tr_temp.begin();
769           trit != tr_temp.end(); ++trit, ++t) {
770         std::cout << "   track  " << t << std::endl;
771         const std::map<size_t, vgl_point_2d<double> >& tcorrs = *trit;
772         for (std::map<size_t, vgl_point_2d<double> >::const_iterator cmpit = tcorrs.begin();
773             cmpit != tcorrs.end(); ++cmpit) {
774           std::cout << "     " << cmpit->first << ' ' << cmpit->second.x() << ' ' << cmpit->second.y() << std::endl;
775         }
776       }
777     }
778     std::cout << std::endl;
779   }
780 }
781 
782 
783 void
adjust_affine_cams(std::map<size_t,vgl_vector_2d<double>> & cam_translations)784 acal_match_graph::adjust_affine_cams(std::map<size_t, vgl_vector_2d<double> >& cam_translations)
785 {
786   for (std::map<size_t, vgl_vector_2d<double> >::iterator cit = cam_translations.begin();
787       cit != cam_translations.end(); ++cit) {
788     size_t cidx = cit->first;
789     if (!all_acams_.count(cidx))
790       continue;
791     vpgl_affine_camera<double>& acam = all_acams_[cidx];
792     vnl_matrix_fixed<double, 3, 4> m = acam.get_matrix();
793     m[0][3] += cit->second.x();  m[1][3] += cit->second.y();
794     acam.set_matrix(m);
795   }
796 }
797 
798 
799 bool
save_graph_dot_format(std::string const & path)800 acal_match_graph::save_graph_dot_format(std::string const& path)
801 {
802   std::ofstream ostr(path.c_str());
803   if (!ostr) {
804     std::cout << "Can't open " << path << " to write dot file" << std::endl;
805     return false;
806   }
807   ostr << "graph" << std::endl;
808   ostr << "graphN {" << std::endl;
809 
810   size_t ne = match_edges_.size();
811   if (!ne) {
812     std::cout << "no edges in graph" << std::endl;
813     return false;
814   }
815   size_t ncc = conn_comps_.size();
816   std::vector<std::vector<std::string> > ccomp(ncc);
817   for (size_t i =0; i<ne; ++i) {
818     std::shared_ptr<match_edge> e = match_edges_[i];
819     if (!e)
820       continue;
821     size_t n_matches = e->matches_.size();
822     size_t v0_id = e->v0_->cam_id_;
823     size_t v1_id = e->v1_->cam_id_;
824     std::stringstream ss;
825     ss << v0_id << "--"<< v1_id << " [ label = \" " << n_matches << "\" ];";
826     size_t cc = -1;
827     for (size_t c = 0; c<ncc; ++c) {
828       bool found = false;
829       for (std::vector<std::shared_ptr<match_vertex> >::iterator vit = conn_comps_[c].begin();
830           vit != conn_comps_[c].end()&&!found; ++vit)
831         if ((*vit)->cam_id_ == v0_id ||(*vit)->cam_id_ == v1_id) {
832           found = true;
833           cc = c;
834         }
835       if (!found)
836         continue;
837     }
838     if (cc == -1) {
839       std::cout << "edge(" << v0_id << ' ' << v1_id << ") not found in any connected component" << std::endl;
840       return false;
841     }
842     ccomp[cc].push_back(ss.str());
843   }
844 
845   for (size_t c = 0; c<ncc; ++c) {
846     std::stringstream scc;
847     // note, a subgraph id must begin with cluster
848     scc << "subgraph cluster_cc" << c << " { label = \"conn comp " << c << " m = " << focus_track_metric_[c]<< "\";";
849     ostr << scc.str() << std::endl;
850     for (size_t s = 0; s<ccomp[c].size();++s)
851       ostr << ccomp[c][s] << std::endl;
852     ostr << "}" << std::endl;
853   }
854   ostr << "}" << std::endl;
855   ostr.close();
856   return true;
857 }
858 
859 
860 bool
save_focus_graphs_dot_format(size_t ccomp_index,std::string const & path)861 acal_match_graph::save_focus_graphs_dot_format(size_t ccomp_index, std::string const& path)
862 {
863   std::ofstream ostr(path.c_str());
864   if (!ostr) {
865     std::cout << "Can't open " << path << " to write dot file" << std::endl;
866     return false;
867   }
868   ostr << "graph" << std::endl;
869   ostr << "graphN {" << std::endl;
870 
871   std::map<size_t, std::vector< std::map<size_t, vgl_point_2d<double> > > >& ftrk = focus_tracks_[ccomp_index];
872   if (ftrk.size()==0) {
873     std::cout << "no focus tracks for connected component " << ccomp_index << std::endl;
874     return false;
875   }
876   for (std::map<size_t, std::vector< std::map<size_t, vgl_point_2d<double> > > >::iterator fit = ftrk.begin();
877       fit != ftrk.end(); ++fit) {
878     size_t cidx = fit->first;
879     std::vector< std::map<size_t, vgl_point_2d<double> > >& trks = fit->second;
880     size_t ntrk = trks.size();
881     if (ntrk == 0)
882       continue;
883     std::map<size_t, vgl_point_2d<double> >& trk = trks[0];
884     std::stringstream ss;
885     ss << " subgraph cluster" << cidx << " {\n label = \"focus vert id " << cidx << "\";";
886     ostr << ss.str() << std::endl;
887     for (std::map<size_t, vgl_point_2d<double> >::iterator cit = trk.begin();
888         cit != trk.end(); ++cit) {
889       if (cit->first == cidx)
890         continue;
891       std::stringstream css;
892       css << "  " << cidx << "--" << cit->first<<"." << cidx << " [ label = \" " << ntrk << "\" ];";
893       ostr << css.str() << std::endl;
894     }
895     ostr << " }" << std::endl;
896   }
897   ostr << "}" << std::endl;
898   ostr.close();
899   return true;
900 }
901 
902 
903 bool
save_match_trees_dot_format(size_t ccomp_index,std::string const & path,size_t num_trees)904 acal_match_graph::save_match_trees_dot_format(size_t ccomp_index, std::string const& path, size_t num_trees)
905 {
906   std::ofstream ostr(path.c_str());
907   if (!ostr) {
908     std::cout << "Can't open " << path << " to write dot file" << std::endl;
909     return false;
910   }
911   ostr << "graph" << std::endl;
912   ostr << "graphN {" << std::endl;
913 
914   std::map<size_t, std::shared_ptr<acal_match_tree> > & mtrees = match_trees_[ccomp_index];
915   if (mtrees.size()==0) {
916     std::cout << "no focus trees for connected component " << ccomp_index << std::endl;
917     return false;
918   }
919   std::vector<std::pair<size_t, std::shared_ptr<acal_match_tree> > > trees;
920   for (std::map<size_t, std::shared_ptr<acal_match_tree> >::iterator mit = mtrees.begin();
921 	  mit != mtrees.end(); ++mit) {
922 	  std::pair<size_t, std::shared_ptr<acal_match_tree> > pr(mit->first, mit->second);
923 	  trees.push_back(pr);
924   }
925   std::sort(trees.begin(), trees.end(), tree_size_greater);
926   size_t n = 0;
927   size_t nt = trees.size();
928   for (size_t i = 0; i<nt&&n<=num_trees; ++i, ++n) {
929     size_t cidx = trees[i].first;
930     std::shared_ptr<acal_match_tree> mt_ptr = trees[i].second;
931     size_t nn = 0;
932     mt_ptr->n_nodes(mt_ptr->root_, nn);
933     std::cout << "n_nodes " << nn << std::endl;
934     std::stringstream ss;
935     ss << " subgraph cluster" << cidx << " {\n label = \"focus vert id " << cidx << "\";";
936     ostr << ss.str() << std::endl;
937     mt_ptr->write_dot(ostr, mt_ptr->root_, mt_ptr->root_->cam_id_);
938     ostr << " }" << std::endl;
939   }
940   ostr << "}" << std::endl;
941   ostr.close();
942   return true;
943 }
944 
945 
946 std::map<size_t, std::string>
image_names()947 acal_match_graph::image_names()
948 {
949   std::map<size_t, std::string> ret;
950   for (std::map<size_t, std::string>::iterator sit = image_paths_.begin();
951       sit != image_paths_.end(); ++sit) {
952     size_t iit = sit->first;
953     std::string path = sit->second;
954     std::string temp = vul_file::strip_directory(path);
955     temp = vul_file::strip_extension(temp);
956     ret[iit] = temp;
957   }
958   return ret;
959 }
960 
961 
962 std::vector<std::shared_ptr<acal_match_tree> >
trees(size_t conn_comp_index)963 acal_match_graph::trees(size_t conn_comp_index)
964 {
965   std::vector<std::shared_ptr<acal_match_tree> > ret;
966   //      focus cam id               tree
967   std::map<size_t, std::shared_ptr<acal_match_tree> > trees = match_trees_[conn_comp_index];
968 
969   for (std::map<size_t, std::shared_ptr<acal_match_tree> >::iterator mit = trees.begin();
970       mit != trees.end(); ++mit)
971     ret.push_back(mit->second);
972   return ret;
973 }
974 
975 
976 // equality for iterable (std::vector, std::map) of std::shared_ptr
977 // requires special handling to compare dereferenced shared_ptr content
978 // std::equal pattern from https://stackoverflow.com/a/8473603
979 template<typename T>
980 bool
is_equal(std::vector<T> const & lhs,std::vector<T> const & rhs)981 is_equal(std::vector<T> const& lhs, std::vector<T> const& rhs)
982 {
983   auto pred = [] (decltype(*lhs.begin()) a, decltype(a) b)
984                  { return is_equal(a, b); };
985 
986   return lhs.size() == rhs.size() &&
987          std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);
988 }
989 
990 template<typename K, typename T>
991 bool
is_equal(std::map<K,T> const & lhs,std::map<K,T> const & rhs)992 is_equal(std::map<K, T> const& lhs, std::map<K, T> const& rhs)
993 {
994   auto pred = [] (decltype(*lhs.begin()) a, decltype(a) b)
995                  { return a.first == b.first && is_equal(a.second, b.second); };
996 
997   return lhs.size() == rhs.size() &&
998          std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);
999 }
1000 
1001 template<typename T>
1002 bool
is_equal(std::shared_ptr<T> const & lhs,std::shared_ptr<T> const & rhs)1003 is_equal(std::shared_ptr<T> const& lhs, std::shared_ptr<T> const& rhs)
1004 {
1005   return *lhs == *rhs;
1006 }
1007 
1008 
1009 // equality operator
1010 bool
operator ==(acal_match_graph const & other) const1011 acal_match_graph::operator==(acal_match_graph const& other) const
1012 {
1013   return this->params_ == other.params_
1014       && this->image_paths_ == other.image_paths_
1015       && this->all_acams_ == other.all_acams_
1016       && is_equal(this->match_vertices_, other.match_vertices_)
1017       && is_equal(this->match_edges_, other.match_edges_)
1018       && is_equal(this->conn_comps_, other.conn_comps_)
1019       && this->focus_tracks_ == other.focus_tracks_
1020       && this->focus_track_metric_ == other.focus_track_metric_
1021       && is_equal(this->match_trees_, other.match_trees_)
1022       && this->match_tree_metric_ == other.match_tree_metric_;
1023 }
1024 
1025