1 // This is core/vpgl/algo/vpgl_ray_intersect.hxx
2 #ifndef vpgl_ray_intersect_hxx_
3 #define vpgl_ray_intersect_hxx_
4 
5 #include <iostream>
6 #include <utility>
7 #include "vpgl_ray_intersect.h"
8 //:
9 // \file
10 #include <vnl/vnl_least_squares_function.h>
11 #include <vnl/algo/vnl_levenberg_marquardt.h>
12 #include <vnl/vnl_numeric_traits.h>
13 #include <vnl/vnl_vector.h>
14 #include <vnl/vnl_double_3.h>
15 #ifdef _MSC_VER
16 #  include <vcl_msvc_warnings.h>
17 #endif
18 #include <vpgl/vpgl_camera.h>
19 
20 template<typename T>
21 class vpgl_ray_intersect_lsqr : public vnl_least_squares_function
22 {
23 public:
24     //: Constructor
25     vpgl_ray_intersect_lsqr(std::vector<const vpgl_camera<T>* >  cams,
26                             std::vector<vgl_point_2d<T> >  image_pts,
27                             unsigned num_residuals);
28 
29     //: Destructor
30     ~vpgl_ray_intersect_lsqr() override = default;
31 
32     //: The main function.
33     //  Given the parameter vector x, compute the vector of residuals fx.
34     //  fx has been sized appropriately before the call.
35     void f(vnl_vector<double> const& intersection_point,
36                    vnl_vector<double>& image_errors) override;
37 
38 #if 0
39     //: Called after each LM iteration to print debugging etc.
40     virtual void trace(int iteration, vnl_vector<double> const& x, vnl_vector<double> const& fx);
41 #endif
42 
43 protected:
44     vpgl_ray_intersect_lsqr();//not valid
45     std::vector<const vpgl_camera<T>* > f_cameras_; //cameras
46     std::vector<vgl_point_2d<T> > f_image_pts_; //image points
47 };
48 
49 template<typename T>
50 vpgl_ray_intersect_lsqr<T>::
vpgl_ray_intersect_lsqr(std::vector<const vpgl_camera<T> * > cams,std::vector<vgl_point_2d<T>> image_pts,unsigned num_residuals)51 vpgl_ray_intersect_lsqr(std::vector<const vpgl_camera<T>* > cams,
52                         std::vector<vgl_point_2d<T> > image_pts,
53                         unsigned num_residuals) :
54 vnl_least_squares_function(3, num_residuals,
55                            vnl_least_squares_function::no_gradient ),
56 f_cameras_(std::move(cams)),
57 f_image_pts_(std::move(image_pts))
58 {}
59 
60 // Define virtual function for the LeastSquaresFunction class.  Given
61 // a conjectured point (intersection_vert) in space, this determines the
62 // error vector formed by appending all <u - conjecture_u, v - conjecture_v>
63 // error tuples for each image.  Here <u, v> is the actual image point, and
64 // <conjecture_u, conjecture_v> is the point corresponding to intersection_vert.
65 template<typename T>
f(vnl_vector<double> const & intersection_point,vnl_vector<double> & image_errors)66 void vpgl_ray_intersect_lsqr<T>::f(vnl_vector<double> const& intersection_point,
67                                 vnl_vector<double>& image_errors)
68 {
69     // Get the size of the error vector
70     std::size_t dim = static_cast<unsigned int>(image_errors.size() / 2);
71 
72     // Initialize huge error
73     double huge = vnl_numeric_traits<double>::maxval;
74     for (unsigned i=0; i<image_errors.size(); i++)
75         image_errors.put(i, huge);
76 
77     // Compute the error in each image
78     double intersection_point_x =  intersection_point[0];
79     double intersection_point_y =  intersection_point[1];
80     double intersection_point_z =  intersection_point[2];
81 #ifdef RAY_INT_DEBUG
82     std::cout << "Error Vector (" << intersection_point_x << ", "
83     << intersection_point_y << ", " << intersection_point_z << ") = ";
84 #endif
85 
86     for (unsigned image_no = 0; image_no < dim; image_no++)
87     {
88         // Get this camera and corresponding image point
89         const vpgl_camera<T>* cam = f_cameras_[image_no];
90         double image_u = f_image_pts_[image_no].x(),
91         image_v = f_image_pts_[image_no].y();
92         // Compute the image of the intersection vert through this camera
93         T cur_image_u, cur_image_v;
94         cam->project(intersection_point_x, intersection_point_y,
95                      intersection_point_z, cur_image_u, cur_image_v);
96         // Compute and store the error with respect to actual image
97         image_errors.put(2*image_no,
98                          (cur_image_u - image_u));
99         image_errors.put(2*image_no+1,
100                          (cur_image_v - image_v));
101 #ifdef RAY_INT_DEBUG
102         std::cout << " x_err = " << cur_image_u << '-' << image_u << '='
103         << image_errors[2*image_no]
104         << " y_err = " << cur_image_v << '-' << image_v << '='
105         << image_errors[2*image_no+1];
106 #endif
107 
108 
109 #ifdef RAY_INT_DEBUG
110         std::cout << '\n';
111 #endif
112     }
113 }
114 
115 // Constructor.
116 template<typename T>
vpgl_ray_intersect(unsigned dim)117 vpgl_ray_intersect<T>::vpgl_ray_intersect(unsigned dim): dim_(dim)
118 {}
119 
120 // A function to get the point closest to the rays coming out of image_pts
121 // in images, whose cameras (cams) are known.  Point is stored in intersection.
122 // Returns true if successful, else false
123 template<typename T>
124 bool vpgl_ray_intersect<T>::
intersect(std::vector<const vpgl_camera<T> * > const & cams,std::vector<vgl_point_2d<T>> const & image_pts,vgl_point_3d<T> const & initial_intersection,vgl_point_3d<T> & intersection)125 intersect(std::vector<const vpgl_camera<T>* > const& cams,
126           std::vector<vgl_point_2d<T> > const& image_pts,
127           vgl_point_3d<T> const& initial_intersection,
128           vgl_point_3d<T>& intersection)
129 {
130   // Make sure the dimension is at least 2
131   if (dim_ < 2)
132   {
133     std::cerr << "The dimension is too small.  There must be at least 2 images"
134              << '\n';
135     return false;
136   }
137 
138   // Make sure there are correct number of cameras
139   if (cams.size() != dim_)
140   {
141     std::cerr << "Please provide correct number of cameras" << '\n';
142     return false;
143   }
144 
145   // Make sure there are correct number of image points
146   if (image_pts.size() != dim_)
147   {
148     std::cerr << "Please provide correct number of image points" << '\n';
149     return false;
150   }
151 
152   // cache the image points and camera points
153   f_cameras_ = cams;
154   f_image_pts_ = image_pts;
155 
156   // Create the Levenberg Marquardt minimizer
157   vpgl_ray_intersect_lsqr<T> lqf(cams, image_pts, 2*dim_);
158   vnl_levenberg_marquardt levmarq(lqf);
159 #ifdef RAY_INT_DEBUG
160   std::cout << "Created LevenbergMarquardt minimizer ... setting tolerances\n";
161   levmarq.set_verbose(true);
162 #endif
163   // Set the x-tolerance.  When the length of the steps taken in X (variables)
164   // are no longer than this, the minimization terminates.
165   levmarq.set_x_tolerance(1e-10);
166 
167   // Set the epsilon-function.  This is the step length for FD Jacobian.
168   levmarq.set_epsilon_function(1.0);
169 
170   // Set the f-tolerance.  When the successive RMS errors are less than this,
171   // minimization terminates.
172   levmarq.set_f_tolerance(1e-10);
173 
174   // Set the maximum number of iterations
175   levmarq.set_max_function_evals(10000);
176   vnl_double_3 intersection_pt;
177   intersection_pt[0]=initial_intersection.x();
178   intersection_pt[1]=initial_intersection.y();
179   intersection_pt[2]=initial_intersection.z();
180 
181 #ifdef RAY_INT_DEBUG
182   std::cout << "Initialized the intersection point " << intersection_pt
183            << " ... minimizing\n";
184 #endif
185 
186   // Minimize the error and get the best intersection point
187   levmarq.minimize(intersection_pt);
188 
189   // Summarize the results
190 #ifdef RAY_INT_DEBUG
191   std::cout << "Min error of " << levmarq.get_end_error() << " at "
192            << '(' << intersection_pt[0] << ", " << intersection_pt[1]
193            << ", " << intersection_pt[2] << ")\n"
194            << "Iterations: " << levmarq.get_num_iterations() << "    "
195            << "Evaluations: " << levmarq.get_num_evaluations() << '\n';
196   levmarq.diagnose_outcome();
197 #endif
198   // Assign the intersection
199   intersection.set(intersection_pt[0],
200                    intersection_pt[1],
201                    intersection_pt[2]);
202   // Return success
203   return true;
204 }
205 
206 #define VPGL_RAY_INTERSECT_INSTANTIATE(T) \
207 template class vpgl_ray_intersect<T >;
208 
209 
210 #endif // vpgl_ray_intersect_hxx_
211