1 // This is brl/bseg/bvxm/bvxm_voxel_world.h
2 #ifndef bvxm_voxel_world_h_
3 #define bvxm_voxel_world_h_
4 //:
5 // \file
6 // \brief A representation of the world using voxel grids of occupancy probability and an appearance model.
7 // \author Daniel Crispell (dec@lems.brown.edu)
8 // \date January 22, 2008
9 //
10 // The world has the ability to store voxel grids of any type defined in bvxm_voxel_traits.h.
11 // These grids are accessible through the get_grid() method.
12 // A voxel_type which is an appearance model type must have an associated appearance model processor class.
13 // The appearance model processor type needs to have the following methods:
14 //
15 // \code
16 //   bvxm_voxel_slab<float> prob_density(bvxm_voxel_slab<APM_PROC::apm_datatype> const& appearance,
17 //                                       bvxm_voxel_slab<APM_PROC::obs_datatype> const& observation);
18 //
19 //   bvxm_voxel_slab<float> prob_range(bvxm_voxel_slab<APM_PROC::apm_datatype> const& appearance,
20 //                                     bvxm_voxel_slab<APM_PROC::obs_datatype> const& observation,
21 //                                     bvxm_voxel_slab<float> pix_range);
22 //
23 //   bool update(bvxm_voxel_slab<APM_PROC::apm_datatype> appearance,
24 //               bvxm_voxel_slab<APM_PROC::obs_datatype> const& observation,
25 //               bvxm_voxel_slab<float> const& weights);
26 //
27 //   bvxm_voxel_slab<APM_PROC::obs_datatype> expected_color(bvxm_voxel_slab<APM_PROC::apm_datatype> const& appearance);
28 // \endcode
29 //
30 // \verbatim
31 //  Modifications:
32 //   Ozge C Ozcanli - 2/20/2008 - added  the method:
33 //           bool mixture_of_gaussians_image(bvxm_image_metadata const& camera,
34 //                                           bvxm_voxel_slab<apm_datatype> &mog_image);
35 //
36 //   Isabel Restrepo - 2/23/2008
37 //               -Changed class to support different VOXEL_GRID_TYPES simultaneously.
38 //                 Thus, the calss is not templated anymore but rather the individual functions.
39 //               - There is no need for bvxm_world_base, hence this class is not subclassed form it anymore
40 //               - Subclassed form vbl_ref_count
41 //
42 //   Ozge C Ozcanli - 2/27/2008 - made get_grid method public
43 //
44 //   Ibrahim Eden - 03/06/2008 - added the method:
45 //           bool expected_edge_image(bvxm_image_metadata const& camera,vil_image_view_base_sptr &expected);
46 //
47 //   Ibrahim Eden - 03/07/2008 - added the method:
48 //           bool update_edges(bvxm_image_metadata const& metadata);
49 //
50 //   Ibrahim Eden - 03/28/2008 - added the method:
51 //           bool save_edges_raw(std::string filename);
52 //
53 //   Ibrahim Eden - 06/03/2008 - added the method:
54 //           bool save_edges_vff(std::string filename);
55 //
56 //   Ozge C. Ozcanli - 04/18/2008 - added the method:
57 //           bool mog_most_probable_image(bvxm_image_metadata const& camera, bvxm_voxel_slab_base_sptr& mog_image, unsigned bin_index);
58 //
59 //   Ibrahim Eden - 08/01/2008 - added the method: update_edges_lidar
60 //
61 //   Ozge C. Ozcanli - 12/15/2008 - added the method:
62 //           bool mog_image_with_random_order_sampling(bvxm_image_metadata const& camera, unsigned n_samples, bvxm_voxel_slab_base_sptr& mog_image,unsigned bin_index, unsigned scale_idx)
63 //
64 //   Ibrahim Eden - 02/03/2009 - added the method: init_edges_prob
65 //
66 //   Gamze Tunali - 06/16/2009 - update_lidar and save_occupancy_raw methods are templated and moved from .cxx file to the header
67 //
68 //   Yi Dong - 11/26/2013 - added the option to use memory-based voxel grid in update method (require sufficient memory if chosen and all previous voxel grid on the disk will be ignored)
69 // \endverbatim
70 //
71 ////////////////////////////////////////////////////////////////////////////////
72 
73 #include <string>
74 #include <vector>
75 #include <set>
76 #include <iostream>
77 #include <sstream>
78 #include <utility>
79 #include <cassert>
80 #include <vbl/vbl_ref_count.h>
81 
82 #include <vgl/vgl_point_3d.h>
83 #include <vgl/vgl_vector_3d.h>
84 #include <vgl/vgl_plane_3d.h>
85 #include <vgl/algo/vgl_h_matrix_2d.h>
86 
87 #include <vnl/vnl_math.h>
88 
89 #include <vil/vil_image_view.h>
90 #include <vpgl/vpgl_camera_double_sptr.h>
91 
92 #include "bvxm_image_metadata.h"
93 #include "bvxm_mog_grey_processor.h"
94 #include "grid/bvxm_voxel_grid.h"
95 #include "bvxm_voxel_traits.h"
96 #include "bvxm_world_params.h"
97 #include "bvxm_util.h"
98 
99 // These includes are for the implementations of the templated methods,
100 // which should be moved from the header file if possible.
101 #ifdef _MSC_VER
102 #  include <vcl_msvc_warnings.h>
103 #endif
104 #include <vil/vil_image_view_base.h>
105 #include <vul/vul_file_iterator.h>
106 
107 // For heightmap function
108 #include <vil/algo/vil_median.h>
109 #include <vil/algo/vil_structuring_element.h>
110 #include <vil/algo/vil_threshold.h>
111 #include <vil/algo/vil_gauss_filter.h>
112 
113 #include <bsta/bsta_sampler.h>
114 #include <vpgl/file_formats/vpgl_geo_camera.h>
115 
116 class bvxm_voxel_world: public vbl_ref_count
117 {
118  public:
119 
120   //: default constructor
121   bvxm_voxel_world() = default;
122 
123   //: construct world with parameters
bvxm_voxel_world(bvxm_world_params_sptr params)124   bvxm_voxel_world(bvxm_world_params_sptr params) { params_ = params; }
125 
126   //: destructor
127   ~bvxm_voxel_world() override;
128 
129   //: update voxel grid with data from image/camera pair. Based on algorithm published in Pollard + Mundy 06.
130   template<bvxm_voxel_type APM_T>
131   bool update(bvxm_image_metadata const& observation, unsigned bin_index = 0, unsigned scale_idx=0, bool use_memory = false);
132 
133   //: update voxel grid with data from image/camera pair and return pixel probability densities.
134   // Based on algorithm published in Pollard and Mundy 06.
135   // The probability density of observing each pixel in the observation is returned in pixel_prob_density, which should be allocated by the caller.
136   template<bvxm_voxel_type APM_T>
137   bool update(bvxm_image_metadata const& observation,
138               vil_image_view<float> &pixel_prob_density,
139               vil_image_view<bool> &mask,
140               unsigned bin_index = 0,
141               unsigned scale_idx=0,
142               bool use_memory = false);
143 
144   //: update voxel grid with data from LIDAR/camera pair
145   template<bvxm_voxel_type APM_T>
146   bool update_lidar(bvxm_image_metadata const& observation, unsigned scale_idx=0);
147 
148   //: update voxel grid with data from LIDAR/camera pair and return pixel probability densities.
149   template<bvxm_voxel_type APM_T>
150   bool update_lidar(bvxm_image_metadata const& observation, vil_image_view<float> &pixel_prob_density, vil_image_view<bool> &mask, unsigned scale_idx=0);
151 
152   //: update voxel grid edge probabilities with data from LIDAR/camera pair
153   bool update_edges_lidar(vil_image_view_base_sptr& lidar_height,
154                           vil_image_view_base_sptr& lidar_edges,
155                           vil_image_view_base_sptr& lidar_edges_prob,
156                           vpgl_camera_double_sptr& camera,
157                           unsigned scale_idx=0);
158   //: update voxel grid edge probabilities with data from LIDAR/camera pair
159   template<bvxm_voxel_type APM_T>
160   bool update_point_cloud(std::vector<vgl_point_3d<float> > & points);
161 
162   //: generate the expected image from the specified viewpoint. the expected image and mask should be allocated by the caller.
163   template<bvxm_voxel_type APM_T>
164   bool expected_image(bvxm_image_metadata const& camera,
165                       vil_image_view_base_sptr &expected,
166                       vil_image_view<float> &mask, unsigned bin_index = 0, unsigned scale_idx=0);
167 
168   //: probability that the observed pixels were _not_ produced by a voxel in the grid.
169   // The range determines how much tolerance to allow the (continuous) pixel values in order to convert from a density to a discrete probability.
170   // Default value of 0.008 is approximately two 8-bit levels in either direction (assuming intensity is normalized 0-1)
171   template<bvxm_voxel_type APM_T>
172   bool inv_pixel_range_probability(bvxm_image_metadata const& observation,
173                                    vil_image_view<float> &inv_prob,
174                                    unsigned bin_index = 0,unsigned scale_idx=0,
175                                    float pixel_range = 0.008f);
176 
177   //: for each pixel, return the sum along the corresponding ray of voxels that the observation was produced by the voxel.
178   // Based on algorithm published in Pollard + Mundy 06.
179   // The returned values are approximate samples of a probability density, with the pixel values being the independent value.
180   template<bvxm_voxel_type APM_T>
181   bool pixel_probability_density(bvxm_image_metadata const& observation,
182                                  vil_image_view<float> &pixel_probability,
183                                  vil_image_view<bool> &mask,
184                                  unsigned bin_index = 0, unsigned scale_idx=0);
185 
186   //: for each pixel in a region specified by mask , return the probability that the observation was produced by the voxel.
187   // The returned values are approximate samples of a probability density, with the pixel values being the independent value.
188   template<bvxm_voxel_type APM_T>
189   bool region_probability_density(bvxm_image_metadata const& observation,
190                                   vil_image_view_base_sptr const& mask,
191                                   bvxm_voxel_grid_base_sptr &pixel_probability,
192                                   unsigned bin_index , unsigned scale_idx);
193 
194   //: generate the mixture of gaussians slab from the specified viewpoint. the slab should be allocated by the caller.
195   template<bvxm_voxel_type APM_T>
196   bool mixture_of_gaussians_image(bvxm_image_metadata const& camera,
197                                   bvxm_voxel_slab_base_sptr& mog_image,
198                                   unsigned bin_index = 0, unsigned scale_idx=0);
199 
200   //: generate the mixture of gaussians slab from the specified viewpoint. the slab should be allocated by the caller.
201   //  generate samples from each voxel's distribution along the ray of a pixel to train a new mog at the pixel
202   //  use a random ordering to select the voxels based on their visibility probabilities to avoid ordering problem
203   template<bvxm_voxel_type APM_T>
204   bool mog_image_with_random_order_sampling(bvxm_image_metadata const& camera, unsigned n_samples,
205                                             bvxm_voxel_slab_base_sptr& mog_image,
206                                             unsigned bin_index = 0, unsigned scale_idx=0);
207 
208   //: generate the mixture of gaussians slab from the specified viewpoint
209   //  Uses the most probable modes of the distributions at each voxel along the ray.
210   //  The slab should be allocated by the caller.
211   template<bvxm_voxel_type APM_T>
212   bool mog_most_probable_image(bvxm_image_metadata const& camera,
213                                bvxm_voxel_slab_base_sptr& mog_image,
214                                unsigned bin_index = 0, unsigned scale_idx=0);
215 
216   //: return the original image, viewed from a new viewpoint
217   template<bvxm_voxel_type APM_T>
218   bool virtual_view(bvxm_image_metadata const& original_view,
219                     const vpgl_camera_double_sptr virtual_camera,
220                     vil_image_view_base_sptr &virtual_view,
221                     vil_image_view<float> &vis_prob, unsigned bin_index = 0, unsigned scale_idx=0);
222 
223   //: generate a heightmap from the viewpoint of a virtual camera
224   // The pixel values are the z values of the most likely voxel intercepted by the corresponding camera ray
225   bool heightmap(const vpgl_camera_double_sptr& virtual_camera, vil_image_view<unsigned> &heightmap, vil_image_view<float> &conf_map, unsigned scale_idx=0);
226 
227   //: generate a heightmap from the viewpoint of a virtual camera
228   // The pixel values are the expected z values and variance along the corresponding camera ray
229   bool 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=0);
230 
231   //: measure the average uncertainty along the rays
232   bool uncertainty(const vpgl_camera_double_sptr& virtual_camera, vil_image_view<float> &uncertainty, unsigned scale_idx=0);
233 
234   //: generate a heightmap from the viewpoint of a virtual camera
235   // The pixel values are the z values of the most likely voxel intercepted by the corresponding camera ray
236   // This version of the function assumes that there is also image data associated with the virtual camera
237   template<bvxm_voxel_type APM_T>
238   bool heightmap(bvxm_image_metadata const& virtual_camera, vil_image_view<unsigned> &heightmap, unsigned bin_index = 0, unsigned scale_idx =0);
239 
240   //: use the ortho z map (given by method heightmap() ) of the scene to make an input image ortho.
241   template<typename T>
242   bool orthorectify(vil_image_view_base_sptr z_map, vpgl_camera_double_sptr z_map_camera, vil_image_view<T>& input_image, vpgl_camera_double_sptr input_camera, vil_image_view<T>& out_image, unsigned scale_idx=0);
243 
244   //: return a planar approximation to the world
245   vgl_plane_3d<double> fit_plane();
246 
247   //: get the world parameters
get_params()248   bvxm_world_params_sptr get_params() const { return params_; }
249 
250   //: set the world parameters
set_params(bvxm_world_params_sptr params)251   void set_params(bvxm_world_params_sptr params) { params_ = params; }
252 
253   // === Operators that allow voxel world to be placed in a brdb database ===
254 
255   //: equality operator
256   bool operator == (bvxm_voxel_world const& that) const;
257 
258   //: less than operator
259   bool operator < (bvxm_voxel_world  const& that) const;
260 
261   //: get a grid from the map: creates a new one if nothing exists at the specified index.
262   template<bvxm_voxel_type VOX_T>
263   bvxm_voxel_grid_base_sptr get_grid(unsigned bin_index, unsigned scale, bool use_memory = false);
264 
265   //: save the occupancy grid as a 3-d tiff image
266   bool save_occupancy_vff(const std::string& filename, unsigned scale_idx=0);
267 
268   //: save the occupancy grid in a ".raw" format readable by Drishti volume rendering software
269   template<bvxm_voxel_type APM_T>
270   bool save_occupancy_raw(std::string filename, unsigned scale_idx=0);
271 
272   //: remove all voxel data from disk - use with caution!
273   bool clean_grids();
274 
275   //: get the observation count of a voxel type at a specific bin
276   template<bvxm_voxel_type VOX_T>
277   unsigned int num_observations(unsigned int bin_idx = 0, unsigned scale = 0, bool use_memory = false);
278 
279   //: increment the observation count of a voxel type at a specific bin
280   template<bvxm_voxel_type VOX_T>
281   void increment_observations( unsigned int bin_idx = 0, unsigned scale = 0, bool use_memory = false);
282 
283   //: zero the number of observations at a specific bin
284   template<bvxm_voxel_type VOX_T>
285   void zero_observations( unsigned int bin_idx = 0, unsigned scale = 0, bool use_memory = false);
286 
287   vgl_point_3d<float> voxel_index_to_xyz(unsigned vox_i, unsigned vox_j, int vox_k, unsigned scale=0);
288 
289   void compute_plane_image_H(vpgl_camera_double_sptr const& cam,
290                              int grid_k,
291                              vgl_h_matrix_2d<double> &H_plane_to_image,
292                              vgl_h_matrix_2d<double> &H_image_to_plane, unsigned scale_idx=0);
293 
294  protected:
295 
296 #if 0
297   //: appearance model voxel storage
298   std::map<bvxm_voxel_type, std::map<unsigned int, bvxm_voxel_grid_base_sptr> > grid_map_;
299 #endif
300 
301   //: map of a map of a map of voxel grids which is indexed by voxel type,illumination bin and scale
302   std::map<bvxm_voxel_type, std::map<unsigned int, std::map<unsigned int, bvxm_voxel_grid_base_sptr> > > grid_map_;
303 
304   //: the world parameters
305   bvxm_world_params_sptr params_;
306 
307  private:
308 
309   template <bvxm_voxel_type APM_T>
310   bool update_impl(bvxm_image_metadata const& metadata,
311                    bool return_prob, vil_image_view<float> &pix_prob_density,
312                    bool return_mask, vil_image_view<bool> &mask, unsigned bin_index, unsigned scale_idx=0, bool use_memory = false);
313 
314   //: Update voxel grid with data from LIDAR image/camera pair and return probability density of pixel values.
315   template<bvxm_voxel_type APM_T>
316   bool update_lidar_impl(bvxm_image_metadata const& metadata,
317                          bool return_prob,
318                          vil_image_view<float> &pix_prob_density,
319                          bool return_mask,
320                          vil_image_view<bool> &mask, unsigned scale_idx=0);
321 };
322 
323 
324 //: output description of voxel world to stream.
325 std::ostream&  operator<<(std::ostream& s, bvxm_voxel_world const& vox_world);
326 
327 #if 0
328 typedef vbl_smart_ptr<bvxm_voxel_world> bvxm_voxel_world_sptr;
329 #endif
330 //////////////////////////////////////////////////////////
331 // TODO: Move everything below here to .txx files -DEC
332 //////////////////////////////////////////////////////////
333 
334 //: get the observation count of a voxel type at a specific bin
335 template<bvxm_voxel_type VOX_T>
num_observations(unsigned int bin_idx,unsigned int scale_idx,bool use_memory)336 unsigned int bvxm_voxel_world::num_observations(unsigned int bin_idx, unsigned int scale_idx, bool use_memory)
337 {
338   // call get_grid so data will be loaded from disk if necessary.
339   typedef typename bvxm_voxel_traits<VOX_T>::voxel_datatype vox_datatype;
340   bvxm_voxel_grid<vox_datatype> *grid = static_cast<bvxm_voxel_grid<vox_datatype>*>(this->get_grid<VOX_T>(bin_idx,scale_idx,use_memory).ptr());
341   return grid->num_observations();
342 }
343 
344 
345 //: increment the observation count of a voxel type at a specific bin
346 template<bvxm_voxel_type VOX_T>
increment_observations(unsigned int bin_idx,unsigned int scale_idx,bool use_memory)347 void bvxm_voxel_world::increment_observations(unsigned int bin_idx, unsigned int scale_idx, bool use_memory)
348 {
349   // call get_grid so data will be loaded from disk if necessary.
350   typedef typename bvxm_voxel_traits<VOX_T>::voxel_datatype vox_datatype;
351   bvxm_voxel_grid<vox_datatype> *grid = static_cast<bvxm_voxel_grid<vox_datatype>*>(this->get_grid<VOX_T>(bin_idx,scale_idx,use_memory).ptr());
352   grid->increment_observations();
353 }
354 
355 //: zero the observation count at a specific bin
356 template<bvxm_voxel_type VOX_T>
zero_observations(unsigned int bin_idx,unsigned int scale_idx,bool use_memory)357 void bvxm_voxel_world::zero_observations(unsigned int bin_idx, unsigned int scale_idx, bool use_memory)
358 {
359   // call get_grid so data will be loaded from disk if necessary.
360   typedef typename bvxm_voxel_traits<VOX_T>::voxel_datatype vox_datatype;
361   bvxm_voxel_grid<vox_datatype> *grid = static_cast<bvxm_voxel_grid<vox_datatype>*>(this->get_grid<VOX_T>(bin_idx,scale_idx,use_memory).ptr());
362   grid->zero_observations();
363 }
364 
365 //: Returns the voxel_grid that corresponds to a given bvxm_voxel_type and a bin number
366 template<bvxm_voxel_type VOX_T>
get_grid(unsigned bin_index,unsigned scale_idx,bool use_memory)367 bvxm_voxel_grid_base_sptr bvxm_voxel_world::get_grid(unsigned bin_index, unsigned scale_idx, bool use_memory)
368 {
369   assert(scale_idx <= params_->max_scale());
370 
371   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
372   // retrieve map for current bvxm_voxel_type
373   // if no map found create a new one
374   if (grid_map_.find(VOX_T) == grid_map_.end())
375   {
376     // create map
377     std::map<unsigned, std::map<unsigned, bvxm_voxel_grid_base_sptr > > bin_map;
378 
379     // look for existing appearance model grids in the directory
380 
381     std::string storage_directory = params_->model_dir();
382 
383     std::stringstream grid_glob;
384     std::string fname_prefix = bvxm_voxel_traits<VOX_T>::filename_prefix();
385     grid_glob << storage_directory << '/' << fname_prefix << "*.vox";
386 
387     // insert grids
388     for (vul_file_iterator file_it = grid_glob.str().c_str(); file_it; ++file_it)
389     {
390       std::string match_str = file_it.filename();
391       unsigned idx_start = match_str.find("scale") + 6;
392       unsigned idx_end = match_str.find(".vox");
393       std::stringstream idx_str;
394       idx_str << match_str.substr(idx_start,idx_end - idx_start);
395       int scale = -1;
396       idx_str >> scale;
397       match_str.erase(idx_start,idx_end - idx_start);
398 
399       unsigned bin_idx_start = match_str.find("bin") + 4;
400       unsigned bin_idx_end = match_str.find("scale") - 1;
401 
402       std::stringstream bin_idx_str;
403       bin_idx_str <<  match_str.substr(bin_idx_start,bin_idx_end - bin_idx_start);
404       int bin_idx = -1;
405       bin_idx_str >> bin_idx;
406 
407       if (scale < 0 || bin_idx <0) {
408         std::cerr << "error parsing filename " << file_it() << '\n';
409       }
410       else
411       {
412         vgl_vector_3d<unsigned int> grid_size_scale = params_->num_voxels(scale);
413         // create voxel grid and insert into map (use storage_disk_cache if use_memory is set)
414         bvxm_voxel_grid_base_sptr grid;
415         typedef typename bvxm_voxel_traits<VOX_T>::voxel_datatype voxel_datatype;
416         if (use_memory)
417           grid = new bvxm_voxel_grid<voxel_datatype>(grid_size_scale, grid_size_scale.z());
418         else
419           grid = new bvxm_voxel_grid<voxel_datatype>(file_it(), grid_size_scale);
420         std::map<unsigned, bvxm_voxel_grid_base_sptr > scale_map;
421         scale_map.insert(std::make_pair((unsigned)scale, grid));
422 
423         if (bin_map.find(bin_idx)==bin_map.end())
424           bin_map.insert(std::make_pair((unsigned)bin_idx ,scale_map));
425         else
426           bin_map[bin_idx][scale]=grid;
427       }
428     }
429 
430     grid_map_.insert(std::make_pair(VOX_T, bin_map));
431   }
432 
433   // retrieve map containing voxel_grid
434   std::map<unsigned, std::map<unsigned, bvxm_voxel_grid_base_sptr> > voxel_map = grid_map_[VOX_T];
435 
436   /* retrieve voxel_grid for current bin
437   if no grid exists at bin location create one filled with default values*/
438 
439   if (voxel_map.find(bin_index) == voxel_map.end())
440   {
441     std::map<unsigned, bvxm_voxel_grid_base_sptr> scale_map;
442 
443     std::string storage_directory = params_->model_dir();
444 
445     std::stringstream apm_fname;
446     std::string fname_prefix = bvxm_voxel_traits<VOX_T>::filename_prefix();
447     apm_fname << storage_directory << '/' << fname_prefix << "_bin_" << bin_index << "_scale_" << scale_idx  << ".vox";
448 
449     typedef typename bvxm_voxel_traits<VOX_T>::voxel_datatype voxel_datatype;
450     bvxm_voxel_grid<voxel_datatype> * grid;
451     if (use_memory)
452       grid = new bvxm_voxel_grid<voxel_datatype>(grid_size, grid_size.z());
453     else
454       grid = new bvxm_voxel_grid<voxel_datatype>(apm_fname.str(),grid_size);
455     // fill grid with default value
456     if (!grid->initialize_data(bvxm_voxel_traits<VOX_T>::initial_val())) {
457       std::cerr << "error initializing voxel grid\n";
458       return bvxm_voxel_grid_base_sptr(nullptr);
459     }
460 
461     // Insert voxel grid into map
462     // bvxm_voxel_grid_base_sptr grid_sptr = grid;
463     //
464     scale_map.insert(std::make_pair(scale_idx, grid));
465 
466     grid_map_[VOX_T].insert(std::make_pair(bin_index,scale_map));
467   }
468 
469   std::map<unsigned, bvxm_voxel_grid_base_sptr> scale_map = grid_map_[VOX_T][bin_index];
470 
471   if (scale_map.find(scale_idx) == scale_map.end())
472   {
473 #ifdef DEBUG
474     std::cout<<"\n Scale not found ";
475 #endif // DEBUG
476     std::string storage_directory = params_->model_dir();
477 
478     std::stringstream apm_fname;
479     std::string fname_prefix = bvxm_voxel_traits<VOX_T>::filename_prefix();
480     apm_fname << storage_directory << '/' << fname_prefix << "_bin_" << bin_index << "_scale_" << scale_idx  << ".vox";
481 
482     typedef typename bvxm_voxel_traits<VOX_T>::voxel_datatype voxel_datatype;
483     bvxm_voxel_grid<voxel_datatype> *grid;
484     if (use_memory) {
485       grid = new bvxm_voxel_grid<voxel_datatype>(grid_size, grid_size.z());
486     }
487     else
488       grid = new bvxm_voxel_grid<voxel_datatype>(apm_fname.str(), grid_size);
489 
490     // fill grid with default value
491     if (!grid->initialize_data(bvxm_voxel_traits<VOX_T>::initial_val())) {
492       std::cerr << "error initializing voxel grid\n";
493       return bvxm_voxel_grid_base_sptr(nullptr);
494     }
495 
496     // Insert voxel grid into map
497     bvxm_voxel_grid_base_sptr grid_sptr = grid;
498 
499     grid_map_[VOX_T][bin_index].insert(std::make_pair(scale_idx, grid_sptr));
500   }
501   return grid_map_[VOX_T][bin_index][scale_idx];
502 }
503 
504 
505 // Update a voxel grid with data from image/camera pair
506 template <bvxm_voxel_type APM_T>
update(bvxm_image_metadata const & observation,unsigned bin_index,unsigned scale_idx,bool use_momory)507 bool bvxm_voxel_world::update(bvxm_image_metadata const& observation, unsigned bin_index, unsigned scale_idx, bool use_momory)
508 {
509   assert(scale_idx<params_->max_scale());
510   vil_image_view<float> dummy;
511   vil_image_view<bool> mask;
512   return this->update_impl<APM_T>(observation, false, dummy, false, mask, bin_index,scale_idx, use_momory);
513 }
514 
515 
516 // Update a voxel grid with data from image/camera pair and return probability density of pixel values.
517 template<bvxm_voxel_type APM_T>
update(bvxm_image_metadata const & observation,vil_image_view<float> & pix_prob_density,vil_image_view<bool> & mask,unsigned bin_index,unsigned scale_idx,bool use_momory)518 bool bvxm_voxel_world::update(bvxm_image_metadata const& observation,
519                               vil_image_view<float> &pix_prob_density, vil_image_view<bool> &mask, unsigned bin_index, unsigned scale_idx,  bool use_momory)
520 {
521   // check image sizes
522   if ( (observation.img->ni() != pix_prob_density.ni()) || (observation.img->nj() != pix_prob_density.nj()) ) {
523     std::cerr << "error: metadata image size does not match probability image size.\n";
524   }
525   if ( (observation.img->ni() != mask.ni()) || (observation.img->nj() != mask.nj()) ) {
526     std::cerr << "error: metadata image size does not match mask image size.\n";
527   }
528   return this->update_impl<APM_T>(observation, true, pix_prob_density, true, mask, bin_index,  scale_idx, use_momory);
529 }
530 
531 
532 // Update voxel grid with data from image/camera pair and return probability density of pixel values.
533 template<bvxm_voxel_type APM_T>
update_impl(bvxm_image_metadata const & metadata,bool return_prob,vil_image_view<float> & pix_prob_density,bool return_mask,vil_image_view<bool> & mask,unsigned bin_index,unsigned scale_idx,bool use_momory)534 bool bvxm_voxel_world::update_impl(bvxm_image_metadata const& metadata,
535                                    bool return_prob,
536                                    vil_image_view<float> &pix_prob_density,
537                                    bool return_mask,
538                                    vil_image_view<bool> &mask, unsigned bin_index, unsigned scale_idx, bool use_momory)
539 {
540   // datatype for current appearance model
541   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype;
542   // datatype of the pixels that the processor operates on.
543   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;
544   typedef typename bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
545 
546   // the appearance model processor
547   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
548 
549   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
550   ocp_datatype min_vox_prob = params_->min_occupancy_prob();
551   ocp_datatype max_vox_prob = params_->max_occupancy_prob();
552 
553   // compute homographies from voxel planes to image coordinates and vise-versa.
554   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
555   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
556   {
557     vgl_h_matrix_2d<double> Hp2i, Hi2p;
558     for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
559     {
560       compute_plane_image_H(metadata.camera,z,Hp2i,Hi2p,scale_idx);
561       H_plane_to_img.push_back(Hp2i);
562       H_img_to_plane.push_back(Hi2p);
563     }
564   }
565 
566   // convert image to a voxel_slab
567   bvxm_voxel_slab<obs_datatype> image_slab(metadata.img->ni(), metadata.img->nj(), 1);
568   if (!bvxm_util::img_to_slab(metadata.img,image_slab)) {
569     std::cerr << "error converting image to voxel slab of observation type for bvxm_voxel_type " << (int)APM_T << '\n';
570     return false;
571   }
572 
573   // temporary voxel grids to hold preX and PI*visX values
574   bvxm_voxel_grid<float> preX(grid_size);
575   bvxm_voxel_grid<float> PIvisX(grid_size);
576 
577   bvxm_voxel_slab<float> PIPX(grid_size.x(),grid_size.y(),1);
578   bvxm_voxel_slab<float> PXvisX(grid_size.x(), grid_size.y(),1);
579 
580   bvxm_voxel_slab<float> preX_accum(image_slab.nx(),image_slab.ny(),1);
581   bvxm_voxel_slab<float> visX_accum(image_slab.nx(),image_slab.ny(),1);
582   bvxm_voxel_slab<float> img_scratch(image_slab.nx(),image_slab.ny(),1);
583   bvxm_voxel_slab<float> PIPX_img(image_slab.nx(), image_slab.ny(),1);
584   bvxm_voxel_slab<float> PX_img(image_slab.nx(), image_slab.ny(),1);
585   bvxm_voxel_slab<float> mask_slab(image_slab.nx(), image_slab.ny(),1);
586 
587   preX_accum.fill(0.0f);
588   visX_accum.fill(1.0f);
589 
590   // slabs for holding backprojections of visX
591   bvxm_voxel_slab<float> visX(grid_size.x(),grid_size.y(),1);
592 
593   bvxm_voxel_slab<obs_datatype> frame_backproj(grid_size.x(),grid_size.y(),1);
594 
595   std::cout << "Pass 1:" << std::endl;
596 
597   // get occupancy probability grid
598   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx, use_momory);
599   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
600 
601   // get appearance model grid
602   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_idx, use_momory);
603   bvxm_voxel_grid<apm_datatype> *apm_grid  = static_cast<bvxm_voxel_grid<apm_datatype>*>(apm_grid_base.ptr());
604 
605   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
606   typename bvxm_voxel_grid<apm_datatype>::iterator apm_slab_it = apm_grid->begin();
607   typename bvxm_voxel_grid<float>::iterator preX_slab_it = preX.begin();
608   typename bvxm_voxel_grid<float>::iterator PIvisX_slab_it = PIvisX.begin();
609 
610   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it, ++apm_slab_it, ++preX_slab_it, ++PIvisX_slab_it)
611   {
612     std::cout << '.';
613     std::cout.flush();
614 
615     if ( (ocp_slab_it == ocp_grid->end()) || (apm_slab_it == apm_grid->end()) ) {
616       std::cerr << "error: reached end of grid slabs at z = " << z << ".  nz = " << grid_size.z() << '\n';
617       return false;
618     }
619 
620     // backproject image onto voxel plane
621     bvxm_util::warp_slab_bilinear(image_slab, H_plane_to_img[z], frame_backproj);
622 #ifdef BVXM_DEBUG
623     bvxm_util::write_slab_as_image(frame_backproj,"C:/research/registration/output/frame_backproj.tiff");
624 #endif
625     // transform preX to voxel plane for this level
626     bvxm_util::warp_slab_bilinear(preX_accum, H_plane_to_img[z], *preX_slab_it);
627     // transform visX to voxel plane for this level
628     bvxm_util::warp_slab_bilinear(visX_accum, H_plane_to_img[z], visX);
629 
630     // initialize PIvisX with PI(X)
631     bvxm_voxel_slab<float> PI = apm_processor.prob_density(*apm_slab_it, frame_backproj);
632 
633     // now multiply by visX
634     bvxm_util::multiply_slabs(visX,PI,*PIvisX_slab_it);
635 
636     // update appearance model, using PX*visX as the weights
637     bvxm_util::multiply_slabs(visX,*ocp_slab_it,PXvisX);
638     apm_processor.update(*apm_slab_it, frame_backproj, PXvisX);
639 
640     // multiply to get PIPX
641     bvxm_util::multiply_slabs(PI,*ocp_slab_it,PIPX);
642 #ifdef BVXM_DEBUG
643     bvxm_util::write_slab_as_image(PI,"PI.tiff");
644     bvxm_util::write_slab_as_image(*ocp_slab_it,"PX.tiff");
645 #endif
646     // warp PIPX back to image domain
647     bvxm_util::warp_slab_bilinear(PIPX, H_img_to_plane[z], PIPX_img);
648 
649     // multiply PIPX by visX and add to preX_accum
650     typename bvxm_voxel_slab<float>::iterator PIPX_img_it = PIPX_img.begin();
651     typename bvxm_voxel_slab<float>::iterator visX_accum_it = visX_accum.begin();
652     typename bvxm_voxel_slab<float>::iterator preX_accum_it = preX_accum.begin();
653 
654     for (; preX_accum_it != preX_accum.end(); ++preX_accum_it, ++PIPX_img_it, ++visX_accum_it) {
655       *preX_accum_it += (*PIPX_img_it) * (*visX_accum_it);
656     }
657 #ifdef BVXM_DEBUG
658     bvxm_util::write_slab_as_image(PIPX_img,"PIPX_img.tiff");
659     bvxm_util::write_slab_as_image(visX_accum,"visX_accum.tiff");
660     bvxm_util::write_slab_as_image(preX_accum,"preX_accum.tiff");
661 #endif
662     // scale and offset voxel probabilities to get (1-P(X))
663     // transform (1-P(X)) to image plane to accumulate visX for next level
664     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[z], PX_img);
665 
666     if (return_mask) {
667       bvxm_util::add_slabs(PX_img,mask_slab,mask_slab);
668     }
669 
670     // note: doing scale and offset in image domain so invalid pixels become 1.0 and don't affect visX
671     typename bvxm_voxel_slab<float>::iterator PX_img_it = PX_img.begin();
672     visX_accum_it = visX_accum.begin();
673     for (; visX_accum_it != visX_accum.end(); ++visX_accum_it, ++PX_img_it) {
674       *visX_accum_it *= (1 - *PX_img_it);
675     }
676   }
677   // now traverse a second time, computing new P(X) along the way.
678 
679   bvxm_voxel_slab<float> preX_accum_vox(grid_size.x(),grid_size.y(),1);
680   bvxm_voxel_slab<float> visX_accum_vox(grid_size.x(),grid_size.y(),1);
681 #ifdef BVXM_DEBUG
682   bvxm_util::write_slab_as_image(visX_accum,"visX_accum.tiff");
683   bvxm_util::write_slab_as_image(preX_accum,"preX_accum.tiff");
684 #endif
685   std::cout << "\nPass 2:" << std::endl;
686   PIvisX_slab_it = PIvisX.begin();
687   preX_slab_it = preX.begin();
688   typename bvxm_voxel_grid<ocp_datatype>::iterator ocp_slab_it2 = ocp_grid->begin();
689   for (unsigned z = 0; z < (unsigned)grid_size.z(); ++z, ++PIvisX_slab_it, ++preX_slab_it, ++ocp_slab_it2)
690   {
691     std::cout << '.';
692     std::cout.flush();
693     // transform preX_sum to current level
694     bvxm_util::warp_slab_bilinear(preX_accum, H_plane_to_img[z], preX_accum_vox);
695 
696     // transform visX_sum to current level
697     bvxm_util::warp_slab_bilinear(visX_accum, H_plane_to_img[z], visX_accum_vox);
698 
699     const float preX_sum_thresh = 0.01f;
700 
701     typename bvxm_voxel_slab<float>::const_iterator preX_it = preX_slab_it->begin(),
702                                                     PIvisX_it = PIvisX_slab_it->begin(),
703                                                     preX_sum_it = preX_accum_vox.begin(),
704                                                     visX_sum_it = visX_accum_vox.begin();
705     typename bvxm_voxel_slab<float>::iterator PX_it = ocp_slab_it2->begin();
706 
707     for (; PX_it != ocp_slab_it2->end(); ++PX_it, ++preX_it, ++PIvisX_it, ++preX_sum_it, ++visX_sum_it) {
708       // if preX_sum is zero at the voxel, no ray passed through the voxel (out of image)
709       if (*preX_sum_it > preX_sum_thresh) {
710         float multiplier = (*PIvisX_it + *preX_it) / *preX_sum_it;
711         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.
712         *PX_it *= multiplier * ray_norm;
713       }
714       if (*PX_it < min_vox_prob)
715         *PX_it = min_vox_prob;
716       if (*PX_it > max_vox_prob)
717         *PX_it = max_vox_prob;
718     }
719   }
720   std::cout << "\ndone." << std::endl;
721 
722   if (return_prob) {
723     // fill pixel_probabilities with preX_accum
724     typename vil_image_view<float>::iterator pix_prob_it = pix_prob_density.begin();
725     typename bvxm_voxel_slab<float>::const_iterator preX_accum_it = preX_accum.begin();
726     typename bvxm_voxel_slab<float>::const_iterator visX_accum_it = visX_accum.begin();
727     for (; pix_prob_it != pix_prob_density.end(); ++pix_prob_it, ++preX_accum_it, ++visX_accum_it) {
728       *pix_prob_it = *preX_accum_it / (1.0f - *visX_accum_it);
729     }
730   }
731 
732   if (return_mask) {
733     // fill mask values
734     typename vil_image_view<bool>::iterator mask_it = mask.begin();
735     typename bvxm_voxel_slab<float>::const_iterator mask_slab_it = mask_slab.begin();
736 
737     for (; mask_it != mask.end(); ++mask_it, ++mask_slab_it) {
738       *mask_it = (*mask_slab_it > 0);
739     }
740   }
741 
742   // increment the observation count
743   this->increment_observations<APM_T>(bin_index);
744 
745   return true;
746 }
747 // Update a voxel grid with data from lidar/camera pair
748 template<bvxm_voxel_type APM_T>
update_lidar(bvxm_image_metadata const & observation,unsigned scale)749 bool bvxm_voxel_world::update_lidar(bvxm_image_metadata const& observation, unsigned scale)
750 {
751   vil_image_view<float> dummy;
752   vil_image_view<bool> mask;
753   return this->update_lidar_impl<APM_T>(observation, false, dummy, false, mask, scale);
754 }
755 
756 
757 // Update a voxel grid with data from lidar/camera pair and return probability density of pixel values.
758 template<bvxm_voxel_type APM_T>
update_lidar(bvxm_image_metadata const & observation,vil_image_view<float> & pix_prob_density,vil_image_view<bool> & mask,unsigned scale)759 bool bvxm_voxel_world::update_lidar(bvxm_image_metadata const& observation,
760                                     vil_image_view<float> &pix_prob_density, vil_image_view<bool> &mask, unsigned scale)
761 {
762   // check image sizes
763   if ( (observation.img->ni() != pix_prob_density.ni()) || (observation.img->nj() != pix_prob_density.nj()) ) {
764     std::cerr << "error: metadata image size does not match probability image size.\n";
765   }
766   if ( (observation.img->ni() != mask.ni()) || (observation.img->nj() != mask.nj()) ) {
767     std::cerr << "error: metadata image size does not match mask image size.\n";
768   }
769   return this->update_lidar_impl<APM_T>(observation, true, pix_prob_density, true, mask, scale);
770 }
771 
772 // Update voxel grid with data from LIDAR image/camera pair and return probability density of pixel values.
773 
774 template<bvxm_voxel_type APM_T>
update_lidar_impl(bvxm_image_metadata const & metadata,bool return_prob,vil_image_view<float> & pix_prob_density,bool return_mask,vil_image_view<bool> & mask,unsigned scale)775 bool bvxm_voxel_world::update_lidar_impl(bvxm_image_metadata const& metadata,
776                                          bool return_prob,
777                                          vil_image_view<float> &pix_prob_density,
778                                          bool return_mask,
779                                          vil_image_view<bool> &mask, unsigned scale)
780 {
781   typedef typename bvxm_voxel_traits<LIDAR>::voxel_datatype obs_datatype;
782   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype ocp_datatype;
783 
784   vpgl_camera_double_sptr dummy_cam = metadata.camera;
785   double lidar_pixel_size = 1.0;
786   if (dummy_cam->type_name()=="vpgl_geo_camera") {
787     vpgl_geo_camera* lcam = static_cast<vpgl_geo_camera*>(dummy_cam.ptr());
788     std::cout << "Lidar Camera\n" << *lcam << std::endl;
789     lidar_pixel_size = lcam->pixel_spacing();
790   }
791   // typedef bvxm_voxel_traits<LIDAR>::lidar_processor bvxm_lidar_processor;
792   bvxm_lidar_processor lidar_processor(10);
793 
794   // parameters
795   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale);
796   ocp_datatype min_vox_prob = params_->min_occupancy_prob();
797   ocp_datatype max_vox_prob = params_->max_occupancy_prob();
798 
799   // compute homographies from voxel planes to image coordinates and vise-versa.
800   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
801   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
802   {
803     vgl_h_matrix_2d<double> Hp2i, Hi2p;
804     for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
805     {
806       compute_plane_image_H(metadata.camera,z,Hp2i,Hi2p, scale);
807       H_plane_to_img.push_back(Hp2i);
808       H_img_to_plane.push_back(Hi2p);
809     }
810   }
811 
812   // convert image to a voxel_slab
813   bvxm_voxel_slab<obs_datatype> image_slab(metadata.img->ni(), metadata.img->nj(), 1);
814   if (!bvxm_util::img_to_slab(metadata.img,image_slab)) {
815     std::cerr << "error converting image to voxel slab of observation type for bvxm_voxel_type: LIDAR\n";
816     return false;
817   }
818 
819 #ifdef DEBUG
820   vil_save(*metadata.img, "lidar_img.tiff");
821   bvxm_util::write_slab_as_image(image_slab,"lidar_slab.tiff");
822 #endif
823 
824 
825   // temporary voxel grids to hold preX and PL*visX values
826   bvxm_voxel_grid<float> preX(grid_size);
827   bvxm_voxel_grid<float> PLvisX(grid_size);
828 
829   bvxm_voxel_slab<float> PLPX(grid_size.x(),grid_size.y(),1);
830   bvxm_voxel_slab<float> PXvisX(grid_size.x(), grid_size.y(),1);
831 
832   bvxm_voxel_slab<float> preX_accum(image_slab.nx(),image_slab.ny(),1);
833   bvxm_voxel_slab<float> visX_accum(image_slab.nx(),image_slab.ny(),1);
834   bvxm_voxel_slab<float> img_scratch(image_slab.nx(),image_slab.ny(),1);
835   bvxm_voxel_slab<float> PLPX_img(image_slab.nx(), image_slab.ny(),1);
836   bvxm_voxel_slab<float> PX_img(image_slab.nx(), image_slab.ny(),1);
837   bvxm_voxel_slab<float> mask_slab(image_slab.nx(), image_slab.ny(),1);
838 
839   preX_accum.fill(0.0f);
840   visX_accum.fill(1.0f);
841 
842   // slabs for holding backprojections of visX
843   bvxm_voxel_slab<float> visX(grid_size.x(),grid_size.y(),1);
844 
845   bvxm_voxel_slab<obs_datatype> frame_backproj(grid_size.x(),grid_size.y(),1);
846 
847   std::cout << "Pass 1:" << std::endl;
848 
849   // get occupancy probability grid
850   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<APM_T>(0,  scale);
851   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
852 
853   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
854   typename bvxm_voxel_grid<float>::iterator preX_slab_it = preX.begin();
855   typename bvxm_voxel_grid<float>::iterator PLvisX_slab_it = PLvisX.begin();
856 #ifdef DEBUG
857   double p_max = 0.0;
858 #endif
859   // The default values for the LIDAR Gaussian error ellipsoid
860   // X-Y standard deviation set to 1/Sqrt(2) pixel spacing (arbitrary)
861   // standard deviation in z measured from actual Buckeye data (0.03 m)
862   double pix_sd = lidar_pixel_size*vnl_math::sqrt1_2;
863   float xy_var = static_cast<float>(pix_sd*pix_sd);
864   // The vector of spherical Gaussian variances
865   vnl_vector_fixed<float,3> vars(xy_var, xy_var, 0.0009f);
866 
867   for (unsigned k_idx=0; k_idx<(unsigned)grid_size.z(); ++k_idx, ++ocp_slab_it, ++preX_slab_it, ++PLvisX_slab_it)
868   {
869     std::cout << k_idx << std::endl;
870 
871     // backproject image onto voxel plane
872     bvxm_util::warp_slab_bilinear(image_slab, H_plane_to_img[k_idx], frame_backproj);
873 
874 #ifdef DEBUG
875     std::stringstream ss;
876     ss << "./frame_backproj_" << k_idx <<".tiff";
877     bvxm_util::write_slab_as_image(frame_backproj,ss.str());
878 #endif
879     // transform preX to voxel plane for this level
880     bvxm_util::warp_slab_bilinear(preX_accum, H_plane_to_img[k_idx], *preX_slab_it);
881     // transform visX to voxel plane for this level
882     bvxm_util::warp_slab_bilinear(visX_accum, H_plane_to_img[k_idx], visX);
883 
884     // initialize PLvisX with PL(X)
885 
886     bvxm_voxel_slab<float> PL(frame_backproj.nx(), frame_backproj.ny(), frame_backproj.nz());
887     PL.fill(0.0);
888 
889     vil_image_view_base_sptr lidar = metadata.img;
890 
891     vgl_point_3d<float> local_xyz = voxel_index_to_xyz(0, 0, k_idx,scale);
892 
893     for (unsigned i_idx=0; i_idx<frame_backproj.nx(); i_idx++)
894     {
895       for (unsigned j_idx=0; j_idx<frame_backproj.ny(); j_idx++)
896       {
897         std::vector<vgl_homg_point_2d<double> > vp(4);
898         int i = i_idx+1;
899         int j = j_idx-1;
900         vp[0] = vgl_homg_point_2d<double>(i, j);
901         vp[1] = vgl_homg_point_2d<double>(i+1, j);
902         vp[2] = vgl_homg_point_2d<double>(i, j+1);
903         vp[3] = vgl_homg_point_2d<double>(i+1, j+1);
904 
905         vgl_h_matrix_2d<double> h_max = H_plane_to_img[k_idx];
906         vgl_h_matrix_2d<double> h_min;
907         if (k_idx == (unsigned)grid_size.z()-1)
908           h_min = H_plane_to_img[k_idx];
909         else
910           h_min = H_plane_to_img[k_idx+1];
911         vgl_box_2d<double> lidar_roi;
912 
913         for (unsigned i=0; i<4; i++) {
914           vgl_homg_point_2d<double> img_pos_h_min = h_min*vp[i];
915           vgl_point_2d<double> img_pos_min(img_pos_h_min);
916           lidar_roi.add(img_pos_min);
917         }
918 
919         float p = lidar_processor.prob_density(lidar, local_xyz.z(), vars, lidar_roi, params_->voxel_length(scale));
920 
921 #ifdef DEBUG
922         if (p > p_max) {
923           p_max = p;
924           std::cout << "-------------max_p=" << p << std::endl;
925         }
926         if (p >1.0) {
927           std::cout << "ERROR!" << std::endl;
928 //       /   p=max_vox_prob;
929         }
930 #endif
931         PL(i_idx, j_idx) = p;
932       }
933     }
934 
935     // now multiply by visX
936     bvxm_util::multiply_slabs(visX,PL,*PLvisX_slab_it);
937 
938     // Is this needed? - FIXME
939     // update appearance model, using PX*visX as the weights
940     bvxm_util::multiply_slabs(*ocp_slab_it,visX,PXvisX);
941 
942     // multiply to get PLPX
943     bvxm_util::multiply_slabs(*ocp_slab_it,PL,PLPX);
944 #ifdef DEBUG
945     std::stringstream ss1, ss2, ss3;
946     ss1 << "PL_" << k_idx <<".tiff";
947     ss2 <<"PX_" << k_idx <<".tiff";
948     ss3 << "PL_P" << k_idx <<".tiff";
949     bvxm_util::write_slab_as_image(PL,ss1.str());
950     bvxm_util::write_slab_as_image(*ocp_slab_it,ss2.str());
951 #if 0
952     bvxm_util::write_slab_as_image(PL_p,ss3.str());
953 #endif // 0
954 #endif // DEBUG
955     // warp PLPX back to image domain
956     bvxm_util::warp_slab_bilinear(PLPX, H_img_to_plane[k_idx], PLPX_img);
957 
958     // multiply PLPX by visX and add to preX_accum
959     typename bvxm_voxel_slab<float>::iterator PLPX_img_it = PLPX_img.begin();
960     typename bvxm_voxel_slab<float>::iterator visX_accum_it = visX_accum.begin();
961     typename bvxm_voxel_slab<float>::iterator preX_accum_it = preX_accum.begin();
962 
963     for (; preX_accum_it != preX_accum.end(); ++preX_accum_it, ++PLPX_img_it, ++visX_accum_it) {
964       *preX_accum_it += (*PLPX_img_it) * (*visX_accum_it);
965     }
966 #ifdef DEBUG
967     std::stringstream plpx, vis, prex;
968     plpx << "PLPX_" << k_idx <<".tiff";
969     vis  << "visX_" << k_idx <<".tiff";
970     prex << "preX_" << k_idx <<".tiff";
971     bvxm_util::write_slab_as_image(PLPX_img,plpx.str());
972     bvxm_util::write_slab_as_image(visX_accum,vis.str());
973     bvxm_util::write_slab_as_image(preX_accum,prex.str());
974 #endif
975     // scale and offset voxel probabilities to get (1-P(X))
976     // transform (1-P(X)) to image plane to accumulate visX for next level
977     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[k_idx], PX_img);
978 
979     if (return_mask) {
980       bvxm_util::add_slabs(PX_img,mask_slab,mask_slab);
981     }
982 
983     // note: doing scale and offset in image domain so invalid pixels become 1.0 and don't affect visX
984     typename bvxm_voxel_slab<float>::iterator PX_img_it = PX_img.begin();
985     visX_accum_it = visX_accum.begin();
986     for (; visX_accum_it != visX_accum.end(); ++visX_accum_it, ++PX_img_it) {
987 #if 0
988       ocp_datatype o = *PX_img_it;
989 #endif // 0
990       *visX_accum_it *= 1 - *PX_img_it; //-(o - 1);
991     }
992   }
993   // now traverse a second time, computing new P(X) along the way.
994 
995   bvxm_voxel_slab<float> preX_accum_vox(grid_size.x(),grid_size.y(),1);
996   bvxm_voxel_slab<float> visX_accum_vox(grid_size.x(),grid_size.y(),1);
997 
998 #ifdef DEBUG
999   std::stringstream vis2, prex2;
1000   vis2  << "visX2_.tiff";
1001   prex2 << "preX2_.tiff";
1002   bvxm_util::write_slab_as_image(visX_accum,vis2.str());
1003   bvxm_util::write_slab_as_image(preX_accum,prex2.str());
1004 #endif
1005 
1006   std::cout << "\nPass 2:" << std::endl;
1007   PLvisX_slab_it = PLvisX.begin();
1008   preX_slab_it = preX.begin();
1009   typename bvxm_voxel_grid<ocp_datatype>::iterator ocp_slab_it2 = ocp_grid->begin();
1010   for (unsigned k_idx = 0; k_idx < (unsigned)grid_size.z(); ++k_idx, ++PLvisX_slab_it, ++preX_slab_it, ++ocp_slab_it2)
1011   {
1012     std::cout << '.';
1013 
1014     // transform preX_sum to current level
1015     bvxm_util::warp_slab_bilinear(preX_accum, H_plane_to_img[k_idx], preX_accum_vox);
1016 
1017     // transform visX_sum to current level
1018     bvxm_util::warp_slab_bilinear(visX_accum, H_plane_to_img[k_idx], visX_accum_vox);
1019 
1020     const float preX_sum_thresh = 0.0f;
1021 
1022     typename bvxm_voxel_slab<float>::const_iterator preX_it = preX_slab_it->begin(),
1023                                                     PLvisX_it = PLvisX_slab_it->begin(),
1024                                                     preX_sum_it = preX_accum_vox.begin(),
1025                                                     visX_sum_it = visX_accum_vox.begin();
1026     typename bvxm_voxel_slab<ocp_datatype>::iterator PX_it = ocp_slab_it2->begin();
1027 
1028     for (; PX_it != ocp_slab_it2->end(); ++PX_it, ++preX_it, ++PLvisX_it, ++preX_sum_it, ++visX_sum_it) {
1029       // if preX_sum is zero at the voxel, no ray passed through the voxel (out of image)
1030       if (*preX_sum_it > preX_sum_thresh) {
1031         float multiplier = (*PLvisX_it + *preX_it) / *preX_sum_it;
1032 #if 0 // leave out normalization for now - results seem a little better without it.  -DEC
1033         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.
1034 #endif // 0
1035         *PX_it *= multiplier; // * ray_norm;
1036       }
1037       if (*PX_it < min_vox_prob)
1038         *PX_it = min_vox_prob;
1039       if (*PX_it > max_vox_prob)
1040         *PX_it = max_vox_prob;
1041     }
1042   }
1043   std::cout << "\ndone." << std::endl;
1044 
1045   if (return_prob) {
1046     // fill pixel_probabilities with preX_accum
1047     typename vil_image_view<float>::iterator pix_prob_it = pix_prob_density.begin();
1048     typename bvxm_voxel_slab<float>::const_iterator preX_accum_it = preX_accum.begin();
1049 
1050     for (; pix_prob_it != pix_prob_density.end(); ++pix_prob_it, ++preX_accum_it) {
1051       *pix_prob_it = *preX_accum_it;
1052     }
1053   }
1054 
1055   if (return_mask) {
1056     // fill mask values
1057     typename vil_image_view<bool>::iterator mask_it = mask.begin();
1058     typename bvxm_voxel_slab<float>::const_iterator mask_slab_it = mask_slab.begin();
1059 
1060     for (; mask_it != mask.end(); ++mask_it, ++mask_slab_it) {
1061       *mask_it = (*mask_slab_it > 0);
1062     }
1063   }
1064 
1065 #ifdef DEBUG
1066   bvxm_util::write_slab_as_image(preX_accum,"prob.tiff");
1067 #endif
1068 
1069 #if 0 // Check:
1070   // increment the observation count
1071   this->increment_observations<APM_T>(bin_index);
1072 #endif // 0
1073 
1074   return true;
1075 }
1076 
1077 template<bvxm_voxel_type APM_T>
update_point_cloud(std::vector<vgl_point_3d<float>> & points)1078 bool bvxm_voxel_world::update_point_cloud(std::vector<vgl_point_3d<float> > & points)
1079 {
1080   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype ocp_datatype;
1081   // parameters
1082   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels();
1083 
1084   std::cout<<"Get Grid"<<std::endl;
1085   // get occupancy probability grid
1086   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<APM_T>(0,0);
1087   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
1088   typename bvxm_voxel_grid<ocp_datatype>::iterator ocp_slab_it = ocp_grid->begin();
1089   //: initializing the grid with 1
1090   for (unsigned k_idx=0; k_idx<(unsigned)grid_size.z(); ++k_idx, ++ocp_slab_it)
1091     ocp_slab_it->fill(ocp_datatype(1.0f));
1092 
1093   float xy_spacing=params_->voxel_length();
1094   vnl_vector_fixed<float,3> cov(xy_spacing,xy_spacing,xy_spacing/2);
1095 
1096   unsigned int kernel_size=2;
1097   unsigned int z=0;
1098 
1099   unsigned minz=z<kernel_size?0:z-kernel_size;
1100   unsigned maxz=z+kernel_size+1>grid_size.z()?grid_size.z()-1:z+kernel_size;
1101   ocp_slab_it=ocp_grid->slab_iterator(minz,maxz-minz+1);
1102 
1103   for (unsigned i=0;i<points.size();++i)
1104   {
1105     vgl_vector_3d<float> index=(points[i]-params_->corner())/params_->voxel_length();
1106 
1107     unsigned k=(unsigned)std::floor(grid_size.z()-1.f-index.z());
1108     unsigned x=(unsigned)std::floor(index.x());
1109     unsigned y=(unsigned)std::floor(index.y());
1110 
1111     if (k<grid_size.z() && x<grid_size.x() && y<grid_size.y()  )
1112     {
1113       minz=k<kernel_size?0:k-kernel_size;
1114       maxz=k+kernel_size+1>grid_size.z()?grid_size.z()-1:k+kernel_size;
1115 
1116       unsigned minx=x<kernel_size?0:x-kernel_size;
1117       unsigned miny=y<kernel_size?0:y-kernel_size;
1118       unsigned maxx=x+kernel_size+1>grid_size.x()?grid_size.x()-1:x+kernel_size;
1119       unsigned maxy=y+kernel_size+1>grid_size.y()?grid_size.y()-1:y+kernel_size;
1120       //: go to the next slab
1121       if (z<k)
1122       {
1123         z=k;
1124         ocp_slab_it.write_slab();
1125         ocp_slab_it=ocp_grid->slab_iterator(minz,maxz-minz+1);
1126       }
1127 
1128       vnl_vector_fixed<float,3> m(index.x(), index.y(), grid_size.z() -1.f -index.z());
1129       bsta_gauss_if3 gauss(m, cov);
1130       for (unsigned ind_k=minz;ind_k<=maxz;ind_k++)
1131       {
1132         for (unsigned ind_i=minx;ind_i<=maxx;ind_i++)
1133         {
1134           for (unsigned ind_j=miny;ind_j<=maxy;ind_j++)
1135           {
1136             vnl_vector_fixed<float,3> min_vec((float)ind_i-0.5f,(float)ind_j-0.5f,(float)ind_k-0.5f);
1137             vnl_vector_fixed<float,3> max_vec((float)ind_i+0.5f,(float)ind_j+0.5f,(float)ind_k+0.5f);
1138 
1139             float p1 = gauss.probability(min_vec,max_vec);
1140             (*ocp_slab_it)(ind_i,ind_j,ind_k-minz) *= 1-p1;
1141           }
1142         }
1143       }
1144     }
1145     if (i%10000==0)
1146       std::cout<<".";
1147   }
1148   ocp_slab_it = ocp_grid->begin();
1149   //: initializing the grid with 1
1150   for (unsigned k_idx=0; k_idx<(unsigned)grid_size.z(); ++k_idx, ++ocp_slab_it)
1151   {
1152     typename bvxm_voxel_slab<ocp_datatype>::iterator slab_it = ocp_slab_it->begin();
1153     for (; slab_it != ocp_slab_it->end(); ++slab_it) {
1154       *slab_it = ocp_datatype(1)-*slab_it;
1155     }
1156   }
1157   return true;
1158 }
1159 
1160 template<bvxm_voxel_type APM_T>
expected_image(bvxm_image_metadata const & camera,vil_image_view_base_sptr & expected,vil_image_view<float> & mask,unsigned bin_index,unsigned scale_idx)1161 bool bvxm_voxel_world::expected_image(bvxm_image_metadata const& camera,
1162                                       vil_image_view_base_sptr &expected,
1163                                       vil_image_view<float> &mask,
1164                                       unsigned bin_index,unsigned scale_idx)
1165 {
1166   // threshold used to create mask
1167   float min_PXvisX_accum = 0.01f;
1168   float min_PX = 0.001f;
1169 
1170   // datatype for current appearance model
1171   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype;
1172   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;
1173   typedef typename bvxm_voxel_traits<APM_T>::obs_mathtype obs_mathtype;
1174   typedef typename bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
1175 
1176   // the appearance model processor
1177   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
1178 
1179   // extract global parameters
1180   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
1181 
1182   // compute homographies from voxel planes to image coordinates and vise-versa.
1183   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
1184   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
1185   for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
1186   {
1187     vgl_h_matrix_2d<double> Hp2i, Hi2p;
1188     compute_plane_image_H(camera.camera,z,Hp2i,Hi2p,scale_idx);
1189     H_plane_to_img.push_back(Hp2i);
1190     H_img_to_plane.push_back(Hi2p);
1191   }
1192 
1193   // allocate some images
1194   bvxm_voxel_slab<obs_datatype> expected_slab(expected->ni(),expected->nj(),1);
1195   bvxm_voxel_slab<obs_datatype> expected_slice_img(expected->ni(), expected->nj(),1);
1196   bvxm_voxel_slab<float> slice_ocp_img(expected->ni(),expected->nj(),1);
1197   bvxm_voxel_slab<float> PXvisX_accum(expected->ni(), expected->nj(),1);
1198   bvxm_voxel_slab<float> visX_accum(expected->ni(), expected->nj(),1);
1199 
1200   PXvisX_accum.fill(0.0f);
1201   visX_accum.fill(1.0f);
1202   obs_datatype data(obs_mathtype(0));
1203   expected_slab.fill(data);
1204 
1205   // get occupancy probability grid
1206   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
1207   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
1208 
1209   // get appearance model grid
1210   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_idx);
1211   bvxm_voxel_grid<apm_datatype> *apm_grid  = static_cast<bvxm_voxel_grid<apm_datatype>*>(apm_grid_base.ptr());
1212 
1213   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
1214   typename bvxm_voxel_grid<apm_datatype>::const_iterator apm_slab_it = apm_grid->begin();
1215 
1216   std::cout << "Generating Expected Image:" << std::endl;
1217   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it, ++apm_slab_it)
1218   {
1219     std::cout << '.';
1220 
1221     // get expected observation
1222     bvxm_voxel_slab<obs_datatype> expected_slice = apm_processor.expected_color(*apm_slab_it);
1223     // and project to image plane
1224     bvxm_util::warp_slab_bilinear(expected_slice, H_img_to_plane[z], expected_slice_img);
1225 
1226     // warp slice_probability to image plane
1227     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[z], slice_ocp_img);
1228 
1229     typename bvxm_voxel_slab<obs_datatype>::const_iterator I_it = expected_slice_img.begin();
1230     typename bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_ocp_img.begin();
1231     typename bvxm_voxel_slab<obs_datatype>::iterator out_it = expected_slab.begin();
1232     typename bvxm_voxel_slab<float>::iterator visX_it = visX_accum.begin(), W_it = PXvisX_accum.begin();
1233 
1234     for (; out_it != expected_slab.end(); ++I_it, ++PX_it, ++out_it, ++visX_it, ++W_it) {
1235       if (*PX_it > min_PX) {
1236         float w = *PX_it * *visX_it;
1237         *W_it += w;
1238         *out_it += *I_it * w;
1239         // update visX for next level
1240         *visX_it *= (1.0f - *PX_it);
1241       }
1242     }
1243   }
1244   std::cout << std::endl;
1245 
1246   typename bvxm_voxel_slab<obs_datatype>::iterator out_it = expected_slab.begin();
1247   typename bvxm_voxel_slab<float>::const_iterator W_it = PXvisX_accum.begin();
1248   // normalize expected image by weight sum
1249   for (; out_it != expected_slab.end(); ++out_it, ++W_it) {
1250     if (*W_it > 0)
1251       *out_it /= *W_it;
1252   }
1253 
1254   // convert back to vil_image_view
1255   bvxm_util::slab_to_img(expected_slab, expected);
1256 
1257   // create mask of bottom plane of voxel grid
1258   bvxm_voxel_slab<ocp_datatype> bottom_slab_mask(expected->ni(),expected->nj(),1);
1259   bvxm_voxel_slab<ocp_datatype> ones(grid_size.x(),grid_size.y(),1);
1260   ones.fill(1.0f);
1261   bvxm_util::warp_slab_bilinear(ones,H_img_to_plane[grid_size.z()-1],bottom_slab_mask);
1262 
1263   // generate expected view mask
1264   typename vil_image_view<float>::iterator mask_img_it = mask.begin();
1265   typename bvxm_voxel_slab<float>::iterator PXvisX_accum_it = PXvisX_accum.begin();
1266   typename bvxm_voxel_slab<float>::iterator bottom_slab_mask_it = bottom_slab_mask.begin();
1267   mask.fill(0.0f);
1268   for (; mask_img_it != mask.end(); ++mask_img_it, ++PXvisX_accum_it, ++bottom_slab_mask_it) {
1269     if ( (*PXvisX_accum_it > min_PXvisX_accum) && (*bottom_slab_mask_it > 0) )
1270       *mask_img_it = 1.0f;
1271   }
1272 
1273   return true;
1274 }
1275 
1276 template<bvxm_voxel_type APM_T>
inv_pixel_range_probability(bvxm_image_metadata const & observation,vil_image_view<float> & inv_prob,unsigned bin_index,unsigned scale_idx,float pixel_range)1277 bool bvxm_voxel_world::inv_pixel_range_probability(bvxm_image_metadata const& observation,
1278                                                    vil_image_view<float> &inv_prob,
1279                                                    unsigned bin_index,unsigned scale_idx, float pixel_range)
1280 {
1281   // datatype for current appearance model
1282   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype;
1283   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;
1284   typedef typename bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
1285 
1286   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
1287 
1288   bvxm_world_params_sptr params = params_;
1289 
1290   // check image sizes
1291   if ( (observation.img->ni() != inv_prob.ni()) || (observation.img->nj() != inv_prob.nj()) ) {
1292     std::cerr << "error: observation image size does not match input image size.\n";
1293   }
1294 
1295   vgl_vector_3d<unsigned int> grid_size = params->num_voxels(scale_idx);
1296 
1297   // compute homographies from voxel planes to image coordinates and vise-versa.
1298   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
1299   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
1300   {
1301     vgl_h_matrix_2d<double> Hp2i, Hi2p;
1302     for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
1303     {
1304       compute_plane_image_H(observation.camera,z,Hp2i,Hi2p,scale_idx);
1305       H_plane_to_img.push_back(Hp2i);
1306       H_img_to_plane.push_back(Hi2p);
1307     }
1308   }
1309 
1310   // convert image to a voxel_slab
1311   bvxm_voxel_slab<obs_datatype> image_slab(observation.img->ni(), observation.img->nj(), 1);
1312   bvxm_util::img_to_slab(observation.img,image_slab);
1313 
1314   bvxm_voxel_slab<float> preX, visX, slice_prob, PIPX;
1315 
1316   bvxm_voxel_slab<obs_datatype> frame_backproj(grid_size.x(),grid_size.y(),1);
1317 
1318   bvxm_voxel_slab<float> visX_accum(observation.img->ni(),observation.img->nj(),1);
1319   visX_accum.fill(1.0f);
1320   inv_prob.fill(1.0f);
1321   bvxm_voxel_slab<float> PIPX_image(observation.img->ni(),observation.img->nj(),1);
1322   bvxm_voxel_slab<float> slice_prob_image(observation.img->ni(),observation.img->nj(),1);
1323 
1324   std::cout << "Computing inverse probability of frame +- range: ";
1325 
1326   // get occupancy probability grid
1327   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
1328   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>* >(ocp_grid_base.ptr());
1329 
1330   // get appearance model grid
1331   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_idx);
1332   bvxm_voxel_grid<apm_datatype> *apm_grid  = static_cast<bvxm_voxel_grid<apm_datatype>* >(apm_grid_base.ptr());
1333 
1334   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
1335   typename bvxm_voxel_grid<apm_datatype>::iterator apm_slab_it = apm_grid->begin();
1336 
1337   for (int z=0; z<grid_size.z(); ++z, ++ocp_slab_it, ++apm_slab_it)
1338   {
1339     std::cout << '.';
1340 
1341     if ( (ocp_slab_it == ocp_grid->end()) || (apm_slab_it == apm_grid->end()) ) {
1342       std::cerr << "error: reached end of grid slabs at z = " << z << ".  nz = " << grid_size.z() << '\n';
1343       return false;
1344     }
1345 
1346     // backproject image onto voxel plane
1347     bvxm_util::warp_slab_bilinear(image_slab, H_plane_to_img[z], frame_backproj);
1348 
1349     // create min and max observations: for now, just use same range for every pixel
1350     bvxm_voxel_slab<obs_datatype> obs_min(frame_backproj.nx(),frame_backproj.ny(),frame_backproj.nz());
1351     bvxm_voxel_slab<obs_datatype> obs_max(frame_backproj.ny(),frame_backproj.ny(),frame_backproj.nz());
1352     typename bvxm_voxel_slab<obs_datatype>::const_iterator backproj_it = frame_backproj.begin();
1353     typename bvxm_voxel_slab<obs_datatype>::iterator max_it = obs_max.begin(), min_it = obs_min.begin();
1354     for (; backproj_it != frame_backproj.end(); ++backproj_it, ++max_it, ++min_it) {
1355       *max_it = *backproj_it + pixel_range;
1356       *min_it = *backproj_it - pixel_range;
1357     }
1358 
1359     // transform visX to voxel plane for this level
1360     bvxm_util::warp_slab_bilinear(visX_accum, H_plane_to_img[z], visX);
1361 
1362     // initialize PIPX with PI
1363     PIPX = apm_processor.prob_range(*apm_slab_it, obs_min, obs_max);
1364     // and multiply with PX
1365     bvxm_util::multiply_slabs(*ocp_slab_it,PIPX,PIPX);
1366 
1367     // now transform to image plane
1368     bvxm_util::warp_slab_bilinear(PIPX, H_img_to_plane[z], PIPX_image);
1369     bvxm_util::warp_slab_bilinear(*ocp_slab_it,H_img_to_plane[z], slice_prob_image);
1370 
1371     // update pixel probabilities for this level
1372     typename bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_prob_image.begin();
1373     typename bvxm_voxel_slab<float>::const_iterator PIPX_it = PIPX_image.begin();
1374     typename bvxm_voxel_slab<float>::iterator visX_it = visX_accum.begin(), prob_it = inv_prob.begin();
1375     for (; prob_it != inv_prob.end(); ++prob_it, ++PIPX_it, ++visX_it, ++PX_it) {
1376       float PIPXvisX = *PIPX_it * *visX_it;
1377       *prob_it *= (1 - PIPXvisX);
1378       *visX_it *= (1 - *PX_it);
1379     }
1380   }
1381   std::cout << std::endl;
1382 
1383   return true;
1384 }
1385 
1386 template<bvxm_voxel_type APM_T>
pixel_probability_density(bvxm_image_metadata const & observation,vil_image_view<float> & pixel_probability,vil_image_view<bool> & mask,unsigned bin_index,unsigned scale_idx)1387 bool bvxm_voxel_world::pixel_probability_density(bvxm_image_metadata const& observation,
1388                                                  vil_image_view<float> &pixel_probability, vil_image_view<bool> &mask,
1389                                                  unsigned bin_index,unsigned scale_idx)
1390 {
1391   // datatype for current appearance model
1392   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype;
1393   typedef typename bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
1394   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;
1395 
1396   // the appearance model processor
1397   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
1398 
1399   // check image sizes
1400   if ( (observation.img->ni() != pixel_probability.ni()) || (observation.img->nj() != pixel_probability.nj()) ) {
1401     std::cerr << "error: observation image size does not match input image size.\n";
1402   }
1403 
1404   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
1405 
1406   // compute homographies from voxel planes to image coordinates and vise-versa.
1407   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
1408   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
1409   {
1410     vgl_h_matrix_2d<double> Hp2i, Hi2p;
1411     for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
1412     {
1413       compute_plane_image_H(observation.camera,z,Hp2i,Hi2p,scale_idx);
1414       H_plane_to_img.push_back(Hp2i);
1415       H_img_to_plane.push_back(Hi2p);
1416     }
1417   }
1418 
1419   // convert image to a voxel_slab
1420   bvxm_voxel_slab<obs_datatype> image_slab(observation.img->ni(), observation.img->nj(), 1);
1421   bvxm_util::img_to_slab(observation.img,image_slab);
1422 
1423   bvxm_voxel_slab<float> preX(grid_size.x(),grid_size.y(),1);
1424   bvxm_voxel_slab<float> PIPX(grid_size.x(),grid_size.y(),1);
1425 
1426   bvxm_voxel_slab<float> mask_slab(image_slab.nx(),image_slab.ny(),1);
1427   bvxm_voxel_slab<float> preX_accum(image_slab.nx(),image_slab.ny(),1);
1428   bvxm_voxel_slab<float> visX_accum(image_slab.nx(),image_slab.ny(),1);
1429   bvxm_voxel_slab<float> img_scratch(image_slab.nx(),image_slab.ny(),1);
1430   bvxm_voxel_slab<float> PIPX_img(image_slab.nx(), image_slab.ny(),1);
1431   bvxm_voxel_slab<float> PX_img(image_slab.nx(), image_slab.ny(),1);
1432 
1433   preX_accum.fill(0.0f);
1434   visX_accum.fill(1.0f);
1435   mask_slab.fill(0.0f);
1436 
1437   // slabs for holding backprojections of visX
1438   bvxm_voxel_slab<float> visX(grid_size.x(),grid_size.y(),1);
1439 
1440   bvxm_voxel_slab<obs_datatype> frame_backproj(grid_size.x(),grid_size.y(),1);
1441 
1442   std::cout << "Pass 1:" << std::endl;
1443 
1444   // get occupancy probability grid
1445   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
1446   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
1447 
1448   // get appearance model grid
1449   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_idx);
1450   bvxm_voxel_grid<apm_datatype> *apm_grid  = static_cast<bvxm_voxel_grid<apm_datatype>*>(apm_grid_base.ptr());
1451 
1452   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
1453   typename bvxm_voxel_grid<apm_datatype>::iterator apm_slab_it = apm_grid->begin();
1454 
1455   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it, ++apm_slab_it)
1456   {
1457     std::cout << '.';
1458 
1459     if ( (ocp_slab_it == ocp_grid->end()) || (apm_slab_it == apm_grid->end()) ) {
1460       std::cerr << "error: reached end of grid slabs at z = " << z << ".  nz = " << grid_size.z() << '\n';
1461       return false;
1462     }
1463 
1464     // backproject image onto voxel plane
1465     bvxm_util::warp_slab_bilinear(image_slab, H_plane_to_img[z], frame_backproj);
1466 
1467     // transform preX to voxel plane for this level
1468     bvxm_util::warp_slab_bilinear(preX_accum, H_plane_to_img[z], preX);
1469     // transform visX to voxel plane for this level
1470     bvxm_util::warp_slab_bilinear(visX_accum, H_plane_to_img[z], visX);
1471 
1472     // calculate PI(X)
1473     bvxm_voxel_slab<float> PI = apm_processor.prob_density(*apm_slab_it, frame_backproj);
1474 
1475     // multiply to get PIPX
1476     bvxm_util::multiply_slabs(PI,*ocp_slab_it,PIPX);
1477 #ifdef BVXM_DEBUG
1478     bvxm_util::write_slab_as_image(PI,"PI.tiff");
1479     bvxm_util::write_slab_as_image(*ocp_slab_it,"PX.tiff");
1480 #endif
1481     // warp PIPX back to image domain
1482     bvxm_util::warp_slab_bilinear(PIPX, H_img_to_plane[z], PIPX_img);
1483 
1484     // multiply PIPX by visX and add to preX_accum
1485     typename bvxm_voxel_slab<float>::iterator PIPX_img_it = PIPX_img.begin();
1486     typename bvxm_voxel_slab<float>::iterator visX_accum_it = visX_accum.begin();
1487     typename bvxm_voxel_slab<float>::iterator preX_accum_it = preX_accum.begin();
1488 
1489     for (; preX_accum_it != preX_accum.end(); ++preX_accum_it, ++PIPX_img_it, ++visX_accum_it) {
1490       *preX_accum_it += (*PIPX_img_it) * (*visX_accum_it);
1491     }
1492 #ifdef BVXM_DEBUG
1493     bvxm_util::write_slab_as_image(PIPX_img,"PIPX_img.tiff");
1494     bvxm_util::write_slab_as_image(visX_accum,"visX_accum.tiff");
1495     bvxm_util::write_slab_as_image(preX_accum,"preX_accum.tiff");
1496 #endif
1497     // scale and offset voxel probabilities to get (1-P(X))
1498     // transform (1-P(X)) to image plane to accumulate visX for next level
1499     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[z], PX_img);
1500 
1501     bvxm_util::add_slabs(PX_img,mask_slab,mask_slab);
1502 
1503     // note: doing scale and offset in image domain so invalid pixels become 1.0 and don't affect visX
1504     typename bvxm_voxel_slab<float>::iterator PX_img_it = PX_img.begin();
1505     visX_accum_it = visX_accum.begin();
1506     for (; visX_accum_it != visX_accum.end(); ++visX_accum_it, ++PX_img_it) {
1507       *visX_accum_it *= (1 - *PX_img_it);
1508     }
1509   }
1510 
1511   std::cout << std::endl << "done." << std::endl;
1512 
1513   // fill pixel_probabilities with preX_accum
1514   typename vil_image_view<float>::iterator pix_prob_it = pixel_probability.begin();
1515   typename bvxm_voxel_slab<float>::const_iterator preX_accum_it = preX_accum.begin();
1516   typename bvxm_voxel_slab<float>::const_iterator visX_accum_it = visX_accum.begin();
1517 
1518   for (; pix_prob_it != pixel_probability.end(); ++pix_prob_it, ++preX_accum_it, ++visX_accum_it)
1519   {
1520     // avoid division by zero, the values in this region shouldn't matter size it
1521     // belongs to voxels outside the mask
1522     float visX_a = *visX_accum_it;
1523     if (visX_a >= 1.0f) {
1524       visX_a = 0.5f;
1525     }
1526 
1527     *pix_prob_it = *preX_accum_it / (1.0f - visX_a);
1528   }
1529 
1530   // fill mask values
1531   typename vil_image_view<bool>::iterator mask_it = mask.begin();
1532   typename bvxm_voxel_slab<float>::const_iterator mask_slab_it = mask_slab.begin();
1533 
1534   for (; mask_it != mask.end(); ++mask_it, ++mask_slab_it) {
1535     *mask_it = (*mask_slab_it > 0);
1536   }
1537 
1538   return true;
1539 }
1540 
1541 template<bvxm_voxel_type APM_T>
region_probability_density(bvxm_image_metadata const & observation,vil_image_view_base_sptr const & mask,bvxm_voxel_grid_base_sptr & pixel_probability,unsigned bin_index,unsigned scale_idx)1542 bool bvxm_voxel_world::region_probability_density(bvxm_image_metadata const& observation,
1543                                                   vil_image_view_base_sptr const& mask,
1544                                                   bvxm_voxel_grid_base_sptr &pixel_probability,
1545                                                   unsigned bin_index , unsigned scale_idx)
1546 {
1547   bool debug = false;
1548   // datatype for current appearance model
1549   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype;
1550   typedef typename bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
1551   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;
1552 
1553   // the appearance model processor
1554   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
1555 
1556   // check image sizes
1557   if ( (observation.img->ni() != mask->ni()) || (observation.img->nj() != mask->nj()) ) {
1558     std::cerr << "error: observation image size does not match input image size.\n";
1559   }
1560 
1561   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
1562 
1563   // compute homographies from voxel planes to image coordinates and vise-versa.
1564   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
1565   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
1566 
1567 #if 0
1568   for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
1569   {
1570     vgl_h_matrix_2d<double> Hp2i, Hi2p;
1571     compute_plane_image_H(observation.camera,z,Hp2i,Hi2p,scale_idx);
1572     H_plane_to_img.push_back(Hp2i);
1573     H_img_to_plane.push_back(Hi2p);
1574   }
1575 #endif // 0
1576 
1577   // convert images to a voxel_slab
1578   bvxm_voxel_slab<obs_datatype> image_slab(observation.img->ni(), observation.img->nj(), 1);
1579   bvxm_voxel_slab<float> mask_slab(mask->ni(),mask->nj(),1);
1580 
1581   bvxm_util::img_to_slab(observation.img,image_slab);
1582   bvxm_util::img_to_slab(mask,mask_slab);
1583   if (debug) {
1584     vil_save(*mask, "./test_mask.png");
1585     bvxm_util::write_slab_as_image(mask_slab, "./test_slab.tiff");
1586   }
1587   bvxm_voxel_slab<obs_datatype> frame_backproj(grid_size.x(),grid_size.y(),1);
1588   bvxm_voxel_slab<float> mask_backproj(grid_size.x(),grid_size.y(),1);
1589 
1590   std::cout << "Pass 1:" << std::endl;
1591 
1592   // get occupancy probability grid
1593   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
1594   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
1595 
1596   // get appearance model grid
1597   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_idx);
1598   bvxm_voxel_grid<apm_datatype> *apm_grid  = static_cast<bvxm_voxel_grid<apm_datatype>*>(apm_grid_base.ptr());
1599 
1600   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
1601   typename bvxm_voxel_grid<apm_datatype>::iterator apm_slab_it = apm_grid->begin();
1602   bvxm_voxel_grid<float> *PI_grid = static_cast<bvxm_voxel_grid<float>* >(pixel_probability.ptr());
1603   PI_grid->initialize_data(0.0f);
1604   typename bvxm_voxel_grid<float>::iterator PI_slab_it = PI_grid->begin();
1605 
1606   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it, ++apm_slab_it, ++PI_slab_it)
1607   {
1608     std::cout << '.';
1609     vgl_h_matrix_2d<double> Hp2i, Hi2p;
1610     compute_plane_image_H(observation.camera,z,Hp2i,Hi2p,scale_idx);
1611 
1612     if ( (ocp_slab_it == ocp_grid->end()) || (apm_slab_it == apm_grid->end()) ) {
1613       std::cerr << "error: reached end of grid slabs at z = " << z << ".  nz = " << grid_size.z() << '\n';
1614       return false;
1615     }
1616 
1617     // backproject image onto voxel plane
1618     bvxm_util::warp_slab_bilinear(image_slab, Hp2i, frame_backproj);
1619     bvxm_util::warp_slab_bilinear(mask_slab, Hp2i, mask_backproj);
1620 
1621     if (debug) {
1622       std::stringstream file;
1623       file << "./warped" <<z << ".tif";
1624       bvxm_util::write_slab_as_image( mask_backproj, file.str());
1625     }
1626 
1627     // calculate PI(X)
1628     apm_processor.region_prob_density(*PI_slab_it, *apm_slab_it, frame_backproj,mask_backproj);
1629 
1630     if (debug) {
1631       std::stringstream file;
1632       file << "./p_slab_" <<z << ".tif";
1633       bvxm_util::write_slab_as_image( *PI_slab_it, file.str());
1634     }
1635     // multiply to get PIPX
1636     // bvxm_util::multiply_slabs(PI,*ocp_slab_it,PIPX);
1637   }
1638   return true;
1639 }
1640 
1641 //: generate the mixture of gaussians slab from the specified viewpoint. the slab should be allocated by the caller.
1642 template<bvxm_voxel_type APM_T>
mixture_of_gaussians_image(bvxm_image_metadata const & observation,bvxm_voxel_slab_base_sptr & mog_image,unsigned bin_index,unsigned scale_idx)1643 bool bvxm_voxel_world::mixture_of_gaussians_image(bvxm_image_metadata const& observation,
1644                                                   bvxm_voxel_slab_base_sptr& mog_image, unsigned bin_index,unsigned scale_idx)
1645 {
1646   // datatype for current appearance model
1647   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype; // datatype for current appearance model
1648   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;   // datatype of the pixels that the processor operates on.
1649   typedef typename bvxm_voxel_traits<APM_T>::obs_mathtype obs_mathtype;
1650   typedef typename bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
1651 
1652   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
1653 
1654   // extract global parameters
1655   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
1656 
1657   // compute homographies from voxel planes to image coordinates and vise-versa.
1658   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
1659   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
1660   for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
1661   {
1662     vgl_h_matrix_2d<double> Hp2i, Hi2p;
1663     compute_plane_image_H(observation.camera,z,Hp2i,Hi2p,scale_idx);
1664     H_plane_to_img.push_back(Hp2i);
1665     H_img_to_plane.push_back(Hi2p);
1666   }
1667 
1668   // allocate some images
1669   bvxm_voxel_slab<apm_datatype> mog_slab(observation.img->ni(),observation.img->nj(),1);
1670   bvxm_voxel_slab<obs_datatype> expected_slice_img(observation.img->ni(), observation.img->nj(),1);
1671   bvxm_voxel_slab<float> slice_ocp_img(observation.img->ni(),observation.img->nj(),1);
1672   bvxm_voxel_slab<float> visX_accum(observation.img->ni(), observation.img->nj(),1);
1673 
1674   visX_accum.fill(1.0f);
1675   mog_slab.fill(bvxm_voxel_traits<APM_T>::initial_val());
1676 
1677   // get occupancy probability grid
1678   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
1679   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
1680 
1681   // get appearance model grid
1682   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_idx);
1683   bvxm_voxel_grid<apm_datatype> *apm_grid  = static_cast<bvxm_voxel_grid<apm_datatype>*>(apm_grid_base.ptr());
1684 
1685   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
1686   typename bvxm_voxel_grid<apm_datatype>::const_iterator apm_slab_it = apm_grid->begin();
1687 
1688   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it, ++apm_slab_it)
1689   {
1690     // get expected observation
1691     bvxm_voxel_slab<obs_datatype> expected_slice = apm_processor.expected_color(*apm_slab_it);
1692     // and project to image plane
1693     bvxm_util::warp_slab_bilinear(expected_slice, H_img_to_plane[z], expected_slice_img);
1694 
1695     // warp slice_probability to image plane
1696     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[z], slice_ocp_img);
1697 
1698     typename bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_ocp_img.begin();
1699     typename bvxm_voxel_slab<float>::iterator visX_it = visX_accum.begin();
1700 
1701 #if 0 // do the following update operation from Thom's code
1702     float hard_mult = 1;
1703     for ( unsigned v = 0; v < voxels.size(); v++ ) {
1704       float this_color = voxels[v]->appearance->expected_color( light );
1705       if ( this_color >= 0 )
1706         mog->update( this_color, hard_mult*voxels[v]->occupancy_prob[0], light );
1707       hard_mult *= (1-voxels[v]->occupancy_prob[0]);
1708     }
1709 #endif // 0
1710     bvxm_voxel_slab<float> w(observation.img->ni(), observation.img->nj(),1);
1711     w.fill(1.0f);
1712     typename bvxm_voxel_slab<float>::iterator w_it = w.begin();
1713     for (; w_it != w.end(); ++PX_it, ++visX_it, ++w_it) {
1714       *w_it *= *PX_it * *visX_it;
1715       *visX_it *= (1.0f - *PX_it);  // update visX for next level
1716     }
1717 
1718     if (!apm_processor.update(mog_slab, expected_slice_img, w)) {   // check "if (*I_it >= 0)" during update
1719       std::cout << "In bvxm_voxel_world<APM_T>::mixture_of_gaussians_image() -- problems in appearance update\n";
1720       return false;
1721     }
1722   }
1723 
1724   mog_image = new bvxm_voxel_slab<apm_datatype>(mog_slab);
1725 
1726   return true;
1727 }
1728 
1729 //: generate the mixture of gaussians slab from the specified viewpoint
1730 //  Uses the most probable modes of the distributions at each voxel along the ray.
1731 //  The slab should be allocated by the caller.
1732 template<bvxm_voxel_type APM_T>
mog_most_probable_image(bvxm_image_metadata const & observation,bvxm_voxel_slab_base_sptr & mog_image,unsigned bin_index,unsigned scale_idx)1733 bool bvxm_voxel_world::mog_most_probable_image(bvxm_image_metadata const& observation,
1734                                                bvxm_voxel_slab_base_sptr& mog_image, unsigned bin_index,unsigned scale_idx)
1735 {
1736   // datatype for current appearance model
1737   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype; // datatype for current appearance model
1738   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;   // datatype of the pixels that the processor operates on.
1739   typedef typename bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
1740 
1741   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
1742 
1743   // extract global parameters
1744   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
1745 
1746   // compute homographies from voxel planes to image coordinates and vise-versa.
1747   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
1748   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
1749   for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
1750   {
1751     vgl_h_matrix_2d<double> Hp2i, Hi2p;
1752     compute_plane_image_H(observation.camera,z,Hp2i,Hi2p,scale_idx);
1753     H_plane_to_img.push_back(Hp2i);
1754     H_img_to_plane.push_back(Hi2p);
1755   }
1756 
1757   // allocate some images
1758   bvxm_voxel_slab<apm_datatype> mog_slab(observation.img->ni(),observation.img->nj(),1);
1759   bvxm_voxel_slab<obs_datatype> slice_img(observation.img->ni(), observation.img->nj(),1);
1760   bvxm_voxel_slab<float> slice_ocp_img(observation.img->ni(),observation.img->nj(),1);
1761   bvxm_voxel_slab<float> visX_accum(observation.img->ni(), observation.img->nj(),1);
1762 
1763   visX_accum.fill(1.0f);
1764   mog_slab.fill(bvxm_voxel_traits<APM_T>::initial_val());
1765 
1766   // get occupancy probability grid
1767   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
1768   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
1769 
1770   // get appearance model grid
1771   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_idx);
1772   bvxm_voxel_grid<apm_datatype> *apm_grid  = static_cast<bvxm_voxel_grid<apm_datatype>*>(apm_grid_base.ptr());
1773 
1774   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
1775   typename bvxm_voxel_grid<apm_datatype>::const_iterator apm_slab_it = apm_grid->begin();
1776 
1777   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it, ++apm_slab_it)
1778   {
1779     // get expected observation
1780     bvxm_voxel_slab<obs_datatype> slice = apm_processor.most_probable_mode_color(*apm_slab_it);
1781     // and project to image plane
1782     bvxm_util::warp_slab_bilinear(slice, H_img_to_plane[z], slice_img);
1783 
1784     // warp slice_probability to image plane
1785     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[z], slice_ocp_img);
1786 
1787     typename bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_ocp_img.begin();
1788     typename bvxm_voxel_slab<float>::iterator visX_it = visX_accum.begin();
1789 
1790 #if 0 // do the following update operation from Thom's code
1791     float hard_mult = 1;
1792     for ( unsigned v = 0; v < voxels.size(); v++ ) {
1793       float this_color = voxels[v]->appearance->expected_color( light );
1794       if ( this_color >= 0 )
1795         mog->update( this_color, hard_mult*voxels[v]->occupancy_prob[0], light );
1796       hard_mult *= (1-voxels[v]->occupancy_prob[0]);
1797     }
1798 #endif // 0
1799     bvxm_voxel_slab<float> w(observation.img->ni(), observation.img->nj(),1);
1800     w.fill(1.0f);
1801     typename bvxm_voxel_slab<float>::iterator w_it = w.begin();
1802     for (; w_it != w.end(); ++PX_it, ++visX_it, ++w_it) {
1803       *w_it *= *PX_it * *visX_it;
1804       *visX_it *= (1.0f - *PX_it);  // update visX for next level
1805     }
1806 
1807     if (!apm_processor.update(mog_slab, slice_img, w)) {   // check "if (*I_it >= 0)" during update
1808       std::cout << "In bvxm_voxel_world<APM_T>::mixture_of_gaussians_image() -- problems in appearance update\n";
1809       return false;
1810     }
1811   }
1812 
1813   mog_image = new bvxm_voxel_slab<apm_datatype>(mog_slab);
1814 
1815   return true;
1816 }
1817 
1818 //: generate the mixture of gaussians slab from the specified viewpoint. the slab should be allocated by the caller.
1819 //  generate samples from each voxel's distribution along the ray of a pixel to train a new mog at the pixel
1820 //  use a random ordering to select the voxels based on their visibility probabilities to avoid ordering problem
1821 //  n_samples is the number fo samples to be generated per ray
1822 template<bvxm_voxel_type APM_T>
mog_image_with_random_order_sampling(bvxm_image_metadata const & observation,unsigned n_samples,bvxm_voxel_slab_base_sptr & mog_image,unsigned bin_index,unsigned scale_idx)1823 bool bvxm_voxel_world::mog_image_with_random_order_sampling(bvxm_image_metadata const& observation, unsigned n_samples,
1824                                                             bvxm_voxel_slab_base_sptr& mog_image,
1825                                                             unsigned bin_index, unsigned scale_idx)
1826 {
1827   // datatype for current appearance model
1828   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype; // datatype for current appearance model
1829   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;   // datatype of the pixels that the processor operates on.
1830   typedef typename bvxm_voxel_traits<APM_T>::obs_mathtype obs_mathtype;
1831   typedef typename bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
1832 
1833   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
1834 
1835   // extract global parameters
1836   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
1837 
1838   // compute homographies from voxel planes to image coordinates and vise-versa.
1839   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
1840   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
1841   for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
1842   {
1843     vgl_h_matrix_2d<double> Hp2i, Hi2p;
1844     compute_plane_image_H(observation.camera,z,Hp2i,Hi2p,scale_idx);
1845     H_plane_to_img.push_back(Hp2i);
1846     H_img_to_plane.push_back(Hi2p);
1847   }
1848 
1849   // allocate some images
1850   bvxm_voxel_slab<apm_datatype> mog_slab(observation.img->ni(),observation.img->nj(),1);
1851   bvxm_voxel_slab<apm_datatype> slice_img(observation.img->ni(), observation.img->nj(),1);
1852   bvxm_voxel_slab<float> slice_ocp_img(observation.img->ni(),observation.img->nj(),1);
1853   bvxm_voxel_slab<float> visX_accum(observation.img->ni(), observation.img->nj(),1);
1854   std::vector<bvxm_voxel_slab_base_sptr > visX;
1855 
1856   visX_accum.fill(1.0f);
1857   mog_slab.fill(bvxm_voxel_traits<APM_T>::initial_val());
1858 
1859   // get occupancy probability grid
1860   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
1861   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
1862 
1863   // get appearance model grid
1864   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_idx);
1865   bvxm_voxel_grid<apm_datatype> *apm_grid  = static_cast<bvxm_voxel_grid<apm_datatype>*>(apm_grid_base.ptr());
1866 
1867   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
1868 
1869   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it)
1870   {
1871     // warp slice_probability to image plane
1872     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[z], slice_ocp_img);
1873 
1874     typename bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_ocp_img.begin();
1875     typename bvxm_voxel_slab<float>::iterator visX_it = visX_accum.begin();
1876 
1877     bvxm_voxel_slab_base_sptr v = new bvxm_voxel_slab<float>(observation.img->ni(), observation.img->nj(),1);
1878     ((bvxm_voxel_slab<float>*)v.ptr())->fill(1.0f);
1879     typename bvxm_voxel_slab<float>::iterator v_it = ((bvxm_voxel_slab<float>*)v.ptr())->begin();
1880     for (; v_it != ((bvxm_voxel_slab<float>*)v.ptr())->end(); ++PX_it, ++visX_it, ++v_it) {
1881       *v_it *= *PX_it * *visX_it;
1882       *visX_it *= (1.0f - *PX_it);  // update visX for next level
1883     }
1884 
1885     visX.push_back(v);
1886   }
1887 
1888   //: create {sample, prob} vector for each column (ray)
1889   std::vector<float> prob_vec;
1890   std::vector<std::vector<float> > tmp2(observation.img->nj(), prob_vec);
1891   std::vector<std::vector<std::vector<float> > > column_probs(observation.img->ni(), tmp2);
1892   tmp2.clear();
1893 
1894   //: normalize visX
1895   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z)
1896   {
1897     typename bvxm_voxel_slab<float>::iterator visX_it = visX_accum.begin();
1898     bvxm_voxel_slab<float>* v = (bvxm_voxel_slab<float>*)visX[z].ptr();
1899 
1900     typename bvxm_voxel_slab<float>::iterator v_it = v->begin();
1901     for (; v_it != v->end(); ++visX_it, ++v_it) {
1902       float norm = (1.0f - *visX_it);
1903       if (norm > 0.0f)
1904         *v_it /= norm;
1905     }
1906 
1907     for (unsigned i = 0; i < observation.img->ni(); i++) {
1908       for (unsigned j = 0; j < observation.img->nj(); j++) {
1909         column_probs[i][j].push_back((*v)(i,j));
1910       }
1911     }
1912   }
1913 
1914   //: create {sample, prob} vector for each column (ray)
1915   std::vector<unsigned> s_vec;
1916   std::vector<std::vector<unsigned> > tmp3(observation.img->nj(), s_vec);
1917   std::vector<std::vector<std::vector<unsigned> > > column_samples(observation.img->ni(), tmp3);
1918   tmp3.clear();
1919 
1920   std::vector<unsigned> col_ids;
1921   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z)
1922     col_ids.push_back(z);
1923 
1924   //: now sample from each column (ray)
1925   unsigned cnt = 0;
1926   for (unsigned i = 0; i < observation.img->ni(); ++i) {
1927     for (unsigned j = 0; j < observation.img->nj(); ++j) {
1928       if (bsta_sampler<unsigned>::sample(col_ids, column_probs[i][j], n_samples, column_samples[i][j]))
1929         ++cnt;
1930     }
1931   }
1932 
1933   std::cout << "sampled " << n_samples << " from " << cnt << " columns with probs summing to 1.0"
1934            << std::endl;
1935 
1936   //: release column_probs
1937   for (unsigned i = 0; i < observation.img->ni(); i++)
1938     for (unsigned j = 0; j < observation.img->nj(); j++)
1939       column_probs[i][j].clear();
1940   for (unsigned i = 0; i < observation.img->ni(); i++)
1941     column_probs[i].clear();
1942   column_probs.clear();
1943 
1944   vnl_random rng;
1945   std::vector<bvxm_voxel_slab_base_sptr > samples, weights;
1946   for (unsigned k = 0; k < n_samples; k++) {
1947     bvxm_voxel_slab_base_sptr v = new bvxm_voxel_slab<obs_datatype>(observation.img->ni(), observation.img->nj(),1);
1948     obs_datatype e;
1949     ((bvxm_voxel_slab<obs_datatype>*)v.ptr())->fill(e);
1950     samples.push_back(v);
1951 
1952     bvxm_voxel_slab_base_sptr w = new bvxm_voxel_slab<float> (observation.img->ni(), observation.img->nj(),1);
1953     ((bvxm_voxel_slab<float>*)w.ptr())->fill(0.0f);
1954     weights.push_back(w);
1955   }
1956 
1957   typename bvxm_voxel_grid<apm_datatype>::const_iterator apm_slab_it = apm_grid->begin();
1958   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++apm_slab_it)
1959   {
1960     std::cout << '.' << std::flush;
1961 
1962     apm_datatype init;
1963     slice_img.fill(init);
1964     bvxm_util::warp_slab_nearest_neighbor(*apm_slab_it, H_img_to_plane[z], slice_img);
1965 
1966     for (unsigned k = 0; k < n_samples; k++) {
1967       bvxm_voxel_slab<obs_datatype>* obs_samples = (bvxm_voxel_slab<obs_datatype>*)(samples[k].ptr());
1968       bvxm_voxel_slab<float>* w = (bvxm_voxel_slab<float>*)(weights[k].ptr());
1969       //: now fill in the samples for z from this slab
1970       for (unsigned i = 0; i < observation.img->ni(); i++) {
1971         for (unsigned j = 0; j < observation.img->nj(); j++) {
1972           if (column_samples[i][j].size() != 0 && column_samples[i][j][k] == z) {
1973             if (slice_img(i,j).num_components() != 0) {
1974               (*obs_samples)(i,j) = slice_img(i,j).sample(rng);
1975               (*w)(i,j) = 1.0f;  // make the weight non-zero for the column, even if there is only 1 sample
1976             }
1977           }
1978         }
1979       }
1980     }
1981   }
1982 
1983   std::cout << "sampled " << n_samples << " from " << cnt << " columns with probs summing to 1.0"
1984            << std::endl;
1985 
1986   //: now create the mixture image from the samples
1987   for (unsigned k = 0; k < n_samples; k++) {
1988     bvxm_voxel_slab<obs_datatype>* obs_samples = (bvxm_voxel_slab<obs_datatype>*)(samples[k].ptr());
1989     bvxm_voxel_slab<float>* w = (bvxm_voxel_slab<float>*)(weights[k].ptr());
1990     if (!apm_processor.update(mog_slab, *obs_samples, *w)) {
1991       std::cout << "In mog_image_with_random_order_sampling() -- problems in appearance update\n";
1992       return false;
1993     }
1994   }
1995 
1996   mog_image = new bvxm_voxel_slab<apm_datatype>(mog_slab);
1997 
1998   return true;
1999 }
2000 
2001 
2002 template<bvxm_voxel_type APM_T>
virtual_view(bvxm_image_metadata const & original_view,const vpgl_camera_double_sptr virtual_camera,vil_image_view_base_sptr & virtual_view,vil_image_view<float> & vis_prob,unsigned,unsigned scale_idx)2003 bool bvxm_voxel_world::virtual_view(bvxm_image_metadata const& original_view,
2004                                     const vpgl_camera_double_sptr virtual_camera,
2005                                     vil_image_view_base_sptr &virtual_view,
2006                                     vil_image_view<float> &vis_prob,
2007                                     unsigned /*bin_index*/,
2008                                     unsigned scale_idx)
2009 {
2010   typedef bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
2011   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;
2012   // extract global parameters
2013   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
2014 
2015   // compute homographies from voxel planes to image coordinates and vise-versa.
2016   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
2017   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
2018   std::vector<vgl_h_matrix_2d<double> > H_plane_to_virtual_img;
2019   std::vector<vgl_h_matrix_2d<double> > H_virtual_img_to_plane;
2020   std::vector<vgl_h_matrix_2d<double> > H_virtual_img_to_img;
2021 
2022   for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
2023   {
2024     vgl_h_matrix_2d<double> Hp2i, Hi2p;
2025     // real camera
2026     compute_plane_image_H(original_view.camera,z,Hp2i,Hi2p,scale_idx);
2027     H_plane_to_img.push_back(Hp2i);
2028     H_img_to_plane.push_back(Hi2p);
2029     // virtual camera
2030     compute_plane_image_H(virtual_camera,z,Hp2i,Hi2p,scale_idx);
2031     H_plane_to_virtual_img.push_back(Hp2i);
2032     H_virtual_img_to_plane.push_back(Hi2p);
2033     // image to image
2034     H_virtual_img_to_img.push_back(H_plane_to_img.back()*H_virtual_img_to_plane.back());
2035   }
2036 
2037   // allocate some images
2038   bvxm_voxel_slab<obs_datatype> virtual_view_slab(virtual_view->ni(),virtual_view->nj(),1);
2039   bvxm_voxel_slab<float> visX_accum_virtual(virtual_view->ni(), virtual_view->nj(),1);
2040   bvxm_voxel_slab<unsigned> heightmap(virtual_view->ni(),virtual_view->nj(),1);
2041   bvxm_voxel_slab<ocp_datatype> slice_prob_img(virtual_view->ni(),virtual_view->nj(),1);
2042 
2043   // get occupancy probability grid
2044   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_idx);
2045   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
2046 
2047   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
2048 
2049   // generate heightmap from virtual cameras point of view
2050   vil_image_view<unsigned> heightmap_img(virtual_view->ni(),virtual_view->nj(),1);
2051   vil_image_view<float> heightmap_img_conf(virtual_view->ni(),virtual_view->nj(),1);
2052   this->heightmap(virtual_camera,heightmap_img,heightmap_img_conf);
2053   typename vil_image_view<unsigned>::iterator hmap_img_it = heightmap_img.begin();
2054   typename bvxm_voxel_slab<unsigned>::iterator hmap_it = heightmap.begin();
2055   for (; hmap_it != heightmap.end(); ++hmap_it, ++hmap_img_it) {
2056     *hmap_it = *hmap_img_it;
2057   }
2058 
2059   // create virtual image based on smoothed height map
2060   bvxm_voxel_slab<obs_datatype> frame_virtual_proj(virtual_view->ni(),virtual_view->nj(),1);
2061 
2062   bvxm_voxel_slab<ocp_datatype> visX_accum(original_view.img->ni(),original_view.img->nj(),1);
2063   bvxm_voxel_slab<ocp_datatype> visX_accum_virtual_proj(virtual_view->ni(),virtual_view->nj(),1);
2064   visX_accum.fill(1.0f);
2065 
2066   std::cout <<"Pass 2 - generating virtual image: ";
2067 
2068   ocp_slab_it = ocp_grid->begin();
2069   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it) {
2070     std::cout << '.';
2071 
2072     // project image to virtual image
2073     bvxm_voxel_slab<obs_datatype> image_slab(original_view.img->ni(),original_view.img->nj(),1);
2074     bvxm_util::img_to_slab(original_view.img,image_slab);
2075     bvxm_util::warp_slab_bilinear(image_slab, H_virtual_img_to_img[z], frame_virtual_proj);
2076 
2077     // project visX_accum from image to virtual image
2078     bvxm_util::warp_slab_bilinear(visX_accum, H_virtual_img_to_img[z], visX_accum_virtual_proj);
2079 #ifdef BVXM_DEBUG
2080     bvxm_util::write_slab_as_image(visX_accum,"C:/research/registration/output/visX_accum.tiff");
2081     bvxm_util::write_slab_as_image(visX_accum_virtual,"C:/research/registration/output/visX_accum_virtual.tiff");
2082 #endif
2083     // project slice probabilities into virtual camera
2084     bvxm_voxel_slab<float> slice_prob_vimg(virtual_view->ni(),virtual_view->nj(),1);
2085     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_virtual_img_to_plane[z], slice_prob_vimg);
2086 
2087     typename bvxm_voxel_slab<obs_datatype>::const_iterator frame_it = frame_virtual_proj.begin();
2088     typename bvxm_voxel_slab<obs_datatype>::iterator vframe_it = virtual_view_slab.begin();
2089     typename bvxm_voxel_slab<unsigned>::const_iterator height_it = heightmap.begin();
2090     typename bvxm_voxel_slab<float>::const_iterator PX_vproj_it = slice_prob_vimg.begin();
2091     typename bvxm_voxel_slab<float>::iterator visX_accum_vproj_it = visX_accum_virtual_proj.begin();
2092     typename vil_image_view<float>::iterator vis_it = vis_prob.begin();
2093 
2094     for (; vframe_it != virtual_view_slab.end(); ++vframe_it, ++frame_it, ++height_it, ++vis_it, ++visX_accum_vproj_it, ++PX_vproj_it) {
2095       // fill in virtual image
2096       if (*height_it == z) {
2097         *vframe_it = *frame_it;
2098         *vis_it = (*visX_accum_vproj_it);// * (*PX_vproj_it);
2099       }
2100     }
2101     // project slice probabilities into real camera
2102     bvxm_util::warp_slab_bilinear(*ocp_slab_it, H_img_to_plane[z], slice_prob_img);
2103 
2104     // update visX_accum
2105     typename bvxm_voxel_slab<ocp_datatype>::iterator PX_it = slice_prob_img.begin(), visX_accum_it = visX_accum.begin();
2106     for (; visX_accum_it != visX_accum.end(); ++visX_accum_it, ++PX_it) {
2107       *visX_accum_it *= (1.0f - *PX_it);
2108     }
2109 #ifdef  BVXM_DEBUG
2110     bvxm_util::write_slab_as_image(visX_accum,"C:/research/registration/output/visX_accum.tiff");
2111 #endif
2112   }
2113   std::cout << std::endl;
2114 
2115   // mask out pixels whose rays did not intersect any voxels in the original and virtual frames
2116   std::cout << "Normalizing visibility probability. ";
2117   const float visX_thresh = 1.0f - params_->min_occupancy_prob();
2118 
2119   // set mask to 0 for all pixels whose corresponding pixel in the original image did not pass through a voxel.
2120   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z)
2121   {
2122     std::cout << '.';
2123 
2124     // project final visX_accum from image to virtual image
2125     bvxm_util::warp_slab_bilinear(visX_accum, H_virtual_img_to_img[z], visX_accum_virtual_proj);
2126 
2127     typename bvxm_voxel_slab<unsigned>::const_iterator height_it = heightmap.begin();
2128     typename vil_image_view<float>::iterator vis_it = vis_prob.begin();
2129     typename bvxm_voxel_slab<ocp_datatype>::iterator visX_virtual_proj_it = visX_accum_virtual_proj.begin();
2130 
2131     for (; vis_it != vis_prob.end(); ++vis_it, ++visX_virtual_proj_it, ++height_it)
2132       if (*height_it == z) {
2133         // visX_accum == 1 means that the ray did not intersect any voxels.
2134         if (*visX_virtual_proj_it > visX_thresh) {
2135           *vis_it = 0.0f;
2136         }
2137       }
2138   }
2139   // set mask to 0 for all pixels in virtual image that did not intersect a voxel.
2140   typename bvxm_voxel_slab<ocp_datatype>::iterator visX_accum_virtual_it = visX_accum_virtual.begin();
2141   typename vil_image_view<float>::iterator vis_it = vis_prob.begin();
2142   for (; vis_it != vis_prob.end(); ++vis_it, ++visX_accum_virtual_it) {
2143     if (*visX_accum_virtual_it > visX_thresh ) {
2144       *vis_it = 0.0f;
2145     }
2146   }
2147 
2148   bvxm_util::slab_to_img(virtual_view_slab,virtual_view);
2149 
2150   return true;
2151 }
2152 
2153 
2154 //: generate a heightmap from the viewpoint of a virtual camera
2155 // The pixel values are the z values of the most likely voxel intercepted by the corresponding camera ray
2156 // This version of the function assumes that there is also image data associated with the virtual camera
2157 template<bvxm_voxel_type APM_T>
heightmap(bvxm_image_metadata const & virtual_camera,vil_image_view<unsigned> & heightmap,unsigned bin_index,unsigned scale_index)2158 bool bvxm_voxel_world::heightmap(bvxm_image_metadata const& virtual_camera, vil_image_view<unsigned> &heightmap, unsigned bin_index, unsigned scale_index)
2159 {
2160   typedef bvxm_voxel_traits<OCCUPANCY>::voxel_datatype ocp_datatype;
2161   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype apm_datatype;
2162   typedef typename bvxm_voxel_traits<APM_T>::obs_datatype obs_datatype;
2163   // the appearance model processor
2164   typename bvxm_voxel_traits<APM_T>::appearance_processor apm_processor;
2165 
2166   // extract global parameters
2167   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels();
2168 
2169   // compute homographies from voxel planes to image coordinates and vise-versa.
2170   std::vector<vgl_h_matrix_2d<double> > H_plane_to_virtual_img;
2171   std::vector<vgl_h_matrix_2d<double> > H_virtual_img_to_plane;
2172 
2173   // convert image to a voxel_slab
2174   bvxm_voxel_slab<obs_datatype> image_slab(virtual_camera.img->ni(), virtual_camera.img->nj(), 1);
2175   if (!bvxm_util::img_to_slab(virtual_camera.img,image_slab)) {
2176     std::cerr << "error converting image to voxel slab of observation type for bvxm_voxel_type:" << APM_T << '\n';
2177     return false;
2178   }
2179 
2180   for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
2181   {
2182     vgl_h_matrix_2d<double> Hp2i, Hi2p;
2183     compute_plane_image_H(virtual_camera.camera,z,Hp2i,Hi2p);
2184     H_plane_to_virtual_img.push_back(Hp2i);
2185     H_virtual_img_to_plane.push_back(Hi2p);
2186   }
2187 
2188   // allocate some images
2189   bvxm_voxel_slab<float> visX_accum_virtual(heightmap.ni(), heightmap.nj(),1);
2190   bvxm_voxel_slab<float> heightmap_rough(heightmap.ni(),heightmap.nj(),1);
2191   bvxm_voxel_slab<float> max_prob_image(heightmap.ni(), heightmap.nj(), 1);
2192   bvxm_voxel_slab<ocp_datatype> slice_prob_img(heightmap.ni(),heightmap.nj(),1);
2193   bvxm_voxel_slab<obs_datatype> frame_backproj(heightmap.ni(),heightmap.nj(),1);
2194   bvxm_voxel_slab<float> PI_image(heightmap.ni(),heightmap.nj(),1);
2195 
2196   heightmap_rough.fill((float)grid_size.z());
2197   visX_accum_virtual.fill(1.0f);
2198   max_prob_image.fill(0.0f);
2199 
2200   // get occupancy probability grid
2201   bvxm_voxel_grid_base_sptr ocp_grid_base = this->get_grid<OCCUPANCY>(0,scale_index);
2202   bvxm_voxel_grid<ocp_datatype> *ocp_grid  = static_cast<bvxm_voxel_grid<ocp_datatype>*>(ocp_grid_base.ptr());
2203   bvxm_voxel_grid_base_sptr apm_grid_base = this->get_grid<APM_T>(bin_index,scale_index);
2204   bvxm_voxel_grid<apm_datatype> *apm_grid = static_cast<bvxm_voxel_grid<apm_datatype>*>(apm_grid_base.ptr());
2205 
2206   typename bvxm_voxel_grid<ocp_datatype>::const_iterator ocp_slab_it = ocp_grid->begin();
2207   typename bvxm_voxel_grid<apm_datatype>::const_iterator apm_slab_it = apm_grid->begin();
2208 
2209   std::cout << "generating height map from virtual camera:" << std::endl;
2210   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z, ++ocp_slab_it, ++apm_slab_it)
2211   {
2212     std::cout << '.';
2213 
2214     // backproject image onto voxel plane
2215     bvxm_util::warp_slab_bilinear(image_slab, H_plane_to_virtual_img[z], frame_backproj);
2216     // compute PI
2217     bvxm_voxel_slab<float> PI = apm_processor.prob_density(*apm_slab_it, frame_backproj);
2218     // convert PI to image domain
2219     bvxm_util::warp_slab_bilinear(PI,H_virtual_img_to_plane[z],PI_image);
2220 
2221     // compute PXvisX for virtual camera and update visX
2222     bvxm_util::warp_slab_bilinear(*ocp_slab_it,H_virtual_img_to_plane[z],slice_prob_img);
2223     typename bvxm_voxel_slab<ocp_datatype>::const_iterator PX_it = slice_prob_img.begin();
2224     typename bvxm_voxel_slab<float>::const_iterator PI_it = PI_image.begin();
2225     typename bvxm_voxel_slab<float>::iterator max_it = max_prob_image.begin(), visX_it = visX_accum_virtual.begin();
2226     typename bvxm_voxel_slab<float>::iterator hmap_it = heightmap_rough.begin();
2227 
2228     for (; hmap_it != heightmap_rough.end(); ++hmap_it, ++PX_it, ++max_it, ++visX_it, ++PI_it) {
2229       float PIPXvisX = (*visX_it) * (*PX_it) * (*PI_it);
2230       if (PIPXvisX > *max_it) {
2231         *max_it = PIPXvisX;
2232         *hmap_it = (float)z;
2233       }
2234       // update virtual visX
2235       *visX_it *= (1.0f - *PX_it);
2236     }
2237   }
2238   std::cout << std::endl;
2239 
2240 #ifdef  DEBUG
2241   bvxm_util::write_slab_as_image(heightmap_rough,"heightmap_rough.tiff");
2242   std::cerr << "Wrote height map to TIFF file heightmap_rough.tiff\n";
2243 #endif
2244   // now clean up height map
2245   unsigned n_smooth_iterations = 70;
2246   float conf_thresh = 0.2f;
2247   int medfilt_halfsize = 4;
2248   float med_diff_thresh = 8.0;
2249 
2250   // convert confidence and heightmap to vil images
2251   vil_image_view<float>* conf_img = new vil_image_view<float>(heightmap.ni(),heightmap.nj());
2252   vil_image_view_base_sptr conf_img_sptr = conf_img;
2253   bvxm_util::slab_to_img(max_prob_image,conf_img_sptr);
2254   vil_image_view<float>* heightmap_rough_img = new vil_image_view<float>(heightmap.ni(),heightmap.nj());
2255   vil_image_view_base_sptr heightmap_rough_img_sptr = heightmap_rough_img;
2256   bvxm_util::slab_to_img(heightmap_rough,heightmap_rough_img_sptr);
2257 
2258   // first, median filter heightmap
2259   vil_image_view<float> heightmap_med_img(heightmap.ni(),heightmap.nj());
2260   std::vector<int> strel_vec;
2261   for (int i=-medfilt_halfsize; i <= medfilt_halfsize; ++i)
2262     strel_vec.push_back(i);
2263   vil_structuring_element strel(strel_vec,strel_vec);
2264   vil_median(*heightmap_rough_img,heightmap_med_img,strel);
2265 
2266   // detect inliers as points which don't vary drastically from the median image
2267   vil_image_view<float> med_abs_diff(heightmap.ni(),heightmap.nj());
2268   vil_math_image_abs_difference(heightmap_med_img,*heightmap_rough_img,med_abs_diff);
2269   vil_image_view<bool> inliers(heightmap.ni(),heightmap.nj());
2270   vil_threshold_below(med_abs_diff,inliers,med_diff_thresh);
2271 
2272   std::cout << "smoothing height map: ";
2273   vil_image_view<float> heightmap_filtered_img(heightmap.ni(),heightmap.nj(),1);
2274   vil_image_view<bool> conf_mask(heightmap.ni(),heightmap.nj());
2275   // threshold confidence
2276   vil_threshold_above(*conf_img,conf_mask,conf_thresh);
2277 
2278 #ifdef HMAP_DEBUG
2279   vil_save(*conf_img,"C:/research/registration/output/heightmap_conf.tiff");
2280   vil_save(heightmap_med_img,"C:/research/registration/output/heightmap_med.tiff");
2281 #endif
2282 
2283   // initialize with rough heightmap
2284   typename vil_image_view<float>::const_iterator hmap_rough_it = heightmap_rough_img->begin();
2285   typename vil_image_view<float>::iterator hmap_filt_it = heightmap_filtered_img.begin();
2286   for (; hmap_filt_it != heightmap_filtered_img.end(); ++hmap_filt_it, ++hmap_rough_it) {
2287     *hmap_filt_it = (float)(*hmap_rough_it);
2288   }
2289 
2290   for (unsigned i=0; i< n_smooth_iterations; ++i) {
2291     std::cout << '.';
2292     // smooth heightmap
2293     vil_gauss_filter_2d(heightmap_filtered_img, heightmap_filtered_img, 1.0, 2, vil_convolve_constant_extend);
2294     // reset values we are confident in
2295     typename vil_image_view<bool>::const_iterator mask_it = conf_mask.begin(), inlier_it = inliers.begin();
2296     typename vil_image_view<float>::const_iterator hmap_med_it = heightmap_med_img.begin();
2297     hmap_filt_it = heightmap_filtered_img.begin();
2298     for (; hmap_filt_it != heightmap_filtered_img.end(); ++hmap_filt_it, ++hmap_med_it, ++mask_it, ++inlier_it) {
2299       if (*mask_it && *inlier_it) {
2300         *hmap_filt_it = (float)(*hmap_med_it);
2301       }
2302     }
2303   }
2304   std::cout << std::endl;
2305 
2306   // finally, median filter final heightmap
2307   vil_image_view<float> heightmap_filtered_med(heightmap.ni(),heightmap.nj());
2308   vil_median(heightmap_filtered_img,heightmap_filtered_med,strel);
2309 
2310 #ifdef HMAP_DEBUG
2311   vil_save(heightmap_filtered_med,"C:/research/registration/output/heightmap_filtered.tiff");
2312 #endif
2313 
2314   // convert back to unsigned
2315   typename vil_image_view<unsigned>::iterator hmap_it = heightmap.begin();
2316   hmap_filt_it = heightmap_filtered_med.begin();
2317   for (; hmap_it != heightmap.end(); ++hmap_filt_it, ++hmap_it) {
2318     *hmap_it = (unsigned)(*hmap_filt_it); // should we do some rounding here?
2319   }
2320   return true;
2321 }
2322 
2323 //: save the occupancy grid in a ".raw" format readable by Drishti volume rendering software
2324 template<bvxm_voxel_type APM_T>
save_occupancy_raw(std::string filename,unsigned scale)2325 bool bvxm_voxel_world::save_occupancy_raw(std::string filename,unsigned scale)
2326 {
2327   assert(scale<=params_->max_scale());
2328   std::fstream ofs(filename.c_str(),std::ios::binary | std::ios::out);
2329   if (!ofs.is_open()) {
2330     std::cerr << "error opening file " << filename << " for write!\n";
2331     return false;
2332   }
2333   typedef typename bvxm_voxel_traits<APM_T>::voxel_datatype ocp_datatype;
2334 
2335   // write header
2336   unsigned char data_type = 0; // 0 means unsigned byte
2337 
2338   bvxm_voxel_grid<ocp_datatype> *ocp_grid =
2339     dynamic_cast<bvxm_voxel_grid<ocp_datatype>*>(get_grid<APM_T>(0,scale).ptr());
2340 
2341   vxl_uint_32 nx = ocp_grid->grid_size().x();
2342   vxl_uint_32 ny = ocp_grid->grid_size().y();
2343   vxl_uint_32 nz = ocp_grid->grid_size().z();
2344 
2345   ofs.write(reinterpret_cast<char*>(&data_type),sizeof(data_type));
2346   ofs.write(reinterpret_cast<char*>(&nx),sizeof(nx));
2347   ofs.write(reinterpret_cast<char*>(&ny),sizeof(ny));
2348   ofs.write(reinterpret_cast<char*>(&nz),sizeof(nz));
2349 
2350   // write data
2351   // iterate through slabs and fill in memory array
2352   char *ocp_array = new char[nx*ny*nz];
2353 
2354   typename bvxm_voxel_grid<ocp_datatype>::iterator ocp_it = ocp_grid->begin();
2355   for (unsigned k=0; ocp_it != ocp_grid->end(); ++ocp_it, ++k) {
2356     std::cout << '.';
2357     for (unsigned i=0; i<(*ocp_it).nx(); ++i) {
2358       for (unsigned j=0; j < (*ocp_it).ny(); ++j) {
2359         ocp_array[i*ny*nz + j*nz + k] = (unsigned char)((*ocp_it)(i,j) * 255.0);
2360       }
2361     }
2362   }
2363   std::cout << std::endl;
2364   ofs.write(reinterpret_cast<char*>(ocp_array),sizeof(unsigned char)*nx*ny*nz);
2365 
2366   ofs.close();
2367 
2368   delete[] ocp_array;
2369 
2370   return true;
2371 }
2372 
2373 //: use the ortho z map (given by method heightmap() ) of the scene to make an input image ortho.
2374 template <typename T>
orthorectify(vil_image_view_base_sptr z_map,vpgl_camera_double_sptr z_map_camera,vil_image_view<T> & input_image,vpgl_camera_double_sptr input_camera,vil_image_view<T> & out_image,unsigned scale_idx)2375 bool bvxm_voxel_world::orthorectify(vil_image_view_base_sptr z_map,
2376                                     vpgl_camera_double_sptr z_map_camera,
2377                                     vil_image_view<T>& input_image,
2378                                     vpgl_camera_double_sptr input_camera,
2379                                     vil_image_view<T>& out_image, unsigned scale_idx)
2380 {
2381   // extract global parameters
2382   vgl_vector_3d<unsigned int> grid_size = params_->num_voxels(scale_idx);
2383 
2384   // compute homographies from voxel planes to image coordinates and vise-versa.
2385   std::vector<vgl_h_matrix_2d<double> > H_plane_to_img;
2386   std::vector<vgl_h_matrix_2d<double> > H_img_to_plane;
2387 
2388   for (unsigned z=0; z < (unsigned)grid_size.z(); ++z)
2389   {
2390     vgl_h_matrix_2d<double> Hp2i, Hi2p;
2391     compute_plane_image_H(input_camera,z,Hp2i,Hi2p,scale_idx);
2392     H_plane_to_img.push_back(Hp2i);
2393     H_img_to_plane.push_back(Hi2p);
2394   }
2395 
2396   // allocate some images
2397   bvxm_voxel_slab<float> z_slab(z_map->ni(),z_map->nj(),1);
2398   if (!bvxm_util::img_to_slab(z_map,z_slab)) {
2399     std::cerr << "error converting image to voxel slab!\n";
2400     return false;
2401   }
2402 
2403   // compute homography from ortho z slab to voxel grid (note that ortho camera orientation may be different than voxel grid orientation)
2404   bvxm_voxel_slab<float> ortho_backproj(grid_size.x(),grid_size.y(),1);
2405   vgl_h_matrix_2d<double> Hp2i_ortho, Hi2p_ortho;
2406   compute_plane_image_H(z_map_camera,0,Hp2i_ortho,Hi2p_ortho,scale_idx);  // use height 0 only since this height map is already ortho
2407   ortho_backproj.fill(0.0f);
2408   bvxm_util::warp_slab_nearest_neighbor(z_slab, Hp2i_ortho, ortho_backproj);
2409   //bvxm_util::write_slab_as_image(ortho_backproj,"C:/projects/temp/temp_ortho_backproj.tif");
2410 
2411   bvxm_voxel_slab<T> out_slab_temp(grid_size.x(),grid_size.y(),1);
2412   out_slab_temp.fill(T(0));
2413 
2414   // convert image to a voxel_slab
2415   bvxm_voxel_slab<T> image_slab(input_image.ni(), input_image.nj(), 1);
2416   typename vil_image_view<T>::const_iterator img_it = input_image.begin();
2417   typename bvxm_voxel_slab<T>::iterator slab_it = image_slab.begin();
2418   for (; img_it != input_image.end(); ++img_it, ++slab_it) {
2419     *slab_it = *img_it;
2420   }
2421 
2422   bvxm_voxel_slab<T> frame_backproj(grid_size.x(),grid_size.y(),1);
2423 
2424   for (unsigned z=0; z<(unsigned)grid_size.z(); ++z) {
2425     std::cout << '.';
2426 
2427     // backproject image onto voxel plane
2428     frame_backproj.fill(T(0));
2429     bvxm_util::warp_slab_nearest_neighbor(image_slab, H_plane_to_img[z], frame_backproj);
2430 
2431     //std::stringstream s_temp; s_temp << "C:/projects/temp/temp_img_" << z << ".tif";
2432     //bvxm_util::write_slab_as_image(frame_backproj,s_temp.str());
2433 
2434     typename bvxm_voxel_slab<T>::iterator frame_it = frame_backproj.begin();
2435     bvxm_voxel_slab<float>::iterator ortho_backproj_it = ortho_backproj.begin();
2436     typename bvxm_voxel_slab<T>::iterator out_slab_it = out_slab_temp.begin();
2437 
2438     for (; frame_it != frame_backproj.end(); ++ortho_backproj_it, ++frame_it, ++out_slab_it) {
2439       unsigned val = (unsigned)(std::floor(*ortho_backproj_it+0.5f)); // truncate the height to nearest int
2440       if (val >= z)
2441         *out_slab_it = *frame_it;
2442     }
2443   }
2444   std::cout << std::endl;
2445 
2446   //vil_image_view_base_sptr outfloatimg = new vil_image_view<float>(out_image->ni(), out_image->nj(), 1);
2447 
2448   bvxm_voxel_slab<T> out_slab(z_map->ni(),z_map->nj(),1);
2449   out_slab.fill(T(0));
2450 
2451   // warp back to input ortho cam orientation
2452   bvxm_util::warp_slab_nearest_neighbor(out_slab_temp, Hi2p_ortho, out_slab);
2453 
2454   typename vil_image_view<T>::iterator img_it2 = out_image.begin();
2455   slab_it = out_slab.begin();
2456   for (; img_it2 != out_image.end(); ++img_it2, ++slab_it) {
2457     *img_it2 = *slab_it;
2458   }
2459 
2460   return true;
2461 }
2462 
2463 class bvxm_mog_image_creation_methods
2464 {
2465  public:
2466   enum mog_creation_methods {
2467     MOST_PROBABLE_MODE,   // use the mean value of most probable mode of the mixture at each voxel along the ray to update a mog image (Thom Pollard's original algo)
2468     EXPECTED_VALUE,       // use the expected value of the mixture at each voxel along the ray to update a mog image
2469     SAMPLING,   // randomly sample voxels wrt to visibility probabilities and sample from their mixtures to update a mog image
2470   };
2471 };
2472 
2473 #include <bvxm/bvxm_voxel_world_sptr.h>
2474 
2475 #endif // bvxm_voxel_world_h_
2476