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