1 #include <cmath>
2 #include <iostream>
3 #include <algorithm>
4 #include <volm/conf/volm_conf_query.h>
5 //:
6 // \file
7 #include <cassert>
8 #ifdef _MSC_VER
9 # include "vcl_msvc_warnings.h"
10 #endif
11 #include <bpgl/bpgl_camera_utils.h>
12 #include "vul/vul_file.h"
13 #include "vil/vil_load.h"
14 #include "vil/vil_save.h"
15 #include <vsph/vsph_spherical_coord.h>
16 #include <vsph/vsph_sph_point_3d.h>
17 #include "vgl/vgl_polygon_scan_iterator.h"
18
19 // default constructor
volm_conf_query()20 volm_conf_query::volm_conf_query()
21 {
22 ni_ = 0; nj_ = 0; ncam_ = 0; nobj_ = 0; nref_ = 0;
23 dm_ = nullptr;
24 cameras_.clear(); camera_strings_.clear();
25 ref_obj_name_.clear(); conf_objects_.clear();
26 tol_in_pixel_ = 0;
27 }
28
29 // create query from labeled depth map scene
volm_conf_query(const volm_camera_space_sptr & cam_space,const depth_map_scene_sptr & depth_scene,int const & tol_in_pixel)30 volm_conf_query::volm_conf_query(const volm_camera_space_sptr& cam_space, const depth_map_scene_sptr& depth_scene, int const& tol_in_pixel)
31 {
32 tol_in_pixel_ = tol_in_pixel;
33 dm_ = depth_scene;
34 ni_ = dm_->ni(); nj_ = dm_->nj();
35 altitude_ = cam_space->altitude();
36 // create reference objects from depth scene
37 bool success = this->parse_ref_object(dm_);
38 assert(success && "volm_conf_query: parse reference object list from depth scene failed");
39 nref_ = (unsigned)this->ref_obj_name_.size();
40 std::cout << nref_ << " reference configurational objects are loaded: ";
41 for (unsigned i = 0; i < nref_; i++)
42 std::cout << ref_obj_name_[i] << ' ';
43 std::cout << '\n';
44 // create cameras
45 success = this->create_perspective_cameras(cam_space);
46 assert(success && "volm_conf_query: construct perspective cameras from camera space failed");
47 ncam_ = (unsigned)this->cameras_.size();
48 std::cout << ncam_ << " cameras are created: " << std::endl;
49 // construct configurational object from 3d polygons
50 success = this->create_conf_object();
51 assert(success && "volm_conf_query: construct configurational objects failed");
52 }
53
parse_ref_object(const depth_map_scene_sptr & dm)54 bool volm_conf_query::parse_ref_object(const depth_map_scene_sptr& dm)
55 {
56 ref_obj_name_.clear();
57 if (dm == nullptr)
58 return false;
59 // note that sky object should never be reference object
60 if (dm->ground_plane().size())
61 for (unsigned i = 0; i < dm->ground_plane().size(); i++)
62 if ( dm->ground_plane()[i]->is_ref() && dm->ground_plane()[i]->active())
63 ref_obj_name_.push_back(dm->ground_plane()[i]->name());
64 if (dm->scene_regions().size())
65 for (unsigned i = 0; i < dm->scene_regions().size(); i++)
66 if ( dm->scene_regions()[i]->is_ref() && dm->scene_regions()[i]->active())
67 ref_obj_name_.push_back(dm->scene_regions()[i]->name());
68 if (ref_obj_name_.empty())
69 return false;
70 return true;
71 }
72
create_perspective_cameras(const volm_camera_space_sptr & cam_space)73 bool volm_conf_query::create_perspective_cameras(const volm_camera_space_sptr& cam_space)
74 {
75 // iterate over valid cameras in the camera space
76 // Note that ground plane construct has been applied on camera space
77 cameras_.clear(); camera_strings_.clear(); camera_angles_.clear();
78 std::vector<unsigned> const& valid_cams = cam_space->valid_indices();
79 for (unsigned int valid_cam : valid_cams) {
80 vpgl_perspective_camera<double> cam = cam_space->camera(valid_cam);
81 std::string cam_str = cam_space->get_string(valid_cam);
82 cameras_.push_back(cam);
83 camera_strings_.push_back(cam_str);
84 camera_angles_.push_back(cam_space->camera_angles(valid_cam));
85 }
86 return true;
87 }
88
89
create_conf_object()90 bool volm_conf_query::create_conf_object()
91 {
92 conf_objects_.clear();
93 // loop over each calibrated camera to construct the list of configurational objects
94 for (unsigned cam_id =0; cam_id < ncam_; cam_id++)
95 {
96 std::cout << "camera: " << this->camera_strings_[cam_id] << std::endl;
97 vpgl_perspective_camera<double> pcam = cameras_[cam_id];
98 // create a map of volm_conf_object
99 std::map<std::string, volm_conf_object_sptr> conf_object;
100 std::map<std::string, std::pair<unsigned, unsigned> > conf_pixels;
101 // only consider non-planar objects
102 std::vector<depth_map_region_sptr> regions = dm_->scene_regions();
103 for (unsigned r_idx = 0; r_idx < regions.size(); r_idx++)
104 {
105 if (!regions[r_idx]->active()) {
106 std::cout << "\t\t region " << r_idx << " " << regions[r_idx]->name() << " is not active, IGNORED" << std::endl;
107 continue;
108 }
109 //std::cout << "\t\t projecting " << regions[r_idx]->name() << "..." << std::flush << std::endl;
110 vgl_polygon<double> poly = bsol_algs::vgl_from_poly(regions[r_idx]->region_2d());
111 float theta = -1.0f, dist = -1.0f, height = -1.0f;
112 unsigned i, j;
113 // project all ground vertices on the polygon to 3-d world points if the vertex is under the horizon
114 this->project(pcam, poly, dist, theta, i, j);
115 // here to use the distance from depth map scene
116 dist = regions[r_idx]->min_depth();
117 height = regions[r_idx]->height();
118 //std::cout << "\t\t min_dist: " << dist << ", phi: " << theta << ", pixel: " << i << "x" << j << std::flush << std::endl;
119 if (theta < 0)
120 continue;
121 // create a configurational object for it
122 unsigned char land_id = regions[r_idx]->land_id();
123 volm_conf_object_sptr conf_obj = new volm_conf_object(theta, dist, height, land_id);
124 //std::cout << "conf_obj: "; conf_obj->print(std::cout);
125 conf_object.insert(std::pair<std::string, volm_conf_object_sptr>(regions[r_idx]->name(), conf_obj));
126 std::pair<unsigned, unsigned> tmp_pair(i,j);
127 conf_pixels.insert(std::pair<std::string, std::pair<unsigned, unsigned> >(regions[r_idx]->name(), tmp_pair));
128 }
129 // update the configurational object for current camera
130 conf_objects_.push_back(conf_object);
131 conf_objects_pixels_.push_back(conf_pixels);
132 }
133
134 if (tol_in_pixel_ == 0)
135 {
136 for (unsigned cam_id =0; cam_id < ncam_; cam_id++)
137 {
138 std::map<std::string, volm_conf_object_sptr> conf_objs = conf_objects_[cam_id];
139 std::map<std::string, std::pair<unsigned, unsigned> > conf_pixels = conf_objects_pixels_[cam_id];
140 std::map<std::string, std::pair<float, float> > conf_dist_tol;
141 for (auto & conf_pixel : conf_pixels)
142 {
143 float min_dist = conf_objs[conf_pixel.first]->dist()*0.8;
144 float max_dist = conf_objs[conf_pixel.first]->dist()*1.2;
145 conf_dist_tol.insert(std::pair<std::string, std::pair<float, float> >(conf_pixel.first, std::pair<float, float>(min_dist, max_dist)));
146 }
147 conf_objects_d_tol_.push_back(conf_dist_tol);
148 }
149 }
150 else
151 {
152 // calculate the distance tolerance for each configuration object
153 int nbrs4_delta[4][2] = { {-1*tol_in_pixel_, tol_in_pixel_},
154 { tol_in_pixel_, tol_in_pixel_},
155 { tol_in_pixel_, -1*tol_in_pixel_},
156 {-1*tol_in_pixel_, -1*tol_in_pixel_}
157 };
158 unsigned num_nbrs = 4;
159 for (unsigned cam_id =0; cam_id < ncam_; cam_id++)
160 {
161 vpgl_perspective_camera<double> pcam = cameras_[cam_id];
162 std::map<std::string, volm_conf_object_sptr> conf_objs = conf_objects_[cam_id];
163 std::map<std::string, std::pair<unsigned, unsigned> > conf_pixels = conf_objects_pixels_[cam_id];
164 std::map<std::string, std::pair<float, float> > conf_dist_tol;
165 for (auto & conf_pixel : conf_pixels)
166 {
167 unsigned i = conf_pixel.second.first; unsigned j = conf_pixel.second.second;
168 float min_dist = conf_objs[conf_pixel.first]->dist();
169 float max_dist = min_dist;
170 for (unsigned k = 0; k < num_nbrs; k++) {
171 int nbr_i = i + nbrs4_delta[k][0];
172 int nbr_j = j + nbrs4_delta[k][1];
173 float dist, phi;
174 this->project(pcam, nbr_i, nbr_j, dist, phi);
175 if (dist < 0 || phi < 0)
176 continue;
177 if (dist <= min_dist) min_dist = dist;
178 if (dist >= max_dist) max_dist = dist;
179 //std::cout << "\t pixel " << nbr_i << ", " << nbr_j << ", dist: " << dist << ", min_dist: " << min_dist << ", max_dist: " << max_dist << std::endl;
180 }
181 conf_dist_tol.insert(std::pair<std::string, std::pair<float, float> >(conf_pixel.first, std::pair<float, float>(min_dist, max_dist)));
182 }
183 conf_objects_d_tol_.push_back(conf_dist_tol);
184 }
185 }
186
187 return true;
188 }
189
190 // calculate the minimum distance, the average angle values from the label boundary and the pixel associated with it
project(vpgl_perspective_camera<double> const & cam,vgl_polygon<double> const & poly,float & min_dist,float & phi,unsigned & i,unsigned & j)191 void volm_conf_query::project(vpgl_perspective_camera<double> const& cam,
192 vgl_polygon<double> const& poly,
193 float& min_dist, float& phi, unsigned& i, unsigned& j)
194 {
195 min_dist = -1.0f; phi = -1.0f; i = 0; j=0;
196 // only consider the first sheet
197 unsigned n_vertices = poly[0].size();
198 std::map<float, float> pt_pairs;
199 std::map<float, std::pair<unsigned, unsigned> > pt_pixels;
200 // calculate the angle value from all polygons
201 std::set<float> dist_values;
202 std::vector<float> phi_values;
203 std::vector<std::pair<double, double> > pixel_values;
204 for (unsigned v_idx = 0; v_idx < n_vertices; v_idx++)
205 {
206 double x = poly[0][v_idx].x();
207 double y = poly[0][v_idx].y();
208 if (x < 0 || x >= ni_ || y < 0 || y >= nj_)
209 continue;
210 float dist, phi;
211 this->project(cam, x, y, dist, phi);
212 if (dist < 0)
213 continue;
214 dist_values.insert(dist);
215 phi_values.push_back(phi);
216 pixel_values.emplace_back(x,y);
217 }
218 if (phi_values.empty()) // no vertices projects to ground
219 return;
220 min_dist = *(dist_values.begin());
221 // calculate the average angle
222 float phi_value = 0.0f;
223 double xi = 0.0, yi = 0.0;
224 for (float pi : phi_values)
225 phi_value += pi;
226 for (auto & pixel_value : pixel_values) {
227 xi += pixel_value.first;
228 yi += pixel_value.second;
229 }
230 phi_value /= phi_values.size();
231 xi /= pixel_values.size();
232 yi /= pixel_values.size();
233
234 phi = phi_value;
235 i = (unsigned)(std::floor(xi+0.5));
236 j = (unsigned)(std::floor(yi+0.5));
237 return;
238
239 # if 0
240 for (unsigned v_idx = 0; v_idx < n_vertices; v_idx++) {
241 double x = poly[0][v_idx].x();
242 double y = poly[0][v_idx].y();
243 float dist, phi;
244 this->project(cam, x, y, dist, phi);
245 if (dist < 0)
246 continue;
247 pt_pairs.insert(std::pair<float, float>(dist, phi));
248 #if 0
249 std::cout << "\t\tpixel: " << x << "x" << y << " is under horizon, has ray " << cp
250 << " and spherical coords: " << sp << " dist: " << dist << ", theta: " << sp.phi_ << std::flush << std::endl;
251 #endif
252
253
254 pt_pixels.insert(std::pair<float, std::pair<unsigned, unsigned> >(dist, std::pair<unsigned, unsigned>((unsigned)x,(unsigned)y)));
255 }
256 if (pt_pairs.empty()) // no vertices projects to ground
257 return;
258 min_dist = pt_pairs.begin()->first;
259 phi = pt_pairs[min_dist];
260
261 i = pt_pixels[min_dist].first;
262 j = pt_pixels[min_dist].second;
263 //std::cout << "\t\tmin_dist: " << min_dist << ", phi: " << phi << ", pixel: " << i << "x" << j << std::flush << std::endl;
264 return;
265 #endif
266
267 }
268
project(vpgl_perspective_camera<double> const & cam,double const & pixel_i,double const & pixel_j,float & dist,float & phi)269 void volm_conf_query::project(vpgl_perspective_camera<double> const& cam,
270 double const& pixel_i, double const& pixel_j,
271 float& dist, float& phi)
272 {
273 dist = -1.0; phi = -1.0;
274 vgl_line_2d<double> horizon = bpgl_camera_utils::horizon(cam);
275 if (pixel_i < 0 || pixel_i >= ni_ || pixel_j < 0 || pixel_j >= nj_)
276 return;
277 double yl = line_coord(horizon, pixel_i);
278 if (pixel_j < yl) // given img point is above the horizon
279 return;
280 // obtain the back project ray to calculate the distance
281 vgl_ray_3d<double> ray = cam.backproject(pixel_i, pixel_j);
282 // obtain the angular relative to camera x axis
283 vgl_point_3d<double> cp(ray.direction().x(), ray.direction().y(), ray.direction().z());
284 vsph_spherical_coord sph_coord;
285 vsph_sph_point_3d sp;
286 sph_coord.spherical_coord(cp, sp);
287 // calculate the distance
288 dist = std::tan(vnl_math::pi - sp.theta_)*altitude_;
289 phi = sp.phi_;
290 return;
291 }
292
line_coord(vgl_line_2d<double> const & line,double const & x)293 double volm_conf_query::line_coord(vgl_line_2d<double> const& line, double const& x)
294 {
295 if (line.b() == 0)
296 return 0.0;
297 else
298 return -line.a()/line.b()*x - line.c()/line.b();
299 }
300
301
visualize_ref_objs(std::string const & in_file,std::string const & out_folder)302 bool volm_conf_query::visualize_ref_objs(std::string const& in_file, std::string const& out_folder)
303 {
304 if (!vul_file::exists(in_file))
305 return false;
306 vil_image_view<vil_rgb<vxl_byte> > src_img = vil_load(in_file.c_str());
307 std::vector<depth_map_region_sptr> regions = dm_->scene_regions();
308
309 for (unsigned cam_id = 0; cam_id < ncam_; cam_id++) {
310 vil_image_view<vil_rgb<vxl_byte> > img;
311 img.deep_copy(src_img);
312 // plot horizontal line
313 vgl_line_2d<double> h_line = bpgl_camera_utils::horizon(cameras_[cam_id]);
314 std::vector<vgl_point_2d<double> > h_line_pixels;
315 h_line_pixels.clear();
316 for (unsigned x = 0; x < ni_; x++) {
317 double y = (std::floor)(line_coord(h_line, x));
318 h_line_pixels.emplace_back((double)x, y);
319 }
320 this->plot_line_into_image(img, h_line_pixels, 0, 0, 0, 6);
321 // plot the non-ground depth map scenes first
322 for (auto & region : regions) {
323 // not plot the object that is not projected based on current camera
324 if (conf_objects_[cam_id].find(region->name()) == conf_objects_[cam_id].end())
325 continue;
326 vgl_polygon<double> poly = bsol_algs::vgl_from_poly(region->region_2d());
327 poly[0].push_back(poly[0][0]);
328 unsigned char r,g,b;
329 double width;
330 if (std::find(ref_obj_name_.begin(), ref_obj_name_.end(), region->name()) == ref_obj_name_.end()) {
331 r = volm_osm_category_io::volm_land_table[region->land_id()].color_.r;
332 g = volm_osm_category_io::volm_land_table[region->land_id()].color_.g;
333 b = volm_osm_category_io::volm_land_table[region->land_id()].color_.b;
334 width = 5.0;
335 } else {
336 r = 255; g = 0; b = 0; width = 7.0;
337 }
338 //std::cout << "object: " << regions[i]->name() << ", land: " << (int)regions[i]->land_id() << ", color: " << (int)r << "," << (int)g << "," << (int)b << std::endl;
339 this->plot_line_into_image(img, poly[0],r,g,b,width);
340 }
341 // plot the configurational object
342 for (auto mit = conf_objects_pixels_[cam_id].begin(); mit != conf_objects_pixels_[cam_id].end(); ++mit ) {
343 unsigned char land_id = conf_objects_[cam_id][mit->first]->land();
344 unsigned char r,g,b;
345 double width;
346 if (std::find(ref_obj_name_.begin(), ref_obj_name_.end(), mit->first) == ref_obj_name_.end()) {
347 r = volm_osm_category_io::volm_land_table[land_id].color_.r;
348 g = volm_osm_category_io::volm_land_table[land_id].color_.g;
349 b = volm_osm_category_io::volm_land_table[land_id].color_.b;
350 width = 20.0;
351 }else {
352 r = 255; g = 255; b = 255; width = 30.0;
353 }
354 this->plot_dot_into_image(img, vgl_point_2d<double>((double)mit->second.first, (double)mit->second.second),r,g,b,width);
355 }
356 std::string filename = vul_file::strip_extension(vul_file::strip_directory(in_file));
357 std::string out_file = out_folder + "/" + filename + "_" + camera_strings_[cam_id] + ".tif";
358 vil_save(img, out_file.c_str());
359 }
360 return true;
361
362 }
363
364 #if 0
365 bool volm_conf_query::generate_top_views(std::string const& out_folder, std::string const& filename_pre)
366 {
367 // ensure a maximum image size
368 unsigned half_ni = 0; unsigned half_nj = 0;
369 for (unsigned cam_id = 0; cam_id < ncam_; cam_id++)
370 {
371 std::map<std::string, volm_conf_object_sptr> conf_obj = conf_objects_[cam_id];
372 for (std::map<std::string, volm_conf_object_sptr>::iterator mit = conf_obj.begin(); mit != conf_obj.end(); ++mit) {
373 float x = mit->second->dist() * std::cos(mit->second->theta());
374 float y = mit->second->dist() * std::sin(mit->second->theta());
375 if (half_ni < std::ceil(x)) half_ni = std::ceil(x);
376 if (half_nj < std::ceil(y)) half_nj = std::ceil(y);
377 //std::cout << "obj " << mit->first << " has distance " << mit->second->dist() << " and angle " << mit->second->theta()
378 // << ", pixel " << std::ceil(x) << "x" << std::ceil(y)
379 // << ", img size" << 2*half_ni << "x" << 2*half_nj << std::endl;
380 }
381 }
382 unsigned ni,nj;
383 ni = 2*half_ni;
384 nj = 2*half_nj;
385 for (unsigned cam_id = 0; cam_id < ncam_; cam_id++)
386 {
387 std::string cam_string = camera_strings_[cam_id];
388 vil_image_view<vil_rgb<vxl_byte> > img(ni, nj);
389 img.fill(vil_rgb<vxl_byte>(127,127,127));
390 // perform the coordinate transformation (putting camera center to the image center)
391 float xo = half_ni, yo = half_nj;
392 // plot camera center
393 this->plot_dot_into_image(img, vgl_point_2d<double>(xo,yo), 0, 0, 0, 5.0);
394
395 std::map<std::string, volm_conf_object_sptr> conf_obj = conf_objects_[cam_id];
396 for (std::map<std::string, volm_conf_object_sptr>::iterator mit = conf_obj.begin(); mit != conf_obj.end(); ++mit) {
397 float xc = mit->second->dist() * std::cos(mit->second->theta());
398 float yc = mit->second->dist() * std::sin(mit->second->theta());
399 unsigned char r, g, b; double width;
400 unsigned char land_id = mit->second->land();
401 if (std::find(ref_obj_name_.begin(), ref_obj_name_.end(), mit->first) == ref_obj_name_.end()) {
402 r = volm_osm_category_io::volm_land_table[land_id].color_.r;
403 g = volm_osm_category_io::volm_land_table[land_id].color_.g;
404 b = volm_osm_category_io::volm_land_table[land_id].color_.b;
405 width = 10.0;
406 }else {
407 r = 255; g = 255; b = 255; width = 25.0;
408 }
409 //std::cout << "(" << xc << "," << yc << "), (" << xo << "," << yo << ") --> (" << xc+xo << "," << yo-yc << ")" << std::endl;
410 this->plot_dot_into_image(img, vgl_point_2d<double>(xc+xo,yo-yc), r, g, b, width);
411 }
412 std::string out_file = out_folder + "/" + filename_pre + "_" + cam_string + ".tif";
413 //std::cout << "save image to " << out_file << std::endl;
414 vil_save(img, out_file.c_str());
415 }
416 return true;
417 }
418 #endif
419
plot_line_into_image(vil_image_view<vil_rgb<vxl_byte>> & image,std::vector<vgl_point_2d<double>> const & line,unsigned char const & r,unsigned char const & g,unsigned char const & b,double const & width)420 void volm_conf_query::plot_line_into_image(vil_image_view<vil_rgb<vxl_byte> >& image,
421 std::vector<vgl_point_2d<double> > const& line,
422 unsigned char const& r, unsigned char const& g, unsigned char const& b,
423 double const& width)
424 {
425 vgl_polygon<double> img_poly;
426 volm_io_tools::expend_line(line, width, img_poly);
427 vgl_polygon_scan_iterator<double> it(img_poly, true);
428 for (it.reset(); it.next(); ) {
429 int y = it.scany();
430 for (int x = it.startx(); x <= it.endx(); ++x) {
431 if ( x >= 0 && y >= 0 && x < (int)image.ni() && y < (int)image.nj()) {
432 image(x, y) = vil_rgb<vxl_byte>(r, g, b);
433 }
434 }
435 }
436 }
437
plot_dot_into_image(vil_image_view<vil_rgb<vxl_byte>> & image,vgl_point_2d<double> const & pt,unsigned char const & r,unsigned char const & g,unsigned char const & b,double const & radius)438 void volm_conf_query::plot_dot_into_image(vil_image_view<vil_rgb<vxl_byte> >& image,
439 vgl_point_2d<double> const& pt,
440 unsigned char const& r, unsigned char const& g, unsigned char const& b,
441 double const& radius)
442 {
443 vgl_polygon<float> img_poly;
444 img_poly.new_sheet();
445 std::set<float> angles;
446 for (unsigned i = 0; i <= 36; i++)
447 angles.insert(i*10.0f);
448 angles.insert(45.0f); angles.insert(135.0f); angles.insert(225.0f); angles.insert(315.0f);
449 for (const auto & angle : angles) {
450 float x = pt.x() + radius*std::cos(angle*vnl_math::pi_over_180);
451 float y = pt.y() + radius*std::sin(angle*vnl_math::pi_over_180);
452 img_poly.push_back(x, y);
453 }
454 vgl_polygon_scan_iterator<float> it(img_poly, true);
455 for (it.reset(); it.next(); ) {
456 int y = it.scany();
457 for (int x = it.startx(); x <= it.endx(); ++x) {
458 if ( x>=0 && y>=0 && x<(int)image.ni() && y<(int)image.nj())
459 image(x,y) = vil_rgb<vxl_byte>(r, g, b);
460 }
461 }
462 }
463