1 // This is brl/bpro/core/vpgl_pro/processes/bpgl_transform_perspective_cameras_process.cxx
2 #include <iostream>
3 #include <fstream>
4 #include <bprb/bprb_func_process.h>
5 //:
6 // \file
7 #include <brdb/brdb_value.h>
8 #include <brdb/brdb_selection.h>
9 #include <bprb/bprb_batch_process_manager.h>
10 #include <bprb/bprb_parameters.h>
11 #include <bprb/bprb_macros.h>
12 
13 #include "vul/vul_file.h"
14 #include "vul/vul_file_iterator.h"
15 #include <vgl/algo/vgl_rotation_3d.h>
16 
17 #include "vpgl/vpgl_perspective_camera.h"
18 #include <bpgl/algo/bpgl_transform_camera.h>
19 
20 namespace bpgl_transform_perspective_cameras_process_globals
21 {
22     constexpr unsigned n_inputs_ = 3;
23     constexpr unsigned n_outputs_ = 0;
24 }
25 
26 //: Init function
bpgl_transform_perspective_cameras_process_cons(bprb_func_process & pro)27 bool bpgl_transform_perspective_cameras_process_cons(bprb_func_process& pro)
28 {
29   using namespace bpgl_transform_perspective_cameras_process_globals;
30 
31   //process takes 3 inputs
32   std::vector<std::string> input_types_(n_inputs_);
33   input_types_[0] = "vcl_string"; // transformation file
34   input_types_[1] = "vcl_string"; // Input cam dir
35   input_types_[2] = "vcl_string"; // Output cam dir
36 
37   // process has 0 outputs
38   std::vector<std::string>  output_types_(n_outputs_);
39 
40   return pro.set_input_types(input_types_) &&
41          pro.set_output_types(output_types_);
42 }
43 
44 //: Execute the process
bpgl_transform_perspective_cameras_process(bprb_func_process & pro)45 bool bpgl_transform_perspective_cameras_process(bprb_func_process& pro)
46 {
47   // Sanity check
48   if (!pro.verify_inputs()) {
49     std::cerr << "bpgl_transform_perspective_cameras_process: Invalid inputs\n";
50     return false;
51   }
52 
53   // get the inputs
54   unsigned i=0;
55   std::string xform_file = pro.get_input<std::string>(i++);
56   std::string in_dir = pro.get_input<std::string>(i++);
57   std::string out_dir = pro.get_input<std::string>(i++);
58   std::cout<<"out_dir "<<out_dir<<std::endl;
59   // check if input directory exists
60   if (!vul_file::is_directory(in_dir.c_str()))
61   {
62     std::cout<<"Input Camera directory does not exist"<<std::endl;
63     return false;
64   }
65 
66   // check if output directory exists
67   if (!vul_file::is_directory(out_dir.c_str()))
68   {
69     std::cout<<"Output Camera directory does not exist"<<std::endl;
70     return false;
71   }
72 
73   // read the xform file
74   // * xaxis yaxis zaxis roation angle
75   // * tx ty tz
76   // * scale
77   std::ifstream ifile(xform_file.c_str());
78   if (!ifile)
79   {
80     std::cout<<"Cannot open Xform file"<<std::endl;
81     return false;
82   }
83 
84   double xr,yr,zr;
85   double tx,ty,tz,scale;
86 
87   ifile>>xr>>yr>>zr;
88   ifile>>tx>>ty>>tz;
89   ifile>>scale;
90   vnl_quaternion<double> q(xr,yr,zr);
91   vgl_rotation_3d<double> R(q);
92   vnl_vector_fixed<double, 3> t;
93   t[0] = tx; t[1] = ty; t[2] = tz;
94   // xform and save the xformed cams
95   std::string glob_in_dir = in_dir + "/*.txt";
96   for (vul_file_iterator fn = glob_in_dir.c_str(); fn; ++fn) {
97     std::string f = fn();
98     std::ifstream is(f.c_str());
99     vpgl_perspective_camera<double> cam;
100     is >> cam;
101     is.close();
102     std::string fname = vul_file::strip_directory(f.c_str());
103     vpgl_perspective_camera<double> tcam = bpgl_transform_camera::transform_perspective_camera(cam, R, t, scale);
104     std::string out_file = out_dir + "/"+ fname;
105     std::ofstream os(out_file.c_str());
106     os << tcam;
107     os.close();
108   }
109   return true;
110 }
111 
112 
113 //
114 //   this process is a version of bwm_3d_site_transform.cxx
115 //
116 //   input: pts0 and pts1
117 //     compute similarity transformation that maps space of pt0 to space of pts1
118 //
119 // the input camera is in the coordinate system of pts0. The output camera
120 // is the camera in the coordinate system of pts1, that is,
121 //
122 //  x1 =  K[R0|t0](Hs Hs^-1) X1, where Hs is the similarity transform.
123 //
124 // Thus, the similarity transform is applied to the camera as,
125 // (s = scale)
126 //                        _     _  _      _
127 //                       |s 0 0 0||        |
128 //  K[R' | t'] = K[R0|t0]|0 s 0 0||  Rs  ts|
129 //                       |0 0 s 0||        |
130 //                       |0 0 0 1|| 0 0 0 1|
131 //                        -      - -      -
132 // It follows that R' = R0*Rs and t' = t0/s + R0*ts
133 //
134 //
135 
136 #include <core/bbas_pro/bbas_1d_array_double.h>
137 #include <vpgl/algo/vpgl_ortho_procrustes.h>
138 #include <vgl/algo/vgl_h_matrix_3d.h>
139 #include "vgl/vgl_box_3d.h"
140 
141 static vpgl_perspective_camera<double>
vpgl_transform_space_process_transform_camera(vpgl_perspective_camera<double> const & cam,vgl_rotation_3d<double> const & Rs,vnl_vector_fixed<double,3> const & ts,const double scale)142 vpgl_transform_space_process_transform_camera(vpgl_perspective_camera<double> const& cam,
143                  vgl_rotation_3d<double> const& Rs,
144                  vnl_vector_fixed<double, 3> const& ts,
145                  const double scale)
146 {
147   vnl_matrix_fixed<double,3,3> Rms = Rs.as_matrix();
148   //Get input camera components
149   //note, the homogeneous calibration matrix is unaffected by the scale
150   const vpgl_calibration_matrix<double>& K = cam.get_calibration();
151   vnl_matrix_fixed<double, 3, 3> R = cam.get_rotation().as_matrix();
152   vgl_vector_3d<double> tv = cam.get_translation();
153   vnl_vector_fixed<double, 3> t(tv.x(), tv.y(), tv.z());
154   //compose rotations
155   vnl_matrix_fixed<double, 3, 3> Rt = R*Rms;
156   vgl_rotation_3d<double> Rtr(Rt);
157   //compute new translation
158   vnl_vector_fixed<double, 3> tt = (1.0/scale)*t + R*ts;
159   vgl_vector_3d<double> ttg(tt[0], tt[1], tt[2]);
160   //construct transformed camera
161   vpgl_perspective_camera<double> camt(K, Rtr, ttg);
162   return camt;
163 }
164 
165 //: sets input and output types
vpgl_transform_space_process_cons(bprb_func_process & pro)166 bool vpgl_transform_space_process_cons(bprb_func_process& pro)
167 {
168   //inputs
169   std::vector<std::string> input_types_(8);
170   input_types_[0] = "bbas_1d_array_double_sptr"; // x values of pts0 (points in SPACE 0)
171   input_types_[1] = "bbas_1d_array_double_sptr"; // y values of pts0 (points in SPACE 0)
172   input_types_[2] = "bbas_1d_array_double_sptr"; // z values of pts0 (points in SPACE 0)
173   input_types_[3] = "bbas_1d_array_double_sptr"; // x values of pts1 (points in SPACE 1)
174   input_types_[4] = "bbas_1d_array_double_sptr"; // y values of pts1 (points in SPACE 1)
175   input_types_[5] = "bbas_1d_array_double_sptr"; // z values of pts1 (points in SPACE 1)
176   input_types_[6] = "vcl_string";      // path to input camera folder
177   input_types_[7] = "vcl_string"; // path to output camera folder
178 
179   if (!pro.set_input_types(input_types_))
180     return false;
181   //output
182   std::vector<std::string> output_types_(2);
183   output_types_[0] = "bbas_1d_array_double_sptr"; // output 4 by 4 similarity matrix as a vector of size 16
184                                                   // construct the matrix as follows
185                                                   // 0  1  2  3
186                                                   // 4  5  6  7
187                                                   // 8  9  10 11
188                                                   // 12 13 14 15
189   output_types_[1] = "double"; // also output scale as a double to scale other quantities appropriately if needed, e.g. the voxel_size if a new scene.xml is needed
190   return pro.set_output_types(output_types_);
191 }
192 
vpgl_transform_space_process(bprb_func_process & pro)193 bool vpgl_transform_space_process(bprb_func_process& pro)
194 {
195   //check number of inputs
196   if (!pro.verify_inputs())
197   {
198     std::cout << pro.name() << " invalid inputs" << std::endl;
199     return false;
200   }
201 
202   //get the inputs
203   bbas_1d_array_double_sptr pts0_xs = pro.get_input<bbas_1d_array_double_sptr>(0);
204   bbas_1d_array_double_sptr pts0_ys = pro.get_input<bbas_1d_array_double_sptr>(1);
205   bbas_1d_array_double_sptr pts0_zs = pro.get_input<bbas_1d_array_double_sptr>(2);
206   bbas_1d_array_double_sptr pts1_xs = pro.get_input<bbas_1d_array_double_sptr>(3);
207   bbas_1d_array_double_sptr pts1_ys = pro.get_input<bbas_1d_array_double_sptr>(4);
208   bbas_1d_array_double_sptr pts1_zs = pro.get_input<bbas_1d_array_double_sptr>(5);
209 
210   std::string input_cam_path = pro.get_input<std::string>(6);
211   std::string output_cam_path = pro.get_input<std::string>(7);
212 
213   std::cout << pts0_xs->data_array;
214 
215   vnl_matrix<double> pts0, pts1;
216   auto n = (unsigned)(pts0_xs->data_array.size());
217 
218   pts0.set_size(3,n);
219   pts1.set_size(3,n);
220   for (unsigned i = 0; i<n; ++i) {
221     pts0[0][i] = pts0_xs->data_array[i];  pts1[0][i] = pts1_xs->data_array[i];
222     pts0[1][i] = pts0_ys->data_array[i];  pts1[1][i] = pts1_ys->data_array[i];
223     pts0[2][i] = pts0_zs->data_array[i];  pts1[2][i] = pts1_zs->data_array[i];
224   }
225 
226   // prepare the output matrix as an array
227   bbas_1d_array_double_sptr out = new bbas_1d_array_double(16);
228 
229   // compute the similarity transformation
230   vpgl_ortho_procrustes op(pts0, pts1);
231   vgl_rotation_3d<double> R = op.R();
232   vnl_vector_fixed<double, 3> t = op.t();
233   double scale = op.s();
234   if (! op.compute_ok()) {
235     std::cerr << "Problems during similarity transformation computation! Check if there are enough number of points! Current number of points: " << n << ". Minimum required number of points is 3, suggested to use a minimum of 5!" << std::endl;
236     pro.set_output_val<bbas_1d_array_double_sptr>(0, out);
237     return false;
238   }
239 
240   std::cout << "Copmuted similarity matrix! Ortho procrustes error " << std::sqrt(op.residual_mean_sq_error()) << std::endl;
241 
242   // read each camera and save the output camera in the output folder
243   std::string in_dir = input_cam_path + "/*.txt";
244   for (vul_file_iterator fn = in_dir.c_str(); fn; ++fn) {
245     std::string f = fn();
246     std::ifstream is(f.c_str());
247     vpgl_perspective_camera<double> cam;
248     is >> cam;
249     is.close();
250     std::string fname = vul_file::strip_directory(f.c_str());
251     std::cout << fname << std::endl;
252     vpgl_perspective_camera<double> tcam =
253       vpgl_transform_space_process_transform_camera(cam, R, t, scale);
254     std::cout<<"CC : "<<tcam.camera_center()<<std::endl;
255     std::string out_dir = output_cam_path + "/";
256     std::string out_file = out_dir + fname;
257     std::ofstream os(out_file.c_str());
258     os << tcam;
259     os.close();
260   }
261 
262   // prepare the output matrix, we need the matrix that takes a point in the space of pts0 to the space of pts1,
263   // this is actually the inverse of the transformation computed above!!
264   vnl_matrix_fixed<double, 4, 4> S(0.0), outM(0.0);
265   S[0][0] = S[1][1] = S[2][2] = 1.0/scale; S[3][3] = 1.0;
266   // inverse of t is a little more complicated
267   vnl_vector_fixed<double, 3> t_inverse = -R.inverse().as_matrix()*t;
268   vgl_h_matrix_3d<double> RotT(R.inverse().as_matrix(), t_inverse);
269   outM = RotT.get_matrix()*S;
270 
271   // copy to the output array
272   out->data_array[0] = outM[0][0]; out->data_array[1] = outM[0][1]; out->data_array[2] = outM[0][2]; out->data_array[3] = outM[0][3];
273   out->data_array[4] = outM[1][0]; out->data_array[5] = outM[1][1]; out->data_array[6] = outM[1][2]; out->data_array[7] = outM[1][3];
274   out->data_array[8] = outM[2][0]; out->data_array[9] = outM[2][1]; out->data_array[10] = outM[2][2]; out->data_array[11] = outM[2][3];
275   out->data_array[12] = outM[3][0]; out->data_array[13] = outM[3][1]; out->data_array[14] = outM[3][2]; out->data_array[15] = outM[3][3];
276 
277   pro.set_output_val<bbas_1d_array_double_sptr>(0, out);
278   pro.set_output_val<double>(1,1.0/scale);
279   return true;
280 }
281 
282 // a process to take min and max points of a box in the space of pts0 then transform 8 corner points to the space of pts1
283 // in the transformed space, compute a new axis aligned bounding box and return its min and max points
284 
285 //: sets input and output types
vpgl_transform_box_process_cons(bprb_func_process & pro)286 bool vpgl_transform_box_process_cons(bprb_func_process& pro)
287 {
288   //inputs
289   std::vector<std::string> input_types_(3);
290   input_types_[0] = "bbas_1d_array_double_sptr"; // min pt x y z values in SPACE 0
291   input_types_[1] = "bbas_1d_array_double_sptr"; // max pt x y z values in SPACE 0
292   input_types_[2] = "bbas_1d_array_double_sptr"; // input 4 by 4 similarity matrix as a vector of size 16
293                                                   // construct the matrix as follows
294                                                   // 0  1  2  3
295                                                   // 4  5  6  7
296                                                   // 8  9  10 11
297                                                   // 12 13 14 15
298 
299 
300   if (!pro.set_input_types(input_types_))
301     return false;
302   //output
303   std::vector<std::string> output_types_(2);
304   output_types_[0] = "bbas_1d_array_double_sptr"; // min pt x y z of the bounding box in SPACE 1
305   output_types_[1] = "bbas_1d_array_double_sptr"; // max pt x y z of the bounding box in SPACE 1
306   return pro.set_output_types(output_types_);
307 }
308 
vpgl_transform_box_process(bprb_func_process & pro)309 bool vpgl_transform_box_process(bprb_func_process& pro)
310 {
311   //check number of inputs
312   if (!pro.verify_inputs())
313   {
314     std::cout << pro.name() << " invalid inputs" << std::endl;
315     return false;
316   }
317 
318   //get the inputs
319   bbas_1d_array_double_sptr min_pt_a = pro.get_input<bbas_1d_array_double_sptr>(0);
320   bbas_1d_array_double_sptr max_pt_a = pro.get_input<bbas_1d_array_double_sptr>(1);
321   bbas_1d_array_double_sptr matrix = pro.get_input<bbas_1d_array_double_sptr>(2);
322 
323   // construct the similarity matrix
324   vnl_matrix_fixed<double, 4, 4> SM;
325   SM[0][0] = matrix->data_array[0];  SM[0][1] = matrix->data_array[1];  SM[0][2] = matrix->data_array[2];  SM[0][3] = matrix->data_array[3];
326   SM[1][0] = matrix->data_array[4];  SM[1][1] = matrix->data_array[5];  SM[1][2] = matrix->data_array[6];  SM[1][3] = matrix->data_array[7];
327   SM[2][0] = matrix->data_array[8];  SM[2][1] = matrix->data_array[9];  SM[2][2] = matrix->data_array[10]; SM[2][3] = matrix->data_array[11];
328   SM[3][0] = matrix->data_array[12]; SM[3][1] = matrix->data_array[13]; SM[3][2] = matrix->data_array[14]; SM[3][3] = matrix->data_array[15];
329 
330   // create 8 corners of the input box
331   vgl_box_3d<double> box1, box2;
332   box1.add(vgl_point_3d<double>(min_pt_a->data_array[0], min_pt_a->data_array[1], min_pt_a->data_array[2]));
333   box1.add(vgl_point_3d<double>(max_pt_a->data_array[0], max_pt_a->data_array[1], max_pt_a->data_array[2]));
334   std::vector<vgl_point_3d<double> > vertices = box1.vertices();
335 
336   // transform 8 corners of the box and add to the new box in the transformed space
337   for (auto & vertice : vertices) {
338     vnl_matrix_fixed<double, 4, 1> pt, new_pt;
339     pt[0][0] = vertice.x(); pt[1][0] = vertice.y(); pt[2][0] = vertice.z(); pt[3][0] = 1.0;
340     new_pt = SM*pt;
341     box2.add(vgl_point_3d<double>(new_pt[0][0]/new_pt[3][0], new_pt[1][0]/new_pt[3][0], new_pt[2][0]/new_pt[3][0]));
342   }
343   vgl_point_3d<double> out_min_pt = box2.min_point();
344   vgl_point_3d<double> out_max_pt = box2.max_point();
345 
346   bbas_1d_array_double_sptr outmin = new bbas_1d_array_double(3), outmax = new bbas_1d_array_double(3);
347   outmin->data_array[0] = out_min_pt.x(); outmin->data_array[1] = out_min_pt.y(); outmin->data_array[2] = out_min_pt.z();
348   outmax->data_array[0] = out_max_pt.x(); outmax->data_array[1] = out_max_pt.y(); outmax->data_array[2] = out_max_pt.z();
349 
350   pro.set_output_val<bbas_1d_array_double_sptr>(0, outmin);
351   pro.set_output_val<bbas_1d_array_double_sptr>(1, outmax);
352   return true;
353 }
354