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