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