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