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