vpgl_proj_camera.hxx
Go to the documentation of this file.
1 // This is core/vpgl/vpgl_proj_camera.hxx
2 #ifndef vpgl_proj_camera_hxx_
3 #define vpgl_proj_camera_hxx_
4 //:
5 // \file
6 
7 #include <iostream>
8 #include <fstream>
9 #include "vpgl_proj_camera.h"
10 #ifdef _MSC_VER
11 # include <vcl_msvc_warnings.h>
12 #endif
13 #include <vgl/vgl_point_2d.h>
14 #include <vgl/vgl_point_3d.h>
15 #include <vgl/vgl_ray_3d.h>
16 #include <vnl/vnl_vector_fixed.h>
18 
19 
20 // CONSTRUCTORS:--------------------------------------------------------------------
21 
22 //------------------------------------
23 template <class T>
25  cached_svd_(nullptr)
26 {
27  P_ = vnl_matrix_fixed<T,3,4>( (T)0 );
28  P_(0,0) = P_(1,1) = P_(2,2) = (T)1;
29 }
30 
31 //------------------------------------
32 template <class T>
34  P_( camera_matrix ),
35  cached_svd_(nullptr)
36 {
37 }
38 
39 //------------------------------------
40 template <class T>
41 vpgl_proj_camera<T>::vpgl_proj_camera( const T* camera_matrix ) :
42  P_( camera_matrix ),
43  cached_svd_(nullptr)
44 {
45 }
46 
47 //------------------------------------
48 template <class T>
50  vpgl_camera<T>(),
51  P_( cam.get_matrix() ),
52  cached_svd_(nullptr)
53 {
54 }
55 
56 //------------------------------------
57 template <class T>
59 {
60  P_ = cam.get_matrix();
61  if ( cached_svd_ != nullptr ) delete cached_svd_;
62  cached_svd_ = nullptr;
63  return *this;
64 }
65 
66 //------------------------------------
67 template <class T>
69 {
70  if ( cached_svd_ != nullptr ) delete cached_svd_;
71  cached_svd_ = nullptr;
72 }
73 
74 template <class T>
76 {
77  return new vpgl_proj_camera<T>(*this);
78 }
79 
80 
81 // PROJECTIONS AND BACKPROJECTIONS:----------------------------------------------
82 
83 //------------------------------------
84 template <class T>
86 {
87  // For efficiency, manually compute the multiplication rather than converting to
88  // vnl and converting back.
89  vgl_homg_point_2d<T> image_point(
90  P_(0,0)*world_point.x() + P_(0,1)*world_point.y() +
91  P_(0,2)*world_point.z() + P_(0,3)*world_point.w(),
92 
93  P_(1,0)*world_point.x() + P_(1,1)*world_point.y() +
94  P_(1,2)*world_point.z() + P_(1,3)*world_point.w(),
95 
96  P_(2,0)*world_point.x() + P_(2,1)*world_point.y() +
97  P_(2,2)*world_point.z() + P_(2,3)*world_point.w() );
98  return image_point;
99 }
100 
101 // -----------------------------------
102 template <class T>
103 void
104 vpgl_proj_camera<T>::project(const T x, const T y, const T z, T& u, T& v) const
105 {
106  vgl_homg_point_3d<T> world_point(x, y, z);
107  vgl_homg_point_2d<T> image_point = this->project(world_point);
108  if (image_point.ideal(static_cast<T>(1.0e-10)))
109  {
110  u = 0; v = 0;
111  std::cerr << "Warning: projection to ideal image point in vpgl_proj_camera -"
112  << " result not valid\n";
113  return;
114  }
115  u = image_point.x()/image_point.w();
116  v = image_point.y()/image_point.w();
117 }
118 
119 //------------------------------------
120 template <class T>
122  const vgl_line_segment_3d<T>& world_line ) const
123 {
124  vgl_homg_point_3d<T> point1_w( world_line.point1() );
125  vgl_homg_point_3d<T> point2_w( world_line.point2() );
126  vgl_point_2d<T> point1_im( project( point1_w ) );
127  vgl_point_2d<T> point2_im( project( point2_w ) );
128  vgl_line_segment_2d<T> image_line( point1_im, point2_im );
129  return image_line;
130 }
131 
132 //: Project an infinite line in the world onto an infinite line in the image plane.
133 template <class T>
135 {
136  vgl_homg_point_3d<T> point1_w( world_line.point() );
137  vgl_homg_point_3d<T> point2_w( world_line.point_t(T(1)) );
138  vgl_point_2d<T> point1_im( project( point1_w ) );
139  vgl_point_2d<T> point2_im( project( point2_w ) );
140  vgl_line_2d<T> image_line( point1_im, point2_im );
141  return image_line;
142 }
143 
144 //------------------------------------
145 template <class T>
147  const vgl_homg_point_2d<T>& image_point ) const
148 {
149  // First find any point in the world that projects to the "image_point".
150  vnl_vector_fixed<T,4> vnl_wp = svd()->solve(
151  vnl_vector_fixed<T,3>( image_point.x(), image_point.y(), image_point.w() ).as_ref() );
152  vgl_homg_point_3d<T> wp( vnl_wp[0], vnl_wp[1], vnl_wp[2], vnl_wp[3] );
153 
154  // The ray is then defined by that point and the camera center.
155  if ( wp.ideal(.000001f) ) // modified 11/6/2005 by tpollard
156  return vgl_homg_line_3d_2_points<T>( camera_center(), wp );
157  return vgl_homg_line_3d_2_points<T>( wp, camera_center() );
158 }
159 
160  //: Find the 3d ray that goes through the camera center and the provided image point.
161 template <class T>
163 {
164  vnl_vector_fixed<T,4> vnl_wp = svd()->solve(
165  vnl_vector_fixed<T,3>( image_point.x(), image_point.y(), image_point.w() ).as_ref() );
166  vgl_homg_point_3d<T> wp( vnl_wp[0], vnl_wp[1], vnl_wp[2], vnl_wp[3] );
167  //in this case the world point defines a direction
168  if ( wp.ideal(.000001f) ) {
169  vgl_vector_3d<T> dir(wp.x(), wp.y(), wp.z());
170  return vgl_ray_3d<T>(this->camera_center(), dir);
171  }
172  vgl_point_3d<T> wpn(wp);//normalizes
173  return vgl_ray_3d<T>(this->camera_center(), wpn);
174 }
175 
176 //------------------------------------
177 template <class T>
179  const vgl_homg_line_2d<T>& image_line ) const
180 {
181  vnl_vector_fixed<T,3> image_line_vnl(image_line.a(),image_line.b(),image_line.c());
182  vnl_vector_fixed<T,4> world_plane = P_.transpose() * image_line_vnl;
183  return vgl_homg_plane_3d<T>( world_plane(0), world_plane(1),
184  world_plane(2), world_plane(3) );
185 }
186 
187 
188 // MISC CAMERA FUNCTIONS:-----------------------------------------------------
189 
190 //------------------------------------
191 template <class T>
193 {
194  vnl_matrix<T> ns = svd()->nullspace();
195  return vgl_homg_point_3d<T>(ns(0,0), ns(1,0), ns(2,0), ns(3,0));
196 }
197 
198 
199 // ACCESSORS:-----------------------------------------------------------------
200 
201 //------------------------------------
202 template <class T>
204 {
205  // Check if the cached copy is valid, if not recompute it.
206  if ( cached_svd_ == nullptr )
207  {
208  cached_svd_ = new vnl_svd<T>(P_.as_ref());
209 
210  // Check that the projection matrix isn't degenerate.
211  if ( cached_svd_->rank() != 3 )
212  std::cerr << "vpgl_proj_camera::svd()\n"
213  << " Warning: Projection matrix is not rank 3, errors may occur.\n";
214  }
215  return cached_svd_;
216 }
217 
218 //------------------------------------
219 template <class T>
221 {
222  P_ = new_camera_matrix;
223  if ( cached_svd_ != nullptr ) delete cached_svd_;
224  cached_svd_ = nullptr;
225  return true;
226 }
227 
228 //------------------------------------
229 template <class T>
230 bool vpgl_proj_camera<T>::set_matrix( const T* new_camera_matrix )
231 {
232  P_ = vnl_matrix_fixed<T,3,4>( new_camera_matrix );
233  if ( cached_svd_ != nullptr ) delete cached_svd_;
234  cached_svd_ = nullptr;
235  return true;
236 }
237 
238 
239 
240 
241 // EXTERNAL FUNCTIONS:------------------------------------------------
242 
243 //: Write vpgl_perspective_camera to stream
244 template <class Type>
245 std::ostream& operator<<(std::ostream& s,
246  vpgl_proj_camera<Type> const& p)
247 {
248  s << "projective:"
249  << "\nP\n" << p.get_matrix() << std::endl;
250 
251  return s ;
252 }
253 
254 //: Read vpgl_perspective_camera from stream
255 template <class Type>
256 std::istream& operator>>(std::istream& s,
258 {
259  vnl_matrix_fixed<Type,3,4> new_matrix;
260  s >> new_matrix;
261  p.set_matrix( new_matrix );
262 
263  return s ;
264 }
265 
266 template <class T>
267 void vpgl_proj_camera<T>::save(std::string cam_path)
268 {
269  std::ofstream os(cam_path.c_str());
270  if (!os.is_open()) {
271  std::cout << "unable to open output stream in vpgl_proj_camera<T>::save(.)\n";
272  return;
273  }
274  os << this->get_matrix() << '\n';
275  os.close();
276 }
277 
278 //-------------------------------
279 template <class T>
281 {
282  // If P is a 3x4 rank 3 matrix, Pinv is the pseudo-inverse of P, and l is a
283  // vector such that P*l = 0, then P*[Pinv | l] = [I | 0].
284  vnl_matrix_fixed<T,4,3> Pinv = camera.svd()->pinverse();
285  vnl_vector<T> l = camera.svd()->solve( vnl_vector<T>(3,(T)0) );
286 
288  for ( int i = 0; i < 4; i++ ) {
289  for ( int j = 0; j < 3; j++ )
290  H(i,j) = Pinv(i,j);
291  H(i,3) = l(i);
292  }
293  return vgl_h_matrix_3d<T>( H );
294 }
295 
296 //--------------------------------
297 template <class T>
299 {
300  std::cerr << "fix_cheirality( vpgl_proj_camera<T>& ) not implemented\n";
301 }
302 
303 //--------------------------------
304 template <class T>
306 {
307  vnl_matrix_fixed<T,3,4> can_cam( (T)0 );
308  can_cam(0,0) = can_cam(1,1) = can_cam(2,2) = (T)1;
309  camera.set_matrix( can_cam );
310 }
311 
312 //--------------------------------
313 template <class T>
315  const vnl_matrix_fixed<T,3,3>& transform )
316 {
317  return vpgl_proj_camera<T>( transform*in_camera.get_matrix() );
318 }
319 
320 //--------------------------------
321 template <class T>
323  const vnl_matrix_fixed<T,4,4>& transform )
324 {
325  return vpgl_proj_camera<T>( in_camera.get_matrix()*transform );
326 }
327 
328 //--------------------------------
329 template <class T>
331  const vgl_point_2d<T>& x1,
332  const vpgl_proj_camera<T>& c2,
333  const vgl_point_2d<T>& x2)
334 {
338  for (int i=0; i<4; i++) {
339  A[0][i] = x1.x()*P1[2][i] - P1[0][i];
340  A[1][i] = x1.y()*P1[2][i] - P1[1][i];
341  A[2][i] = x2.x()*P2[2][i] - P2[0][i];
342  A[3][i] = x2.y()*P2[2][i] - P2[1][i];
343  }
344  vnl_svd<T> svd_solver(A.as_ref());
345  vnl_vector_fixed<T, 4> p = svd_solver.nullvector();
346  vgl_homg_point_3d<T> hp(p[0],p[1],p[2],p[3]);
347  return vgl_point_3d<T>(hp);
348 }
349 
350 
351 //: Compute the image projection Jacobians at each point
352 // The returned matrices map a differential change in 3D
353 // to a differential change in the 2D image at each specified 3D point
354 template <class T>
355 std::vector<vnl_matrix_fixed<T,2,3> >
357  const std::vector<vgl_point_3d<T> >& pts)
358 {
359  const vnl_matrix_fixed<T,3,4>& P = camera.get_matrix();
360  vnl_vector_fixed<T,4> denom = P.get_row(2);
361 
363  Du(0,0) = Du(1,1) = Du(2,2) = 0.0;
364  Du(0,1) = P(0,0)*P(2,1) - P(0,1)*P(2,0);
365  Du(0,2) = P(0,0)*P(2,2) - P(0,2)*P(2,0);
366  Du(1,2) = P(0,1)*P(2,2) - P(0,2)*P(2,1);
367  Du(0,3) = P(0,0)*P(2,3) - P(0,3)*P(2,0);
368  Du(1,3) = P(0,1)*P(2,3) - P(0,3)*P(2,1);
369  Du(2,3) = P(0,2)*P(2,3) - P(0,3)*P(2,2);
370  Du(1,0) = -Du(0,1);
371  Du(2,0) = -Du(0,2);
372  Du(2,1) = -Du(1,2);
373 
375  Dv(0,0) = Dv(1,1) = Dv(2,2) = 0.0;
376  Dv(0,1) = P(1,0)*P(2,1) - P(1,1)*P(2,0);
377  Dv(0,2) = P(1,0)*P(2,2) - P(1,2)*P(2,0);
378  Dv(1,2) = P(1,1)*P(2,2) - P(1,2)*P(2,1);
379  Dv(0,3) = P(1,0)*P(2,3) - P(1,3)*P(2,0);
380  Dv(1,3) = P(1,1)*P(2,3) - P(1,3)*P(2,1);
381  Dv(2,3) = P(1,2)*P(2,3) - P(1,3)*P(2,2);
382  Dv(1,0) = -Dv(0,1);
383  Dv(2,0) = -Dv(0,2);
384  Dv(2,1) = -Dv(1,2);
385 
386 
387  const std::size_t num_pts = pts.size();
388  std::vector<vnl_matrix_fixed<T,2,3> > img_jac(num_pts);
389 
390  for (unsigned int i=0; i<num_pts; ++i)
391  {
392  const vgl_point_3d<T>& pt = pts[i];
393  vnl_matrix_fixed<T,2,3>& J = img_jac[i];
394  vnl_vector_fixed<T,4> hpt(pt.x(),pt.y(),pt.z(),1.0);
395 
396  T d = dot_product(denom,hpt);
397  d *= d;
398  J.set_row(0,Du*hpt);
399  J.set_row(1,Dv*hpt);
400  J /= d;
401  }
402 
403  return img_jac;
404 }
405 
406 
407 // Code for easy instantiation.
408 #undef vpgl_PROJ_CAMERA_INSTANTIATE
409 #define vpgl_PROJ_CAMERA_INSTANTIATE(T) \
410 template class vpgl_proj_camera<T >; \
411 template vgl_h_matrix_3d<T > get_canonical_h( vpgl_proj_camera<T >& camera ); \
412 template void fix_cheirality( vpgl_proj_camera<T >& camera ); \
413 template void make_canonical( vpgl_proj_camera<T >& camera ); \
414 template vpgl_proj_camera<T > premultiply( const vpgl_proj_camera<T >& in_camera, \
415  const vnl_matrix_fixed<T,3,3>& transform ); \
416 template vpgl_proj_camera<T > postmultiply(const vpgl_proj_camera<T >& in_camera, \
417  const vnl_matrix_fixed<T,4,4>& transform ); \
418 template vgl_point_3d<T > triangulate_3d_point(const vpgl_proj_camera<T >& c1, \
419  const vgl_point_2d<T >& x1, \
420  const vpgl_proj_camera<T >& c2, \
421  const vgl_point_2d<T >& x2); \
422 template std::vector<vnl_matrix_fixed<T,2,3> > \
423  image_jacobians(const vpgl_proj_camera<T >& camera, \
424  const std::vector<vgl_point_3d<T > >& pts); \
425 template std::ostream& operator<<(std::ostream&, const vpgl_proj_camera<T >&); \
426 template std::istream& operator>>(std::istream&, vpgl_proj_camera<T >&)
427 
428 #endif // vpgl_proj_camera_hxx_
vnl_matrix_ref< T > as_ref()
vnl_svd< T > * svd() const
Get a copy of the svd of the get_matrix.
~vpgl_proj_camera() override
vgl_point_3d< Type > point1() const
vgl_h_matrix_3d< T > get_canonical_h(vpgl_proj_camera< T > &camera)
Return the 3D H-matrix s.t. P * H = [I 0].
std::istream & operator>>(std::istream &is, vpgl_local_rational_camera< T > &p)
Read from stream.
bool ideal(T tol=(T) 0) const
vgl_point_3d< Type > point2() const
vnl_matrix_fixed & set_row(unsigned i, T const *v)
vnl_matrix_fixed< T, 3, 4 > P_
The internal representation of the get_matrix.
void project(const T x, const T y, const T z, T &u, T &v) const override
Projection from base class.
virtual vpgl_proj_camera< T > * clone(void) const
Clone ‘this’: creation of a new object and initialization.
#define v
virtual bool set_matrix(const vnl_matrix_fixed< T, 3, 4 > &new_camera_matrix)
Setters mirror the constructors and return true if the setting was successful.
Type z() const
void fix_cheirality(vpgl_proj_camera< T > &)
Scale the camera matrix so determinant of first 3x3 is 1.
virtual vgl_homg_line_3d_2_points< T > backproject(const vgl_homg_point_2d< T > &image_point) const
Find the 3d ray that goes through the camera center and the provided image point.
vgl_point_3d< Type > point_t(const double t) const
virtual vgl_homg_point_3d< T > camera_center() const
Find the 3d coordinates of the center of the camera.
vgl_point_3d< T > triangulate_3d_point(const vpgl_proj_camera< T > &c1, const vgl_point_2d< T > &x1, const vpgl_proj_camera< T > &c2, const vgl_point_2d< T > &x2)
Linearly intersect two camera rays to form a 3-d point.
Type w() const
std::ostream & operator<<(std::ostream &s, const vpgl_local_rational_camera< T > &p)
Write to stream.
virtual vgl_ray_3d< T > backproject_ray(const vgl_homg_point_2d< T > &image_point) const
Find the 3d ray that goes through the camera center and the provided image point.
const vpgl_proj_camera< T > & operator=(const vpgl_proj_camera &cam)
Assignment.
A camera model using the standard 3x4 matrix representation.
std::vector< vnl_matrix_fixed< T, 2, 3 > > image_jacobians(const vpgl_proj_camera< T > &camera, const std::vector< vgl_point_3d< T > > &pts)
Compute the image projection Jacobians at each point.
const vnl_matrix_fixed< T, 3, 4 > & get_matrix() const
Return a copy of the camera matrix.
void make_canonical(vpgl_proj_camera< T > &camera)
Set the camera matrix to [ I | 0 ].
virtual void save(std::string cam_path)
Save in ascii format.
bool ideal(Type tol=(Type) 0) const
#define l
T dot_product(v const &a, v const &b)
vpgl_proj_camera< T > premultiply(const vpgl_proj_camera< T > &in_camera, const vnl_matrix_fixed< T, 3, 3 > &transform)
Pre-multiply this projection matrix with a 2-d projective transform.
Type y() const
vgl_point_3d< Type > point() const
Type x() const
vpgl_proj_camera< T > postmultiply(const vpgl_proj_camera< T > &in_camera, const vnl_matrix_fixed< T, 4, 4 > &transform)
Post-multiply this projection matrix with a 3-d projective transform.
vnl_vector_fixed< T, num_cols > get_row(unsigned row) const
vpgl_proj_camera()
Default constructor makes an identity camera.