1 // This is brl/bseg/bvxm/bvxm_voxel_world.cxx
2 #include <string>
3 #include <iostream>
4 #include <fstream>
5 #include <sstream>
6 #include "bvxm_voxel_world.h"
7 //:
8 // \file
9
10 #include <cassert>
11 #ifdef _MSC_VER
12 # include "vcl_msvc_warnings.h"
13 #endif
14 #include "vgl/vgl_plane_3d.h"
15 #include "vgl/vgl_vector_3d.h"
16 #include "vgl/vgl_box_2d.h"
17
18 #include <vgl/algo/vgl_h_matrix_2d.h>
19 #include "vil/vil_image_view_base.h"
20 #include "vil/vil_image_view.h"
21 #include "vpgl/vpgl_camera.h"
22 #include "vul/vul_file.h"
23
24 #include "vnl/vnl_math.h"
25
26 #include "vgl/vgl_homg_point_2d.h"
27 #include <vgl/algo/vgl_h_matrix_2d_compute_linear.h>
28
29 #include <vil/algo/vil_median.h>
30 #include <vil/algo/vil_structuring_element.h>
31 #include "vil/vil_math.h"
32 #include <vil/algo/vil_threshold.h>
33 #include <vil/algo/vil_gauss_filter.h>
34
35 #include <vnl/algo/vnl_chi_squared.h>
36
37 #include "grid/bvxm_voxel_grid.h"
38 #include "bvxm_voxel_traits.h"
39 #include "bvxm_lidar_processor.h"
40 #include "bvxm_world_params.h"
41 #include "grid/bvxm_voxel_slab.h"
42 #include "bvxm_image_metadata.h"
43 #include "bvxm_util.h"
44 #include <vpgl/file_formats/vpgl_geo_camera.h>
45 //#define DEBUG
46
47 //: Destructor
48 bvxm_voxel_world::~bvxm_voxel_world()
49 = default;
50
51
52 //: equality operator
operator ==(bvxm_voxel_world const &) const53 bool bvxm_voxel_world::operator == (bvxm_voxel_world const& /*that*/) const
54 {
55 return true;
56 }
57
58 //: less than operator
operator <(bvxm_voxel_world const &) const59 bool bvxm_voxel_world::operator < (bvxm_voxel_world const& /*that*/) const
60 {
61 return false;
62 }
63
64
fit_plane()65 vgl_plane_3d<double> bvxm_voxel_world::fit_plane()
66 {
67 // for now, just return plane of bottom layer. can do something smarter with occupancy probs later.
68 vgl_point_3d<double> corner(params_->corner().x(),params_->corner().y(),params_->corner().z());
69 vgl_vector_3d<double> normal(0,0,1.0);
70
71 return vgl_plane_3d<double>(normal,corner);
72 }
73
74 //: output description of voxel world to stream.
operator <<(std::ostream & s,bvxm_voxel_world const & vox_world)75 std::ostream& operator<<(std::ostream& s, bvxm_voxel_world const& vox_world)
76 {
77 bvxm_world_params_sptr params = vox_world.get_params();
78 s << "bvxm_voxel_world : " << params->num_voxels().x() << " x " << params->num_voxels().y() << " x " << params->num_voxels().z() << std::endl;
79 return s;
80 }
81
82 //: save the occupancy grid as an 8-bit 3-d vff image
save_occupancy_vff(const std::string & filename,unsigned scale_idx)83 bool bvxm_voxel_world::save_occupancy_vff(const std::string& filename,unsigned scale_idx)
84 {
85 // open file for binary writing
86 std::fstream ofs(filename.c_str(),std::ios::binary | std::ios::out);
87 if (!ofs.is_open()) {
88 std::cerr << "error opening file " << filename << " for write!\n";
89 return false;
90 }
91 bvxm_world_params_sptr params = this->get_params();
92
93 typedef bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
94
95 bvxm_voxel_grid<ocp_datatype> *ocp_grid =
96 dynamic_cast<bvxm_voxel_grid<ocp_datatype>*>(get_grid<OCCUPANCY>(0,scale_idx).ptr());
97
98 vxl_uint_32 nx = ocp_grid->grid_size().x();
99 vxl_uint_32 ny = ocp_grid->grid_size().y();
100 vxl_uint_32 nz = ocp_grid->grid_size().z();
101
102 // write header
103 std::stringstream header;
104 header << "ncaa\n"
105 << "title=bvxm occupancy probabilities;\n"
106 << "rank=3;\n"
107 << "type=raster;\n"
108 << "format=slice;\n"
109 << "bits=8;\n"
110 << "bands=1;\n"
111 << "extent=" << nx << ' ' << ny << ' ' << nz << ";\n"
112 << "size=" << nx << ' ' << ny << ' ' << nz << ";\n"
113 << "aspect=1.0 1.0 1.0;\n"
114 << "origin=0 0 0;\n"
115 << "rawsize=" << nx*ny*nz << ";\n\f\n";
116
117 std::string header_string = header.str();
118 unsigned header_len = header_string.size();
119
120 ofs.write(header_string.c_str(),header_len);
121
122 // write data
123 // iterate through slabs and fill in memory array
124 char *ocp_array = new char[nx*ny*nz];
125
126 bvxm_voxel_grid<ocp_datatype>::iterator ocp_it = ocp_grid->begin();
127 for (unsigned k=nz-1; ocp_it != ocp_grid->end(); ++ocp_it, --k) {
128 std::cout << '.';
129 for (unsigned i=0; i<(*ocp_it).nx(); ++i) {
130 for (unsigned j=0; j < (*ocp_it).ny(); ++j) {
131 ocp_array[k*nx*ny + j*nx + i] = (unsigned char)((*ocp_it)(i,j) * 255.0);;
132 }
133 }
134 }
135 std::cout << std::endl;
136 ofs.write(reinterpret_cast<char*>(ocp_array),sizeof(unsigned char)*nx*ny*nz);
137
138 ofs.close();
139
140 delete[] ocp_array;
141
142 return true;
143 }
144
145 //: remove all voxel data from disk - use with caution!
clean_grids()146 bool bvxm_voxel_world::clean_grids()
147 {
148 // look for existing grids in the directory
149 std::string storage_directory = params_->model_dir();
150
151 std::stringstream grid_glob;
152 grid_glob << storage_directory << "/*.vox";
153 bool result = vul_file::delete_file_glob(grid_glob.str().c_str());
154
155 grid_map_.clear();
156
157 return result;
158 }
159
160 #if 0 // the following three methods commented out:
161 // Update a voxel grid with data from lidar/camera pair
162 bool bvxm_voxel_world::update_lidar(bvxm_image_metadata const& observation, unsigned scale)
163 {
164 vil_image_view<float> dummy;
165 vil_image_view<bool> mask;
166 return this->update_lidar_impl(observation, false, dummy, false, mask, scale);
167 }
168
169
170 // Update a voxel grid with data from lidar/camera pair and return probability density of pixel values.
171 bool bvxm_voxel_world::update_lidar(bvxm_image_metadata const& observation,
172 vil_image_view<float> &pix_prob_density, vil_image_view<bool> &mask, unsigned scale)
173 {
174 // check image sizes
175 if ( (observation.img->ni() != pix_prob_density.ni()) || (observation.img->nj() != pix_prob_density.nj()) ) {
176 std::cerr << "error: metadata image size does not match probability image size.\n";
177 }
178 if ( (observation.img->ni() != mask.ni()) || (observation.img->nj() != mask.nj()) ) {
179 std::cerr << "error: metadata image size does not match mask image size.\n";
180 }
181 return this->update_lidar_impl(observation, true, pix_prob_density, true, mask, scale);
182 }
183
184 // Update voxel grid with data from LIDAR image/camera pair and return probability density of pixel values.
185 bool bvxm_voxel_world::update_lidar_impl(bvxm_image_metadata const& metadata,
186 bool return_prob,
187 vil_image_view<float> &pix_prob_density,
188 bool return_mask,
189 vil_image_view<bool> &mask, unsigned scale)
190 {
191 typedef bvxm_voxel_traits<LIDAR>::voxel_datatype obs_datatype;
192 typedef bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
193
194 vpgl_camera_double_sptr dummy_cam = metadata.camera;
195 double lidar_pixel_size = 1.0;
196 if (dummy_cam->type_name()=="vpgl_geo_camera"){
197 vpgl_geo_camera* lcam = static_cast<vpgl_geo_camera*>(dummy_cam.ptr());
198 std::cout << "Lidar Camera\n" << *lcam << std::endl;
199 lidar_pixel_size = lcam->pixel_spacing();
200 }
201 //typedef bvxm_voxel_traits<LIDAR>::lidar_processor lidar_processor;
202 bvxm_lidar_processor lidar_processor(10);
203
204 // parameters
205 vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale);
206 ocp_datatype min_vox_prob = params_->min_occupancy_prob();
207 ocp_datatype max_vox_prob = params_->max_occupancy_prob();
208
209 // compute homographies from voxel planes to image coordinates and vise-versa.
210 std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
211 std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
212 {
213 vgl_h_matrix_2d<double> Hp2i, Hi2p;
214 for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
215 {
216 compute_plane_image_H(metadata.camera,z,Hp2i,Hi2p, scale);
217 H_plane_to_img.push_back(Hp2i);
218 H_img_to_plane.push_back(Hi2p);
219 }
220 }
221
222 // convert image to a voxel_slab
223 bvxm_voxel_slab<obs_datatype> image_slab(metadata.img->ni(), metadata.img->nj(), 1);
224 if (!bvxm_util::img_to_slab(metadata.img,image_slab)) {
225 std::cerr << "error converting image to voxel slab of observation type for bvxm_voxel_type: LIDAR\n";
226 return false;
227 }
228
229 #ifdef DEBUG
230 vil_save(*metadata.img, "lidar_img.tiff");
231 bvxm_util::write_slab_as_image(image_slab,"lidar_slab.tiff");
232 #endif
233
234 // temporary voxel grids to hold preX and PL*visX values
235 bvxm_voxel_grid<float> preX(grid_size);
236 bvxm_voxel_grid<float> PLvisX(grid_size);
237
238 bvxm_voxel_slab<float> PLPX(grid_size.x(),grid_size.y(),1);
239 bvxm_voxel_slab<float> PXvisX(grid_size.x(), grid_size.y(),1);
240
241 bvxm_voxel_slab<float> preX_accum(image_slab.nx(),image_slab.ny(),1);
242 bvxm_voxel_slab<float> visX_accum(image_slab.nx(),image_slab.ny(),1);
243 bvxm_voxel_slab<float> img_scratch(image_slab.nx(),image_slab.ny(),1);
244 bvxm_voxel_slab<float> PLPX_img(image_slab.nx(), image_slab.ny(),1);
245 bvxm_voxel_slab<float> PX_img(image_slab.nx(), image_slab.ny(),1);
246 bvxm_voxel_slab<float> mask_slab(image_slab.nx(), image_slab.ny(),1);
247
248 preX_accum.fill(0.0f);
249 visX_accum.fill(1.0f);
250
251 // slabs for holding backprojections of visX
252 bvxm_voxel_slab<float> visX(grid_size.x(),grid_size.y(),1);
253
254 bvxm_voxel_slab<obs_datatype> frame_backproj(grid_size.x(),grid_size.y(),1);
255
256 std::cout << "Pass 1:" << std::endl;
257
258 // get occupancy probability grid
259 bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0, scale);
260 bvxm_voxel_grid<ocp_datatype> *ocp_grid = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
261
262
263 bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
264
265 bvxm_voxel_grid<float>::iterator preX_slab_it = preX.begin();
266 bvxm_voxel_grid<float>::iterator PLvisX_slab_it = PLvisX.begin();
267 #ifdef DEBUG
268 double p_max = 0.0;
269 #endif
270 //The default values for the LIDAR Gaussian error ellipsoid
271 // X-Y standard deviation set to 1/Sqrt(2) pixel spacing (arbitrary)
272 // standard deviation in z measured from actual Buckeye data (0.03 m)
273 double pix_sd = lidar_pixel_size*vnl_math::sqrt1_2;
274 float xy_var = static_cast<float>(pix_sd*pix_sd);
275 // The vector of spherical Gaussian variances
276 vnl_vector_fixed<float,3> vars(xy_var, xy_var, 0.0009f);
277
278 for (unsigned k_idx=0; k_idx<(unsigned)grid_size.z(); ++k_idx, ++ocp_slab_it, ++preX_slab_it, ++PLvisX_slab_it)
279 {
280 std::cout << k_idx << std::endl;
281
282 // backproject image onto voxel plane
283 bvxm_util::warp_slab_bilinear(image_slab, H_plane_to_img[k_idx], frame_backproj);
284
285 #ifdef DEBUG
286 std::stringstream ss;
287 ss << "./frame_backproj_" << k_idx <<".tiff";
288 bvxm_util::write_slab_as_image(frame_backproj,ss.str());
289 #endif
290 // transform preX to voxel plane for this level
291 bvxm_util::warp_slab_bilinear(preX_accum, H_plane_to_img[k_idx], *preX_slab_it);
292 // transform visX to voxel plane for this level
293 bvxm_util::warp_slab_bilinear(visX_accum, H_plane_to_img[k_idx], visX);
294
295 // initialize PLvisX with PL(X)
296
297 bvxm_voxel_slab<float> PL(frame_backproj.nx(), frame_backproj.ny(), frame_backproj.nz());
298 PL.fill(0.0);
299 vil_image_view_base_sptr lidar = metadata.img;
300
301 vgl_point_3d<float> local_xyz = voxel_index_to_xyz(0, 0, k_idx,scale);
302
303 for (unsigned i_idx=0; i_idx<frame_backproj.nx(); i_idx++) {
304 for (unsigned j_idx=0; j_idx<frame_backproj.ny(); j_idx++) {
305 std::vector<vgl_homg_point_2d<double> > vp(4);
306 int i = i_idx+1;
307 int j = j_idx-1;
308 vp[0] = vgl_homg_point_2d<double>(i, j);
309 vp[1] = vgl_homg_point_2d<double>(i+1, j);
310 vp[2] = vgl_homg_point_2d<double>(i, j+1);
311 vp[3] = vgl_homg_point_2d<double>(i+1, j+1);
312
313 vgl_h_matrix_2d<double> h_max = H_plane_to_img[k_idx];
314 vgl_h_matrix_2d<double> h_min;
315 if (k_idx == (unsigned)grid_size.z()-1)
316 h_min = H_plane_to_img[k_idx];
317 else
318 h_min = H_plane_to_img[k_idx+1];
319 vgl_box_2d<double> lidar_roi;
320
321 for (unsigned i=0; i<4; i++) {
322 vgl_homg_point_2d<double> img_pos_h_min = h_min*vp[i];
323 vgl_point_2d<double> img_pos_min(img_pos_h_min);
324 lidar_roi.add(img_pos_min);
325 }
326
327 float p = lidar_processor.prob_density(lidar, local_xyz.z(), vars, lidar_roi, params_->voxel_length(scale));
328
329 #ifdef DEBUG
330 if (p > p_max) {
331 p_max = p;
332 std::cout << "-------------max_p=" << p << std::endl;
333 }
334 if (p >1.0) {
335 std::cout << "ERROR!" << std::endl;
336 p=max_vox_prob;
337 }
338 #endif
339 PL(i_idx, j_idx) = p;
340 }
341 }
342
343 // now multiply by visX
344 bvxm_util::multiply_slabs(visX,PL,*PLvisX_slab_it);
345
346 //Is this needed?
347 // update appearance model, using PX*visX as the weights
348 bvxm_util::multiply_slabs(visX,*ocp_slab_it,PXvisX);
349
350 // multiply to get PLPX
351 bvxm_util::multiply_slabs(PL,*ocp_slab_it,PLPX);
352 #ifdef DEBUG
353 std::stringstream ss1, ss2, ss3;
354 ss1 << "PL_" << k_idx <<".tiff";
355 ss2 <<"PX_" << k_idx <<".tiff";
356 ss3 << "PL_P" << k_idx <<".tiff";
357 bvxm_util::write_slab_as_image(PL,ss1.str());
358 bvxm_util::write_slab_as_image(*ocp_slab_it,ss2.str());
359 //bvxm_util::write_slab_as_image(PL_p,ss3.str());
360 #endif
361 // warp PLPX back to image domain
362 bvxm_util::warp_slab_bilinear(PLPX, H_img_to_plane[k_idx], PLPX_img);
363
364 // multiply PLPX by visX and add to preX_accum
365 bvxm_voxel_slab<float>::iterator PLPX_img_it = PLPX_img.begin();
366 bvxm_voxel_slab<float>::iterator visX_accum_it = visX_accum.begin();
367 bvxm_voxel_slab<float>::iterator preX_accum_it = preX_accum.begin();
368
369 for (; preX_accum_it != preX_accum.end(); ++preX_accum_it, ++PLPX_img_it, ++visX_accum_it) {
370 *preX_accum_it += (*PLPX_img_it) * (*visX_accum_it);
371 }
372 #ifdef DEBUG
373 std::stringstream plpx, vis, prex;
374 plpx << "PLPX_" << k_idx <<".tiff";
375 vis << "visX_" << k_idx <<".tiff";
376 prex << "preX_" << k_idx <<".tiff";
377 bvxm_util::write_slab_as_image(PLPX_img,plpx.str());
378 bvxm_util::write_slab_as_image(visX_accum,vis.str());
379 bvxm_util::write_slab_as_image(preX_accum,prex.str());
380 #endif
381 // scale and offset voxel probabilities to get (1-P(X))
382 // transform (1-P(X)) to image plane to accumulate visX for next level
383 bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[k_idx], PX_img);
384
385 if (return_mask) {
386 bvxm_util::add_slabs(PX_img,mask_slab,mask_slab);
387 }
388
389 // note: doing scale and offset in image domain so invalid pixels become 1.0 and don't affect visX
390 bvxm_voxel_slab<float>::iterator PX_img_it = PX_img.begin();
391 visX_accum_it = visX_accum.begin();
392 for (; visX_accum_it != visX_accum.end(); ++visX_accum_it, ++PX_img_it) {
393 *visX_accum_it *= (1 - *PX_img_it);
394 }
395 }
396 // now traverse a second time, computing new P(X) along the way.
397
398 bvxm_voxel_slab<float> preX_accum_vox(grid_size.x(),grid_size.y(),1);
399 bvxm_voxel_slab<float> visX_accum_vox(grid_size.x(),grid_size.y(),1);
400
401 #ifdef DEBUG
402 std::stringstream vis2, prex2;
403 vis2 << "visX2_.tiff";
404 prex2 << "preX2_.tiff";
405 bvxm_util::write_slab_as_image(visX_accum,vis2.str());
406 bvxm_util::write_slab_as_image(preX_accum,prex2.str());
407 #endif
408
409 std::cout << "\nPass 2:" << std::endl;
410 PLvisX_slab_it = PLvisX.begin();
411 preX_slab_it = preX.begin();
412 bvxm_voxel_grid<ocp_datatype>::iterator ocp_slab_it2 = ocp_grid->begin();
413 for (unsigned k_idx = 0; k_idx < (unsigned)grid_size.z(); ++k_idx, ++PLvisX_slab_it, ++preX_slab_it, ++ocp_slab_it2) {
414 std::cout << '.';
415
416 // transform preX_sum to current level
417 bvxm_util::warp_slab_bilinear(preX_accum, H_plane_to_img[k_idx], preX_accum_vox);
418
419 // transform visX_sum to current level
420 bvxm_util::warp_slab_bilinear(visX_accum, H_plane_to_img[k_idx], visX_accum_vox);
421
422 const float preX_sum_thresh = 0.0f;
423
424 bvxm_voxel_slab<float>::const_iterator preX_it = preX_slab_it->begin(), PLvisX_it = PLvisX_slab_it->begin(), preX_sum_it = preX_accum_vox.begin(), visX_sum_it = visX_accum_vox.begin();
425 bvxm_voxel_slab<float>::iterator PX_it = ocp_slab_it2->begin();
426
427 for (; PX_it != ocp_slab_it2->end(); ++PX_it, ++preX_it, ++PLvisX_it, ++preX_sum_it, ++visX_sum_it) {
428 // if preX_sum is zero at the voxel, no ray passed through the voxel (out of image)
429 if (*preX_sum_it > preX_sum_thresh) {
430 float multiplier = (*PLvisX_it + *preX_it) / *preX_sum_it;
431 // leave out normalization for now - results seem a little better without it. -DEC
432 //float ray_norm = 1 - *visX_sum_it; //normalize based on probability that a surface voxel is located along the ray. This was not part of the original Pollard + Mundy algorithm.
433 *PX_it *= multiplier; // * ray_norm;
434 }
435 if (*PX_it < min_vox_prob)
436 *PX_it = min_vox_prob;
437 if (*PX_it > max_vox_prob)
438 *PX_it = max_vox_prob;
439 }
440 }
441 std::cout << "\ndone." << std::endl;
442
443 if (return_prob) {
444 // fill pixel_probabilities with preX_accum
445 vil_image_view<float>::iterator pix_prob_it = pix_prob_density.begin();
446 bvxm_voxel_slab<float>::const_iterator preX_accum_it = preX_accum.begin();
447
448 for (; pix_prob_it != pix_prob_density.end(); ++pix_prob_it, ++preX_accum_it) {
449 *pix_prob_it = *preX_accum_it;
450 }
451 }
452
453 if (return_mask) {
454 // fill mask values
455 vil_image_view<bool>::iterator mask_it = mask.begin();
456 bvxm_voxel_slab<float>::const_iterator mask_slab_it = mask_slab.begin();
457
458 for (; mask_it != mask.end(); ++mask_it, ++mask_slab_it) {
459 *mask_it = (*mask_slab_it > 0);
460 }
461 }
462
463 #ifdef DEBUG
464 bvxm_util::write_slab_as_image(preX_accum,"prob.tiff");
465 #endif
466
467 //Check:
468 // increment the observation count
469 //this->increment_observations<APM_T>(bin_index);
470
471 return true;
472 }
473 #endif // 0
474
update_edges_lidar(vil_image_view_base_sptr & lidar_height,vil_image_view_base_sptr & lidar_edges,vil_image_view_base_sptr & lidar_edges_prob,vpgl_camera_double_sptr & camera,unsigned scale)475 bool bvxm_voxel_world::update_edges_lidar(vil_image_view_base_sptr& lidar_height,
476 vil_image_view_base_sptr& lidar_edges,
477 vil_image_view_base_sptr& lidar_edges_prob,
478 vpgl_camera_double_sptr& camera,
479 unsigned scale)
480 {
481 typedef bvxm_voxel_traits<EDGES>::voxel_datatype edges_datatype;
482
483 //typedef bvxm_voxel_traits<LIDAR>::lidar_processor lidar_processor;
484 bvxm_lidar_processor lidar_processor(10);
485
486 // parameters
487 vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale);
488
489 // compute homographies from voxel planes to image coordinates and vise-versa.
490 std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
491 std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
492 {
493 vgl_h_matrix_2d<double> Hp2i, Hi2p;
494 for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
495 {
496 compute_plane_image_H(camera,z,Hp2i,Hi2p,scale);
497 H_plane_to_img.push_back(Hp2i);
498 H_img_to_plane.push_back(Hi2p);
499 }
500 }
501
502 // convert image to a voxel_slab
503 bvxm_voxel_slab<float> lidar_height_slab(lidar_height->ni(), lidar_height->nj(), 1);
504 bvxm_voxel_slab<float> lidar_edges_slab(lidar_edges->ni(), lidar_edges->nj(), 1);
505 bvxm_voxel_slab<float> lidar_edges_prob_slab(lidar_edges_prob->ni(), lidar_edges_prob->nj(), 1);
506 if ((!bvxm_util::img_to_slab(lidar_height,lidar_height_slab)) ||
507 (!bvxm_util::img_to_slab(lidar_edges,lidar_edges_slab)) ||
508 (!bvxm_util::img_to_slab(lidar_edges_prob,lidar_edges_prob_slab))) {
509 std::cerr << "error converting image to voxel slab of observation type for bvxm_voxel_type: LIDAR\n";
510 return false;
511 }
512
513 bvxm_voxel_slab<float> lidar_height_backproj(grid_size.x(),grid_size.y(),1);
514 bvxm_voxel_slab<float> lidar_edges_backproj(grid_size.x(),grid_size.y(),1);
515 bvxm_voxel_slab<float> lidar_edges_prob_backproj(grid_size.x(),grid_size.y(),1);
516
517 // get edges probability grid
518 bvxm_voxel_grid_base_sptr edges_grid_base = this->get_grid<EDGES>(0,scale);
519 auto *edges_grid = static_cast<bvxm_voxel_grid<edges_datatype>*>(edges_grid_base.ptr());
520
521 bvxm_voxel_grid<edges_datatype>::iterator edges_slab_it = edges_grid->begin();
522
523 for (unsigned k_idx=0; k_idx<(unsigned)grid_size.z(); ++k_idx, ++edges_slab_it)
524 {
525 std::cout << k_idx << std::endl;
526
527 // backproject image onto voxel plane
528 bvxm_util::warp_slab_bilinear(lidar_height_slab, H_plane_to_img[k_idx], lidar_height_backproj);
529 bvxm_util::warp_slab_bilinear(lidar_edges_slab, H_plane_to_img[k_idx], lidar_edges_backproj);
530 bvxm_util::warp_slab_bilinear(lidar_edges_prob_slab, H_plane_to_img[k_idx], lidar_edges_prob_backproj);
531
532 bvxm_voxel_slab<float> lidar_prob(lidar_height_backproj.nx(), lidar_height_backproj.ny(), lidar_height_backproj.nz());
533 lidar_prob.fill(0.0);
534
535 vnl_vector_fixed<float,3> sigmas(0.5f,0.5f,0.0009f);
536 vgl_point_3d<float> local_xyz = voxel_index_to_xyz(0, 0, k_idx,scale);
537
538 for (unsigned i_idx=0; i_idx<lidar_height_backproj.nx(); i_idx++) {
539 for (unsigned j_idx=0; j_idx<lidar_height_backproj.ny(); j_idx++) {
540 std::vector<vgl_homg_point_2d<double> > vp(4);
541 int i = i_idx+1;
542 int j = j_idx-1;
543 vp[0] = vgl_homg_point_2d<double>(i, j);
544 vp[1] = vgl_homg_point_2d<double>(i+1, j);
545 vp[2] = vgl_homg_point_2d<double>(i, j+1);
546 vp[3] = vgl_homg_point_2d<double>(i+1, j+1);
547
548 vgl_h_matrix_2d<double> h_max = H_plane_to_img[k_idx];
549 vgl_h_matrix_2d<double> h_min;
550 if (k_idx == (unsigned)grid_size.z()-1)
551 h_min = H_plane_to_img[k_idx];
552 else
553 h_min = H_plane_to_img[k_idx+1];
554 vgl_box_2d<double> lidar_roi;
555
556 for (unsigned i=0; i<4; i++) {
557 vgl_homg_point_2d<double> img_pos_h_min = h_min*vp[i];
558 vgl_point_2d<double> img_pos_min(img_pos_h_min);
559 lidar_roi.add(img_pos_min);
560 }
561
562 lidar_prob(i_idx, j_idx) = lidar_processor.prob_density(lidar_height, local_xyz.z(), sigmas, lidar_roi, params_->voxel_length(scale));
563 }
564 }
565
566 bvxm_voxel_slab<float>::iterator lidar_prob_it = lidar_prob.begin();
567 bvxm_voxel_slab<float>::iterator edges_slab_it_it = (*edges_slab_it).begin();
568
569 bvxm_voxel_slab<float>::iterator lidar_edges_backproj_it = lidar_edges_backproj.begin();
570 bvxm_voxel_slab<float>::iterator lidar_edges_prob_backproj_it = lidar_edges_prob_backproj.begin();
571
572 for (; lidar_prob_it != lidar_prob.end(); ++lidar_prob_it, ++edges_slab_it_it, ++lidar_edges_backproj_it, ++lidar_edges_prob_backproj_it) {
573 (*edges_slab_it_it) = 0.1f + 0.8f*(*lidar_prob_it)*(*lidar_edges_backproj_it);
574 }
575 }
576
577 this->increment_observations<EDGES>(0,scale);
578
579 return true;
580 }
581 //: generate a heightmap from the viewpoint of a virtual camera
582 // The pixel values are the z values of the most likely voxel intercepted by the corresponding camera ray
heightmap(const vpgl_camera_double_sptr & virtual_camera,vil_image_view<unsigned> & heightmap,vil_image_view<float> & conf_map,unsigned scale_idx)583 bool bvxm_voxel_world::heightmap(const vpgl_camera_double_sptr& virtual_camera, vil_image_view<unsigned> &heightmap, vil_image_view<float> &conf_map, unsigned scale_idx)
584 {
585 typedef bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
586
587 // extract global parameters
588 vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
589
590
591 // compute homographies from voxel planes to image coordinates and vise-versa.
592 std::vector<vgl_h_matrix_2d<double> > H_plane_to_virtual_img;
593 std::vector<vgl_h_matrix_2d<double> > H_virtual_img_to_plane;
594
595 for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
596 {
597 vgl_h_matrix_2d<double> Hp2i, Hi2p;
598 compute_plane_image_H(virtual_camera,z,Hp2i,Hi2p,scale_idx);
599 H_plane_to_virtual_img.push_back(Hp2i);
600 H_virtual_img_to_plane.push_back(Hi2p);
601 }
602
603 // allocate some images
604 bvxm_voxel_slab<float> visX_accum_virtual(heightmap.ni(), heightmap.nj(),1);
605 bvxm_voxel_slab<float> heightmap_rough(heightmap.ni(),heightmap.nj(),1);
606 bvxm_voxel_slab<float> max_prob_image(heightmap.ni(), heightmap.nj(), 1);
607 bvxm_voxel_slab<ocp_datatype> slice_prob_img(heightmap.ni(),heightmap.nj(),1);
608
609 heightmap_rough.fill((float)grid_size.z());
610 visX_accum_virtual.fill(1.0f);
611 max_prob_image.fill(0.0f);
612
613 // get occupancy probability grid
614 bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
615 auto *ocp_grid = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
616
617 bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
618
619 std::cout << "generating height map from virtual camera:" << std::endl;
620 for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it) {
621 std::cout << '.';
622
623 // compute PXvisX for virtual camera and update visX
624 bvxm_util::warp_slab_bilinear(*ocp_slab_it,H_virtual_img_to_plane[z],slice_prob_img);
625 bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_prob_img.begin();
626 bvxm_voxel_slab<float>::iterator max_it = max_prob_image.begin(), visX_it = visX_accum_virtual.begin();
627 bvxm_voxel_slab<float>::iterator hmap_it = heightmap_rough.begin();
628
629 for (; hmap_it != heightmap_rough.end(); ++hmap_it, ++PX_it, ++max_it, ++visX_it) {
630 float PXvisX = (*visX_it) * (*PX_it);
631 //float PXvisX = *PX_it;
632 if (PXvisX > *max_it) {
633 *max_it = PXvisX;
634 *hmap_it = (float)z;
635 }
636 // update virtual visX
637 *visX_it *= (1.0f - *PX_it);
638 }
639 }
640 std::cout << std::endl;
641
642 #define HMAP_DEBUG
643 #ifdef DEBUG
644 bvxm_util::write_slab_as_image(heightmap_rough,"./heightmap_rough.tiff");
645 #endif
646 // now clean up height map
647
648 // convert confidence and heightmap to vil images
649 //vil_image_view<float>* conf_img = new vil_image_view<float>(heightmap.ni(),heightmap.nj());
650 //vil_image_view_base_sptr conf_img_sptr = conf_img;
651 vil_image_view_base_sptr conf_img_sptr = new vil_image_view<float>(conf_map);
652 bvxm_util::slab_to_img(max_prob_image,conf_img_sptr);
653
654 auto* heightmap_rough_img = new vil_image_view<float>(heightmap.ni(),heightmap.nj());
655 vil_image_view_base_sptr heightmap_rough_img_sptr = heightmap_rough_img;
656 bvxm_util::slab_to_img(heightmap_rough,heightmap_rough_img_sptr);
657 /*
658 // first, median filter heightmap
659 vil_image_view<float> heightmap_med_img(heightmap.ni(),heightmap.nj());
660 std::vector<int> strel_vec;
661 for (int i=-medfilt_halfsize; i <= medfilt_halfsize; ++i)
662 strel_vec.push_back(i);
663 vil_structuring_element strel(strel_vec,strel_vec);
664 vil_median(*heightmap_rough_img,heightmap_med_img,strel);
665
666 // detect inliers as points which don't vary drastically from the median image
667 vil_image_view<float> med_abs_diff(heightmap.ni(),heightmap.nj());
668 vil_math_image_abs_difference(heightmap_med_img,*heightmap_rough_img,med_abs_diff);
669 vil_image_view<bool> inliers(heightmap.ni(),heightmap.nj());
670 vil_threshold_below(med_abs_diff,inliers,med_diff_thresh);
671
672 std::cout << "smoothing height map: ";
673 vil_image_view<float> heightmap_filtered_img(heightmap.ni(),heightmap.nj(),1);
674 vil_image_view<bool> conf_mask(heightmap.ni(),heightmap.nj());
675 // threshold confidence
676 vil_threshold_above(*conf_img,conf_mask,conf_thresh);
677
678 #ifdef HMAP_DEBUG
679 vil_save(*conf_img,"./heightmap_conf.tiff");
680 vil_save(heightmap_med_img,"./heightmap_med.tiff");
681 #endif
682
683 // initialize with rough heightmap
684 vil_image_view<float>::const_iterator hmap_rough_it = heightmap_rough_img->begin();
685 vil_image_view<float>::iterator hmap_filt_it = heightmap_filtered_img.begin();
686 for (; hmap_filt_it != heightmap_filtered_img.end(); ++hmap_filt_it, ++hmap_rough_it) {
687 *hmap_filt_it = (float)(*hmap_rough_it);
688 }
689
690 for (unsigned i=0; i< n_smooth_iterations; ++i) {
691 std::cout << '.';
692 // smooth heightmap
693 vil_gauss_filter_2d(heightmap_filtered_img, heightmap_filtered_img, 1.0, 2, vil_convolve_constant_extend);
694 // reset values we are confident in
695 vil_image_view<bool>::const_iterator mask_it = conf_mask.begin(), inlier_it = inliers.begin();
696 vil_image_view<float>::const_iterator hmap_med_it = heightmap_med_img.begin();
697 hmap_filt_it = heightmap_filtered_img.begin();
698 for (; hmap_filt_it != heightmap_filtered_img.end(); ++hmap_filt_it, ++hmap_med_it, ++mask_it, ++inlier_it) {
699 if (*mask_it && *inlier_it) {
700 *hmap_filt_it = (float)(*hmap_med_it);
701 }
702 }
703 }
704 std::cout << std::endl;
705
706 // finally, median filter final heightmap
707 vil_image_view<float> heightmap_filtered_med(heightmap.ni(),heightmap.nj());
708 vil_median(heightmap_filtered_img,heightmap_filtered_med,strel);
709
710 #ifdef HMAP_DEBUG
711 vil_save(heightmap_filtered_med,"./heightmap_filtered.tiff");
712 #endif
713
714 // convert back to unsigned
715 vil_image_view<unsigned>::iterator hmap_it = heightmap.begin();
716 hmap_filt_it = heightmap_filtered_med.begin();
717 for (; hmap_it != heightmap.end(); ++hmap_filt_it, ++hmap_it) {
718 *hmap_it = (unsigned)(*hmap_filt_it); // should we do some rounding here?
719 }
720 */
721
722 // convert back to unsigned
723 vil_image_view<float>::const_iterator hmap_rough_it = heightmap_rough_img->begin();
724 vil_image_view<unsigned>::iterator hmap_it = heightmap.begin();
725 for (; hmap_rough_it != heightmap_rough_img->end(); ++hmap_it, ++hmap_rough_it) {
726 *hmap_it = (unsigned)(*hmap_rough_it);
727 }
728 return true;
729 }
730
731 //: generate a heightmap from the viewpoint of a virtual camera
732 // The pixel values are the expected z values and variance along the corresponding camera ray
heightmap_exp(const vpgl_camera_double_sptr & virtual_camera,vil_image_view<float> & heightmap,vil_image_view<float> & var,float & max_depth,unsigned scale_idx)733 bool bvxm_voxel_world::heightmap_exp(const vpgl_camera_double_sptr& virtual_camera, vil_image_view<float> &heightmap, vil_image_view<float> &var, float& max_depth, unsigned scale_idx)
734 {
735 typedef bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
736
737 // extract global parameters
738 vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
739
740 // compute homographies from voxel planes to image coordinates and vise-versa.
741 std::vector<vgl_h_matrix_2d<double> > H_plane_to_virtual_img;
742 std::vector<vgl_h_matrix_2d<double> > H_virtual_img_to_plane;
743
744 for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
745 {
746 vgl_h_matrix_2d<double> Hp2i, Hi2p;
747 compute_plane_image_H(virtual_camera,z,Hp2i,Hi2p,scale_idx);
748 H_plane_to_virtual_img.push_back(Hp2i);
749 H_virtual_img_to_plane.push_back(Hi2p);
750 }
751
752 // compute the height for initial pre_x, pre_y, pre_z
753 bvxm_voxel_slab<float> x_slab(grid_size.x(),grid_size.y(),1);
754 bvxm_voxel_slab<float> y_slab(grid_size.x(),grid_size.y(),1);
755 bvxm_voxel_slab<float> z_slab(grid_size.x(),grid_size.y(),1);
756
757 for (unsigned i = 0; i < grid_size.x(); i++)
758 for (unsigned j = 0; j < grid_size.y(); j++) {
759 vgl_point_3d<float> pt = this->voxel_index_to_xyz(i, j, -1, 0);
760 x_slab(i, j) = pt.x();
761 y_slab(i, j) = pt.y();
762 z_slab(i, j) = pt.z();
763 }
764
765 vgl_h_matrix_2d<double> Hp2i, Hi2p;
766 compute_plane_image_H(virtual_camera,-1,Hp2i,Hi2p,scale_idx); // compute for z = -1 (slab above the world volume)
767
768 // allocate some images
769 bvxm_voxel_slab<float> visX_accum_virtual(heightmap.ni(), heightmap.nj(),1);
770 bvxm_voxel_slab<float> depth(heightmap.ni(),heightmap.nj(),1);
771 bvxm_voxel_slab<float> exp_depth(heightmap.ni(),heightmap.nj(),1);
772 bvxm_voxel_slab<float> exp_depth_square(heightmap.ni(),heightmap.nj(),1);
773 bvxm_voxel_slab<ocp_datatype> slice_prob_img(heightmap.ni(),heightmap.nj(),1);
774
775 bvxm_voxel_slab<float> slab_x_virtual(heightmap.ni(), heightmap.nj(), 1);
776 bvxm_voxel_slab<float> slab_y_virtual(heightmap.ni(), heightmap.nj(), 1);
777 bvxm_voxel_slab<float> slab_z_virtual(heightmap.ni(), heightmap.nj(), 1);
778 bvxm_voxel_slab<float> slab_x_pre_virtual(heightmap.ni(), heightmap.nj(), 1);
779 bvxm_voxel_slab<float> slab_y_pre_virtual(heightmap.ni(), heightmap.nj(), 1);
780 bvxm_voxel_slab<float> slab_z_pre_virtual(heightmap.ni(), heightmap.nj(), 1);
781
782 // initialize the pres using z = -1 height
783 bvxm_util::warp_slab_bilinear(x_slab,Hi2p,slab_x_pre_virtual);
784 bvxm_util::warp_slab_bilinear(y_slab,Hi2p,slab_y_pre_virtual);
785 bvxm_util::warp_slab_bilinear(z_slab,Hi2p,slab_z_pre_virtual);
786
787 //heightmap_rough.fill((float)grid_size.z());
788 visX_accum_virtual.fill(1.0f);
789 depth.fill(0.0f); // depth from the ceiling of the scene! so start with 0
790 exp_depth.fill(0.0f);
791 exp_depth_square.fill(0.0f);
792
793 // get occupancy probability grid
794 bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
795 auto *ocp_grid = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
796
797 bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
798
799 std::cout << "generating depth map from virtual camera:" << std::endl;
800 for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it) {
801 std::cout << '.';
802
803 // compute PXvisX for virtual camera and update visX
804 bvxm_util::warp_slab_bilinear(*ocp_slab_it,H_virtual_img_to_plane[z],slice_prob_img);
805
806 // compute the current x,y,z
807 for (unsigned i = 0; i < grid_size.x(); i++)
808 for (unsigned j = 0; j < grid_size.y(); j++) {
809 vgl_point_3d<float> pt = this->voxel_index_to_xyz(i, j, z, 0);
810 x_slab(i, j) = pt.x();
811 y_slab(i, j) = pt.y();
812 z_slab(i, j) = pt.z();
813 }
814 bvxm_util::warp_slab_bilinear(x_slab,Hi2p,slab_x_virtual);
815 bvxm_util::warp_slab_bilinear(y_slab,Hi2p,slab_y_virtual);
816 bvxm_util::warp_slab_bilinear(z_slab,Hi2p,slab_z_virtual);
817
818
819 // compute the current depths
820 bvxm_voxel_slab<float>::iterator depth_it = depth.begin(), x_it = slab_x_virtual.begin(), y_it = slab_y_virtual.begin(), z_it = slab_z_virtual.begin();
821 bvxm_voxel_slab<float>::iterator x_pre_it = slab_x_pre_virtual.begin(), y_pre_it = slab_y_pre_virtual.begin(), z_pre_it = slab_z_pre_virtual.begin();
822 for (; depth_it != depth.end(); ++depth_it, ++x_it, ++y_it, ++z_it, ++x_pre_it, ++y_pre_it, ++z_pre_it) {
823 float inc_x = *x_pre_it - *x_it;
824 float inc_y = *y_pre_it - *y_it;
825 float inc_z = *z_pre_it - *z_it;
826 float d = std::sqrt(inc_x*inc_x + inc_y*inc_y + inc_z*inc_z);
827 *depth_it += d;
828 *x_pre_it = *x_it;
829 *y_pre_it = *y_it;
830 *z_pre_it = *z_it;
831 }
832
833 bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_prob_img.begin();
834 bvxm_voxel_slab<float>::iterator exp_depth_it = exp_depth.begin(), exp_depth_square_it = exp_depth_square.begin(), visX_it = visX_accum_virtual.begin();
835 depth_it = depth.begin();
836
837 for (; exp_depth_it != exp_depth.end(); ++exp_depth_it, ++PX_it, ++exp_depth_square_it, ++visX_it, ++depth_it) {
838 float PXvisX = (*visX_it) * (*PX_it);
839 *exp_depth_it += *depth_it * PXvisX;
840 *exp_depth_square_it += *depth_it * *depth_it * PXvisX;
841
842 // update virtual visX
843 *visX_it *= (1.0f - *PX_it);
844 }
845 }
846 std::cout << std::endl;
847
848 // find the depth at the slab below the world volume
849 for (unsigned i = 0; i < grid_size.x(); i++)
850 for (unsigned j = 0; j < grid_size.y(); j++) {
851 vgl_point_3d<float> pt = this->voxel_index_to_xyz(i, j, grid_size.z(), 0);
852 x_slab(i, j) = pt.x();
853 y_slab(i, j) = pt.y();
854 z_slab(i, j) = pt.z();
855 }
856
857 bvxm_util::warp_slab_bilinear(x_slab,Hi2p,slab_x_virtual);
858 bvxm_util::warp_slab_bilinear(y_slab,Hi2p,slab_y_virtual);
859 bvxm_util::warp_slab_bilinear(z_slab,Hi2p,slab_z_virtual);
860
861 // compute the current depths
862 bvxm_voxel_slab<float>::iterator depth_it = depth.begin(), x_it = slab_x_virtual.begin(), y_it = slab_y_virtual.begin(), z_it = slab_z_virtual.begin();
863 bvxm_voxel_slab<float>::iterator x_pre_it = slab_x_pre_virtual.begin(), y_pre_it = slab_y_pre_virtual.begin(), z_pre_it = slab_z_pre_virtual.begin();
864 for (; depth_it != depth.end(); ++depth_it, ++x_it, ++y_it, ++z_it, ++x_pre_it, ++y_pre_it, ++z_pre_it) {
865 float inc_x = *x_pre_it - *x_it;
866 float inc_y = *y_pre_it - *y_it;
867 float inc_z = *z_pre_it - *z_it;
868 float d = std::sqrt(inc_x*inc_x + inc_y*inc_y + inc_z*inc_z);
869 *depth_it += d;
870 }
871
872 // normalize the depths, the visibility at the end of the ray may not have diminished completely
873 bvxm_voxel_slab<float>::iterator exp_depth_it = exp_depth.begin(), exp_depth_square_it = exp_depth_square.begin(), visX_it = visX_accum_virtual.begin();
874 depth_it = depth.begin();
875 vil_image_view<float>::iterator hmap_it = heightmap.begin(), var_it = var.begin();
876 for (; exp_depth_it != exp_depth.end(); ++exp_depth_it, ++exp_depth_square_it, ++depth_it, ++visX_it, ++hmap_it, ++var_it) {
877 *hmap_it = *exp_depth_it + *depth_it * *visX_it;
878 *var_it = *exp_depth_square_it + *depth_it * *depth_it * *visX_it - (*exp_depth_it * *exp_depth_it);
879 }
880 // return the max depth for the ray (0,0)
881 max_depth = *(depth.begin());
882
883 return true;
884 }
885
886 //: measure the average uncertainty along the rays
uncertainty(const vpgl_camera_double_sptr & virtual_camera,vil_image_view<float> & uncertainty,unsigned scale_idx)887 bool bvxm_voxel_world::uncertainty(const vpgl_camera_double_sptr& virtual_camera, vil_image_view<float> &uncertainty, unsigned scale_idx)
888 {
889 typedef bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
890
891 // extract global parameters
892 vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
893
894 // compute homographies from voxel planes to image coordinates and vise-versa.
895 std::vector<vgl_h_matrix_2d<double> > H_plane_to_virtual_img;
896 std::vector<vgl_h_matrix_2d<double> > H_virtual_img_to_plane;
897
898 for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
899 {
900 vgl_h_matrix_2d<double> Hp2i, Hi2p;
901 compute_plane_image_H(virtual_camera,z,Hp2i,Hi2p,scale_idx);
902 H_plane_to_virtual_img.push_back(Hp2i);
903 H_virtual_img_to_plane.push_back(Hi2p);
904 }
905
906 // allocate some images
907 bvxm_voxel_slab<float> visX_accum_virtual(uncertainty.ni(), uncertainty.nj(),1);
908 bvxm_voxel_slab<float> uncer(uncertainty.ni(),uncertainty.nj(),1);
909 bvxm_voxel_slab<float> uncer_cnt(uncertainty.ni(),uncertainty.nj(),1);
910 bvxm_voxel_slab<ocp_datatype> slice_prob_img(uncertainty.ni(),uncertainty.nj(),1);
911
912 visX_accum_virtual.fill(1.0f);
913 uncer.fill(0.0f); // depth from the ceiling of the scene! so start with 0
914 uncer_cnt.fill(0.0f);
915
916 // get occupancy probability grid
917 bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
918 auto *ocp_grid = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
919
920 bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
921
922 std::cout << "generating depth map from virtual camera:" << std::endl;
923 for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it) {
924 std::cout << '.';
925
926 // compute PXvisX for virtual camera and update visX
927 bvxm_util::warp_slab_bilinear(*ocp_slab_it,H_virtual_img_to_plane[z],slice_prob_img);
928
929 bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_prob_img.begin();
930 bvxm_voxel_slab<float>::iterator uncer_it = uncer.begin(), uncer_cnt_it = uncer_cnt.begin(), visX_it = visX_accum_virtual.begin();
931 for (; uncer_it != uncer.end(); ++uncer_it, ++uncer_cnt_it, ++PX_it, ++visX_it) {
932 float PX = (*PX_it);
933 float PXminus = 1.0f-(*PX_it);
934 float ratio = PX/PXminus;
935 if (ratio > 1)
936 ratio = 1.0f/ratio;
937 //*uncer_it += ratio;
938 //*uncer_cnt_it += 1.0f;
939 *uncer_it += *visX_it * ratio;
940 *uncer_cnt_it += *visX_it;
941
942 // update virtual visX
943 *visX_it *= (1.0f - *PX_it);
944 }
945 }
946 std::cout << std::endl;
947
948 // compute the average
949 bvxm_voxel_slab<float>::iterator uncer_it = uncer.begin(), uncer_cnt_it = uncer_cnt.begin();
950 vil_image_view<float>::iterator uncer_img_it = uncertainty.begin();
951 for (; uncer_it != uncer.end(); ++uncer_it, ++uncer_cnt_it, ++uncer_img_it) {
952 *uncer_img_it = *uncer_it / *uncer_cnt_it;
953 }
954
955 return true;
956 }
957
voxel_index_to_xyz(unsigned vox_i,unsigned vox_j,int vox_k,unsigned scale_idx)958 vgl_point_3d<float> bvxm_voxel_world::voxel_index_to_xyz(unsigned vox_i, unsigned vox_j, int vox_k, unsigned scale_idx)
959 {
960 float vox_len = params_->voxel_length(scale_idx);
961 vgl_vector_3d<unsigned> num_vox = params_->num_voxels(scale_idx);
962
963 // corner in parameters refers to the bottom. we want the top since slice 0 is the top-most slice.
964 vgl_point_3d<float> grid_origin = params_->corner() + (vgl_vector_3d<float>(0.5f, 0.5f, (float(num_vox.z()) - 0.5f))*vox_len);
965 vgl_vector_3d<float> step_i = vox_len*params_->base_x();
966 vgl_vector_3d<float> step_j = vox_len*params_->base_y();
967 vgl_vector_3d<float> step_k = (-vox_len)*params_->base_z();
968
969 return grid_origin + step_i*vox_i + step_j*vox_j + step_k*vox_k;
970 }
971
compute_plane_image_H(vpgl_camera_double_sptr const & cam,int k_idx,vgl_h_matrix_2d<double> & H_plane_to_image,vgl_h_matrix_2d<double> & H_image_to_plane,unsigned scale_idx)972 void bvxm_voxel_world::compute_plane_image_H(vpgl_camera_double_sptr const& cam, int k_idx, vgl_h_matrix_2d<double> &H_plane_to_image, vgl_h_matrix_2d<double> &H_image_to_plane, unsigned scale_idx)
973 {
974 vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
975
976 std::vector<vgl_homg_point_2d<double> > voxel_corners_img;
977 std::vector<vgl_homg_point_2d<double> > voxel_corners_vox;
978
979 // create vectors containing four corners of grid, and their projections into the image
980 double u=0, v=0;
981 vgl_point_3d<float> corner_world;
982
983 voxel_corners_vox.emplace_back(0,0);
984 corner_world = this->voxel_index_to_xyz(0,0,k_idx,scale_idx);
985 cam->project(corner_world.x(),corner_world.y(),corner_world.z(),u,v);
986 voxel_corners_img.emplace_back(u,v);
987
988 voxel_corners_vox.emplace_back(grid_size.x()-1,0);
989 corner_world = this->voxel_index_to_xyz(grid_size.x()-1,0,k_idx,scale_idx);
990 cam->project(corner_world.x(),corner_world.y(),corner_world.z(),u,v);
991 voxel_corners_img.emplace_back(u,v);
992
993 voxel_corners_vox.emplace_back(grid_size.x()-1,grid_size.y()-1);
994 corner_world = this->voxel_index_to_xyz(grid_size.x()-1,grid_size.y()-1,k_idx,scale_idx);
995 cam->project(corner_world.x(),corner_world.y(),corner_world.z(),u,v);
996 voxel_corners_img.emplace_back(u,v);
997
998 voxel_corners_vox.emplace_back(0,(grid_size.y()-1));
999 corner_world = this->voxel_index_to_xyz(0,grid_size.y()-1,k_idx,scale_idx);
1000 cam->project(corner_world.x(),corner_world.y(),corner_world.z(),u,v);
1001 voxel_corners_img.emplace_back(u,v);
1002
1003
1004 vgl_h_matrix_2d_compute_linear comp_4pt;
1005 if (!comp_4pt.compute(voxel_corners_img,voxel_corners_vox, H_image_to_plane)) {
1006 std::cerr << "ERROR computing homography from image to voxel slice.\n";
1007 for (const auto & i : voxel_corners_img)
1008 std::cerr << i << std::endl;
1009 for (const auto & i : voxel_corners_vox)
1010 std::cerr << i << std::endl;
1011 }
1012 if (!comp_4pt.compute(voxel_corners_vox,voxel_corners_img, H_plane_to_image)) {
1013 std::cerr << "ERROR computing homography from voxel slice to image.\n";
1014 }
1015 return;
1016 }
1017