1 // This is contrib/brl/bbas/bpgl/algo/bpgl_heightmap_from_disparity.hxx
2 #ifndef bpgl_heightmap_from_disparity_hxx_
3 #define bpgl_heightmap_from_disparity_hxx_
4 
5 #include <algorithm>
6 #include <vnl/vnl_math.h>
7 #include <vgl/vgl_box_3d.h>
8 #include <vgl/vgl_distance.h>
9 
10 #include "bpgl_3d_from_disparity.h"
11 #include "bpgl_heightmap_from_disparity.h"
12 #include "bpgl_gridding.h"
13 
14 
15 // main convenience function - transform disparity to heightmap
16 template<class T, class CAM_T>
bpgl_heightmap_from_disparity(CAM_T const & cam1,CAM_T const & cam2,vil_image_view<T> const & disparity,vgl_box_3d<T> heightmap_bounds,T ground_sample_distance)17 vil_image_view<T> bpgl_heightmap_from_disparity(
18     CAM_T const& cam1,
19     CAM_T const& cam2,
20     vil_image_view<T> const& disparity,
21     vgl_box_3d<T> heightmap_bounds,
22     T ground_sample_distance)
23 {
24   // triangulated image
25   vil_image_view<T> tri_3d = bpgl_3d_from_disparity(cam1, cam2, disparity);
26 
27   // convert triangulated image to heightmap
28   bpgl_heightmap<T> bh(heightmap_bounds, ground_sample_distance);
29   vil_image_view<T> heightmap_output;
30   bh.heightmap_from_tri(tri_3d, heightmap_output);
31 
32   // cleanup
33   return heightmap_output;
34 }
35 
36 
37 // ----------
38 // 3D pointset from triangulated input
39 // compute 3D pointset using disparity to triangulate 3D data
40 // ----------
41 
42 // no scalar added to pointset
43 template<class T>
pointset_from_tri(const vil_image_view<T> & tri_3d,vgl_pointset_3d<T> & ptset_output)44 void bpgl_heightmap<T>::pointset_from_tri(
45     const vil_image_view<T>& tri_3d,
46     vgl_pointset_3d<T>& ptset_output)
47 {
48   vil_image_view<T> empty_scalar;
49   this->_pointset_from_tri(
50       tri_3d, empty_scalar, ptset_output, true);
51 }
52 
53 // add scalar to pointset
54 template<class T>
pointset_from_tri(const vil_image_view<T> & tri_3d,const vil_image_view<T> & scalar,vgl_pointset_3d<T> & ptset_output)55 void bpgl_heightmap<T>::pointset_from_tri(
56     const vil_image_view<T>& tri_3d,
57     const vil_image_view<T>& scalar,
58     vgl_pointset_3d<T>& ptset_output)
59 {
60   this->_pointset_from_tri(
61       tri_3d, scalar, ptset_output, false);
62 }
63 
64 // private function, scalar usage controlled by "ignore_scalar"
65 template<class T>
_pointset_from_tri(const vil_image_view<T> & tri_3d,const vil_image_view<T> & scalar,vgl_pointset_3d<T> & ptset_output,bool ignore_scalar)66 void bpgl_heightmap<T>::_pointset_from_tri(
67     const vil_image_view<T>& tri_3d,
68     const vil_image_view<T>& scalar,
69     vgl_pointset_3d<T>& ptset_output,
70     bool ignore_scalar)
71 {
72   // bounds with tolerance (avoid any floating point error in comparison)
73   T tol = 1e-3;
74   auto bounds_with_tolerance = heightmap_bounds_;
75   bounds_with_tolerance.expand_about_centroid(tol);
76 
77   // add triangulated points to pointset
78   for (size_t j=0; j < tri_3d.nj(); ++j) {
79     for (size_t i=0; i < tri_3d.ni(); ++i) {
80       if (vnl_math::isfinite(tri_3d(i,j,0)) &&
81           vnl_math::isfinite(tri_3d(i,j,1)) &&
82           vnl_math::isfinite(tri_3d(i,j,2)) )
83       {
84         // confirm 3D point is within bounds
85         vgl_point_3d<T> point(tri_3d(i,j,0), tri_3d(i,j,1), tri_3d(i,j,2));
86         if(!bounds_with_tolerance.contains(point))
87           continue;
88 
89         // add point/scalar to pointset
90         if (ignore_scalar) {
91           ptset_output.add_point(point);
92         } else {
93           T value = scalar(i,j);
94           ptset_output.add_point_with_scalar(point, value);
95         }
96       }
97     }
98   }
99 
100 }
101 
102 
103 // ----------
104 // Heightmap from 3D pointset
105 // Transform 3D pointset to 2D heightmap
106 // ----------
107 
108 // without scalar output
109 template<class T>
heightmap_from_pointset(const vgl_pointset_3d<T> & ptset,vil_image_view<T> & heightmap_output)110 void bpgl_heightmap<T>::heightmap_from_pointset(
111     const vgl_pointset_3d<T>& ptset,
112     vil_image_view<T>& heightmap_output)
113 {
114   vil_image_view<T> empty_scalar_output;
115   this->_heightmap_from_pointset(
116       ptset, heightmap_output, empty_scalar_output, true);
117 }
118 
119 // with scalar output
120 template<class T>
heightmap_from_pointset(const vgl_pointset_3d<T> & ptset,vil_image_view<T> & heightmap_output,vil_image_view<T> & scalar_output)121 void bpgl_heightmap<T>::heightmap_from_pointset(
122     const vgl_pointset_3d<T>& ptset,
123     vil_image_view<T>& heightmap_output,
124     vil_image_view<T>& scalar_output)
125 {
126   this->_heightmap_from_pointset(
127       ptset, heightmap_output, scalar_output, false);
128 }
129 
130 // private function, scalar usage controlled by "ignore_scalar"
131 template<class T>
_heightmap_from_pointset(const vgl_pointset_3d<T> & ptset,vil_image_view<T> & heightmap_output,vil_image_view<T> & scalar_output,bool ignore_scalar)132 void bpgl_heightmap<T>::_heightmap_from_pointset(
133     const vgl_pointset_3d<T>& ptset,
134     vil_image_view<T>& heightmap_output,
135     vil_image_view<T>& scalar_output,
136     bool ignore_scalar)
137 {
138   // check pointset sufficency
139   if (ptset.npts() < min_neighbors_) {
140     throw std::runtime_error("Not enough points in pointset for interpolation");
141   }
142 
143   // pointset as vectors
144   std::vector<vgl_point_2d<T> > triangulated_xy;
145   std::vector<T> height_vals;
146 
147   for (const auto& point_3d : ptset.points()) {
148     vgl_point_2d<T> point_2d(point_3d.x(), point_3d.y());
149     triangulated_xy.emplace_back(point_2d);
150     height_vals.emplace_back(point_3d.z());
151   }
152 
153   // image upper left & size
154   // image must contain all samples within bounds, inclusive
155   vgl_point_2d<T> upper_left(heightmap_bounds_.min_x(), heightmap_bounds_.max_y());
156   size_t ni = static_cast<size_t>(std::floor(heightmap_bounds_.width() / ground_sample_distance_ + 1));
157   size_t nj = static_cast<size_t>(std::floor(heightmap_bounds_.height() / ground_sample_distance_ + 1));
158 
159   // maximum neighbor distance
160   T max_dist = neighbor_dist_factor_ * ground_sample_distance_;
161 
162   // default interpolation function
163   bpgl_gridding::linear_interp<T,T> interp_fun;
164 
165   // heightmap gridding
166   heightmap_output = bpgl_gridding::grid_data_2d(
167       interp_fun,
168       triangulated_xy, height_vals,
169       upper_left, ni, nj, ground_sample_distance_,
170       min_neighbors_, max_neighbors_, max_dist);
171 
172   // bounds check to remove outliers
173   T min_z = heightmap_bounds_.min_z();
174   T max_z = heightmap_bounds_.max_z();
175 
176   for (int j=0; j<nj; ++j) {
177     for (int i=0; i<ni; ++i) {
178       if ((heightmap_output(i,j) < min_z) || (heightmap_output(i,j) > max_z)) {
179         heightmap_output(i,j) = NAN;
180       }
181     }
182   }
183 
184   // scalar interpolation
185   if (!ignore_scalar) {
186 
187     // scalar gridding
188     scalar_output = bpgl_gridding::grid_data_2d(
189         interp_fun,
190         triangulated_xy, ptset.scalars(),
191         upper_left, ni, nj, ground_sample_distance_,
192         min_neighbors_, max_neighbors_, max_dist);
193 
194     // remove scalar without corresponding height
195     for (int j=0; j<nj; ++j) {
196       for (int i=0; i<ni; ++i) {
197         if (!vnl_math::isfinite(heightmap_output(i,j))) {
198           scalar_output(i,j) = NAN;
199         }
200       }
201     }
202 
203   } // end scalar interpolation
204 
205 }
206 template<class T>
heightmap_from_pointset(const vgl_pointset_3d<T> & ptset,vil_image_view<T> & heightmap_output,vil_image_view<T> & scalar_output,vil_image_view<T> & radial_std_dev)207 void bpgl_heightmap<T>::heightmap_from_pointset(
208         const vgl_pointset_3d<T>& ptset,
209         vil_image_view<T>& heightmap_output,
210         vil_image_view<T>& scalar_output,
211         vil_image_view<T>& radial_std_dev){
212 
213   _heightmap_from_pointset(ptset,
214                            heightmap_output,
215                            scalar_output,
216                            false);
217 
218   vgl_point_2d<T> upper_left(heightmap_bounds_.min_x(), heightmap_bounds_.max_y());
219   size_t ni = heightmap_output.ni(), nj = heightmap_output.nj();
220   // maximum neighbor distance
221   T max_dist = neighbor_dist_factor_ * ground_sample_distance_;
222 
223   radial_std_dev.set_size(ni,nj);
224   radial_std_dev.fill(NAN);
225 
226   std::vector<vgl_point_2d<T> > pts_2d;
227   for(size_t i = 0; i<ptset.size(); ++i)
228     pts_2d.emplace_back(ptset.p(i).x(), ptset.p(i).y());
229 
230   bvgl_k_nearest_neighbors_2d<T> knn(pts_2d);
231   if (!knn.is_valid()) {
232     throw std::runtime_error("KNN initialization failure");
233   }
234 
235   for(size_t j = 0; j<nj; ++j)
236     for(size_t i = 0; i<ni; ++i){
237       vgl_point_2d<T> loc(upper_left.x()+ i*ground_sample_distance_, upper_left.y()- j*ground_sample_distance_);
238       std::vector<vgl_point_2d<T> > neighbor_locs;
239       std::vector<unsigned> neighbor_indices;
240       if (!knn.knn(loc, max_neighbors_, neighbor_locs, neighbor_indices, max_dist)) {
241         throw std::runtime_error("KNN failed to return neighbors");
242       }
243       size_t nn = neighbor_indices.size();
244       if (nn < min_neighbors_)
245         continue;
246       T var_sum = T(0), p_sum = T(0);
247       for(size_t k = 0; k<nn; ++k){
248         T prob = ptset.sc(neighbor_indices[k]);
249         T d = vgl_distance<T>(loc, neighbor_locs[k]);
250         var_sum += prob*d*d;
251         p_sum += prob;
252       }
253       T std_dev = sqrt(var_sum/p_sum);
254       radial_std_dev(i,j) = std_dev;
255     }
256 }
257 
258 // ----------
259 // Heightmap from triangulated input
260 // Convert triangulated input directly to heigtmap
261 // ----------
262 
263 // without scalar output
264 template<class T>
heightmap_from_tri(const vil_image_view<T> & tri_3d,vil_image_view<T> & heightmap_output)265 void bpgl_heightmap<T>::heightmap_from_tri(
266     const vil_image_view<T>& tri_3d,
267     vil_image_view<T>& heightmap_output)
268 {
269   vgl_pointset_3d<T> ptset;
270   this->pointset_from_tri(
271       tri_3d, ptset);
272   this->heightmap_from_pointset(
273       ptset, heightmap_output);
274 }
275 
276 // with scalar output
277 template<class T>
heightmap_from_tri(const vil_image_view<T> & tri_3d,const vil_image_view<T> & scalar,vil_image_view<T> & heightmap_output,vil_image_view<T> & scalar_output)278 void bpgl_heightmap<T>::heightmap_from_tri(
279     const vil_image_view<T>& tri_3d,
280     const vil_image_view<T>& scalar,
281     vil_image_view<T>& heightmap_output,
282     vil_image_view<T>& scalar_output)
283 {
284   vgl_pointset_3d<T> ptset;
285   this->pointset_from_tri(
286       tri_3d, scalar, ptset);
287   this->heightmap_from_pointset(
288       ptset, heightmap_output, scalar_output);
289 }
290 
291 
292 // ----------
293 // CLEANUP
294 // ----------
295 
296 // macros for Templates
297 #undef BPGL_HEIGHTMAP_INSTANIATE
298 #define BPGL_HEIGHTMAP_INSTANIATE(T) \
299 template class bpgl_heightmap<T>
300 
301 #undef BPGL_HEIGHTMAP_FROM_DISPARITY_INSTANIATE
302 #define BPGL_HEIGHTMAP_FROM_DISPARITY_INSTANIATE(T, CAM_T) \
303 template vil_image_view<T> bpgl_heightmap_from_disparity<T, CAM_T>( \
304     CAM_T const& cam1, \
305     CAM_T const& cam2, \
306     vil_image_view<T> const& disparity, \
307     vgl_box_3d<T> heightmap_bounds, \
308     T ground_sample_distance)
309 
310 #endif
311