1 // This is brl/bseg/bvxm/pro/processes/bvxm_roi_init_process.cxx
2 #include "bvxm_roi_init_process.h"
3 //:
4 // \file
5 #include <bvxm/bvxm_voxel_world.h>
6 #include <bvxm/bvxm_world_params.h>
7 #include <bvxm/bvxm_util.h>
8 
9 #include "vgl/vgl_point_2d.h"
10 #include "vgl/vgl_box_2d.h"
11 #include "vgl/vgl_box_3d.h"
12 #include "vgl/vgl_vector_3d.h"
13 
14 #include <vsol/vsol_box_2d_sptr.h>
15 #include <vsol/vsol_box_2d.h>
16 
17 #include <brip/brip_roi.h>
18 
19 #include "vil/vil_load.h"
20 #include <vil/file_formats/vil_nitf2_image.h>
21 #include "vil/vil_image_view_base.h"
22 #include "vpgl/vpgl_local_rational_camera.h"
23 
24 #include <bprb/bprb_parameters.h>
25 
26 //: set input and output types
bvxm_roi_init_process_cons(bprb_func_process & pro)27 bool bvxm_roi_init_process_cons(bprb_func_process& pro)
28 {
29   using namespace bvxm_roi_init_process_globals;
30   //this process takes 4 input:
31   //the filename of the image, the camera and the voxel world
32   std::vector<std::string> input_types_(n_inputs_);
33   unsigned  i=0;
34   input_types_[i++] = "vcl_string";                // NITF image path
35   input_types_[i++] = "vpgl_camera_double_sptr";   // rational camera
36   input_types_[i++] = "bvxm_voxel_world_sptr";     // voxel world spec
37   input_types_[i++] = "bool";                      // option to keep less significant bits as casting nitf image
38   if (!pro.set_input_types(input_types_))
39     return false;
40 
41   //output
42   unsigned j = 0;
43   std::vector<std::string> output_types_(n_outputs_);
44   output_types_[j++] = "vpgl_camera_double_sptr"; // unadjusted local rational camera
45   output_types_[j++] = "vil_image_view_base_sptr";  // image ROI
46   output_types_[j++] = "float"; // uncertainty
47   return pro.set_output_types(output_types_);
48 }
49 
50 
bvxm_roi_init_process(bprb_func_process & pro)51 bool bvxm_roi_init_process(bprb_func_process& pro)
52 {
53   using namespace bvxm_roi_init_process_globals;
54   //static const parameters
55   static const std::string error = "error";
56 
57   if ( pro.n_inputs() < n_inputs_ ) {
58     std::cout << pro.name() << " The input number should be " << n_inputs_<< std::endl;
59     return false;
60   }
61 
62   // get the inputs:
63   unsigned i = 0;
64   // image
65   std::string image_path = pro.get_input<std::string>(i++);
66   // camera
67   vpgl_camera_double_sptr camera = pro.get_input<vpgl_camera_double_sptr>(i++);
68   //voxel_world
69   bvxm_voxel_world_sptr voxel_world = pro.get_input<bvxm_voxel_world_sptr>(i++);
70   // option to truncated last 3 less important bits
71   bool is_all_bits = pro.get_input<bool>(i++);
72 
73   // uncertainty (meters) -- SHOULD BE A PARAM
74   float uncertainty=0;
75   if ( !pro.parameters()->get_value(error, uncertainty) ) {
76       std::cout << pro.name() << ": error in retrieving parameters\n";
77     return false;
78   }
79 
80   bvxm_world_params_sptr world_params = voxel_world->get_params();
81 
82 
83   auto* rat_camera =
84     dynamic_cast<vpgl_rational_camera<double>*> (camera.as_pointer());
85   if (!rat_camera) {
86     std::cerr << "The camera input is not a rational camera\n";
87     return false;
88   }
89 
90   vpgl_local_rational_camera<double> local_camera;
91 
92   if (!is_all_bits) {  // throw the less important 3 bits in the nitf image and output an byte image
93     auto* img_ptr = new vil_image_view<unsigned char>();
94     if (!roi_init(image_path, rat_camera, world_params, uncertainty, img_ptr, local_camera)) {
95       std::cerr << "The process has failed!\n";
96       return false;
97     }
98     if (img_ptr->ni() == 0 || img_ptr->nj() == 0)
99       return false;
100     //Store outputs
101     unsigned j = 0;
102     // update the camera and store
103     pro.set_output_val<vpgl_camera_double_sptr >(j++, new vpgl_local_rational_camera<double> (local_camera));
104     // store image output
105     pro.set_output_val<vil_image_view_base_sptr>(j++, img_ptr);
106     // store uncertainty
107     pro.set_output_val<float>(j++, uncertainty);
108 
109     return true;
110   }
111   else {                // keep all 11 bits in the nitf image and output an short image
112     auto* img_ptr = new vil_image_view<vxl_uint_16>();
113     if (!roi_init(image_path, rat_camera, world_params, uncertainty, img_ptr, local_camera)) {
114       std::cerr << pro.name() << ": The process has failed!\n";  return false;
115     }
116     if (img_ptr->ni() == 0 || img_ptr->nj() == 0) {
117       std::cerr << pro.name() << ": The process has failed!\n";  return false;
118     }
119     // store outputs
120     unsigned j = 0;
121     // update the camera and store
122     pro.set_output_val<vpgl_camera_double_sptr >(j++, new vpgl_local_rational_camera<double> (local_camera));
123     // store image output
124     pro.set_output_val<vil_image_view_base_sptr>(j++, img_ptr);
125     // store uncertainty
126     pro.set_output_val<float>(j++, uncertainty);
127     return true;
128   }
129 
130 }
131 
132 //: roi_init function
roi_init(std::string const & image_path,vpgl_rational_camera<double> * camera,const bvxm_world_params_sptr & world_params,float error,vil_image_view<unsigned char> * nitf_image_unsigned_char,vpgl_local_rational_camera<double> & local_camera)133 bool bvxm_roi_init_process_globals::roi_init( std::string const& image_path,
134                                               vpgl_rational_camera<double>* camera,
135                                               const bvxm_world_params_sptr& world_params,
136                                               float error,
137                                               vil_image_view<unsigned char>* nitf_image_unsigned_char,
138                                               vpgl_local_rational_camera<double>& local_camera)
139 {
140   // read the image and extract the camera
141   vil_image_resource_sptr img = vil_load_image_resource(image_path.c_str());
142   std::string format = img->file_format();
143   std::string prefix = format.substr(0,4);
144   if (prefix.compare("nitf") != 0) {
145     std::cerr << "bvxm_roi_init_process::execute - The image should be an NITF\n";
146     return false;
147   }
148 
149   auto* nitf =  static_cast<vil_nitf2_image*> (img.ptr());
150 
151   vgl_vector_3d<unsigned int> dims = world_params->num_voxels();
152   int dimx = dims.x();
153   int dimy = dims.y();
154   int dimz = dims.z();
155   double min_position[3];
156   double voxel_length = world_params->voxel_length();
157   min_position[0] = world_params->corner().x();
158   min_position[1] = world_params->corner().y();
159   min_position[2] = world_params->corner().z();
160   vgl_box_3d<double> box(min_position,
161                          dimx*voxel_length,
162                          dimy*voxel_length,
163                          dimz*voxel_length,
164                          vgl_box_3d<double>::min_pos);
165 
166   vpgl_lvcs_sptr lvcs = world_params->lvcs();
167   vgl_box_2d<double>* roi_box = project_box(camera, lvcs, box, error);
168   std::cout << " roi box: " << *roi_box << std::endl;
169 
170   brip_roi broi(nitf->ni(), nitf->nj());
171   vsol_box_2d_sptr bb = new vsol_box_2d();
172   bb->add_point(roi_box->min_x(), roi_box->min_y());
173   bb->add_point(roi_box->max_x(), roi_box->max_y());
174   bb = broi.clip_to_image_bounds(bb);
175 
176   if (bb->width() <= 0 || bb->height() <= 0) {
177     std::cerr << "bvxm_roi_init_process::roi_init()-- clipping box is out of image boundaries\n";
178     return false;
179   }
180 
181   vil_image_view_base_sptr roi =
182     nitf->get_copy_view((unsigned int)bb->get_min_x(),
183                         (unsigned int)bb->width(),
184                         (unsigned int)bb->get_min_y(),
185                         (unsigned int)bb->height());
186 
187   if (!roi) {
188     std::cerr << "bvxm_roi_init_process::roi_init()-- cannot get copy view from image!\n";
189     return false;
190   }
191 
192   if (!roi.as_pointer())
193     return false;
194 
195   if (roi->pixel_format() == VIL_PIXEL_FORMAT_UINT_16)
196   {
197     vil_image_view<vxl_uint_16> nitf_image_vxl_uint_16(roi);
198 
199     *nitf_image_unsigned_char = vil_image_view<unsigned char> (roi->ni(),roi->nj(),roi->nplanes());
200 
201     int bigendian = 0;
202     { union { unsigned int i; char c[4]; } u; u.i = 1; bigendian = u.c[0] == 0; }
203     for (unsigned m=0; m<nitf_image_unsigned_char->ni(); ++m)
204     {
205       for (unsigned n=0; n<nitf_image_unsigned_char->nj(); ++n)
206       {
207         for (unsigned p=0; p<nitf_image_unsigned_char->nplanes(); ++p)
208         {
209         // we will ignore the most significant 5 bits and less significant 3 bits
210           vxl_uint_16 curr_pixel_val = nitf_image_vxl_uint_16(m,n,p);
211 
212           if (bigendian) {
213             auto* arr = (unsigned char*) &curr_pixel_val;
214             // [defgh3][5abc]
215             // --> [abcdefgh]
216             unsigned char big = *arr;
217             unsigned char small = *(++arr);
218             big >>= 3;
219             small <<= 5;
220             curr_pixel_val = big | small;
221           }
222           else { // little endian
223             // [5abc][defgh3]
224             // --> [abcdefgh35]
225             curr_pixel_val <<= 5;
226             // [abcdefgh35]
227             // --> [abcdefgh]
228             curr_pixel_val >>= 8;
229           }
230 
231           auto pixel_val = static_cast<unsigned char> (curr_pixel_val);
232 
233 #if 0
234           //This is how Thom use to get the region
235           int temp_pix_val = int(int(curr_pixel_val)*255.0/1500.0);
236           if (temp_pix_val > 255)
237             temp_pix_val =255;
238           unsigned char pixel_val = static_cast<unsigned char>(temp_pix_val);
239           //end hack
240 #endif
241 
242           (*nitf_image_unsigned_char)(m,n,p) = pixel_val;
243         }
244       }
245     }
246   }
247   else if (roi->pixel_format() == VIL_PIXEL_FORMAT_BYTE) {
248     *nitf_image_unsigned_char = vil_image_view<unsigned char>(roi);
249   }
250   else
251     std::cout << "bvxm_roi_init_process - Unsupported Pixel Format = " << roi->pixel_format() << std::endl;
252 
253   double u, v;
254   camera->image_offset(u, v);
255   //double tu =  u - roi_box->min_x();
256   //double tv =  v - roi_box->min_y();
257   // bug fix: use image bounding box instead of roi_box to modify the local cam offsets
258   double tu =  u - bb->get_min_x();
259   double tv =  v - bb->get_min_y();
260 
261   //camera->set_image_offset(tu, tv);  // DO NOT MODIFY THE ORIGINAL CAMERA!! It may be used outside of this process in the python process pipeline
262   local_camera = vpgl_local_rational_camera<double> (*lvcs, *camera);
263   local_camera.set_image_offset(tu, tv);
264   delete roi_box;
265   return true;
266 }
267 
roi_init(std::string const & image_path,vpgl_rational_camera<double> * camera,const bvxm_world_params_sptr & world_params,float error,vil_image_view<vxl_uint_16> * nitf_image_unsigned_short,vpgl_local_rational_camera<double> & local_camera)268 bool bvxm_roi_init_process_globals::roi_init( std::string const& image_path,
269                                               vpgl_rational_camera<double>* camera,
270                                               const bvxm_world_params_sptr& world_params,
271                                               float error,
272                                               vil_image_view<vxl_uint_16>* nitf_image_unsigned_short,
273                                               vpgl_local_rational_camera<double>& local_camera)
274 {
275   // read the image and extract the camera
276   vil_image_resource_sptr img = vil_load_image_resource(image_path.c_str());
277   std::string format = img->file_format();
278   std::string prefix = format.substr(0,4);
279   if (prefix.compare("nitf") != 0) {
280     std::cerr << "bvxm_roi_init_process::execute - The image should be an NITF\n";
281     return false;
282   }
283 
284   auto* nitf =  static_cast<vil_nitf2_image*> (img.ptr());
285 
286   vgl_vector_3d<unsigned int> dims = world_params->num_voxels();
287   int dimx = dims.x();
288   int dimy = dims.y();
289   int dimz = dims.z();
290   double min_position[3];
291   double voxel_length = world_params->voxel_length();
292   min_position[0] = world_params->corner().x();
293   min_position[1] = world_params->corner().y();
294   min_position[2] = world_params->corner().z();
295   vgl_box_3d<double> box(min_position,
296                          dimx*voxel_length,
297                          dimy*voxel_length,
298                          dimz*voxel_length,
299                          vgl_box_3d<double>::min_pos);
300 
301   vpgl_lvcs_sptr lvcs = world_params->lvcs();
302   vgl_box_2d<double>* roi_box = project_box(camera, lvcs, box, error);
303   std::cout << " roi box: " << *roi_box << std::endl;
304 
305   brip_roi broi(nitf->ni(), nitf->nj());
306   vsol_box_2d_sptr bb = new vsol_box_2d();
307   bb->add_point(roi_box->min_x(), roi_box->min_y());
308   bb->add_point(roi_box->max_x(), roi_box->max_y());
309   bb = broi.clip_to_image_bounds(bb);
310 
311   if (bb->width() <= 0 || bb->height() <= 0) {
312     std::cerr << "bvxm_roi_init_process::roi_init()-- clipping box is out of image boundaries\n";
313     return false;
314   }
315 
316   vil_image_view_base_sptr roi =
317     nitf->get_copy_view((unsigned int)bb->get_min_x(),
318                         (unsigned int)bb->width(),
319                         (unsigned int)bb->get_min_y(),
320                         (unsigned int)bb->height());
321 
322   if (!roi) {
323     std::cerr << "bvxm_roi_init_process::roi_init()-- cannot get copy view from image!\n";
324     return false;
325   }
326 
327   if (!roi.as_pointer())
328     return false;
329 
330   if (roi->pixel_format() == VIL_PIXEL_FORMAT_UINT_16)
331   {
332     vil_image_view<vxl_uint_16> nitf_image_vxl_uint_16(roi);
333 
334     *nitf_image_unsigned_short = vil_image_view<vxl_uint_16> (roi->ni(),roi->nj(),roi->nplanes());
335 
336     int bigendian = 0;
337     { union { unsigned int i; char c[4]; } u; u.i = 1; bigendian = u.c[0] == 0; }
338     for (unsigned m=0; m<nitf_image_unsigned_short->ni(); ++m)
339     {
340       for (unsigned n=0; n<nitf_image_unsigned_short->nj(); ++n)
341       {
342         for (unsigned p=0; p<nitf_image_unsigned_short->nplanes(); ++p)
343         {
344           // we will ignore the most significant 5 bits but keep all other 11 bits for nitf image
345           vxl_uint_16 curr_pixel_val = nitf_image_vxl_uint_16(m,n,p);
346           if (bigendian) {
347             auto* arr = (unsigned char*) &curr_pixel_val;
348             // [defgh3][5abc]
349             // --> [abcdefgh3]
350             unsigned char big = *arr;
351             unsigned char small = *(++arr);
352             small <<= 5;
353             curr_pixel_val = big | small;
354           }
355           else { // little endian
356             // [5abc][defgh3]
357             // --> [abcdefgh35]
358             curr_pixel_val <<= 5;
359             // [abcdefgh35]
360             // --> [abcdefgh3]
361             curr_pixel_val >>= 5;
362           }
363           (*nitf_image_unsigned_short)(m,n,p) = curr_pixel_val;
364         }
365       }
366     }
367   }
368   else if (roi->pixel_format() == VIL_PIXEL_FORMAT_BYTE) {
369     vil_image_view<unsigned char> nitf_image_vxl_unsigned_char(roi);
370     *nitf_image_unsigned_short = vil_image_view<vxl_uint_16> (roi->ni(),roi->nj(),roi->nplanes());
371     for (unsigned m=0; m<nitf_image_unsigned_short->ni(); ++m)
372       for (unsigned n=0; n<nitf_image_unsigned_short->nj(); ++n)
373         for (unsigned p=0; p<nitf_image_unsigned_short->nplanes(); ++p)
374           (*nitf_image_unsigned_short)(m,n,p) = static_cast<vxl_uint_16>(nitf_image_vxl_unsigned_char(m,n,p));
375   }
376   else {
377     std::cout << "bvxm_roi_init_process - Unsupported Pixel Format = " << roi->pixel_format() << std::endl;
378     return false;
379   }
380 
381   double u, v;
382   camera->image_offset(u, v);
383   //double tu =  u - roi_box->min_x();
384   //double tv =  v - roi_box->min_y();
385   // bug fix: use image bounding box instead of roi_box to modify the local cam offsets
386   double tu =  u - bb->get_min_x();
387   double tv =  v - bb->get_min_y();
388 
389   //camera->set_image_offset(tu, tv);  // DO NOT MODIFY THE ORIGINAL CAMERA!! It may be used outside of this process in the python process pipeline
390   local_camera = vpgl_local_rational_camera<double> (*lvcs, *camera);
391   local_camera.set_image_offset(tu, tv);
392   delete roi_box;
393   return true;
394 }
395 
396 //: project_box function
project_box(vpgl_rational_camera<double> * cam,const vpgl_lvcs_sptr & lvcs,vgl_box_3d<double> box,float r)397 vgl_box_2d<double>* bvxm_roi_init_process_globals::project_box( vpgl_rational_camera<double>* cam,
398                                                                 const vpgl_lvcs_sptr& lvcs,
399                                                                 vgl_box_3d<double> box,
400                                                                 float r)
401 {
402   double xoff, yoff, zoff;
403   xoff = cam->offset(vpgl_rational_camera<double>::X_INDX);
404   yoff = cam->offset(vpgl_rational_camera<double>::Y_INDX);
405   zoff = cam->offset(vpgl_rational_camera<double>::Z_INDX);
406 
407   // global to local
408   double lx, ly, lz;
409   lvcs->global_to_local(xoff, yoff, zoff, vpgl_lvcs::wgs84, lx, ly, lz, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
410   double center[3];
411   center[0] = lx;
412   center[1] = ly;
413   center[2] = lz;
414 
415   // create a box with uncertainty
416   vgl_box_3d<double> cam_box(center, 2*r, 2*r, 2*r, vgl_box_3d<double>::centre);
417   std::vector<vgl_point_3d<double> > cam_corners = bvxm_util::corners_of_box_3d<double>(cam_box);
418   std::vector<vgl_point_3d<double> > box_corners = bvxm_util::corners_of_box_3d<double>(box);
419   auto* roi = new vgl_box_2d<double>();
420 
421   double lon, lat, gz;
422   for (auto cam_corner : cam_corners) {
423     lvcs->local_to_global(cam_corner.x(), cam_corner.y(), cam_corner.z(),
424                           vpgl_lvcs::wgs84, lon, lat, gz, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
425     vpgl_rational_camera<double>* new_cam = cam->clone();
426     new_cam->set_offset(vpgl_rational_camera<double>::X_INDX, lon);
427     new_cam->set_offset(vpgl_rational_camera<double>::Y_INDX, lat);
428     new_cam->set_offset(vpgl_rational_camera<double>::Z_INDX, gz);
429 
430     // project the box
431     for (auto & box_corner : box_corners) {
432       // convert the box corners to world coordinates
433       lvcs->local_to_global(box_corner.x(), box_corner.y(), box_corner.z(),
434                             vpgl_lvcs::wgs84, lon, lat, gz, vpgl_lvcs::DEG, vpgl_lvcs::METERS);
435       vgl_point_2d<double> p2d = new_cam->project(vgl_point_3d<double>(lon, lat, gz));
436       roi->add(p2d);
437     }
438   }
439   return roi;
440 }
441