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