vpgl_camera_convert.cxx
Go to the documentation of this file.
1 // This is core/vpgl/algo/vpgl_camera_convert.cxx
2 #ifndef vpgl_camera_convert_cxx_
3 #define vpgl_camera_convert_cxx_
4 
5 #include <iostream>
6 #include <cstdlib>
7 #include <cmath>
8 #include "vpgl_camera_convert.h"
9 //:
10 // \file
11 #include <cassert>
12 #ifdef _MSC_VER
13 # include <vcl_msvc_warnings.h>
14 #endif
15 #include <vnl/vnl_numeric_traits.h>
16 #include <vnl/vnl_det.h>
17 #include <vnl/vnl_vector_fixed.h>
18 #include <vnl/vnl_matrix_fixed.h>
19 #include <vnl/vnl_random.h>
20 #include <vnl/algo/vnl_svd.h>
21 #include <vnl/algo/vnl_qr.h>
24 #include <vgl/vgl_homg_point_3d.h>
28 #include <vgl/vgl_point_2d.h>
29 #include <vgl/vgl_point_3d.h>
30 #include <vgl/vgl_box_3d.h>
31 #include <vgl/vgl_homg_point_2d.h>
32 #include <vgl/vgl_ray_3d.h>
33 #include <vgl/vgl_plane_3d.h>
34 #include <vbl/vbl_array_2d.h>
35 #include <vpgl/vpgl_lvcs.h>
37 
38 
39 static std::vector<double>
40 pvector(const double x, const double y, const double z)
41 {
42  //fill the vector
43  std::vector<double> pv(20);
44  pv[0]= x*x*x;
45  pv[1]= x*x*y;
46  pv[2]= x*x*z;
47  pv[3]= x*x;
48  pv[4]= x*y*y;
49  pv[5]= x*y*z;
50  pv[6]= x*y;
51  pv[7]= x*z*z;
52  pv[8]= x*z;
53  pv[9]= x;
54  pv[10]= y*y*y;
55  pv[11]= y*y*z;
56  pv[12]= y*y;
57  pv[13]= y*z*z;
58  pv[14]= y*z;
59  pv[15]= y;
60  pv[16]= z*z*z;
61  pv[17]= z*z;
62  pv[18]= z;
63  pv[19]= 1;
64  return pv;
65 }
66 
67 static std::vector<double>
68 power_vector_dx(const double x, const double y, const double z)
69 {
70  //fill the vector
71  std::vector<double> pv(20, 0.0);
72  pv[0]= 3*x*x;
73  pv[1]= 2*x*y;
74  pv[2]= 2*x*z;
75  pv[3]= 2*x;
76  pv[4]= y*y;
77  pv[5]= y*z;
78  pv[6]= y;
79  pv[7]= z*z;
80  pv[8]= z;
81  pv[9]= 1;
82  return pv;
83 }
84 
85 static std::vector<double>
86 power_vector_dy(const double x, const double y, const double z)
87 {
88  //fill the vector
89  std::vector<double> pv(20, 0.0);
90  pv[1]= x*x;
91  pv[4]= 2*x*y;
92  pv[5]= x*z;
93  pv[6]= x;
94  pv[10]= 3*y*y;
95  pv[11]= 2*y*z;
96  pv[12]= 2*y;
97  pv[13]= z*z;
98  pv[14]= z;
99  pv[15]= 1;
100  return pv;
101 }
102 
103 static std::vector<double>
104 power_vector_dz(const double x, const double y, const double z)
105 {
106  //fill the vector
107  std::vector<double> pv(20, 0.0);
108  pv[2]= x*x;
109  pv[5]= x*y;
110  pv[7]= 2*x*z;
111  pv[8]= x;
112  pv[11]= y*y;
113  pv[13]= 2*y*z;
114  pv[14]= y;
115  pv[16]= 3*z*z;
116  pv[17]= 2*z;
117  pv[18]= 1;
118  return pv;
119 }
120 
121 //convert the value of the polynomial
122 static double pval(std::vector<double> const& pv, std::vector<double>const& coef)
123 {
124  double sum = 0.0;
125  for (unsigned i = 0; i<20; ++i)
126  sum += pv[i]*coef[i];
127  return sum;
128 }
129 
130 // Find an approximate projective camera that approximates a rational camera
131 // at the world center.
134  vgl_point_3d<double> const& world_center,
135  vpgl_proj_camera<double>& camera)
136 {
137  double x0 = world_center.x(), y0 = world_center.y(), z0 = world_center.z();
138  //normalized world center
139  double nx0 =
140  rat_cam.scl_off(vpgl_rational_camera<double>::X_INDX).normalize(x0);
141  double ny0 =
142  rat_cam.scl_off(vpgl_rational_camera<double>::Y_INDX).normalize(y0);
143  double nz0 =
144  rat_cam.scl_off(vpgl_rational_camera<double>::Z_INDX).normalize(z0);
145 
146  // get the rational coefficients
147  std::vector<std::vector<double> > coeffs = rat_cam.coefficients();
148  std::vector<double> neu_u = coeffs[0];
149  std::vector<double> den_u = coeffs[1];
150  std::vector<double> neu_v = coeffs[2];
151  std::vector<double> den_v = coeffs[3];
152 
153  // normalize for numerical precision
154  double nmax_u = -vnl_numeric_traits<double>::maxval;
155  double dmax_u = nmax_u, nmax_v = nmax_u,dmax_v = nmax_u;
156  for (unsigned i = 0; i<20; ++i)
157  {
158  if (std::fabs(neu_u[i])>nmax_u)
159  nmax_u=std::fabs(neu_u[i]);
160  if (std::fabs(den_u[i])>dmax_u)
161  dmax_u=std::fabs(den_u[i]);
162  if (std::fabs(neu_v[i])>nmax_v)
163  nmax_v=std::fabs(neu_v[i]);
164  if (std::fabs(den_v[i])>dmax_v)
165  dmax_v=std::fabs(den_v[i]);
166  }
167  // Normalize polys so that ratio of numerator and denominator is unchanged
168  double norm_u = nmax_u, norm_v = nmax_v;
169  if (norm_u<dmax_u) norm_u = dmax_u;
170  if (norm_v<dmax_v) norm_v = dmax_v;
171  for (unsigned i = 0; i<20; ++i)
172  {
173  neu_u[i] /= norm_u; den_u[i] /= norm_u;
174  neu_v[i] /= norm_v; den_v[i] /= norm_v;
175  }
176 
177  // Convert linear approximations to each poly
178  //lin_p(x, y, z)=p(0,0,0) + (dp/dx)(x-nx0) + (dp/dy)(y-ny0) + (dp/dz)(z-nz0)
179  std::vector<double> pv0 = pvector(nx0,ny0,nz0);
180  double neu_u_0 = pval(pv0,neu_u), den_u_0 = pval(pv0,den_u);
181  double neu_v_0 = pval(pv0,neu_v), den_v_0 = pval(pv0,den_v);
182  std::vector<double> pv_dx0 = power_vector_dx(nx0,ny0,nz0);
183  double neu_u_dx0 = pval(pv_dx0,neu_u), den_u_dx0 = pval(pv_dx0,den_u);
184  double neu_v_dx0 = pval(pv_dx0,neu_v), den_v_dx0 = pval(pv_dx0,den_v);
185  std::vector<double> pv_dy0 = power_vector_dy(nx0,ny0,nz0);
186  double neu_u_dy0 = pval(pv_dy0,neu_u), den_u_dy0 = pval(pv_dy0,den_u);
187  double neu_v_dy0 = pval(pv_dy0,neu_v), den_v_dy0 = pval(pv_dy0,den_v);
188  std::vector<double> pv_dz0 = power_vector_dz(nx0,ny0,nz0);
189  double neu_u_dz0 = pval(pv_dz0,neu_u), den_u_dz0 = pval(pv_dz0,den_u);
190  double neu_v_dz0 = pval(pv_dz0,neu_v), den_v_dz0 = pval(pv_dz0,den_v);
191 
192  //Construct the matrix to convert the center of projection
193  vnl_matrix<double> C(4,4);
194  C[0][0]=neu_u_dx0; C[0][1]=neu_u_dy0; C[0][2]=neu_u_dz0; C[0][3]=neu_u_0;
195  C[1][0]=den_u_dx0; C[1][1]=den_u_dy0; C[1][2]=den_u_dz0; C[1][3]=den_u_0;
196  C[2][0]=neu_v_dx0; C[2][1]=neu_v_dy0; C[2][2]=neu_v_dz0; C[2][3]=neu_v_0;
197  C[3][0]=den_v_dx0; C[3][1]=den_v_dy0; C[3][2]=den_v_dz0; C[3][3]=den_v_0;
198 
199  vnl_svd<double> svd(C);
200  vnl_vector<double> nv = svd.nullvector();
201  //assume not at infinity
202  nv/=nv[3];
203 #if 1
204  std::cout << "Center of projection\n" << nv << '\n'
205  << "Residual\n" << C*nv << '\n';
206 #endif
207  //Normalize with respect to the principal plane normal (principal ray)
208  double ndu = std::sqrt(den_u_dx0*den_u_dx0 + den_u_dy0*den_u_dy0 +
209  den_u_dz0*den_u_dz0);
210  double ndv = std::sqrt(den_v_dx0*den_v_dx0 + den_v_dy0*den_v_dy0 +
211  den_v_dz0*den_v_dz0);
212 
213  // determine if the projection is affine
214  if (ndu/std::fabs(den_u_0)<1.0e-10||ndv/std::fabs(den_v_0)<1.0e-10)
215  {
216  std::cout << "Camera is nearly affine - approximation not implemented!\n";
217  return false;
218  }
219  //Construct M by joined scale factor vector
221  for (unsigned i = 0; i<3; ++i)
222  {
223  M[0][i]=C[0][i]/ndu;
224  M[1][i]=C[2][i]/ndv;
225  M[2][i]=(C[1][i]/ndu + C[3][i]/ndv)/2;
226  }
227 #if 1
228  std::cout << "M matrix\n" << M << '\n';
229 
231  for ( int i = 0; i < 3; ++i )
232  for ( int j = 0; j < 3; ++j )
233  Mf(i,j) = M(2-j,2-i);
234  vnl_qr<double> QR( Mf.as_ref() );
235  vnl_matrix_fixed<double,3,3> q,r,Qf,Rf, uq,ur;
236  q = QR.Q();
237  r = QR.R();
238  for ( int i = 0; i < 3; ++i ) {
239  for ( int j = 0; j < 3; ++j ) {
240  Qf(i,j) = q(2-j,2-i);
241  Rf(i,j) = r(2-j,2-i);
242  }
243  }
244  std::cout << "Flipped Rotation\n" << Qf << '\n'
245  << "Flipped Upper Triangular\n" << Rf << '\n';
246  vnl_qr<double> uqr(M.as_ref());
247  uq = uqr.Q();
248  ur = uqr.R();
249  std::cout << "UnFlipped Rotation\n" << uq << '\n'
250  << "UnFlipped Upper Triangular\n" << ur << '\n'
251  << "Det uq " << vnl_det<double>(uq) << '\n';
252  //Normalized denominators
253  vnl_vector<double> c1(3), c3(3);
254  for (unsigned i = 0; i<3; ++i) {
255  c1[i]=C[1][i]/ndu;
256  c3[i]=C[3][i]/ndv;
257  }
258 
259  std::cout << "Denominators\n"
260  << "C1 " << c1
261  << "C3 " << c3;
262 #endif
263  //convert p3 the fourth column of the projection matrix
265  for (unsigned i = 0; i<3; ++i)
266  c[i]=nv[i];
267  vnl_vector_fixed<double, 3> p3 = -M*c;
268  //Form the full projection matrix
270  for (unsigned r = 0; r<3; ++r)
271  for (unsigned c = 0; c<3; ++c)
272  pmatrix[r][c] = M[r][c];
273  for (unsigned r = 0; r<3; ++r)
274  pmatrix[r][3] = p3[r];
275  //account for the image scale and offsets
276  double uscale = rat_cam.scale(vpgl_rational_camera<double>::U_INDX);
277  double uoff = rat_cam.offset(vpgl_rational_camera<double>::U_INDX);
278  double vscale = rat_cam.scale(vpgl_rational_camera<double>::V_INDX);
279  double voff = rat_cam.offset(vpgl_rational_camera<double>::V_INDX);
281  Kr.fill(0.0);
282  Kr[0][0]=uscale; Kr[0][2]=uoff;
283  Kr[1][1]=vscale; Kr[1][2]=voff;
284  Kr[2][2]=1.0;
285 #if 1
286  std::cout << "Kr\n" << Kr << '\n';
287  vnl_matrix_fixed<double,3,3> KRf, KR=Kr*uq;
288  for ( int i = 0; i < 3; ++i )
289  for ( int j = 0; j < 3; ++j )
290  KRf(i,j) = KR(2-j,2-i);
291  vnl_qr<double> krQR( KRf.as_ref() );
292  vnl_matrix_fixed<double,3,3> krq,krr,krQf,krRf;
293  krq = krQR.Q();
294  krr = krQR.R();
295  for ( int i = 0; i < 3; ++i ) {
296  for ( int j = 0; j < 3; ++j ) {
297  krQf(i,j) = krq(2-j,2-i);
298  krRf(i,j) = krr(2-j,2-i);
299  }
300  }
301  std::cout << "Flipped Rotation (KR)\n" << krQf << '\n'
302  << "Flipped Upper Triangular (KR)\n" << krRf << '\n';
303 
304  int r0pos = krRf(0,0) > 0 ? 1 : -1;
305  int r1pos = krRf(1,1) > 0 ? 1 : -1;
306  int r2pos = krRf(2,2) > 0 ? 1 : -1;
307  int diag[3] = { r0pos, r1pos, r2pos };
309  for ( int i = 0; i < 3; ++i ) {
310  for ( int j = 0; j < 3; ++j ) {
311  K1(i,j) = diag[j]*krRf(i,j);
312  R1(i,j) = diag[i]*krQf(i,j);
313  }
314  }
315  K1 = K1/K1(2,2);
316  std::cout << "K1\n" << K1 <<'\n'
317  << "R1\n" << R1 << '\n'
318  << "Det R1 " << vnl_det<double>(R1) << '\n';
319 #endif
320 
321  //Need to offset x0, y0 and z0 as well.
323  T.fill(0.0);
324  T[0][0]=1.0; T[1][1]=1.0; T[2][2]=1.0; T[3][3]=1.0;
325  T[0][3] = -nx0; T[1][3]= -ny0; T[2][3]=-nz0;
326  pmatrix = Kr*pmatrix*T;
327 #if 0
328  std::cout << "P Matrix\n" << pmatrix << '\n';
329 #endif
330  camera.set_matrix(pmatrix);
331  return true;
332 }
333 
334 //:obtain a scaling transformation to normalize world geographic coordinates
335 //The resulting values will be on the range [-1, 1]
336 //The transform is valid anywhere the rational camera is valid
339 {
340  double xscale = rat_cam.scale(vpgl_rational_camera<double>::X_INDX);
341  double xoff = rat_cam.offset(vpgl_rational_camera<double>::X_INDX);
342  double yscale = rat_cam.scale(vpgl_rational_camera<double>::Y_INDX);
343  double yoff = rat_cam.offset(vpgl_rational_camera<double>::Y_INDX);
344  double zscale = rat_cam.scale(vpgl_rational_camera<double>::Z_INDX);
345  double zoff = rat_cam.offset(vpgl_rational_camera<double>::Z_INDX);
347  T.set_identity();
348  T.set(0,0,1/xscale); T.set(1,1,1/yscale); T.set(2,2,1/zscale);
349  T.set(0,3, -xoff/xscale); T.set(1,3, -yoff/yscale);
350  T.set(2,3, -zoff/zscale);
351  return T;
352 }
353 
354 
357  vgl_box_3d<double> const& approximation_volume,
359  vgl_h_matrix_3d<double>& norm_trans)
360 {
371  auto ni = static_cast<unsigned>(2*sou.scale());//# image columns
372  auto nj = static_cast<unsigned>(2*sov.scale());//# image rows
373  norm_trans.set_identity();
374  norm_trans.set(0,0,1/sox.scale()); norm_trans.set(1,1,1/soy.scale());
375  norm_trans.set(2,2,1/soz.scale());
376  norm_trans.set(0,3, -sox.offset()/sox.scale());
377  norm_trans.set(1,3, -soy.offset()/soy.scale());
378  norm_trans.set(2,3, -soz.offset()/soz.scale());
379 
380  vgl_point_3d<double> minp = approximation_volume.min_point();
381  vgl_point_3d<double> maxp = approximation_volume.max_point();
382  double xmin = minp.x(), ymin = minp.y(), zmin = minp.z();
383  double xrange = maxp.x()-xmin, yrange = maxp.y()-ymin,
384  zrange = maxp.z()-zmin;
385  if (xrange<0||yrange<0||zrange<0)
386  return false;
387  //Randomly generate points
388  unsigned n = 100;
389  std::vector<vgl_point_3d<double> > world_pts;
390  unsigned count = 0, ntrials = 0;
391  while (count<n)
392  {
393  ntrials++;
394  double rx = xrange*(std::rand()/(RAND_MAX+1.0));
395  double ry = yrange*(std::rand()/(RAND_MAX+1.0));
396  double rz = zrange*(std::rand()/(RAND_MAX+1.0));
397  vgl_point_3d<double> wp(xmin+rx, ymin+ry, zmin+rz);
398  vgl_point_2d<double> ip = rat_cam.project(wp);
399  if (ip.x()<0||ip.x()>ni||ip.y()<0||ip.y()>nj)
400  continue;
401  world_pts.push_back(wp);
402  count++;
403  }
404  std::cout << "Ntrials " << ntrials << '\n';
405 
406  //Normalize world and image points to the range [-1,1]
407  std::vector<vgl_point_3d<double> > norm_world_pts;
408  std::vector<vgl_point_2d<double> > image_pts, norm_image_pts;
409  auto N = static_cast<unsigned int>(world_pts.size());
410  for (unsigned i = 0; i<N; ++i)
411  {
412  vgl_point_3d<double> wp = world_pts[i];
413  vgl_point_2d<double> ip = rat_cam.project(wp);
414  image_pts.push_back(ip);
415  vgl_point_2d<double> nip(sou.normalize(ip.x()), sov.normalize(ip.y()));
416  norm_image_pts.push_back(nip);
417  vgl_point_3d<double> nwp(sox.normalize(wp.x()),
418  soy.normalize(wp.y()),
419  soz.normalize(wp.z()));
420  norm_world_pts.push_back(nwp);
421  }
422  //Assume identity calibration matrix initially, since image point
423  //normalization remove any scale and offset from image coordinates
425  kk.fill(0);
426  kk[0][0]= 1.0;
427  kk[1][1]= 1.0;
428  kk[2][2]=1.0;
429  //Convert solution for rotation and translation and calibration matrix of
430  //the perspective camera
432  if (! vpgl_perspective_camera_compute::compute(norm_image_pts,
433  norm_world_pts,
434  K, camera))
435  return false;
436  std::cout << camera << '\n';
437  //form the full camera by premultiplying by the image normalization
438  vpgl_calibration_matrix<double> Kmin = camera.get_calibration();
440  kk_min = Kmin.get_matrix();
441  kk[0][0]= sou.scale(); kk[0][2]= sou.offset();
442  kk[1][1]= sov.scale(); kk[1][2]= sov.offset();
443  kk *= kk_min;
444  camera.set_calibration(kk);
445 
446  //project the points approximated
447  double err_max = 0, err_min = 1e10;
448  vgl_point_3d<double> min_pt, max_pt;
449  for (unsigned i = 0; i<N; ++i)
450  {
451  vgl_point_3d<double> nwp = norm_world_pts[i];
452  double U ,V;
453  camera.project(nwp.x(), nwp.y(), nwp.z(), U, V);
454  vgl_point_2d<double> ip = image_pts[i];
455  double error = std::sqrt((ip.x()-U)*(ip.x()-U) + (ip.y()-V)*(ip.y()-V));
456  if ( error > err_max )
457  {
458  err_max = error;
459  max_pt = world_pts[i];
460  }
461  if (error < err_min)
462  {
463  err_min = error;
464  min_pt = world_pts[i];
465  }
466  }
467  std::cout << "Max Error = " << err_max << " at " << max_pt << '\n'
468  << "Min Error = " << err_min << " at " << min_pt << '\n'
469  << "final cam\n" << camera << '\n';
470  return true;
471 }
472 
473 
476  vgl_box_3d<double> const& approximation_volume,
478  vgl_h_matrix_3d<double>& norm_trans)
479 {
480  // Set up the geo converter.
481  double lon_low = approximation_volume.min_x();
482  double lat_low = approximation_volume.min_y();
483 #ifdef DEBUG
484  double lon_high = approximation_volume.max_x();
485  double lat_high = approximation_volume.max_y();
486  assert( lat_low < lat_high && lon_low < lon_high );
487 #endif
488  vpgl_lvcs lvcs_converter( lat_low, lon_low,
489  .5*(approximation_volume.min_z()+approximation_volume.max_z()),
491 
492  // Get a new local bounding box.
493  double min_lx=1e99, min_ly=1e99, min_lz=1e99, max_lx=0, max_ly=0, max_lz=0;
494  for ( int cx = 0; cx < 2; ++cx ) {
495  for ( int cy = 0; cy < 2; ++cy ) {
496  for ( int cz = 0; cz < 2; ++cz ) {
497  vgl_point_3d<double> wc(approximation_volume.min_x()*cx + approximation_volume.max_x()*(1-cx),
498  approximation_volume.min_y()*cy + approximation_volume.max_y()*(1-cy),
499  approximation_volume.min_z()*cz + approximation_volume.max_z()*(1-cz) );
500  double lcx, lcy, lcz;
501  lvcs_converter.global_to_local(wc.x(), wc.y(), wc.z(), vpgl_lvcs::wgs84, lcx, lcy, lcz );
502  vgl_point_3d<double> wc_loc( lcx, lcy, lcz );
503  if ( cx == 0 && cy == 0 && cz == 0 ) {
504  min_lx = wc_loc.x(); max_lx = wc_loc.x();
505  min_ly = wc_loc.y(); max_ly = wc_loc.y();
506  min_lz = wc_loc.z(); max_lz = wc_loc.z();
507  continue;
508  }
509  if ( wc_loc.x() < min_lx ) min_lx = wc_loc.x();
510  if ( wc_loc.y() < min_ly ) min_ly = wc_loc.y();
511  if ( wc_loc.z() < min_lz ) min_lz = wc_loc.z();
512  if ( wc_loc.x() > max_lx ) max_lx = wc_loc.x();
513  if ( wc_loc.y() > max_ly ) max_ly = wc_loc.y();
514  if ( wc_loc.z() > max_lz ) max_lz = wc_loc.z();
515  }
516  }
517  }
518  double dlx = max_lx-min_lx, dly = max_ly-min_ly, dlz = max_lz-min_lz;
519 
520  norm_trans.set_identity();
521  norm_trans.set(0,0,2/dlx); norm_trans.set(1,1,2/dly); norm_trans.set(2,2,2/dlz);
522  norm_trans.set(0,3, -1-2*min_lx/dlx );
523  norm_trans.set(1,3, -1-2*min_ly/dly);
524  norm_trans.set(2,3, -1-2*min_lz/dlz);
525 
530 #if 0 // unused
537 #endif
538  auto ni = static_cast<unsigned>(2*sou.scale());//# image columns
539  auto nj = static_cast<unsigned>(2*sov.scale());//# image rows
540 
541  vgl_point_3d<double> minp = approximation_volume.min_point();
542  vgl_point_3d<double> maxp = approximation_volume.max_point();
543  double xmin = minp.x(), ymin = minp.y(), zmin = minp.z();
544  double xrange = maxp.x()-xmin,
545  yrange = maxp.y()-ymin,
546  zrange = maxp.z()-zmin;
547  if (xrange<0||yrange<0||zrange<0)
548  return false;
549  //Randomly generate points
550  unsigned n = 100;
551  std::vector<vgl_point_3d<double> > world_pts;
552  unsigned count = 0, ntrials = 0;
553  while (count<n)
554  {
555  ++ntrials;
556  double rx = xrange*(std::rand()/(RAND_MAX+1.0));
557  double ry = yrange*(std::rand()/(RAND_MAX+1.0));
558  double rz = zrange*(std::rand()/(RAND_MAX+1.0));
559  vgl_point_3d<double> wp(xmin+rx, ymin+ry, zmin+rz);
560  vgl_point_2d<double> ip = rat_cam.project(wp);
561  if (ip.x()<0||ip.x()>ni||ip.y()<0||ip.y()>nj)
562  continue;
563  world_pts.push_back(wp);
564  count++;
565  }
566  std::cout << "Ntrials " << ntrials << '\n';
567 
568  //Normalize world and image points to the range [-1,1]
569  std::vector<vgl_point_3d<double> > norm_world_pts;
570  std::vector<vgl_point_2d<double> > image_pts, norm_image_pts;
571  auto N = static_cast<unsigned int>(world_pts.size());
572  for (unsigned i = 0; i<N; ++i)
573  {
574  vgl_point_3d<double> wp = world_pts[i];
575  vgl_point_2d<double> ip = rat_cam.project(wp);
576  image_pts.push_back(ip);
577  vgl_point_2d<double> nip(sou.normalize(ip.x()), sov.normalize(ip.y()));
578  norm_image_pts.push_back(nip);
579 
580  // Convert to local coords.
581  double lcx, lcy, lcz;
582  lvcs_converter.global_to_local(wp.x(), wp.y(), wp.z(), vpgl_lvcs::wgs84, lcx, lcy, lcz );
583  vgl_homg_point_3d<double> wp_loc( lcx, lcy, lcz );
584 
585  vgl_homg_point_3d<double> nwp = norm_trans*wp_loc;
586  assert( std::fabs(nwp.x()) <= 1
587  && std::fabs(nwp.y()) <= 1
588  && std::fabs(nwp.z()) <= 1 );
589  norm_world_pts.emplace_back(nwp );
590  }
591  //Assume identity calibration matrix initially, since image point
592  //normalization remove any scale and offset from image coordinates
594  kk.fill(0);
595  kk[0][0]= 1.0;
596  kk[1][1]= 1.0;
597  kk[2][2]=1.0;
598  //Convert solution for rotation and translation and calibration matrix of
599  //the perspective camera
601  if (! vpgl_perspective_camera_compute::compute(norm_image_pts,
602  norm_world_pts,
603  K, camera))
604  return false;
605  std::cout << camera << '\n';
606  //form the full camera by premultiplying by the image normalization
607  vpgl_calibration_matrix<double> Kmin = camera.get_calibration();
609  kk_min = Kmin.get_matrix();
610  kk[0][0]= sou.scale(); kk[0][2]= sou.offset();
611  kk[1][1]= sov.scale(); kk[1][2]= sov.offset();
612  kk *= kk_min;
613  camera.set_calibration(kk);
614 
615  //project the points approximated
616  double err_max = 0, err_min = 1e10;
617  vgl_point_3d<double> min_pt, max_pt;
618  for (unsigned i = 0; i<N; ++i)
619  {
620  vgl_point_3d<double> nwp = norm_world_pts[i];
621  double U ,V;
622  camera.project(nwp.x(), nwp.y(), nwp.z(), U, V);
623  vgl_point_2d<double> ip = image_pts[i];
624  double error = std::sqrt((ip.x()-U)*(ip.x()-U) + (ip.y()-V)*(ip.y()-V));
625  if ( error > err_max )
626  {
627  err_max = error;
628  max_pt = world_pts[i];
629  }
630  if (error < err_min)
631  {
632  err_min = error;
633  min_pt = world_pts[i];
634  }
635  }
636  std::cout << "Max Error = " << err_max << " at " << max_pt << '\n'
637  << "Min Error = " << err_min << " at " << min_pt << '\n'
638  << "final cam\n" << camera << '\n';
639  return true;
640 }
641 
642 //
643 //linear interpolation based on a set of 4 neighboring rays
644 // X
645 // X r X
646 // X
647 // ray r at the center pixel is interpolated from the neighboring X's
648 // as shown. This method is used to test if ray interpolation
649 // is sufficiently accurate
650 //
651 static bool interp_ray(std::vector<vgl_ray_3d<double> > const& ray_nbrs,
652  vgl_ray_3d<double> & intrp_ray)
653 {
654  auto nrays = static_cast<unsigned int>(ray_nbrs.size());
655  if (nrays!=4) return false;
656  vgl_ray_3d<double> r0 = ray_nbrs[0], r1 = ray_nbrs[1];
657  vgl_ray_3d<double> r2 = ray_nbrs[2], r3 = ray_nbrs[3];
658  vgl_point_3d<double> org0 = r0.origin(), org1 = r1.origin();
659  vgl_point_3d<double> org2 = r2.origin(), org3 = r3.origin();
660  vgl_vector_3d<double> dir0 = r0.direction(), dir1 = r1.direction();
661  vgl_vector_3d<double> dir2 = r2.direction(), dir3 = r3.direction();
662  // first order partial derivatives
663  vgl_vector_3d<double> dodu = 0.5*(org2-org1);
664  vgl_vector_3d<double> dodv = 0.5*(org3-org0);
665  vgl_vector_3d<double> dddu = 0.5*(dir2-dir1);
666  vgl_vector_3d<double> dddv = 0.5*(dir3-dir0);
667  vgl_point_3d<double> temp = org1 + dodu + dodv;
668  double ox = 0.5*(org0.x()+ temp.x());
669  double oy = 0.5*(org0.y()+ temp.y());
670  double oz = 0.5*(org0.z()+ temp.z());
671  vgl_point_3d<double> iorg(ox, oy, oz);
672  vgl_vector_3d<double> idir = 0.5*(dir1 + dddu + dir0 + dddv);
673  intrp_ray.set(iorg, idir);
674  return true;
675 }
676 
677 // convert tolerances on ray origin and ray direction to test interpolation
678 static bool ray_tol(vpgl_local_rational_camera<double> const& rat_cam,
679  double mid_u, double mid_v,
680  vgl_plane_3d<double> const& high,
681  vgl_point_3d<double> const& high_guess,
682  vgl_plane_3d<double> const& low,
683  vgl_point_3d<double> const& low_guess,
684  double& org_tol, double& dir_tol)
685 {
686  vgl_point_2d<double> ip(mid_u,mid_v);
687  vgl_point_3d<double> high_pt, low_pt, low_pt_pix;
688  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, high, high_guess, high_pt))
689  return false;
690  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, low, low_guess, low_pt))
691  return false;
692  //move 1 pixel
693  ip.set(mid_u+1.0, mid_v+1.0);
694  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, low, low_pt, low_pt_pix))
695  return false;
696  // Ground Sampling Distance
697  double gsd = (low_pt-low_pt_pix).length();
698  // tolerance
699  double tfact = 0.001;
700  org_tol = tfact*gsd;
701  vgl_vector_3d<double> dir0 = low_pt-high_pt;
702  vgl_vector_3d<double> dir1 = low_pt_pix-high_pt;
703  double ang = angle(dir0, dir1);
704  dir_tol = tfact*ang;
705  return true;
706 }
707 
708 // produce rays at sub pixel locations by interpolating four neighbors
709 //
710 // X <- r0
711 // _______
712 // | o o |
713 // r1-> X | | X <-r2
714 // | o o |
715 // -------
716 // X <- r3
717 // the interporlated rays are indicated by the o's
718 // the neighbor rays are shown as X's
719 // This method is used to populate higher resolution layers of the
720 // ray pyramid
721 
723 upsample_rays(std::vector<vgl_ray_3d<double> > const& ray_nbrs,
724  vgl_ray_3d<double> const& ray,
725  std::vector<vgl_ray_3d<double> >& interp_rays)
726 {
727  auto nrays = static_cast<unsigned int>(ray_nbrs.size());
728  if (nrays!=4) return false;
729  vgl_ray_3d<double> r00 = ray_nbrs[0],
730  r01 = ray_nbrs[1];
731  vgl_ray_3d<double> r10 = ray_nbrs[2],
732  r11 = ray_nbrs[3];
733  vgl_point_3d<double> org00 = r00.origin(),
734  org01 = r01.origin();
735  vgl_point_3d<double> org10 = r10.origin(),
736  org11 = r11.origin();
737  vgl_vector_3d<double> dir00 = r00.direction(), dir01 = r01.direction();
738  vgl_vector_3d<double> dir10 = r10.direction(), dir11 = r11.direction();
739 
740  //first sub ray
741  interp_rays[0] = ray;
742 
743  //second sub ray
744  vgl_point_3d<double> iorg = org00+ (org01-org00)*0.5;
745  vgl_vector_3d<double> idir = dir00*0.5 + dir01*0.5;
746  interp_rays[1].set(iorg, idir);
747 
748  //third sub ray
749  iorg = org00+ (org10-org00)*0.5;
750  idir = 0.5*dir00 + 0.5*dir10;
751  interp_rays[2].set(iorg, idir);
752 
753  //fourth sub ray
754  iorg = org00+0.25*(org01-org00) + 0.25*(org10-org00)+ 0.25*(org11-org00);
755  idir = 0.25*dir00 + 0.25*dir01+ 0.25*dir10+0.25*dir11;
756  interp_rays[3].set(iorg, idir);
757 
758  return true;
759 }
760 
761 // r0 and r1 are rays spaced a unit grid distane apart (either row or col)
762 // r is the interpolated ray at n_grid unit distances past r1
765  vgl_ray_3d<double> const& r1,
766  double n_grid)
767 {
768  vgl_vector_3d<double> v01 = r1.origin()-r0.origin();
769  vgl_point_3d<double> intp_org = r1.origin()+ n_grid*v01;
770  vgl_vector_3d<double> d01 = r1.direction()-r0.direction();
771  vgl_vector_3d<double> intp_dir = r1.direction()+ n_grid*d01;
772  return vgl_ray_3d<double>(intp_org, intp_dir);
773 }
774 
775 
776 //
777 // convert a generic camera from a local rational camera
778 // the approach is to form a pyramid and convert rays by
779 // back-projecting to top and bottom planes of the valid
780 // elevations of the rational camera. Successive higher resolution
781 // layers of the pyramid are populated until the interpolation of
782 // a ray by the four adjacent neighbors is sufficiently accurate
783 // The remaining layers of the pyramid are filled in by interpolation
784 //
787  int ni, int nj, vpgl_generic_camera<double> & gen_cam, unsigned level)
788 {
789  // get z bounds
790  double zoff = rat_cam.offset(vpgl_rational_camera<double>::Z_INDX);
791  double zscl = rat_cam.scale(vpgl_rational_camera<double>::Z_INDX);
792  // construct high and low planes
793  // NOTE: z_scale seems to usually be much larger than actual dimensions of scene, which
794  // sometimes causes trouble for vpgl_backproj_plane, which causes the conversion to fail.
795  // Using half scale value for now, but maybe we should consider taking "top" and "bottom"
796  // z values as user-specified inputs. -dec 15 Nov 2011
797  double el_low = zoff-zscl/2;
798  double el_high = zoff+zscl/2;
799  double lon = rat_cam.offset(vpgl_rational_camera<double>::X_INDX);
800  double lat = rat_cam.offset(vpgl_rational_camera<double>::Y_INDX);
801  double x,y, z_low, z_high;
802  // convert high and low elevations to local z values
803  rat_cam.lvcs().global_to_local(lon,lat,el_low,vpgl_lvcs::wgs84,x,y,z_low,vpgl_lvcs::DEG);
804  rat_cam.lvcs().global_to_local(lon,lat,el_high,vpgl_lvcs::wgs84,x,y,z_high,vpgl_lvcs::DEG);
805  return convert(rat_cam, ni, nj, gen_cam, z_low, z_high, level);
806 }
807 
808 //
809 // convert a generic camera from a local rational camera
810 // the approach is to form a pyramid and convert rays by
811 // back-projecting to top and bottom planes of the valid
812 // elevations of the rational camera. Successive higher resolution
813 // layers of the pyramid are populated until the interpolation of
814 // a ray by the four adjacent neighbors is sufficiently accurate
815 // The remaining layers of the pyramid are filled in by interpolation
816 //
817 #if 0
820  int ni, int nj, vpgl_generic_camera<double> & gen_cam,
821  double local_z_min, double local_z_max, unsigned level)
822 {
823  vgl_plane_3d<double> high(0.0, 0.0, 1.0, -local_z_max);
824  vgl_plane_3d<double> low(0.0, 0.0, 1.0, -local_z_min);
825 
826  // initial guess for backprojection to planes
827  vgl_point_3d<double> org(0.0, 0.0, local_z_max), endpt(0.0, 0.0, local_z_min);
828  // initialize the ray pyramid
829  // convert the required number of levels
830  double dim = ni;
831  if (nj<ni)
832  dim = nj;
833  double lv = std::log(dim)/std::log(2.0);
834  int n_levels = static_cast<int>(lv+1.0);// round up
835  if (dim*std::pow(0.5, static_cast<double>(n_levels-1)) < 3.0) n_levels--;
836  // construct pyramid of ray indices
837  // the row and column dimensions at each level
838  std::vector<int> nr(n_levels,0), nc(n_levels,0);
839  // the scale factor at each level (to transform back to level 0)
840  std::vector<double> scl(n_levels,1.0);
841  std::vector<vbl_array_2d<vgl_ray_3d<double> > > ray_pyr(n_levels);
842  ray_pyr[0].resize(nj, ni);
843  ray_pyr[0].fill(vgl_ray_3d<double>(vgl_point_3d<double>(0,0,0),vgl_vector_3d<double>(0,0,1)));
844  nr[0]=nj; nc[0]=ni; scl[0]=1.0;
845  std::cout<<"(ni,nj)"<<ni<<","<<nj<<std::endl;
846  int di = (ni+1)/2 +1,
847  dj = (nj+1)/2 +1;
848 
849  for (int i=1; i<n_levels; ++i)
850  {
851  std::cout<<"(di,dj)"<<di<<","<<dj<<std::endl;
852  ray_pyr[i].resize(dj,di);
853  ray_pyr[i].fill(vgl_ray_3d<double>(vgl_point_3d<double>(0,0,0),
854  vgl_vector_3d<double>(0,0,1)));
855  nr[i]=dj;
856  nc[i]=di;
857  scl[i]=2.0*scl[i-1];
858  di = (di+1)/2 +1 ;
859  dj = (dj+1)/2 +1;
860 
861  }
862  // convert the ray interpolation tolerances
863  double org_tol = 0.0;
864  double ang_tol = 0.0;
865  double max_org_err = 0.0, max_ang_err = 0.0;
866  if (!ray_tol(rat_cam, ni/2.0, nj/2.0, high,
867  org, low, endpt, org_tol, ang_tol))
868  return false;
869  bool need_interp = true;
870  int lev = n_levels-1;
871  for (; lev>=0&&need_interp; --lev) {
872  // set rays at current pyramid level
873  for (int j =0; j<nr[lev]; ++j) {
874  int sj = static_cast<int>(scl[lev]*j);
875  if (sj>=nj)
876  sj = nj-1;
877  for (int i =0;i<nc[lev]; ++i)
878  {
879  int si = static_cast<int>(scl[lev]*i);
880  if (si>=ni)
881  si = ni-1;
882  vgl_point_2d<double> ip(si,sj);
883  vgl_point_3d<double> prev_org(0.0,0.0,local_z_max);
884  vgl_point_3d<double> prev_endpt(0.0, 0.0, local_z_min);
885  // initialize guess with
886  if (lev < n_levels-1) {
887  double rel_scale = scl[lev]/scl[lev+1];
888  int i_above = static_cast<int>(rel_scale * i);
889  int j_above = static_cast<int>(rel_scale * j);
890 
891  prev_org = ray_pyr[lev+1][j_above][i_above].origin();
892  vgl_vector_3d<double> prev_dir = ray_pyr[lev+1][j_above][i_above].direction();
893  // find endpoint
894  double ray_len = (local_z_min - prev_org.z()) / prev_dir.z();
895  prev_endpt = prev_org + (prev_dir * ray_len);
896  }
897  constexpr double error_tol = 0.25; // allow projection error of 0.25 pixel
898  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, high, prev_org, org, error_tol))
899  return false;
900  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, low, prev_endpt, endpt, error_tol))
901  return false;
902 
903  vgl_vector_3d<double> dir = endpt-org;
904  ray_pyr[lev][j][i].set(org, dir);
905  }
906  }
907 
908  // check for interpolation accuracy at the current level
909  // scan through the array and find largest discrepancy in
910  // ray origin and ray direction
911  need_interp = false;
912  max_org_err = 0.0; max_ang_err = 0.0;
913  std::vector<vgl_ray_3d<double> > ray_nbrs(4);
914 
915  for (int j =1; (j<nr[lev]-1)&&!need_interp; ++j) {
916  for (int i =1;(i<nc[lev]-1)&&!need_interp; ++i) {
917  vgl_ray_3d<double> ray = ray_pyr[lev][j][i];
918  //
919  //collect 4-neighbors of ray
920  //
921  // 0
922  // 1 x 2
923  // 3
924  //
925  ray_nbrs[0]=ray_pyr[lev][j-1][i];
926  ray_nbrs[1]=ray_pyr[lev][j][i-1];
927  ray_nbrs[2]=ray_pyr[lev][j][i+1];
928  ray_nbrs[3]=ray_pyr[lev][j+1][i];
929  //interpolate using neighbors
930  vgl_ray_3d<double> intp_ray;
931  if (!interp_ray(ray_nbrs, intp_ray))
932  return false;
933  double dorg = (ray.origin()-intp_ray.origin()).length();
934  double dang = angle(ray.direction(), intp_ray.direction());
935  if (dorg>max_org_err) max_org_err = dorg;
936  if (dang>max_ang_err) max_ang_err = dang;
937  need_interp = max_org_err>org_tol || max_ang_err>ang_tol;
938  if(need_interp)
939  std::cout<<lev<<", "<<i<<","<<j<<std::endl;
940  }
941  }
942  }
943  // found level where interpolation is within tolerance
944  // fill in values at lower levels
945  for (++lev; lev>0; --lev) {
946  unsigned int ncr = nc[lev];
947  unsigned int nrb = nr[lev];
948  vbl_array_2d<vgl_ray_3d<double> >& clev = ray_pyr[lev];
949  vbl_array_2d<vgl_ray_3d<double> >& nlev = ray_pyr[lev-1];
950  std::vector<vgl_ray_3d<double> > ray_nbrs(4);
951  std::vector<vgl_ray_3d<double> > interp_rays(4);
952  for (unsigned int j = 0; j<nrb; ++j)
953  for (unsigned int i = 0; i<ncr; ++i) {
954  ray_nbrs[0] = clev[j][i];
955  ray_nbrs[1] = clev[j][i];
956  ray_nbrs[2] = clev[j][i];
957  ray_nbrs[3] = clev[j][i];
958  if (i+1<ncr) ray_nbrs[1] = clev[j][i+1];
959  if (j+1<nrb) ray_nbrs[2] = clev[j+1][i];
960  if (i+1<ncr && j+1<nrb) ray_nbrs[3] = clev[j+1][i+1];
961  if (!upsample_rays(ray_nbrs, clev[j][i], interp_rays))
962  return false;
963  if (2*i<nlev.cols() && 2*j<nlev.rows())
964  {
965  nlev[2*j][2*i] =interp_rays[0];
966  if (2*i+1<nlev.cols()) nlev[2*j][2*i+1] =interp_rays[1];
967  if (2*j+1<nlev.rows()) nlev[2*j+1][2*i] =interp_rays[2];
968  if (2*i+1<nlev.cols() && 2*j+1<nlev.rows()) nlev[2*j+1][2*i+1]=interp_rays[3];
969  }
970  }
971  }
972  if ((int)level < n_levels) {
973  gen_cam = vpgl_generic_camera<double>(ray_pyr,nr,nc);
974  return true;
975  }
976  else
977  return false;
978 }
979 #endif
980 
981 
983  int ni, int nj,int offseti, int offsetj,
984  double local_z_min, double local_z_max,
985  int n_levels,std::vector<int> nr, std::vector<int> nc,
986  std::vector<unsigned int> scl,std::vector<vbl_array_2d<vgl_ray_3d<double> > > & ray_pyr )
987 
988 {
989  vgl_plane_3d<double> high(0.0, 0.0, 1.0, -local_z_max);
990  vgl_plane_3d<double> low(0.0, 0.0, 1.0, -local_z_min);
991 
992  // initial guess for backprojection to planes
993  vgl_point_3d<double> org(0.0, 0.0, local_z_max), endpt(0.0, 0.0, local_z_min);
994  // convert the ray interpolation tolerances
995  double org_tol = 0.0;
996  double ang_tol = 0.0;
997  double max_org_err = 0.0, max_ang_err = 0.0;
998  if (!ray_tol(rat_cam,offseti+ ni/2.0, offsetj +nj/2.0, high,
999  org, low, endpt, org_tol, ang_tol))
1000  return false;
1001 
1002  bool need_interp = true;
1003  int lev = n_levels-1;
1004  //std::cout<<" lev "<<lev<<" ";
1005  for (; lev>=0&&need_interp; --lev) {
1006  // set rays at current pyramid level
1007  for (int j =0; j<nr[lev]; ++j) {
1008  int sj = offsetj+static_cast<int>(scl[lev]*j);
1009  //sj = (j == 0)? sj : sj -1;
1010  //if (sj>=gnj)
1011  // sj =gnj;
1012  for (int i =0;i<nc[lev]; ++i)
1013  {
1014  int si = offseti+ static_cast<int>(scl[lev]*i);
1015  //si = (i == 0)? si : si -1;
1016  //if (si>=gni)
1017  // si = gni;
1018  vgl_point_2d<double> ip(si,sj);
1019  vgl_point_3d<double> prev_org(0.0,0.0,local_z_max);
1020  vgl_point_3d<double> prev_endpt(0.0, 0.0, local_z_min);
1021  // initialize guess with
1022  if (lev < n_levels-1) {
1023  double rel_scale = scl[lev]/scl[lev+1];
1024  int i_above = static_cast<int>(rel_scale * i);
1025  int j_above = static_cast<int>(rel_scale * j);
1026  prev_org = ray_pyr[lev+1][j_above][i_above].origin();
1027  vgl_vector_3d<double> prev_dir = ray_pyr[lev+1][j_above][i_above].direction();
1028  // find endpoint
1029  double ray_len = (local_z_min - prev_org.z()) / prev_dir.z();
1030  prev_endpt = prev_org + (prev_dir * ray_len);
1031  }
1032  constexpr double error_tol = 0.5; // allow projection error of 0.25 pixel
1033  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, high, prev_org, org, error_tol))
1034  return false;
1035  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, low, prev_endpt, endpt, error_tol))
1036  return false;
1037  vgl_vector_3d<double> dir = endpt-org;
1038  ray_pyr[lev][j][i].set(org, dir);
1039  }
1040  }
1041  // check for interpolation accuracy at the current level
1042  // scan through the array and find largest discrepancy in
1043  // ray origin and ray direction
1044  need_interp = false;
1045  max_org_err = 0.0; max_ang_err = 0.0;
1046  std::vector<vgl_ray_3d<double> > ray_nbrs(4);
1047 
1048  for (int j =1; (j<nr[lev]-1)&&!need_interp; ++j) {
1049  for (int i =1;(i<nc[lev]-1)&&!need_interp; ++i) {
1050  vgl_ray_3d<double> ray = ray_pyr[lev][j][i];
1051  //
1052  //collect 4-neighbors of ray
1053  //
1054  // 0
1055  // 1 x 2
1056  // 3
1057  //
1058  ray_nbrs[0]=ray_pyr[lev][j-1][i];
1059  ray_nbrs[1]=ray_pyr[lev][j][i-1];
1060  ray_nbrs[2]=ray_pyr[lev][j][i+1];
1061  ray_nbrs[3]=ray_pyr[lev][j+1][i];
1062  //interpolate using neighbors
1063  vgl_ray_3d<double> intp_ray;
1064  if (!interp_ray(ray_nbrs, intp_ray))
1065  return false;
1066  double dorg = (ray.origin()-intp_ray.origin()).length();
1067  double dang = angle(ray.direction(), intp_ray.direction());
1068  if (dorg>max_org_err) max_org_err = dorg;
1069  if (dang>max_ang_err) max_ang_err = dang;
1070  need_interp = max_org_err>org_tol || max_ang_err>ang_tol;
1071 #if 0
1072  if(need_interp)
1073  std::cout<<lev<<", "<<i<<","<<j<<std::endl;
1074 #endif
1075  }
1076  }
1077  }
1078  // found level where interpolation is within tolerance
1079  // fill in values at lower levels
1080  for (++lev; lev>0; --lev) {
1081  unsigned int ncr = nc[lev];
1082  unsigned int nrb = nr[lev];
1083  vbl_array_2d<vgl_ray_3d<double> >& clev = ray_pyr[lev];
1084  vbl_array_2d<vgl_ray_3d<double> >& nlev = ray_pyr[lev-1];
1085  std::vector<vgl_ray_3d<double> > ray_nbrs(4);
1086  std::vector<vgl_ray_3d<double> > interp_rays(4);
1087  for (unsigned int j = 0; j<nrb; ++j) {
1088  for (unsigned int i = 0; i<ncr; ++i) {
1089  ray_nbrs[0] = clev[j][i];
1090  ray_nbrs[1] = clev[j][i];
1091  ray_nbrs[2] = clev[j][i];
1092  ray_nbrs[3] = clev[j][i];
1093  if (i+1<ncr) ray_nbrs[1] = clev[j][i+1];
1094  if (j+1<nrb) ray_nbrs[2] = clev[j+1][i];
1095  if (i+1<ncr && j+1<nrb) ray_nbrs[3] = clev[j+1][i+1];
1096  if (!upsample_rays(ray_nbrs, clev[j][i], interp_rays))
1097  return false;
1098  if (2*i<nlev.cols() && 2*j<nlev.rows())
1099  {
1100  nlev[2*j][2*i] =interp_rays[0];
1101  if (2*i+1<nlev.cols()) nlev[2*j][2*i+1] =interp_rays[1];
1102  if (2*j+1<nlev.rows()) nlev[2*j+1][2*i] =interp_rays[2];
1103  if (2*i+1<nlev.cols() && 2*j+1<nlev.rows()) nlev[2*j+1][2*i+1]=interp_rays[3];
1104  }
1105  }
1106  }
1107  }
1108  return true;
1109 }
1110 #if 1
1111 //: Implementation of breaking up images in 256x256 blocks
1114  int gni, int gnj, vpgl_generic_camera<double> & gen_cam,
1115  double local_z_min, double local_z_max, unsigned level)
1116 {
1117  vgl_plane_3d<double> high(0.0, 0.0, 1.0, -local_z_max);
1118  vgl_plane_3d<double> low(0.0, 0.0, 1.0, -local_z_min);
1119 
1120  // initial guess for backprojection to planes
1121  vgl_point_3d<double> org(0.0, 0.0, local_z_max), endpt(0.0, 0.0, local_z_min);
1122  // initialize the ray pyramid
1123  // convert the required number of levels
1124 
1125  unsigned int mindim = gni < gnj ? gni : gnj;
1126  unsigned int factor = 256;
1127  unsigned int n_levels = 6;
1128 
1129  while( mindim < factor )
1130  {
1131  factor /= 2;
1132  n_levels--;
1133 
1134  std::cout<<"."<<std::endl;
1135  }
1136  unsigned int numi = gni/factor;
1137  unsigned int numj = gnj/factor;
1138 
1139  // construct pyramid of ray indices
1140  // the row and column dimensions at each level
1141  std::vector<int> nr(n_levels,0), nc(n_levels,0);
1142  vbl_array_2d<vgl_ray_3d<double> > finalrays(gnj,gni);
1143 
1144  std::vector<unsigned int> scl(n_levels);
1145  unsigned int running_scale = 1;
1146  std::vector<vbl_array_2d<vgl_ray_3d<double> > > ray_pyr(n_levels);
1147  for(unsigned int i = 0; i < n_levels; i++)
1148  {
1149  nr[i] = factor/running_scale + 1;
1150  nc[i] = factor/running_scale + 1;
1151  ray_pyr[i].resize(nr[i], nc[i]);
1152  ray_pyr[i].fill(vgl_ray_3d<double>(vgl_point_3d<double>(0,0,0),vgl_vector_3d<double>(0,0,1)));
1153  scl[i]=running_scale;
1154  running_scale*=2;
1156  }
1157  for (unsigned int bigi=0 ; bigi<=numi;bigi++)
1158  {
1159  unsigned int ni = factor;
1160  unsigned int offseti = bigi*factor;
1161  if(bigi == numi )
1162  offseti = gni-factor;
1163  for (unsigned int bigj=0 ; bigj<=numj;bigj++)
1164  {
1165  unsigned int nj = factor;
1166  unsigned int offsetj = bigj*factor;
1167  if(bigj == numj )
1168  offsetj = gnj-factor;
1169  // vpgl_generic_camera expects pixels centered at integer values (pyramid_est is agnostic;
1170  // although, as uints, offseti and offsetj will center pixels on integers)
1171  if(!vpgl_generic_camera_convert::pyramid_est(rat_cam,ni,nj,offseti,offsetj,local_z_min, local_z_max,n_levels,nr, nc,scl,ray_pyr ))
1172  return false;
1173  for(unsigned int i = 0; i < factor; i++)
1174  {
1175  for(unsigned int j = 0; j < factor; j++)
1176  {
1177  if(j+offsetj < gnj && i+offseti <gni )
1178  finalrays[j+offsetj][i+offseti] = ray_pyr[0][j][i];
1179  }
1180  }
1181  }
1182  }
1183  if (level < n_levels) {
1184  gen_cam = vpgl_generic_camera<double>(finalrays);
1185  return true;
1186  }
1187  else
1188  return false;
1189 }
1190 #endif
1191 #if 1
1194  int gni, int gnj, vpgl_generic_camera<double> & gen_cam,
1195  double local_z_min, double local_z_max, unsigned /*level*/)
1196 {
1197  vgl_plane_3d<double> high(0.0, 0.0, 1.0, -local_z_max);
1198  vgl_plane_3d<double> low(0.0, 0.0, 1.0, -local_z_min);
1199 
1200  // initial guess for backprojection to planes
1201  vgl_point_3d<double> org(0.0, 0.0, local_z_max), endpt(0.0, 0.0, local_z_min);
1202  // initialize the ray pyramid
1203  // convert the required number of levels
1204  vbl_array_2d<vgl_ray_3d<double> > finalrays(gnj,gni);
1205  for(unsigned i = 0 ; i < gni; i++)
1206  {
1207  for(unsigned j = 0 ; j < gnj; j++)
1208  {
1209  vgl_point_2d<double> ip(i,j);
1210  constexpr double error_tol = 0.5; // allow projection error of 0.25 pixel
1211  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, high, org, org, error_tol))
1212  return false;
1213  if (!vpgl_backproject::bproj_plane(&rat_cam, ip, low, endpt, endpt, error_tol))
1214  return false;
1215  vgl_vector_3d<double> dir = endpt-org;
1216  finalrays[j][i].set(org, dir);
1217  }
1218  }
1219 
1220  gen_cam = vpgl_generic_camera<double>(finalrays);
1221  return true;
1222 }
1223 #endif
1225 convert( vpgl_proj_camera<double> const& prj_cam, int ni, int nj,
1226  vpgl_generic_camera<double> & gen_cam, unsigned level)
1227 {
1228  vbl_array_2d<vgl_ray_3d<double> > rays(nj, ni);
1229  vgl_ray_3d<double> ray;
1231  double scale = (level < 32) ? double(1L<<level) : std::pow(2.0,static_cast<double>(level));
1232  for (int j = 0; j<nj; ++j)
1233  for (int i = 0; i<ni; ++i) {
1234  ipt.set(i*scale, j*scale, 1.0);
1235  ray = prj_cam.backproject_ray(ipt);
1236  rays[j][i]=ray;
1237  }
1238  gen_cam = vpgl_generic_camera<double>(rays);
1239  return true;
1240 }
1241 
1243 convert( vpgl_perspective_camera<double> const& per_cam, int ni, int nj,
1244  vpgl_generic_camera<double> & gen_cam, unsigned level)
1245 {
1246  vbl_array_2d<vgl_ray_3d<double> > rays(nj, ni);
1247  vgl_ray_3d<double> ray;
1249  double scale = (level < 32) ? double(1L<<level) : std::pow(2.0,static_cast<double>(level));
1250  for (int j = 0; j<nj; ++j)
1251  for (int i = 0; i<ni; ++i) {
1252  ipt.set(i*scale, j*scale, 1.0);
1253  ray = per_cam.backproject_ray(ipt);
1254  rays[j][i]=ray;
1255  }
1256  gen_cam = vpgl_generic_camera<double>(rays);
1257  return true;
1258 }
1259 
1262  vpgl_generic_camera<double> & gen_cam, int margin, unsigned level)
1263 {
1264  vbl_array_2d<vgl_ray_3d<double> > rays(nj+2*margin, ni+2*margin);
1265  vgl_ray_3d<double> ray;
1267  double scale = (level < 32) ? double(1L<<level) : std::pow(2.0,static_cast<double>(level));
1268  for (int j = -margin; j<nj+margin; ++j)
1269  for (int i = -margin; i<ni+margin; ++i) {
1270  ipt.set(i*scale, j*scale, 1.0);
1271  ray = per_cam.backproject_ray(ipt);
1272  rays[j+margin][i+margin]=ray;
1273  }
1274  gen_cam = vpgl_generic_camera<double>(rays);
1275  return true;
1276 }
1277 
1278 
1279 // the affine camera defines a principal plane which is
1280 // far enough from the scene origin so that all the scene
1281 // geometry is in front of the plane. The backproject function
1282 // constructs finite ray origins on the principal plane.
1284 convert( vpgl_affine_camera<double> const& aff_cam, int ni, int nj,
1285  vpgl_generic_camera<double> & gen_cam, unsigned level)
1286 {
1287  double scale = (level < 32) ? double(1L<<level) : std::pow(2.0,static_cast<double>(level));
1288  // The ray direction is the camera center (which is at inifnity)
1289  vgl_homg_point_3d<double> cent = aff_cam.camera_center();
1290  vgl_vector_3d<double> dir(cent.x(), cent.y(), cent.z());
1291 
1292  // get the principal plane, on which all ray origins will lie
1293  vgl_homg_plane_3d<double> plane = aff_cam.principal_plane();
1294 
1295  // compute the transformation from image coordinates to X,Y,Z on the principal plane
1297  double u0 = P(0,3);
1298  double v0 = P(1,3);
1299 
1301  for (unsigned j=0; j<3; ++j) {
1302  A(0,j) = P(0,j);
1303  A(1,j) = P(1,j);
1304  }
1305  A(2,0) = plane.a();
1306  A(2,1) = plane.b();
1307  A(2,2) = plane.c();
1308 
1309  // invA maps (u-u0, v-v0, -d) to X,Y,Z on the principal plane
1311 
1312  // construct the array of camera rays
1313  vgl_point_3d<double> org;;
1314  vbl_array_2d<vgl_ray_3d<double> > rays(nj, ni);
1315  for (int j = 0; j<nj; ++j) {
1316  for (int i = 0; i<ni; ++i) {
1317  vnl_vector_fixed<double,3> ipt(i*scale-u0, j*scale-v0, -plane.d());
1318  vnl_vector_fixed<double,3> org_vnl = invA * ipt;
1319  // convert from vnl_vector to vgl_vector
1320  org.set(org_vnl[0], org_vnl[1], org_vnl[2]);
1321  rays[j][i].set(org, dir);
1322  }
1323  }
1324  gen_cam = vpgl_generic_camera<double>(rays);
1325  return true;
1326 }
1327 
1328 
1330 convert( vpgl_camera_double_sptr const& camera, int ni, int nj,
1331  vpgl_generic_camera<double> & gen_cam, unsigned level)
1332 {
1333  if (auto* cam =
1334  dynamic_cast<vpgl_local_rational_camera<double>*>(camera.ptr()))
1335  return vpgl_generic_camera_convert::convert(*cam, ni, nj, gen_cam, level);
1336 
1337  if (auto* cam =
1338  dynamic_cast<vpgl_perspective_camera<double>*>(camera.ptr())) {
1339  return vpgl_generic_camera_convert::convert(*cam, ni, nj, gen_cam, level);
1340  }
1341 
1342  if (auto* cam =
1343  dynamic_cast<vpgl_affine_camera<double>*>(camera.ptr()))
1344  return vpgl_generic_camera_convert::convert(*cam, ni, nj, gen_cam, level);
1345 
1346  if (auto* cam =
1347  dynamic_cast<vpgl_proj_camera<double>*>(camera.ptr())) {
1348  return vpgl_generic_camera_convert::convert(*cam, ni, nj, gen_cam, level);
1349  }
1350 
1351  return false;
1352 }
1353 
1354 
1355 #if HAS_GEOTIFF
1356 //: Convert a geocam (transformtaion matrix read from a geotiff header + an lvcs) to a generic camera
1357 bool vpgl_generic_camera_convert::convert( vpgl_geo_camera& geocam, int ni, int nj, double height,
1358  vpgl_generic_camera<double> & gen_cam, unsigned level)
1359 {
1360  double scale = (level < 32) ? double(1L<<level) : std::pow(2.0,static_cast<double>(level));
1361 
1362  //: all rays have the same direction
1363  vgl_vector_3d<double> dir(0.0, 0.0, -1.0);
1364 
1365  vbl_array_2d<vgl_ray_3d<double> > rays(nj, ni);
1367 
1368  for (int j = 0; j<nj; ++j)
1369  for (int i = 0; i<ni; ++i) {
1370  double x,y,z;
1371  geocam.backproject(i*scale, j*scale,x,y,z);
1372  org.set(x, y, height);
1373  rays[j][i].set(org, dir);
1374  }
1375  gen_cam = vpgl_generic_camera<double>(rays);
1376  return true;
1377 }
1378 
1379 //: Convert a geocam (transformtaion matrix read from a geotiff header + an lvcs) to a generic camera using the specified ray direction (not necessarily nadir rays)
1380 // basically creates a camera with parallel rays but the rays can be in any direction
1381 bool vpgl_generic_camera_convert::convert( vpgl_geo_camera& geocam, int ni, int nj, double height, vgl_vector_3d<double>& dir,
1382  vpgl_generic_camera<double> & gen_cam, unsigned level)
1383 {
1384  double scale = (level < 32) ? double(1L<<level) : std::pow(2.0,static_cast<double>(level));
1385 
1386  vbl_array_2d<vgl_ray_3d<double> > rays(nj, ni);
1388 
1389  for (int j = 0; j<nj; ++j)
1390  for (int i = 0; i<ni; ++i) {
1391  double x,y,z;
1392  geocam.backproject(i*scale, j*scale,x,y,z);
1393  org.set(x, y, height);
1394  rays[j][i].set(org, dir);
1395  }
1396  gen_cam = vpgl_generic_camera<double>(rays);
1397  return true;
1398 }
1399 
1402  vgl_box_3d<double> const& region_of_interest,
1403  vpgl_affine_camera<double>& camera_out,
1404  unsigned int num_points)
1405 {
1406  vnl_random rng;
1407  const double width = region_of_interest.width();
1408  const double height = region_of_interest.height();
1409  const double depth = region_of_interest.depth();
1410  double min_x = region_of_interest.min_x();
1411  double min_y = region_of_interest.min_y();
1412  double min_z = region_of_interest.min_z();
1413 
1414  std::vector< vgl_point_2d<double> > image_pts;
1415  std::vector< vgl_point_3d<double> > world_pts;
1416  for (unsigned i=0; i<num_points; ++i) {
1417  double x = rng.drand64()*width + min_x; // sample in local coords
1418  double y = rng.drand64()*depth + min_y;
1419  double z = rng.drand64()*height + min_z;
1420  world_pts.emplace_back(x,y,z);
1421  double u, v;
1422  camera_in.project(x,y,z,u,v); // local rational camera has an lvcs, so it handles, local coord to global to image point projection internally
1423  image_pts.emplace_back(u,v);
1424  }
1425 
1426  bool success = vpgl_affine_camera_compute::compute(image_pts, world_pts, camera_out);
1427  // it is assumed that the camera is above the region of interest
1428  camera_out.set_viewing_distance(height*10);
1429  camera_out.orient_ray_direction(vgl_vector_3d<double>(0,0,-1));
1430  return success;
1431 }
1432 
1433 #endif // HAS_GEOTIFF
1434 
1435 #endif // vpgl_camera_convert_cxx_
void project(const T x, const T y, const T z, T &u, T &v) const override
The generic camera interface. u represents image column, v image row.
vnl_matrix_ref< T > as_ref()
A geographic coordinate system.
static bool convert_bruteforce(vpgl_local_rational_camera< double > const &rat_cam, int gni, int gnj, vpgl_generic_camera< double > &gen_cam, double local_z_min, double local_z_max, unsigned level)
double drand64(double a, double b)
T normalize(const T value) const
static bool convert(vpgl_local_rational_camera< double > const &camera_in, vgl_box_3d< double > const &region_of_interest, vpgl_affine_camera< double > &camera_out, unsigned int num_points=10000)
Convert from rational camera using a local Euclidean coordinate system.
vnl_vector< T > nullvector() const
void global_to_local(const double lon, const double lat, const double gz, cs_names cs_name, double &lx, double &ly, double &lz, AngUnits output_ang_unit=DEG, LenUnits output_len_unit=METERS)
Converts pointin, given in a global coord system described by global_cs_name, to pointout in the loca...
Definition: vpgl_lvcs.cxx:555
Type min_z() const
vgl_h_matrix_3d & set(unsigned int row_index, unsigned int col_index, T value)
Several routines for converting camera types.
size_type rows() const
static bool convert(vpgl_rational_camera< double > const &rat_cam, vgl_box_3d< double > const &approximation_volume, vpgl_perspective_camera< double > &camera, vgl_h_matrix_3d< double > &norm_trans)
Convert from a rational camera.
size_type cols() const
Type max_z() const
vnl_matrix< T > const & Q() const
vnl_matrix< T > inverse() const
vnl_matrix< T > const & R() const
vpgl_scale_offset< T > scl_off(const coor_index coor_index) const
get a specific scale_offset.
Type & z()
static bool convert_local(vpgl_rational_camera< double > const &rat_cam, vgl_box_3d< double > const &approximation_volume, vpgl_perspective_camera< double > &camera, vgl_h_matrix_3d< double > &norm_trans)
Convert from rational camera using a local Euclidean coordinate system.
void backproject(const double u, const double v, double &x, double &y, double &z)
backprojects an image point into local coordinates (based on lvcs_).
Type max_y() const
#define v
static bool convert(vpgl_rational_camera< double > const &rat_cam, vgl_point_3d< double > const &world_center, vpgl_proj_camera< double > &camera)
Find a projective camera that best approximates the rational camera at the world center (lon (deg),...
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
vgl_point_3d< Type > min_point() const
Type min_x() const
static bool convert_with_margin(vpgl_perspective_camera< double > const &per_cam, int ni, int nj, vpgl_generic_camera< double > &gen_cam, int margin, unsigned level=0)
Several routines for computing different kinds of cameras from world-point correspondences.
void fill(T value)
static vgl_h_matrix_3d< double > norm_trans(vpgl_rational_camera< double > const &rat_cam)
An auxiliary matrix that transforms (normalizes) world points prior to projection by a projective cam...
Type depth() const
Methods for projecting geometric structures onto the image.
Type & y()
vnl_matrix_fixed< T, 3, 3 > get_matrix() const
Get the calibration matrix.
vnl_matrix_fixed & fill(T)
std::vector< std::vector< T > > coefficients() const
get the rational polynomial coefficients in a vcl array.
vgl_ray_3d< T > backproject_ray(const vgl_point_2d< T > &image_point) const
Finite ray backprojection.
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.
void set(T px, T py, T pw=(T) 1)
T z() const
vgl_point_3d< Type > origin() const
Type & x()
This class implements the perspective camera class as described in Hartley & Zisserman as a finite ca...
vgl_h_matrix_3d & set_identity()
vgl_homg_point_3d< T > camera_center() const override
Find the 3d coordinates of the center of the camera. Will be an ideal point with the sense of the ray...
Type height() const
void orient_ray_direction(vgl_vector_3d< T > const &look_dir)
flip the ray direction so that dot product with look_dir is positive.
T scale(const coor_index coor_index) const
get a specific scale value.
static bool compute(const std::vector< vgl_point_2d< double > > &image_pts, const std::vector< vgl_point_3d< double > > &world_pts, vpgl_affine_camera< double > &camera)
Compute from two sets of corresponding points.
const vnl_matrix_fixed< T, 3, 4 > & get_matrix() const
Return a copy of the camera matrix.
vgl_point_3d< Type > max_point() const
static bool convert(vpgl_local_rational_camera< double > const &rat_cam, int ni, int nj, vpgl_generic_camera< double > &gen_cam, unsigned level=0)
Convert a local rational camera to a generic camera.
static bool pyramid_est(vpgl_local_rational_camera< double > const &rat_cam, int ni, int nj, int offseti, int offsetj, double local_z_min, double local_z_max, int n_levels, std::vector< int > nr, std::vector< int > nc, std::vector< unsigned int > scl, std::vector< vbl_array_2d< vgl_ray_3d< double > > > &ray_pyr)
void set(T vx, T vy, T vz)
void set(vgl_point_3d< Type > const &p0, vgl_vector_3d< Type > const &direction)
static vgl_ray_3d< double > interp_pair(vgl_ray_3d< double > const &r0, vgl_ray_3d< double > const &r1, double n_grid)
interpolate a span of rays base on a linear interpolation. n_grid is the step distance from r1....
double angle(vgl_ray_3d< Type > const &r0, vgl_ray_3d< Type > const &r1)
vgl_homg_plane_3d< T > principal_plane() const override
Find the world plane perpendicular to the camera rays at viewing distance from the origin.
void set(Type px, Type py, Type pz)
Type min_y() const
void set_viewing_distance(T dist)
set a finite viewing distance to allow the methods below to return finite objects.
static bool compute(const std::vector< vgl_point_2d< double > > &image_pts, const std::vector< vgl_point_3d< double > > &world_pts, const vpgl_calibration_matrix< double > &K, vpgl_perspective_camera< double > &camera)
Compute from two sets of corresponding points.
Type width() const
T offset(const coor_index coor_index) const
get a specific offset value.
double length(v const &a)
Type y() const
Solve min(R,s) ||X-s(RY+t)||, where R is a rotation matrix, X,Y are 3-d points, s is a scalar and t i...
static bool bproj_plane(const vpgl_camera< double > *cam, vnl_double_2 const &image_point, vnl_double_4 const &plane, vnl_double_3 const &initial_guess, vnl_double_3 &world_point, double error_tol=0.05, double relative_diameter=1.0)
Generic camera interfaces (pointer for abstract class).
Type x() const
vgl_vector_3d< Type > direction() const
T * ptr() const
Methods for back_projecting from cameras to 3-d geometric structures.
Type & y()
Type & x()
static bool upsample_rays(std::vector< vgl_ray_3d< double > > const &ray_nbrs, vgl_ray_3d< double > const &ray, std::vector< vgl_ray_3d< double > > &interp_rays)
interpolate rays to fill next higher resolution pyramid layer.
void project(const T x, const T y, const T z, T &u, T &v) const override
The generic camera interface. u represents image column, v image row.
Type max_x() const