1 // This is brl/bseg/bapl/bapl_connectivity.cxx
2 #include <iostream>
3 #include <algorithm>
4 #include <queue>
5 #include "bapl_connectivity.h"
6 //:
7 // \file
8 
9 #include <bapl/bapl_keypoint_set.h>
10 #include <bapl/bapl_keypoint.h>
11 #include <bapl/bapl_lowe_keypoint_sptr.h>
12 #include <bapl/bapl_lowe_keypoint.h>
13 #ifdef _MSC_VER
14 #  include "vcl_msvc_warnings.h"
15 #endif
16 #include <cassert>
17 
18 //: For sorting keypoint_match_sets
second_less(const bapl_keypoint_match_set_sptr & left_set,const bapl_keypoint_match_set_sptr & right_set)19 bool second_less( const bapl_keypoint_match_set_sptr& left_set, const bapl_keypoint_match_set_sptr& right_set)
20 {
21   return left_set->id_right_ < right_set->id_right_;
22 }
23 
24 
25 //: add this match set symmetrically, i.e. into the list of img id 1 as well img id 2, while adding for img id 2, reverse the keypoint pairs
add_sym(const bapl_keypoint_match_set_sptr & set)26 bool bapl_conn_table::add_sym(const bapl_keypoint_match_set_sptr& set)
27 {
28   // first add match set directly for id1, id2:
29   if (this->contains(set->id_left_, set->id_right_) || this->contains(set->id_right_, set->id_left_))
30     return false;
31 
32   bapl_conn& conn = conns_[set->id_left_];
33   auto p = lower_bound(conn.begin(), conn.end(), set, second_less);
34   conn.insert(p, set);
35 
36   //: reverse the match set
37   std::vector<bapl_key_match>& matches = set->matches_;
38   std::vector<bapl_key_match> reversed_matches;
39   for (auto & matche : matches) {
40     bapl_key_match key(matche.second, matche.first);
41     reversed_matches.push_back(key);
42   }
43   bapl_keypoint_match_set_sptr e = new bapl_keypoint_match_set(set->id_right_, set->id_left_, reversed_matches);
44   bapl_conn& conn_other = conns_[set->id_right_];
45   p = lower_bound(conn_other.begin(), conn_other.end(), e, second_less);
46   conn_other.insert(p, e);
47   return true;
48 }
49 
50 //: add this match set
add(const bapl_keypoint_match_set_sptr & set)51 bool bapl_conn_table::add(const bapl_keypoint_match_set_sptr& set)
52 {
53   // first add match set directly for id1, id2:
54   if (this->contains(set->id_left_, set->id_right_))
55     return false;
56 
57   bapl_conn& conn = conns_[set->id_left_];
58   auto p = lower_bound(conn.begin(), conn.end(), set, second_less);
59   conn.insert(p, set);
60   return true;
61 }
62 
63 //: make the table symmetric, only necessary if add() method is used as opposed to add_sym()
make_symmetric()64 void bapl_conn_table::make_symmetric()
65 {
66   for (auto & conn : conns_) {
67     for (const auto& ms : conn) {
68       //: reverse the match set
69       std::vector<bapl_key_match>& matches = ms->matches_;
70       std::vector<bapl_key_match> reversed_matches;
71       for (auto & matche : matches) {
72         bapl_key_match key(matche.second, matche.first);
73         reversed_matches.push_back(key);
74       }
75       bapl_keypoint_match_set_sptr e = new bapl_keypoint_match_set(ms->id_right_, ms->id_left_, reversed_matches);
76       this->add(e);
77     }
78   }
79 }
80 
81 
82 //: check if vector of image id1 already contains a match set for image id2.
83 //  conn is kept sorted with respect to second image ids, binary search to see if set->id_right_ exists
contains(int id1,int id2)84 bool bapl_conn_table::contains(int id1, int id2)
85 {
86   bapl_conn& conn = conns_[id1];
87   std::vector<bapl_key_match> matches; // dummy vector
88   bapl_keypoint_match_set_sptr e = new bapl_keypoint_match_set(id1, id2, matches);
89   std::pair<bapl_conn::const_iterator, bapl_conn::const_iterator> p = equal_range(conn.begin(), conn.end(), e, second_less);
90   return p.first != p.second; // true if not points to the end, false otherwise
91 }
92 
print_table()93 void bapl_conn_table::print_table()
94 {
95   for (const auto& conn : conns_) {
96     int crnt_id = 0;
97     for (auto & j : conn) {
98       int id2 = j->id_right_;
99       while (id2 > crnt_id) {
100         std::cout << "0 ";
101         crnt_id++;
102       }
103       std::cout << j->matches_.size() << ' ';
104       crnt_id++;
105     }
106     std::cout << std::endl;
107   }
108 }
109 
print_table_with_matches()110 void bapl_conn_table::print_table_with_matches()
111 {
112   for (const auto& conn : conns_) {
113     for (auto & j : conn) {
114       std::cout << j->id_left_ << ' ' << j->id_right_ << '\n'
115                << j->matches_.size() << '\n';
116       for (unsigned k = 0; k < j->matches_.size(); k++) {
117         std::cout << j->matches_[k].first->id() << ' ' << j->matches_[k].second->id() << '\n';
118       }
119       std::cout << '\n';
120     }
121   }
122 }
123 
124 //: return the number of neighbors for image with id i
get_number_of_neighbors(unsigned i)125 unsigned bapl_conn_table::get_number_of_neighbors(unsigned i)
126 {
127   return conns_[i].size();
128 }
129 
compare_first(const bapl_key_match & k1,const bapl_key_match & k2)130 bool compare_first(const bapl_key_match &k1, const bapl_key_match &k2)
131 {
132   return k1.first->id() < k2.first->id();
133 }
134 
135 //: compute a set of tracks, each corresponding to a separate 3d point.
136 //  assumes a symmetric connectivity table
compute_tracks(std::vector<bapl_track_data> & tracks,int)137 bool bapl_conn_table::compute_tracks(std::vector<bapl_track_data>& tracks, int  /*new_image_start*/)
138 {
139   unsigned num_images = conns_.size();
140   //: check if image data is set for each image
141   for (unsigned i = 0; i < num_images; i++) {
142     if (!img_data_[i]) {
143       std::cout << "Data of img: " << i << " has not been set!\n";
144       return false;
145     }
146   }
147 
148   // clear all marks for new images
149   for (unsigned i = 0; i < num_images; i++) {
150     // skip images with no neighbors
151     int num_nbrs = (int)this->get_number_of_neighbors(i);
152 
153     if (num_nbrs == 0)
154       continue;
155 
156     int num_features = img_data_[i]->keys_.size();
157     img_data_key_flags_[i].resize(num_features);
158   }
159 
160   int pt_idx = 0;
161 
162   // sort all match lists
163   for (unsigned i = 0; i < num_images; i++) {
164     bapl_conn& conn = conns_[i];
165     for (auto & j : conn) {
166       std::vector<bapl_key_match>& matches = j->matches_;
167       std::sort(matches.begin(), matches.end(), compare_first);
168     }
169   }
170 
171   std::vector<bool> img_visited(num_images, false);
172   std::vector<int> img_touched;
173   img_touched.reserve(num_images);
174 
175   for (unsigned int i = 0; i < num_images; i++) {
176     int num_features = img_data_[i]->keys_.size();
177 
178     // skip images with no neighbors
179     int num_nbrs = (int) this->get_number_of_neighbors(i);
180 
181     if (num_nbrs == 0)
182       continue;
183 
184     for (int j = 0; j < num_features; j++) {
185       bapl_keypoint_sptr kp = img_data_[i]->keys_[j];
186       bapl_image_key_vector features;
187       std::queue<bapl_image_key> features_queue;
188 
189       if (img_data_key_flags_[i][j])
190        continue; // already visited this feature
191 
192       int num_touched = img_touched.size();
193       for (int k = 0; k < num_touched; k++)
194         img_visited[img_touched[k]] = false;
195       img_touched.clear();
196 
197       // breadth first search given this feature
198       img_data_key_flags_[i][j] = true;
199 
200       features.push_back(bapl_image_key(i, kp));
201       features_queue.push(bapl_image_key(i, kp));
202 
203       img_visited[i] = true;
204       img_touched.push_back(i);
205 
206       int num_rounds = 0;
207       while (!features_queue.empty())
208       {
209         num_rounds++;
210 
211         bapl_image_key feature = features_queue.front();
212         features_queue.pop();
213 
214         int img_id = feature.first;
215 
216         bapl_keypoint_sptr dummy_sptr;
217         bapl_key_match dummy(feature.second, dummy_sptr);
218 
219         //int start_idx = (img_id >= new_image_start) ?  new_image_start : 0;
220 
221         bapl_conn &nbrs = this->get_neighbors(img_id);
222 
223         bapl_conn::iterator iter;
224         for (iter = nbrs.begin(); iter != nbrs.end(); iter++) {
225           unsigned int k = (*iter)->id_right_;
226           if (img_visited[k])
227             continue;
228 
229           // binary search for the feature
230           std::pair<std::vector<bapl_key_match>::iterator, std::vector<bapl_key_match>::iterator > p;
231           p = equal_range((*iter)->matches_.begin(), (*iter)->matches_.end(), dummy, compare_first);
232 
233           if (p.first == p.second)  continue;
234           assert((p.first)->first->id() == feature.second->id());
235           int second_id = (p.first)->second->id();
236 
237           assert(second_id < (int)img_data_[k]->keys_.size());
238 
239           if (img_data_key_flags_[k][second_id])
240             continue;
241 
242           img_data_key_flags_[k][second_id] = true;
243           features.push_back(bapl_image_key(k, (p.first)->second));
244           features_queue.push(bapl_image_key(k, (p.first)->second));
245 
246           img_visited[k] = true;
247           img_touched.push_back(k);
248         }
249       }
250 
251       if (features.size() >= 2) {
252         tracks.emplace_back(features);
253         pt_idx++;
254       }
255     }
256   } // for loop over images
257 
258   std::cout << "Found " << tracks.size() << " points!\n";
259   assert(pt_idx == (int) tracks.size());
260 
261   return true;
262 }
263 
264 //: Print tracks as correspondences in BWM_VIDEO_SITE format for visualization
print_tracks(std::ostream & os,std::vector<bapl_track_data> & tracks,int img_width,int img_height)265 void print_tracks(std::ostream& os, std::vector<bapl_track_data>& tracks, int img_width, int img_height)
266 {
267   os << "<BWM_VIDEO_SITE name=\"\">\n"
268      << "<videoPath path=\"\">\n</videoPath>\n"
269      << "<cameraPath path=\"\">\n</cameraPath>\n"
270      << "<videoSiteDir path=\"\">\n</videoSiteDir>\n"
271      << "<Correspondences>\n";
272   for (auto & track : tracks) {
273     os << "<Correspondence>\n";
274     for (unsigned j = 0; j < track.views_.size(); j++) {
275       bapl_lowe_keypoint_sptr kp;
276       kp.vertical_cast(track.views_[j].second);
277       double x = kp->location_j();
278       double y = kp->location_i();
279       if (img_height > 0 && img_width > 0) {
280         x += 0.5 * img_width;
281         y += 0.5 * img_height;
282         y = img_height - y - 1.0;
283       }
284       os << "<CE fr=\"" << track.views_[j].first << "\" u=\"" << y << "\" v=\"" << x << "\">\n</CE>\n";
285     }
286     os << "</Correspondence>\n";
287   }
288   os << "</Correspondences>\n"
289      << "</BWM_VIDEO_SITE>\n";
290 }
291 
292 
293 //: Print a summary of the connectivity data to a stream
operator <<(std::ostream & os,bapl_conn_table const & t)294 std::ostream& operator<< (std::ostream& os, bapl_conn_table const & t)
295 {
296   for (unsigned i = 0; i < t.conns_.size() ; i++) {
297     os << "img("<< i << ") number of neighbors: " << t.conns_[i].size() << std::endl;
298   }
299   return os;
300 }
301 
302 
303 //: Binary io, NOT IMPLEMENTED, signatures defined to use bapl_keypoint_set as a brdb_value
vsl_b_write(vsl_b_ostream &,bapl_conn_table const &)304 void vsl_b_write(vsl_b_ostream & /*os*/, bapl_conn_table const & /*ph*/)
305 {
306   std::cerr << "vsl_b_write() -- Binary io, NOT IMPLEMENTED, signatures defined to use brec_part_hierarchy as a brdb_value\n";
307   return;
308 }
309 
vsl_b_read(vsl_b_istream &,bapl_conn_table &)310 void vsl_b_read(vsl_b_istream & /*is*/, bapl_conn_table & /*ph*/)
311 {
312   std::cerr << "vsl_b_read() -- Binary io, NOT IMPLEMENTED, signatures defined to use brec_part_hierarchy as a brdb_value\n";
313   return;
314 }
315 
vsl_b_read(vsl_b_istream & is,bapl_conn_table * ph)316 void vsl_b_read(vsl_b_istream& is, bapl_conn_table* ph)
317 {
318   delete ph;
319   bool not_null_ptr;
320   vsl_b_read(is, not_null_ptr);
321   if (not_null_ptr)
322   {
323     ph = new bapl_conn_table(1);
324     vsl_b_read(is, *ph);
325   }
326   else
327     ph = nullptr;
328 }
329 
vsl_b_write(vsl_b_ostream & os,const bapl_conn_table * & ph)330 void vsl_b_write(vsl_b_ostream& os, const bapl_conn_table* &ph)
331 {
332   if (ph==nullptr)
333   {
334     vsl_b_write(os, false); // Indicate null pointer stored
335   }
336   else
337   {
338     vsl_b_write(os,true); // Indicate non-null pointer stored
339     vsl_b_write(os,*ph);
340   }
341 }
342