1 // This is brl/brpo/core/vpgl_pro/processes/vpgl_crop_img_using_3d_box_process.cxx
2 //:
3 // \file
4 // Take a cam, an uncertainty value in meter unit and a 3D bounding box in wgs84 coordinates to generate a cropped image
5 // Note the outputs are the cropped camera and the pixel values use to represent the image region being cropped
6 //
7 //
8 //
9 #include <iostream>
10 #include <vil/vil_config.h>
11 #if HAS_GEOTIFF
12
13 #include "vul/vul_file.h"
14 #include "vul/vul_file_iterator.h"
15 #include <bprb/bprb_func_process.h>
16 #include <brip/brip_roi.h>
17 #include "vgl/vgl_point_3d.h"
18 #include "vgl/vgl_point_2d.h"
19 #include "vgl/vgl_box_2d.h"
20 #include "vgl/vgl_box_3d.h"
21 #include "vpgl/vpgl_lvcs.h"
22 #include "vpgl/vpgl_lvcs_sptr.h"
23 #include "vil/vil_image_resource_sptr.h"
24 #include "vil/vil_image_resource.h"
25 #include "vil/vil_image_view.h"
26 #include "vil/vil_crop.h"
27 #include "vil/vil_load.h"
28 #include "vpgl/vpgl_camera_double_sptr.h"
29 #include "vpgl/vpgl_rational_camera.h"
30 #include <vpgl/file_formats/vpgl_geo_camera.h>
31 #include "vpgl/vpgl_local_rational_camera.h"
32 #include <vsol/vsol_box_2d_sptr.h>
33 #include <vsol/vsol_box_2d.h>
34 #ifdef _MSC_VER
35 # include "vcl_msvc_warnings.h"
36 #endif
37
38
39 // global variables and functions
40 namespace vpgl_crop_img_using_3d_box_process_globals
41 {
42 constexpr unsigned n_inputs_ = 11;
43 constexpr unsigned n_outputs_ = 5;
44 }
45
46 // === functions ===
47 bool project_box(const vpgl_rational_camera<double>& rat_cam, const vpgl_lvcs_sptr &lvcs_sptr,
48 const vgl_box_3d<double> &scene_bbox, double uncertainty,
49 vgl_box_2d<double> &roi_box_2d);
50
51 vpgl_local_rational_camera<double> create_local_rational_camera(
52 const vpgl_rational_camera<double>& rat_cam, const vpgl_lvcs& lvcs,
53 unsigned min_x, unsigned min_y);
54
55
56 // initialization
vpgl_crop_img_using_3d_box_process_cons(bprb_func_process & pro)57 bool vpgl_crop_img_using_3d_box_process_cons(bprb_func_process& pro)
58 {
59 using namespace vpgl_crop_img_using_3d_box_process_globals;
60 // process takes 10 inputs
61 std::vector<std::string> input_types_(n_inputs_);
62 //input_types_[0] = "vil_image_resource_sptr"; // image resource
63 input_types_[0] = "unsigned"; // image resource ni
64 input_types_[1] = "unsigned"; // image resource nj
65 input_types_[2] = "vpgl_camera_double_sptr"; // rational camera
66 input_types_[3] = "double"; // lower_left_lon
67 input_types_[4] = "double"; // lower_left_lat
68 input_types_[5] = "double"; // lower_left_elev
69 input_types_[6] = "double"; // upper_right_lon
70 input_types_[7] = "double"; // upper_right_lat
71 input_types_[8] = "double"; // upper_right_elev
72 input_types_[9] = "double"; // uncertainty value (in meter units)
73 input_types_[10] = "vpgl_lvcs_sptr"; // lvcs
74
75 // process takes 5 outputs
76 std::vector<std::string> output_types_(n_outputs_);
77 output_types_[0] = "vpgl_camera_double_sptr"; // crop rational camera
78 output_types_[1] = "unsigned"; // image pixel i0
79 output_types_[2] = "unsigned"; // image pixel j0
80 output_types_[3] = "unsigned"; // image size ni
81 output_types_[4] = "unsigned"; // image size nj
82
83 bool good = pro.set_input_types(input_types_) && pro.set_output_types(output_types_);
84
85 // set input defaults
86 vpgl_lvcs_sptr lvcs = new vpgl_lvcs;
87 pro.set_input(10, new brdb_value_t<vpgl_lvcs_sptr>(lvcs));
88
89 return good;
90 }
91
92 // execute the process
vpgl_crop_img_using_3d_box_process(bprb_func_process & pro)93 bool vpgl_crop_img_using_3d_box_process(bprb_func_process& pro)
94 {
95 using namespace vpgl_crop_img_using_3d_box_process_globals;
96 // sanity check
97 //if (pro.n_inputs() != n_inputs_) {
98 // std::cout << pro.name() << ": The input number should be " << n_inputs_ << std::endl;
99 // return false;
100 //}
101 if (!pro.verify_inputs())
102 {
103 std::cout << pro.name() << ": The input is wrong!!!" << std::endl;
104 return false;
105 }
106
107 // get the input
108 unsigned i = 0;
109 //vil_image_resource_sptr img_res_sptr = pro.get_input<vil_image_resource_sptr>(i++); // image resource
110 auto img_res_ni = pro.get_input<unsigned>(i++);
111 auto img_res_nj = pro.get_input<unsigned>(i++);
112 vpgl_camera_double_sptr cam_sptr = pro.get_input<vpgl_camera_double_sptr>(i++); // rational camera
113 auto lower_left_lon = pro.get_input<double>(i++);
114 auto lower_left_lat = pro.get_input<double>(i++);
115 auto lower_left_elev = pro.get_input<double>(i++);
116 auto upper_right_lon = pro.get_input<double>(i++);
117 auto upper_right_lat = pro.get_input<double>(i++);
118 auto upper_right_elev = pro.get_input<double>(i++);
119 auto uncertainty = pro.get_input<double>(i++);
120 vpgl_lvcs_sptr lvcs_sptr= pro.get_input<vpgl_lvcs_sptr>(i++);
121
122 auto* rat_cam = dynamic_cast<vpgl_rational_camera<double>*>(cam_sptr.as_pointer());
123 if (!rat_cam) {
124 std::cout << pro.name() << ": the input camera is not a rational camera" << std::endl;
125 return false;
126 }
127
128 // generate a lvcs coordinates to transfer camera offset coordinates
129 double ori_lon, ori_lat, ori_elev;
130 lvcs_sptr->get_origin(ori_lat, ori_lon, ori_elev);
131 if ( (ori_lat+ori_lon+ori_elev)*(ori_lat+ori_lon+ori_elev) < 1E-7) {
132 lvcs_sptr = new vpgl_lvcs(lower_left_lat, lower_left_lon, lower_left_elev, vpgl_lvcs::wgs84, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
133 }
134
135 vgl_box_3d<double> scene_bbox(lower_left_lon, lower_left_lat, lower_left_elev,
136 upper_right_lon, upper_right_lat, upper_right_elev);
137
138 vgl_box_2d<double> roi_box_2d;
139 bool good = project_box(*rat_cam, lvcs_sptr, scene_bbox, uncertainty, roi_box_2d);
140 if(!good) {
141 return false;
142 }
143 std::cout << pro.name() << ": projected 2d roi box: " << roi_box_2d << " given uncertainty " << uncertainty << " meters." << std::endl;
144
145 // crop the image
146 //brip_roi broi(img_res_sptr->ni(), img_res_sptr->nj());
147 brip_roi broi(img_res_ni, img_res_nj);
148 vsol_box_2d_sptr bb = new vsol_box_2d();
149 bb->add_point(roi_box_2d.min_x(), roi_box_2d.min_y());
150 bb->add_point(roi_box_2d.max_x(), roi_box_2d.max_y());
151 bb = broi.clip_to_image_bounds(bb);
152
153 // store output
154 auto i0 = (unsigned)bb->get_min_x();
155 auto j0 = (unsigned)bb->get_min_y();
156 auto ni = (unsigned)bb->width();
157 auto nj = (unsigned)bb->height();
158
159 if (ni <= 0 || nj <= 0)
160 {
161 std::cout << pro.name() << ": clipping box is out of image boundary, empty crop image returned" << std::endl;
162 return false;
163 }
164
165 // create the local camera
166 auto local_camera = create_local_rational_camera(*rat_cam, *lvcs_sptr, i0, j0);
167
168 // store output
169 unsigned out_j = 0;
170 pro.set_output_val<vpgl_camera_double_sptr>(out_j++, new vpgl_local_rational_camera<double>(local_camera));
171 pro.set_output_val<unsigned>(out_j++, i0);
172 pro.set_output_val<unsigned>(out_j++, j0);
173 pro.set_output_val<unsigned>(out_j++, ni);
174 pro.set_output_val<unsigned>(out_j++, nj);
175 return true;
176 }
177
178 // global variables and functions
179 namespace vpgl_offset_cam_using_3d_box_process_globals
180 {
181 constexpr unsigned n_inputs_ = 9;
182 constexpr unsigned n_outputs_ = 5;
183 }
184
185 // initialization
vpgl_offset_cam_using_3d_box_process_cons(bprb_func_process & pro)186 bool vpgl_offset_cam_using_3d_box_process_cons(bprb_func_process& pro)
187 {
188 using namespace vpgl_offset_cam_using_3d_box_process_globals;
189 // process takes 9 inputs
190 std::vector<std::string> input_types_(n_inputs_);
191 input_types_[0] = "vpgl_camera_double_sptr"; // rational camera
192 input_types_[1] = "double"; // lower_left_lon
193 input_types_[2] = "double"; // lower_left_lat
194 input_types_[3] = "double"; // lower_left_elev
195 input_types_[4] = "double"; // upper_right_lon
196 input_types_[5] = "double"; // upper_right_lat
197 input_types_[6] = "double"; // upper_right_elev
198 input_types_[7] = "double"; // uncertainty value (in meter units)
199 input_types_[8] = "vpgl_lvcs_sptr"; // lvcs
200
201 // process takes 5 outputs
202 std::vector<std::string> output_types_(n_outputs_);
203 output_types_[0] = "vpgl_camera_double_sptr"; // crop rational camera
204 output_types_[1] = "unsigned"; // image pixel i0
205 output_types_[2] = "unsigned"; // image pixel j0
206 output_types_[3] = "unsigned"; // image size ni
207 output_types_[4] = "unsigned"; // image size nj
208
209 bool good = pro.set_input_types(input_types_) && pro.set_output_types(output_types_);
210
211 // set input defaults
212 brdb_value_sptr lvcs = new brdb_value_t<vpgl_lvcs_sptr>;
213 pro.set_input(8, lvcs);
214
215 return good;
216 }
217
218 // execute the process
vpgl_offset_cam_using_3d_box_process(bprb_func_process & pro)219 bool vpgl_offset_cam_using_3d_box_process(bprb_func_process& pro)
220 {
221 using namespace vpgl_offset_cam_using_3d_box_process_globals;
222 // sanity check
223 if (pro.n_inputs() != n_inputs_) {
224 std::cout << pro.name() << ": The input number should be " << n_inputs_ << std::endl;
225 return false;
226 }
227
228 // get the input
229 unsigned i = 0;
230 vpgl_camera_double_sptr cam_sptr = pro.get_input<vpgl_camera_double_sptr>(i++); // rational camera
231 auto lower_left_lon = pro.get_input<double>(i++);
232 auto lower_left_lat = pro.get_input<double>(i++);
233 auto lower_left_elev = pro.get_input<double>(i++);
234 auto upper_right_lon = pro.get_input<double>(i++);
235 auto upper_right_lat = pro.get_input<double>(i++);
236 auto upper_right_elev = pro.get_input<double>(i++);
237 auto uncertainty = pro.get_input<double>(i++);
238 vpgl_lvcs_sptr lvcs_sptr= pro.get_input<vpgl_lvcs_sptr>(i++);
239
240 auto* rat_cam = dynamic_cast<vpgl_rational_camera<double>*>(cam_sptr.as_pointer());
241 if (!rat_cam) {
242 std::cout << pro.name() << ": the input camera is not a rational camera" << std::endl;
243 return false;
244 }
245
246 // generate a lvcs coordinates to transfer camera offset coordinates
247 if(!lvcs_sptr) {
248 lvcs_sptr = new vpgl_lvcs(lower_left_lat, lower_left_lon, lower_left_elev, vpgl_lvcs::wgs84, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
249 }
250
251 vgl_box_3d<double> scene_bbox(lower_left_lon, lower_left_lat, lower_left_elev,
252 upper_right_lon, upper_right_lat, upper_right_elev);
253
254 vgl_box_2d<double> roi_box_2d;
255 bool good = project_box(*rat_cam, lvcs_sptr, scene_bbox, uncertainty, roi_box_2d);
256 if(!good) {
257 return false;
258 }
259 std::cout << pro.name() << ": projected 2d roi box: " << roi_box_2d << " given uncertainty " << uncertainty << " meters." << std::endl;
260
261 // crop the image
262 vsol_box_2d_sptr bb = new vsol_box_2d();
263 bb->add_point(roi_box_2d.min_x(), roi_box_2d.min_y());
264 bb->add_point(roi_box_2d.max_x(), roi_box_2d.max_y());
265
266 // store output
267 int i0 = bb->get_min_x();
268 auto ni = (unsigned)bb->width();
269 if(i0 < 0) {
270 ni += i0;
271 i0 = 0;
272 }
273
274 int j0 = bb->get_min_y();
275 auto nj = (unsigned)bb->height();
276 if(j0 < 0) {
277 nj += j0;
278 j0 = 0;
279 }
280
281 if (ni <= 0 || nj <= 0) {
282 std::cout << pro.name() << ": projected box too small" << std::endl;
283 return false;
284 }
285
286 // create the local camera
287 auto local_camera = create_local_rational_camera(*rat_cam, *lvcs_sptr, i0, j0);
288
289 // store output
290 unsigned out_j = 0;
291 pro.set_output_val<vpgl_camera_double_sptr>(out_j++, new vpgl_local_rational_camera<double>(local_camera));
292 pro.set_output_val<unsigned>(out_j++, i0);
293 pro.set_output_val<unsigned>(out_j++, j0);
294 pro.set_output_val<unsigned>(out_j++, ni);
295 pro.set_output_val<unsigned>(out_j++, nj);
296 return true;
297 }
298
299 vpgl_local_rational_camera<double>
create_local_rational_camera(const vpgl_rational_camera<double> & rat_cam,const vpgl_lvcs & lvcs,unsigned min_x,unsigned min_y)300 create_local_rational_camera(const vpgl_rational_camera<double>& rat_cam,
301 const vpgl_lvcs& lvcs,
302 unsigned min_x, unsigned min_y)
303 {
304 // calculate local camera offset from image bounding box
305 double global_u, global_v, local_u, local_v;
306 rat_cam.image_offset(global_u, global_v);
307 local_u = global_u - (double)min_x; // the image was cropped by pixel
308 local_v = global_v - (double)min_y;
309
310 // create the local camera
311 vpgl_local_rational_camera<double> local_camera(lvcs, rat_cam);
312 local_camera.set_image_offset(local_u, local_v);
313 return local_camera;
314 }
315
project_box(const vpgl_rational_camera<double> & rat_cam,const vpgl_lvcs_sptr & lvcs_sptr,const vgl_box_3d<double> & scene_bbox,double uncertainty,vgl_box_2d<double> & roi_box_2d)316 bool project_box(const vpgl_rational_camera<double>& rat_cam, const vpgl_lvcs_sptr &lvcs_sptr,
317 const vgl_box_3d<double> &scene_bbox, double uncertainty,
318 vgl_box_2d<double> &roi_box_2d)
319 {
320 // project box
321 double xoff, yoff, zoff;
322 xoff = rat_cam.offset(vpgl_rational_camera<double>::X_INDX);
323 yoff = rat_cam.offset(vpgl_rational_camera<double>::Y_INDX);
324 zoff = rat_cam.offset(vpgl_rational_camera<double>::Z_INDX);
325
326 // global to lcoal (wgs84 to meter in order to apply uncertainty)
327 double lx, ly, lz;
328 lvcs_sptr->global_to_local(xoff, yoff, zoff, vpgl_lvcs::wgs84, lx, ly, lz, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
329 double center[3];
330 center[0] = lx; center[1] = ly; center[2] = lz;
331
332 // create a camera box with uncertainty
333 vgl_box_3d<double> cam_box(center, 2*uncertainty, 2*uncertainty, 2*uncertainty, vgl_box_3d<double>::centre);
334 std::vector<vgl_point_3d<double> > cam_corners = cam_box.vertices();
335
336 // create the 3D box given input coordinates (in geo-coordinates)
337 std::vector<vgl_point_3d<double> > box_corners = scene_bbox.vertices();
338
339 // projection
340 double lon, lat, gz;
341 for (auto & cam_corner : cam_corners)
342 {
343 lvcs_sptr->local_to_global(cam_corner.x(), cam_corner.y(), cam_corner.z(), vpgl_lvcs::wgs84,
344 lon, lat, gz, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
345 vpgl_rational_camera<double>* new_cam = rat_cam.clone();
346 new_cam->set_offset(vpgl_rational_camera<double>::X_INDX, lon);
347 new_cam->set_offset(vpgl_rational_camera<double>::Y_INDX, lat);
348 new_cam->set_offset(vpgl_rational_camera<double>::Z_INDX, gz);
349
350 // project the box to image coords
351 for (auto & box_corner : box_corners) {
352 vgl_point_2d<double> p2d = new_cam->project(vgl_point_3d<double>(box_corner.x(), box_corner.y(), box_corner.z()));
353 roi_box_2d.add(p2d);
354 }
355 delete new_cam;
356 }
357
358 return true;
359 }
360
361 // global variables and functions
362 namespace vpgl_crop_ortho_using_3d_box_process_globals
363 {
364 constexpr unsigned n_inputs_ = 8;
365 constexpr unsigned n_outputs_ = 5;
366 }
367
368 // initialization
vpgl_crop_ortho_using_3d_box_process_cons(bprb_func_process & pro)369 bool vpgl_crop_ortho_using_3d_box_process_cons(bprb_func_process& pro)
370 {
371 using namespace vpgl_crop_ortho_using_3d_box_process_globals;
372 std::vector<std::string> input_types_(n_inputs_);
373 input_types_[0] = "vil_image_resource_sptr"; // ortho image resource
374 input_types_[1] = "vpgl_camera_double_sptr"; // ortho camera as a vpgl_geo_camera
375 input_types_[2] = "double"; // lower_left_lon
376 input_types_[3] = "double"; // lower_left_lat
377 input_types_[4] = "double"; // lower_left_elev
378 input_types_[5] = "double"; // upper_right_lon
379 input_types_[6] = "double"; // upper_right_lat
380 input_types_[7] = "double"; // upper_right_elev
381
382 std::vector<std::string> output_types_(n_outputs_);
383 output_types_[0] = "vpgl_camera_double_sptr"; // geocam of cropped image
384 output_types_[1] = "unsigned"; // image pixel i0
385 output_types_[2] = "unsigned"; // image pixel j0
386 output_types_[3] = "unsigned"; // image size ni
387 output_types_[4] = "unsigned"; // image size nj
388 return pro.set_input_types(input_types_) && pro.set_output_types(output_types_);
389 }
390
391 // execute the process
vpgl_crop_ortho_using_3d_box_process(bprb_func_process & pro)392 bool vpgl_crop_ortho_using_3d_box_process(bprb_func_process& pro)
393 {
394 using namespace vpgl_crop_ortho_using_3d_box_process_globals;
395 // sanity check
396 if (pro.n_inputs() != n_inputs_) {
397 std::cout << pro.name() << ": The input number should be " << n_inputs_ << std::endl;
398 return false;
399 }
400
401 // get the input
402 unsigned i = 0;
403 vil_image_resource_sptr img_res_sptr = pro.get_input<vil_image_resource_sptr>(i++); // image resource
404 vpgl_camera_double_sptr cam = pro.get_input<vpgl_camera_double_sptr>(i++);
405 auto* geocam = dynamic_cast<vpgl_geo_camera*> (cam.ptr());
406
407 auto lower_left_lon = pro.get_input<double>(i++);
408 auto lower_left_lat = pro.get_input<double>(i++);
409 auto lower_left_elev = pro.get_input<double>(i++);
410 auto upper_right_lon = pro.get_input<double>(i++);
411 auto upper_right_lat = pro.get_input<double>(i++);
412 auto upper_right_elev = pro.get_input<double>(i++);
413
414 // create the 3D box given input coordinates (in geo-coordinates)
415 vgl_box_3d<double> bbox(lower_left_lon, lower_left_lat, lower_left_elev, upper_right_lon, upper_right_lat, upper_right_elev);
416 std::vector<vgl_point_3d<double> > box_corners = bbox.vertices();
417
418 // projection
419 vgl_box_2d<double> roi_box_2d;
420
421 // project the box to image coords
422 for (auto & box_corner : box_corners) {
423 double u, v;
424 geocam->global_to_img(box_corner.x(), box_corner.y(), box_corner.z(), u, v);
425 vgl_point_2d<double> p2d(u, v);
426 roi_box_2d.add(p2d);
427 }
428
429 std::cout << pro.name() << ": projected 2d roi box: " << roi_box_2d << std::endl;
430
431 // crop the image
432 brip_roi broi(img_res_sptr->ni(), img_res_sptr->nj());
433 vsol_box_2d_sptr bb = new vsol_box_2d();
434 bb->add_point(roi_box_2d.min_x(), roi_box_2d.min_y());
435 bb->add_point(roi_box_2d.max_x(), roi_box_2d.max_y());
436 bb = broi.clip_to_image_bounds(bb);
437
438 auto i0 = (unsigned)bb->get_min_x();
439 auto j0 = (unsigned)bb->get_min_y();
440 auto ni = (unsigned)bb->width();
441 auto nj = (unsigned)bb->height();
442
443 if (ni <= 0 || nj <= 0)
444 {
445 std::cout << pro.name() << ": clipping box is out of image boundary, empty crop image returned" << std::endl;
446 return false;
447 }
448 if (i0 > img_res_sptr->ni() || j0 > img_res_sptr->nj())
449 {
450 std::cout << pro.name() << ": clipping box is out of image boundary, empty crop image returned" << std::endl;
451 return false;
452 }
453
454 if ( (i0+ni) > img_res_sptr->ni() && (j0+nj) > img_res_sptr->nj())
455 {
456 std::cout << pro.name() << ": clipping box is out of image boundary, empty crop image returned" << std::endl;
457 return false;
458 }
459
460 // create an ortho geocam for the cropped image -- CAUTION: assumes that the image is aligned East-North (no rotation)
461 double lon0, lat0, lonn, latn;
462 geocam->img_to_global(i0, j0, lon0, lat0);
463 geocam->img_to_global(i0+ni-1, j0+nj-1, lonn, latn);
464 double scalingx = (lonn - lon0)/(ni-1);
465 double scalingy = (latn - lat0)/(nj-1);
466
467 vnl_matrix<double> trans_matrix(4,4,0.0);
468 trans_matrix[0][0] = scalingx;
469 trans_matrix[0][1] = 0.0;
470 trans_matrix[1][0] = 0.0;
471 trans_matrix[1][1] = scalingy;
472 trans_matrix[0][3] = lon0;
473 trans_matrix[1][3] = lat0;
474 trans_matrix[3][3] = 1.0;
475
476 vpgl_geo_camera* camera = new vpgl_geo_camera(trans_matrix, geocam->lvcs());
477 camera->set_scale_format(true);
478
479 // store output
480 unsigned out_j = 0;
481 pro.set_output_val<vpgl_camera_double_sptr>(out_j++, camera);
482 pro.set_output_val<unsigned>(out_j++, i0);
483 pro.set_output_val<unsigned>(out_j++, j0);
484 pro.set_output_val<unsigned>(out_j++, ni);
485 pro.set_output_val<unsigned>(out_j++, nj);
486
487 return true;
488 }
489
490 //: process to crop image using its rational camera and a given region. Note that the elevation values are retrieved from
491 // ASTER DEM height maps, which are Geotiff images
492 // global variables and functions
493 namespace vpgl_crop_img_using_3d_box_dem_process_globals
494 {
495 constexpr unsigned n_inputs_ = 10;
496 constexpr unsigned n_outputs_ = 5;
497 //: find the min and max height in a given region from height map resources
498 bool find_min_max_height(double const& ll_lon, double const& ll_lat, double const& uu_lon, double const& uu_lat,
499 std::vector<std::pair<vil_image_view_base_sptr, vpgl_geo_camera*> >& infos,
500 double& min, double& max);
501 void crop_and_find_min_max(std::vector<std::pair<vil_image_view_base_sptr, vpgl_geo_camera*> >& infos,
502 unsigned const& img_id, int const& i0, int const& j0, int const& crop_ni, int const& crop_nj,
503 double& min, double& max);
504 }
505
506 // initialization
vpgl_crop_img_using_3d_box_dem_process_cons(bprb_func_process & pro)507 bool vpgl_crop_img_using_3d_box_dem_process_cons(bprb_func_process& pro)
508 {
509 using namespace vpgl_crop_img_using_3d_box_dem_process_globals;
510 // process takes 10 inputs
511 std::vector<std::string> input_types_(n_inputs_);
512 input_types_[0] = "vil_image_resource_sptr"; // image resource
513 input_types_[1] = "vpgl_camera_double_sptr"; // rational camera
514 input_types_[2] = "double"; // lower left lon
515 input_types_[3] = "double"; // lower left lat
516 input_types_[4] = "double"; // upper right lon
517 input_types_[5] = "double"; // upper right lat
518 input_types_[6] = "vcl_string"; // ASTER DEM image folder
519 input_types_[7] = "double"; // the amount to be added on top of the terrain height
520 input_types_[8] = "double"; // uncertainty values (in meter units)
521 input_types_[9] = "vpgl_lvcs_sptr"; // lvcs
522
523 // process takes 5 outputs
524 std::vector<std::string> output_types_(n_outputs_);
525 output_types_[0] = "vpgl_camera_double_sptr"; // local crop rational camera
526 output_types_[1] = "unsigned"; // image pixel i0
527 output_types_[2] = "unsigned"; // image pixel j0
528 output_types_[3] = "unsigned"; // image size ni
529 output_types_[4] = "unsigned"; // image size nj
530 bool good = pro.set_input_types(input_types_) && pro.set_output_types(output_types_);
531 // set input default
532 vpgl_lvcs_sptr lvcs = new vpgl_lvcs;
533 pro.set_input(9, new brdb_value_t<vpgl_lvcs_sptr>(lvcs));
534 return good;
535 }
536
537 // execute the process
vpgl_crop_img_using_3d_box_dem_process(bprb_func_process & pro)538 bool vpgl_crop_img_using_3d_box_dem_process(bprb_func_process& pro)
539 {
540 using namespace vpgl_crop_img_using_3d_box_dem_process_globals;
541 // sanity check
542 if (!pro.verify_inputs())
543 {
544 std::cerr << pro.name() << ": Wrong inputs!!!" << std::endl;
545 return false;
546 }
547 // get the inputs
548 unsigned in_i = 0;
549 vil_image_resource_sptr img_res_sptr = pro.get_input<vil_image_resource_sptr>(in_i++); // image resource
550 vpgl_camera_double_sptr cam_sptr = pro.get_input<vpgl_camera_double_sptr>(in_i++); // rational camera
551 auto lower_left_lon = pro.get_input<double>(in_i++);
552 auto lower_left_lat = pro.get_input<double>(in_i++);
553 auto upper_right_lon = pro.get_input<double>(in_i++);
554 auto upper_right_lat = pro.get_input<double>(in_i++);
555 std::string dem_folder = pro.get_input<std::string>(in_i++);
556 auto box_height = pro.get_input<double>(in_i++);
557 auto uncertainty = pro.get_input<double>(in_i++);
558 vpgl_lvcs_sptr lvcs_sptr = pro.get_input<vpgl_lvcs_sptr>(in_i++);
559
560 auto* rat_cam = dynamic_cast<vpgl_rational_camera<double>*>(cam_sptr.as_pointer());
561 if (!rat_cam) {
562 std::cerr << pro.name() << ": the input camera is not a rational camera!\n";
563 return false;
564 }
565
566 // load the height map resources
567 std::vector<std::pair<vil_image_view_base_sptr, vpgl_geo_camera*> > infos;
568 std::string file_glob = dem_folder + "/*.tif";
569 for (vul_file_iterator fn = file_glob.c_str(); fn; ++fn)
570 {
571 std::string filename = fn();
572 vil_image_view_base_sptr img_r = vil_load(filename.c_str());
573 vpgl_geo_camera* cam;
574 vpgl_lvcs_sptr lvcs_dummy = new vpgl_lvcs;
575 vil_image_resource_sptr img_res = vil_load_image_resource(filename.c_str());
576 if (!vpgl_geo_camera::init_geo_camera(img_res, lvcs_dummy, cam)) {
577 std::cerr << pro.name() << ": Given height map " << filename << " is NOT a GeoTiff!\n";
578 return false;
579 }
580 infos.emplace_back(img_r, cam);
581 }
582 if (infos.empty()) {
583 std::cerr << pro.name() << ": No image in the folder: " << dem_folder << std::endl;
584 return false;
585 }
586
587 // obtain the height values from height maps
588 double min = 10000.0, max = -10000.0;
589 if (!find_min_max_height(lower_left_lon, lower_left_lat, upper_right_lon, upper_right_lat, infos, min, max)) {
590 std::cerr << pro.name() << ": find min and max height failed!!!\n";
591 return false;
592 }
593 double lower_left_elev = min;
594 double upper_right_elev = max + box_height;
595
596 std::cout << pro.name() << " lower_left_elev: " << lower_left_elev << ", upper_right_elev: " << upper_right_elev << std::endl;
597 // generate local lvcs to transfer camera offset coordinates
598 double ori_lon, ori_lat, ori_elev;
599 lvcs_sptr->get_origin(ori_lat, ori_lon, ori_elev);
600 if ( (ori_lat+ori_lon+ori_elev)*(ori_lat+ori_lon+ori_elev) < 1E-7) {
601 lvcs_sptr = new vpgl_lvcs(lower_left_lat, lower_left_lon, lower_left_elev, vpgl_lvcs::wgs84, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
602 }
603 vgl_box_3d<double> scene_bbox(lower_left_lon, lower_left_lat, lower_left_elev,
604 upper_right_lon, upper_right_lat, upper_right_elev);
605
606 vgl_box_2d<double> roi_box_2d;
607 bool good = project_box(*rat_cam, lvcs_sptr, scene_bbox, uncertainty, roi_box_2d);
608 if(!good) {
609 return false;
610 }
611 std::cout << pro.name() << ": projected 2d roi box: " << roi_box_2d << " given uncertainty " << uncertainty << " meters." << std::endl;
612 // crop the image
613 brip_roi broi(img_res_sptr->ni(), img_res_sptr->nj());
614 vsol_box_2d_sptr bb = new vsol_box_2d();
615 bb->add_point(roi_box_2d.min_x(), roi_box_2d.min_y());
616 bb->add_point(roi_box_2d.max_x(), roi_box_2d.max_y());
617 bb = broi.clip_to_image_bounds(bb);
618 // store output
619 auto i0 = (unsigned)bb->get_min_x();
620 auto j0 = (unsigned)bb->get_min_y();
621 auto ni = (unsigned)bb->width();
622 auto nj = (unsigned)bb->height();
623
624 if (ni <= 0 || nj <= 0)
625 {
626 std::cout << pro.name() << ": clipping box is out of image boundary, empty crop image returned" << std::endl;
627 return false;
628 }
629
630 // create the local camera
631 auto local_camera = create_local_rational_camera(*rat_cam, *lvcs_sptr, i0, j0);
632
633 // store output
634 unsigned out_j = 0;
635 pro.set_output_val<vpgl_camera_double_sptr>(out_j++, new vpgl_local_rational_camera<double>(local_camera));
636 pro.set_output_val<unsigned>(out_j++, i0);
637 pro.set_output_val<unsigned>(out_j++, j0);
638 pro.set_output_val<unsigned>(out_j++, ni);
639 pro.set_output_val<unsigned>(out_j++, nj);
640 return true;
641 }
642
find_min_max_height(double const & ll_lon,double const & ll_lat,double const & ur_lon,double const & ur_lat,std::vector<std::pair<vil_image_view_base_sptr,vpgl_geo_camera * >> & infos,double & min,double & max)643 bool vpgl_crop_img_using_3d_box_dem_process_globals::find_min_max_height(double const& ll_lon, double const& ll_lat, double const& ur_lon, double const& ur_lat,
644 std::vector<std::pair<vil_image_view_base_sptr, vpgl_geo_camera*> >& infos,
645 double& min, double& max)
646 {
647 // find the corner points
648 std::vector<std::pair<unsigned, std::pair<int, int> > > corners;
649 std::vector<vgl_point_2d<double> > pts;
650 pts.emplace_back(ll_lon, ur_lat);
651 pts.emplace_back(ur_lon, ll_lat);
652 pts.emplace_back(ll_lon, ll_lat);
653 pts.emplace_back(ur_lon, ur_lat);
654 for (auto & pt : pts)
655 {
656 // find the image
657 for (unsigned j = 0; j < (unsigned)infos.size(); j++)
658 {
659 double u, v;
660 infos[j].second->global_to_img(pt.x(), pt.y(), 0, u, v);
661 int uu = (int)std::floor(u+0.5);
662 int vv = (int)std::floor(v+0.5);
663 if (uu < 0 || vv < 0 || uu >= (int)infos[j].first->ni() || vv >= (int)infos[j].first->nj())
664 continue;
665 std::pair<unsigned, std::pair<int, int> > pp(j, std::pair<int, int>(uu, vv));
666 corners.push_back(pp);
667 break;
668 }
669 }
670 if (corners.size() != 4) {
671 std::cerr << "Cannot locate all 4 corners among given DEM tiles!\n";
672 return false;
673 }
674 // case 1 all corners are in the same image
675 if (corners[0].first == corners[1].first) {
676 // crop the image
677 int i0 = corners[0].second.first;
678 int j0 = corners[0].second.second;
679 int crop_ni = corners[1].second.first-corners[0].second.first+1;
680 int crop_nj = corners[1].second.second-corners[0].second.second+1;
681 crop_and_find_min_max(infos, corners[0].first, i0, j0, crop_ni, crop_nj, min, max);
682 return true;
683 }
684 // case 2: two corners are in the same image
685 if (corners[0].first == corners[2].first && corners[1].first == corners[3].first) {
686 // crop the first image
687 int i0 = corners[0].second.first;
688 int j0 = corners[0].second.second;
689 int crop_ni = infos[corners[0].first].first->ni() - corners[0].second.first;
690 int crop_nj = corners[2].second.second-corners[0].second.second+1;
691 crop_and_find_min_max(infos, corners[0].first, i0, j0, crop_ni, crop_nj, min, max);
692
693 // crop the second image
694 i0 = 0;
695 j0 = corners[3].second.second;
696 crop_ni = corners[3].second.first + 1;
697 crop_nj = corners[1].second.second-corners[3].second.second+1;
698 crop_and_find_min_max(infos, corners[1].first, i0, j0, crop_ni, crop_nj, min, max);
699 return true;
700 }
701 // case 3: two corners are in the same image
702 if (corners[0].first == corners[3].first && corners[1].first == corners[2].first) {
703 // crop the first image
704 int i0 = corners[0].second.first;
705 int j0 = corners[0].second.second;
706 int crop_ni = corners[3].second.first - corners[0].second.first + 1;
707 int crop_nj = infos[corners[0].first].first->nj() - corners[0].second.second;
708 crop_and_find_min_max(infos, corners[0].first, i0, j0, crop_ni, crop_nj, min, max);
709
710 // crop the second image
711 i0 = corners[2].second.first;
712 j0 = 0;
713 crop_ni = corners[1].second.first - corners[2].second.first + 1;
714 crop_nj = corners[2].second.second + 1;
715 crop_and_find_min_max(infos, corners[1].first, i0, j0, crop_ni, crop_nj, min, max);
716 return true;
717 }
718 // case 4: all corners are in a different image
719 // crop the first image, image of corner 0
720 int i0 = corners[0].second.first;
721 int j0 = corners[0].second.second;
722 int crop_ni = infos[corners[0].first].first->ni() - corners[0].second.first;
723 int crop_nj = infos[corners[0].first].first->nj() - corners[0].second.second;
724 crop_and_find_min_max(infos, corners[0].first, i0, j0, crop_ni, crop_nj, min, max);
725
726 // crop the second image, image of corner 1
727 i0 = 0;
728 j0 = 0;
729 crop_ni = corners[1].second.first + 1;
730 crop_nj = corners[1].second.second + 1;
731 crop_and_find_min_max(infos, corners[1].first, i0, j0, crop_ni, crop_nj, min, max);
732
733 // crop the third image, image of corner 2
734 i0 = corners[2].second.first;
735 j0 = 0;
736 crop_ni = infos[corners[2].first].first->ni() - corners[2].second.first;
737 crop_nj = corners[2].second.second + 1;
738 crop_and_find_min_max(infos, corners[2].first, i0, j0, crop_ni, crop_nj, min, max);
739
740 // crop the fourth image, image of corner 3
741 i0 = 0;
742 j0 = corners[3].second.second;
743 crop_ni = corners[3].second.first + 1;
744 crop_nj = infos[corners[3].first].first->nj() - corners[3].second.second;
745 crop_and_find_min_max(infos, corners[3].first, i0, j0, crop_ni, crop_nj, min, max);
746 return true;
747 }
748
crop_and_find_min_max(std::vector<std::pair<vil_image_view_base_sptr,vpgl_geo_camera * >> & infos,unsigned const & img_id,int const & i0,int const & j0,int const & crop_ni,int const & crop_nj,double & min,double & max)749 void vpgl_crop_img_using_3d_box_dem_process_globals::crop_and_find_min_max(std::vector<std::pair<vil_image_view_base_sptr, vpgl_geo_camera*> >& infos,
750 unsigned const& img_id, int const& i0, int const& j0, int const& crop_ni, int const& crop_nj,
751 double& min, double& max)
752 {
753 if (auto* img = dynamic_cast<vil_image_view<vxl_int_16>*>(infos[img_id].first.ptr()))
754 {
755 vil_image_view<vxl_int_16> img_crop = vil_crop(*img, i0, crop_ni, j0, crop_nj);
756 for (unsigned ii = 0; ii < img_crop.ni(); ii++) {
757 for (unsigned jj = 0; jj < img_crop.nj(); jj++) {
758 if (min > img_crop(ii, jj)) min = img_crop(ii,jj);
759 if (max < img_crop(ii, jj)) max = img_crop(ii,jj);
760 }
761 }
762 }
763 else if (auto* img = dynamic_cast<vil_image_view<float>*>(infos[img_id].first.ptr()))
764 {
765 vil_image_view<float> img_crop = vil_crop(*img, i0, crop_ni, j0, crop_nj);
766 for (unsigned ii = 0; ii < img_crop.ni(); ii++) {
767 for (unsigned jj = 0; jj < img_crop.nj(); jj++) {
768 if (min > img_crop(ii, jj)) min = img_crop(ii,jj);
769 if (max < img_crop(ii, jj)) max = img_crop(ii,jj);
770 }
771 }
772 }
773 return;
774 }
775 #endif
776