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