1 // This is core/vpgl/vpgl_affine_camera.hxx
2 #ifndef vpgl_affine_camera_hxx_
3 #define vpgl_affine_camera_hxx_
4 //:
5 // \file
6 
7 #include "vpgl_affine_camera.h"
8 #include <vnl/vnl_vector_fixed.h>
9 #include <vnl/vnl_matrix_fixed.h>
10 #include <vnl/vnl_cross.h>
11 #include <vnl/vnl_det.h>
12 #include <vnl/vnl_inverse.h>
13 #include <vgl/algo/vgl_rotation_3d.h>
14 #include <vgl/vgl_closest_point.h>
15 #include <vgl/vgl_tolerance.h>
16 #include <vgl/vgl_ray_3d.h>
17 #ifdef _MSC_VER
18 #  include <vcl_msvc_warnings.h>
19 #endif
20 
21 //-------------------------------------------
22 template <class T>
vpgl_affine_camera()23 vpgl_affine_camera<T>::vpgl_affine_camera()
24 {
25   vnl_vector_fixed<T, 4> row1((T)1, (T)0, (T)0, (T)0);
26   vnl_vector_fixed<T, 4> row2((T)0, (T)1, (T)0, (T)0);
27   set_rows(row1, row2);
28   view_distance_ = (T)0;
29 }
30 
31 
32 //-------------------------------------------
33 template <class T>
vpgl_affine_camera(const vnl_vector_fixed<T,4> & row1,const vnl_vector_fixed<T,4> & row2)34 vpgl_affine_camera<T>::vpgl_affine_camera( const vnl_vector_fixed<T,4>& row1,
35                                            const vnl_vector_fixed<T,4>& row2 )
36 {
37   set_rows( row1, row2 );
38   view_distance_ = (T)0;
39 }
40 
41 
42 //------------------------------------------
43 template <class T>
vpgl_affine_camera(const vnl_matrix_fixed<T,3,4> & camera_matrix)44 vpgl_affine_camera<T>::vpgl_affine_camera( const vnl_matrix_fixed<T,3,4>& camera_matrix )
45 {
46   set_matrix( camera_matrix );
47   view_distance_ = (T)0;
48 }
49 
50 template <class T>
51 vpgl_affine_camera<T>::
vpgl_affine_camera(vgl_vector_3d<T> ray,vgl_vector_3d<T> up,vgl_point_3d<T> stare_pt,T u0,T v0,T su,T sv)52 vpgl_affine_camera(vgl_vector_3d<T> ray, vgl_vector_3d<T> up,
53                    vgl_point_3d<T> stare_pt,
54                    T u0, T v0, T su, T sv) {
55 
56   vgl_vector_3d<T> uvec = normalized(up), rvec = normalized(ray);
57   vnl_matrix_fixed<T,3,3> R;
58   if (std::fabs(dot_product<T>(uvec,rvec)-T(1))<1e-5)
59   {
60     T r[] = { 1, 0, 0,
61               0, 1, 0,
62               0, 0, 1 };
63 
64     R = vnl_matrix_fixed<T,3,3>(r);
65   }
66   else if (std::fabs(dot_product<T>(uvec,rvec)-T(-1))<1e-5)
67   {
68       T r[] = { 1, 0, 0,
69               0, 1, 0,
70               0, 0, -1 };
71 
72       R = vnl_matrix_fixed<T,3,3>(r);
73   }
74   else
75   {
76     vgl_vector_3d<T> x = cross_product(-uvec,rvec);
77     vgl_vector_3d<T> y = cross_product(rvec,x);
78     normalize(x);
79     normalize(y);
80 
81     T r[] = { x.x(), x.y(), x.z(),
82               y.x(), y.y(), y.z(),
83               rvec.x(), rvec.y(), rvec.z() };
84 
85     R = vnl_matrix_fixed<T,3,3>(r);
86   }
87 
88   //form affine camera
89   vnl_vector_fixed<T, 4> r0, r1;
90   for (unsigned i = 0; i<3; ++i) {
91     r0[i] = su*R[0][i];
92     r1[i] = sv*R[1][i];
93   }
94   r0[3]= 0.0;   r1[3]= 0.0;
95   this->set_rows(r0, r1);
96   T u, v;
97   this->project(stare_pt.x(), stare_pt.y(), stare_pt.z(), u, v);
98   T tu = (u0-u);
99   T tv = (v0-v);
100   r0[3]=tu; r1[3]=tv;
101   this->set_rows(r0, r1);
102   view_distance_ = (T)0;
103   ray_dir_.set(rvec.x(), rvec.y(), rvec.z());
104 }
105 
106 
107 //------------------------------------------
108 template <class T>
set_rows(const vnl_vector_fixed<T,4> & row1,const vnl_vector_fixed<T,4> & row2)109 void vpgl_affine_camera<T>::set_rows(
110   const vnl_vector_fixed<T,4>& row1,
111   const vnl_vector_fixed<T,4>& row2 )
112 {
113   vnl_matrix_fixed<T,3,4> C( (T)0 );
114   for ( unsigned int i = 0; i < 4; ++i ) {
115     C(0,i) = row1(i);
116     C(1,i) = row2(i);
117   }
118   C(2,3) = (T)1;
119   vpgl_proj_camera<T>::set_matrix( C );
120 
121   // camera center for affine ray direction r is given by
122   //  r . (a00, a01, a02) = r. A0 = 0
123   //  r . (a10, a11, a12) = r. A1 = 0
124   // so r is orthogonal to both A0 and A1
125   vnl_vector_fixed<T, 3> A0, A1, r;
126   for (size_t i = 0; i < 3; ++i) {
127     A0[i] = row1[i];
128     A1[i] = row2[i];
129   }
130   r = vnl_cross_3d<T>(A0, A1);
131   ray_dir_.set(r[0], r[1], r[2]);
132   ray_dir_ /= ray_dir_.length();
133 }
134 
135 template <class T>
set_matrix(const vnl_matrix_fixed<T,3,4> & new_camera_matrix)136 bool vpgl_affine_camera<T>::set_matrix( const vnl_matrix_fixed<T,3,4>& new_camera_matrix )
137 {
138   T norm = new_camera_matrix(2,3);
139   if (norm == T(0)) {
140     std::cerr << "vpgl_affine_camera::set_matrix normalization failure" << std::endl;
141     return false;
142   }
143 
144   vnl_vector_fixed<T, 4> row0, row1;
145   for (size_t i = 0; i < 4; ++i) {
146     row0[i] = new_camera_matrix[0][i] / norm;
147     row1[i] = new_camera_matrix[1][i] / norm;
148   }
149   this->set_rows(row0, row1);
150   return true;
151 }
152 
153 template <class T>
set_matrix(const T * new_camera_matrix_p)154 bool vpgl_affine_camera<T>::set_matrix( const T* new_camera_matrix_p )
155 {
156   vnl_matrix_fixed<T,3,4> new_camera_matrix( new_camera_matrix_p );
157   return set_matrix( new_camera_matrix );
158 }
159 
160 //: Find the 3d coordinates of the center of the camera. Will be an ideal point with the sense of the ray direction.
161 template <class T>
camera_center() const162 vgl_homg_point_3d<T> vpgl_affine_camera<T>::camera_center() const
163 {
164   vgl_homg_point_3d<T> temp(ray_dir_.x(), ray_dir_.y(), ray_dir_.z(), (T)0);
165   return temp;
166 }
167 
168 //: Find the 3d ray that goes through the camera center and the provided image point.
169 template <class T>
170 vgl_homg_line_3d_2_points<T> vpgl_affine_camera<T>::
backproject(const vgl_homg_point_2d<T> & image_point) const171 backproject( const vgl_homg_point_2d<T>& image_point ) const
172 {
173   // use affine construction of ray to produce the line
174   vgl_ray_3d<T> r = this->backproject_ray(image_point);
175   vgl_point_3d<T> org = r.origin();
176   vgl_vector_3d<T> dir = r.direction();
177 
178   // return line (degenerate if appropriate)
179   if(dir.length() == T(0))
180     return vgl_homg_line_3d_2_points<T>(vgl_homg_point_3d<T>(org),vgl_homg_point_3d<T>(org));
181   else
182     return vgl_homg_line_3d_2_points<T>(vgl_homg_point_3d<T>(org),vgl_homg_point_3d<T>(org+dir));
183 }
184 
185 template <class T>
186 vgl_ray_3d<T> vpgl_affine_camera<T>::
backproject_ray(const vgl_homg_point_2d<T> & image_point) const187 backproject_ray( const vgl_homg_point_2d<T>& image_point ) const
188 {
189   // return degnerate ray on failure
190   if(image_point.ideal(vgl_tolerance<T>::position)){
191     std::cerr << "Backproject ray from ideal point - degenerate result" << std::endl;
192     vgl_point_3d<T> org = vgl_point_3d<T>(T(0), T(0), T(0));
193     return vgl_ray_3d<T>(org,org);
194   }
195 
196   // ray direction is already known only have to get a point in the ray.
197   // form the A0, A1 vectors
198   vnl_matrix_fixed<T, 3, 4> P = this->get_matrix();
199   vnl_vector_fixed<T, 3> A0, A1;
200   for(size_t i = 0; i<3; ++i){
201     A0[i] = P[0][i];
202     A1[i] = P[1][i];
203   }
204   vnl_matrix_fixed<T, 2, 2> D(T(0)), Dinv;
205   D[0][0] = dot_product(A0, A0);
206   D[0][1] = dot_product(A0, A1);
207   D[1][0] = D[0][1];
208   D[1][1] = dot_product(A1, A1);
209   double det = vnl_det(D);
210 
211   // check singular determinant
212   if (fabs(det) < T(2)*vgl_tolerance<T>::position) {
213     std::cerr << "Backproject ray singular determinant - degenerate result" << std::endl;
214     vgl_point_3d<T> org = vgl_point_3d<T>(T(0), T(0), T(0));
215     return vgl_ray_3d<T>(org,org);
216   }
217 
218   Dinv = vnl_inverse(D);
219   vnl_vector_fixed<T, 2> uv;
220   uv[0] = image_point.x() / image_point.w();
221   uv[1] = image_point.y() / image_point.w();
222   vnl_vector_fixed<T, 2> UV, a;
223   UV[0] = uv[0]-P[0][3];
224   UV[1] = uv[1]-P[1][3];
225   a = Dinv*UV;
226   vnl_vector_fixed<T, 3> p;
227   p = a[0]*A0 + a[1]*A1;
228   vgl_point_3d<T> org(p[0], p[1], p[2]);
229 
230   // intersect ray with principal plane (specified by "view_distance")
231   // to obtain ray origin on the plane
232   if(view_distance_ == T(0)) {
233      return vgl_ray_3d<T>(org, ray_dir_);
234   } else {
235     T md = -view_distance_;
236     vgl_point_3d<T> pl_org = org + md*ray_dir_;
237     return vgl_ray_3d<T>(pl_org, ray_dir_);
238   }
239 }
240 
clone() const241 template <class T> vpgl_affine_camera<T> *vpgl_affine_camera<T>::clone() const {
242   return new vpgl_affine_camera<T>(*this);
243 }
244 
245 //: Find the world plane parallel to the image plane intersecting the camera center.
246 template <class T>
247 vgl_homg_plane_3d<T> vpgl_affine_camera<T>::
principal_plane() const248 principal_plane() const
249 {
250   //note that d = view_distance_ not -view_distance_,
251   //since dir points towards the origin
252   vgl_homg_plane_3d<T> ret(ray_dir_.x(), ray_dir_.y(),
253                            ray_dir_.z(), view_distance_);
254   return ret;
255 }
256 
257 //: flip the ray direction so that dot product with look_dir is positive
258 template <class T>
orient_ray_direction(vgl_vector_3d<T> const & look_dir)259 void vpgl_affine_camera<T>::orient_ray_direction(vgl_vector_3d<T> const& look_dir)
260 {
261   if (dot_product(look_dir, ray_dir_) < 0 ) {
262     ray_dir_ = -ray_dir_;
263   }
264 }
265 
266 //: Write vpgl_affine_camera to stream
267 template <class Type>
operator <<(std::ostream & s,vpgl_affine_camera<Type> const & c)268 std::ostream&  operator<<(std::ostream& s,
269                          vpgl_affine_camera<Type> const& c)
270 {
271   s << c.get_matrix() << '\n';
272   return s;
273 }
274 
275 //: Read camera from stream
276 template <class Type>
operator >>(std::istream & s,vpgl_affine_camera<Type> & c)277 std::istream&  operator >>(std::istream& s,
278                           vpgl_affine_camera<Type>& c)
279 {
280   vnl_matrix_fixed<Type, 3, 4> P;
281   s >> P;
282   c = vpgl_affine_camera<Type>(P);
283   return s ;
284 }
285 
286 // the homography that transforms the camera to [I2x2|02x2]
287 //                                              [  01x3 |1]
288 //
289 // Note that A  = [I2x2|02x2] H, where H4x4 = [  A_3x4  ]
290 //                [  01x3 |1]                 [ 01x3  |1]
291 //
292 // thus, canonical_h = H^-1
293 //
294 template <class T>
get_canonical_h(const vpgl_affine_camera<T> & camera)295 vgl_h_matrix_3d<T> get_canonical_h(const vpgl_affine_camera<T>& camera ){
296 
297   vnl_matrix_fixed<T, 3,4> A = camera.get_matrix();
298   vnl_svd<T> temp(A.as_ref());
299   vnl_matrix_fixed<T, 4,3> Ainv = temp.pinverse();
300   vnl_matrix_fixed<T, 4, 4> Hinv(0.0), Hp(0.0);
301   for(size_t r = 0; r<4; ++r){
302     Hp[r][r] = T(1);
303     for(size_t c = 0; c<3; ++c){
304       Hinv[r][c] = Ainv[r][c];
305     }
306   }
307   Hinv[3][3]=T(1); Hp[0][3]=-A[0][3]; Hp[1][3]=-A[1][3];//remove 4th column (translation)
308   vgl_h_matrix_3d<T> ret(Hinv*Hp);
309   return ret;
310 }
311 
312 template <class T>
premultiply_a(const vpgl_affine_camera<T> & in_camera,const vnl_matrix_fixed<T,3,3> & transform)313 vpgl_affine_camera<T> premultiply_a( const vpgl_affine_camera<T>& in_camera,
314                                    const vnl_matrix_fixed<T, 3, 3>& transform ){
315   return vpgl_affine_camera<T>(transform*in_camera.get_matrix());
316 }
317 
318 template <class T>
postmultiply_a(const vpgl_affine_camera<T> & in_camera,const vnl_matrix_fixed<T,4,4> & transform)319 vpgl_affine_camera<T> postmultiply_a( const vpgl_affine_camera<T>& in_camera,
320                                     const vnl_matrix_fixed<T,4,4>& transform ){
321   return vpgl_affine_camera<T>(in_camera.get_matrix()*transform);
322 }
323 
324 template <class T>
postmultiply_a(const vpgl_affine_camera<T> & in_camera,const vnl_vector_fixed<T,3> & translation)325 vpgl_affine_camera<T> postmultiply_a( const vpgl_affine_camera<T>& in_camera,
326                                       const vnl_vector_fixed<T,3>& translation ){
327   const vnl_matrix_fixed<T, 3, 4> m = in_camera.get_matrix();
328   vnl_matrix_fixed<T, 3, 4> tm = m;
329   vnl_vector_fixed<T, 4> r0 = m.get_row(0), r1 = m.get_row(1);
330   vnl_vector_fixed<T, 4> t;
331   for(size_t c = 0; c<3; ++c) t[c] = translation[c]; t[3] = T(1);
332   T dp0 = dot_product(r0, t); tm[0][3] = dp0;
333   T dp1 = dot_product(r1, t); tm[1][3] = dp1;
334   return vpgl_affine_camera<T>(tm);
335 }
336 
337 // Code for easy instantiation.
338 #undef vpgl_AFFINE_CAMERA_INSTANTIATE
339 #define vpgl_AFFINE_CAMERA_INSTANTIATE(T) \
340 template class vpgl_affine_camera<T >; \
341 template std::ostream& operator<<(std::ostream&, const vpgl_affine_camera<T >&); \
342 template std::istream& operator>>(std::istream&, vpgl_affine_camera<T >&); \
343 template vgl_h_matrix_3d<T> get_canonical_h(const vpgl_affine_camera<T>&); \
344 template vpgl_affine_camera<T> premultiply_a( const vpgl_affine_camera<T>&, const vnl_matrix_fixed<T,3,3>&); \
345 template vpgl_affine_camera<T> premultiply_a( const vpgl_affine_camera<T>&, const vgl_h_matrix_2d<T>&); \
346 template vpgl_affine_camera<T> postmultiply_a( const vpgl_affine_camera<T>&, const vnl_matrix_fixed<T,4,4>&); \
347 template vpgl_affine_camera<T> postmultiply_a( const vpgl_affine_camera<T>&, const vgl_h_matrix_3d<T>&); \
348 template vpgl_affine_camera<T> postmultiply_a( const vpgl_affine_camera<T>&, const vnl_vector_fixed<T,3>&); \
349 template vpgl_affine_camera<T> postmultiply_a( const vpgl_affine_camera<T>&, const vgl_vector_3d<T>&)
350 
351 
352 #endif // vpgl_affine_camera_hxx_
353