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