1 #include <iostream>
2 #include <cmath>
3 #include "vsph_camera_bounds.h"
4 #include <bpgl/algo/bpgl_project.h>
5 #include <vpgl/algo/vpgl_ray.h>
6 #include <vgl/algo/vgl_rotation_3d.h>
7 #include "vgl/vgl_closest_point.h"
8 #include "vgl/vgl_plane_3d.h"
9 #include "vgl/vgl_intersection.h"
10 #ifdef _MSC_VER
11 #  include "vcl_msvc_warnings.h"
12 #endif
13 #include "vnl/vnl_math.h"
14 
principal_ray_scan(double cone_half_angle,unsigned & n_samples)15 principal_ray_scan::principal_ray_scan(double cone_half_angle,
16                                        unsigned& n_samples)
17 {
18   //temporary array for samples
19   std::vector<double> theta(n_samples+1);
20   std::vector<double> phi(n_samples+1);
21   //find the fraction of the sphere defined by the cone half angle
22   double cone_solid_angle = vnl_math::twopi*(1.0-std::cos(cone_half_angle));
23   double fraction_of_sphere = cone_solid_angle/(4.0*vnl_math::pi);
24   //number of points that would cover the entire sphere
25   auto limit = static_cast<unsigned>(n_samples/fraction_of_sphere);
26   //the following algorithm assumes that the entire sphere is uniformly
27   //sampled
28   // ======   sphere sampling with the spiral algorithm =======
29   double p = 0.5;
30   auto ns = static_cast<double>(limit);
31   double a = 1.0 - 2.0*p/(limit-3);
32   double b = p*(limit+1)/(limit-3);
33   double rkm1 = 0;
34   // spiral starts at South pole of sphere
35   unsigned k = 1;
36   theta[k-1] = vnl_math::pi;
37   phi[k-1]  = 0;
38   double elv = 0;
39   for (k = 2; k<=(limit-1)&&elv<=cone_half_angle; ++k) {
40     double kp  = a*k + b;
41     double h   = -1.0 + 2*(kp-1.0)/(limit-1);
42     double rk  = std::sqrt(1-h*h);
43     theta[k-1] = std::acos(h);
44     // cut off the scan when the elevation reaches the cone half angle
45     elv = vnl_math::pi-theta[k-1];
46     double temp  = phi[k-2] + 3.6/std::sqrt(ns)*2/(rkm1+rk);
47     phi[k-1] = vnl_math::angle_0_to_2pi(temp);
48     rkm1 = rk;
49   }
50   //if the entire sphere is covered then add the North pole
51   if ( limit == n_samples) {
52     theta[n_samples-1] = 0.0;
53     phi[n_samples-1]   = 0.0;
54   }
55   //adjust for the actual number of samples
56   if ((k-1)<n_samples)
57     n_samples = k-1;
58   theta_.resize(n_samples);
59   phi_.resize(n_samples);
60   //move samples to the North pole so the scan is about the identity rotation
61   for (unsigned i = 0; i<n_samples; ++i)
62   {
63     theta_[i]=vnl_math::pi-theta[i];
64     phi_[i]=phi[i];
65   }
66   index_ = 0;
67 }
68 
reset()69 void principal_ray_scan::reset() { index_ = -1; }
70 
next()71 bool principal_ray_scan::next()
72 {return ++index_<static_cast<int>(theta_.size());}
73 
pt_on_unit_sphere(unsigned i) const74 vgl_point_3d<double> principal_ray_scan::pt_on_unit_sphere(unsigned i) const
75 {
76   double th = theta_[i], ph = phi_[i];
77   double st = std::sin(th), ct = std::cos(th);
78   double x = st*std::cos(ph), y = st*std::sin(ph), z = ct;
79   return {x, y, z};
80 }
81 
rot(unsigned i,double alpha) const82 vgl_rotation_3d<double> principal_ray_scan::rot(unsigned i, double alpha) const
83 {
84   double th = theta_[i], ph = phi_[i];
85   double st = std::sin(th), ct = std::cos(th);
86   //principal axis
87   double x = st*std::cos(ph), y = st*std::sin(ph), z = ct;
88   vnl_vector_fixed<double, 3> za(0.0, 0.0, 1.0), v(x, y, z);
89   //rotation from principal axis to z axis
90   vgl_rotation_3d<double> R_axis(v, za);
91   // alpha rotation as a Rodrigues vector
92   vnl_vector_fixed<double, 3> vr = alpha*za;
93   vgl_rotation_3d<double> R_about_axis(vr);
94   return R_about_axis*R_axis;
95 }
96 
solid_angle(double cone_half_angle)97 double vsph_camera_bounds::solid_angle(double cone_half_angle)
98 {
99   return vnl_math::twopi*(1.0-std::cos(cone_half_angle));
100 }
101 
cone_half_angle(double solid_angle)102 double vsph_camera_bounds::cone_half_angle(double solid_angle)
103 {
104   double temp = solid_angle/vnl_math::twopi;
105   temp = std::acos(1.0-temp);
106   return temp;
107 }
108 
109 // the solid angle for a pixel, applies only to perspective camera
110 // cone is tangent to pixel
111 void vsph_camera_bounds::
pixel_solid_angle(vpgl_perspective_camera<double> const & cam,unsigned u,unsigned v,vgl_ray_3d<double> & cone_axis,double & cone_half_angle,double & solid_angle)112 pixel_solid_angle(vpgl_perspective_camera<double> const& cam,
113                   unsigned u, unsigned v,
114                   vgl_ray_3d<double>& cone_axis,
115                   double& cone_half_angle,
116                   double& solid_angle)
117 {
118   cone_axis = cam.backproject(u+0.5, v+0.5);
119   //get ray through upper left corner
120   vgl_ray_3d<double> ul;
121   ul = cam.backproject(u, v);
122   cone_half_angle = angle(ul, cone_axis);
123   solid_angle = vsph_camera_bounds::solid_angle(cone_half_angle);
124 }
125 
126 // solid angle at principal point
127 void vsph_camera_bounds::
pixel_solid_angle(vpgl_perspective_camera<double> const & cam,double & cone_half_angle,double & solid_angle)128 pixel_solid_angle(vpgl_perspective_camera<double> const& cam,
129                   double& cone_half_angle,
130                   double& solid_angle)
131 {
132   vgl_point_2d<double> pp = cam.get_calibration().principal_point();
133   auto u = static_cast<unsigned>(pp.x()),
134            v = static_cast<unsigned>(pp.y());
135   vgl_ray_3d<double> ray;
136   vsph_camera_bounds::pixel_solid_angle(cam, u, v, ray, cone_half_angle,
137                                         solid_angle);
138 }
139 
140 // the solid angle for an image, applies only to perspective camera
141 // cone axis passes through principal point, i.e. principal ray.
142 // tangent to a square defined by image diagonal
143 void vsph_camera_bounds::
image_solid_angle(vpgl_perspective_camera<double> const & cam,vgl_ray_3d<double> & cone_axis,double & cone_half_angle,double & solid_angle)144 image_solid_angle(vpgl_perspective_camera<double> const& cam,
145                   vgl_ray_3d<double>& cone_axis,
146                   double& cone_half_angle,
147                   double& solid_angle)
148 {
149   vgl_point_2d<double> pp = cam.get_calibration().principal_point();
150   cone_axis = cam.backproject(pp);
151   vgl_ray_3d<double> ul = cam.backproject(0.0, 0.0);
152   cone_half_angle = angle(ul, cone_axis);
153   solid_angle = vsph_camera_bounds::solid_angle(cone_half_angle);
154 }
155 
156 void vsph_camera_bounds::
image_solid_angle(vpgl_perspective_camera<double> const & cam,double & cone_half_angle,double & solid_angle)157 image_solid_angle(vpgl_perspective_camera<double> const& cam,
158                   double& cone_half_angle,
159                   double& solid_angle)
160 {
161   vgl_ray_3d<double> ray;
162   vsph_camera_bounds::image_solid_angle(cam, ray, cone_half_angle,
163                                         solid_angle);
164 }
165 
166   // the solid angle for a scene bounding box, the cone is tangent to the box
167 bool vsph_camera_bounds::
box_solid_angle(vpgl_perspective_camera<double> const & cam,vgl_box_3d<double> const & box,vgl_ray_3d<double> & cone_axis,double & cone_half_angle,double & solid_angle)168 box_solid_angle(vpgl_perspective_camera<double> const& cam,
169                 vgl_box_3d<double> const& box,
170                 vgl_ray_3d<double>& cone_axis,
171                 double& cone_half_angle,
172                 double& solid_angle)
173 {
174   //project the box into the image
175   vgl_box_2d<double> b2d = bpgl_project::project_bounding_box(cam, box);
176   if (b2d.min_x()<0||b2d.min_y()<0)
177     return false;//box falls outside the image
178   vgl_point_2d<double> pp = cam.get_calibration().principal_point();
179   if (b2d.max_x()>=2*pp.x()||b2d.max_y()>=2*pp.y())
180     return false;//box falls outside the image
181   //ray corresponding to box center
182   if (!vpgl_ray::ray(cam, box.centroid(), cone_axis))
183     return false;
184   double umin = b2d.min_x(), vmin = b2d.min_y();//assume corners are centered
185   vgl_ray_3d<double> ul = cam.backproject(umin, vmin);
186   cone_half_angle = angle(ul, cone_axis);
187   solid_angle = vsph_camera_bounds::solid_angle(cone_half_angle);
188   return true;
189 }
190 
191 double vsph_camera_bounds::
rotation_angle_interval(vpgl_perspective_camera<double> const & cam)192 rotation_angle_interval(vpgl_perspective_camera<double> const& cam)
193 {
194   //Get the principal point
195   vgl_point_2d<double> pp = cam.get_calibration().principal_point();
196   double rmin = pp.y();
197   if (pp.x()<rmin) rmin = pp.x();
198   if (rmin <= 0) return 0;
199   //half length is 0.5 (1/2 pixel)
200   double half_angle = std::atan(0.5/rmin);
201   return 2.0*half_angle;
202 }
203 
204 void vsph_camera_bounds::
relative_transf(vpgl_perspective_camera<double> const & c0,vpgl_perspective_camera<double> const & c1,vgl_rotation_3d<double> & rel_rot,vgl_vector_3d<double> & rel_trans)205 relative_transf(vpgl_perspective_camera<double> const& c0,
206                 vpgl_perspective_camera<double> const& c1,
207                 vgl_rotation_3d<double>& rel_rot,
208                 vgl_vector_3d<double>& rel_trans)
209 {
210   vgl_vector_3d<double> t0 = c0.get_translation();
211   vgl_vector_3d<double> t1 = c1.get_translation();
212   const vgl_rotation_3d<double>& R0 = c0.get_rotation();
213   const vgl_rotation_3d<double>& R1 = c1.get_rotation();
214   rel_rot = R1*(R0.transpose());
215   vgl_vector_3d<double> td = rel_rot*t0;
216   rel_trans = -td + t1;
217 }
218 
pixel_cylinder(vpgl_generic_camera<double> const & cam,unsigned u,unsigned v,vgl_ray_3d<double> & cylinder_axis,double & cylinder_radius)219 bool vsph_camera_bounds::pixel_cylinder(vpgl_generic_camera<double> const& cam,
220                                         unsigned u, unsigned v,
221                                         vgl_ray_3d<double>& cylinder_axis,
222                                         double& cylinder_radius)
223 {
224   unsigned nc = cam.cols(), nr = cam.rows();
225   cylinder_axis = cam.ray(u, v);
226   vgl_point_3d<double> axis_origin = cylinder_axis.origin();
227   vgl_ray_3d<double> corner_ray;
228   if (u>0&&v>0&&u<nc&&v<nr)
229     corner_ray = cam.ray(u-0.5, v-0.5);
230   else if (u>0&&v==0&&u<nc)
231     corner_ray = cam.ray(u-0.5, v+0.5);
232   else if (u==0&&v>0&&v<nr)
233     corner_ray = cam.ray(u+0.5, v-0.5);
234   else if (u==0&&v==0)
235     corner_ray = cam.ray(u+0.5, v+0.5);
236   else {
237     cylinder_radius = 0;
238     return false;
239   }
240   vgl_point_3d<double> cp = vgl_closest_point(axis_origin, corner_ray);
241   cylinder_radius = (axis_origin-cp).length();
242   return true;
243 }
244 
planar_bounding_box(vpgl_perspective_camera<double> const & c,vgl_box_2d<double> & bbox,double z_plane)245 bool vsph_camera_bounds::planar_bounding_box(vpgl_perspective_camera<double> const& c,
246                                              vgl_box_2d<double>& bbox,
247                                              double z_plane)
248 {
249   //principal point for image size
250   vgl_point_2d<double> pp = c.get_calibration().principal_point();
251 
252   //backproject four corners of the iamge
253   vgl_ray_3d<double> ul = c.backproject(0.0, 0.0);
254   vgl_ray_3d<double> ur = c.backproject(2*pp.x(), 0.0);
255   vgl_ray_3d<double> bl = c.backproject(0.0, 2*pp.y());
256   vgl_ray_3d<double> br = c.backproject(2*pp.x(), 2*pp.y());
257 
258   //define z plane
259   vgl_plane_3d<double> zp( vgl_point_3d<double>( 1.0,  1.0, z_plane),
260                            vgl_point_3d<double>( 1.0, -1.0, z_plane),
261                            vgl_point_3d<double>(-1.0,  1.0, z_plane) );
262 
263   //intersect each ray with z plane
264   vgl_point_3d<double> ulp, urp, blp, brp;
265   bool good =    vgl_intersection(ul, zp, ulp)
266               && vgl_intersection(ur, zp, urp)
267               && vgl_intersection(bl, zp, blp)
268               && vgl_intersection(br, zp, brp);
269 
270   //add points to box
271   if (good) {
272     bbox.add( vgl_point_2d<double>(ulp.x(),ulp.y()) );
273     bbox.add( vgl_point_2d<double>(urp.x(),urp.y()) );
274     bbox.add( vgl_point_2d<double>(blp.x(),blp.y()) );
275     bbox.add( vgl_point_2d<double>(brp.x(),brp.y()) );
276   }
277   return good;
278 }
279 
planar_bounding_box(std::vector<vpgl_perspective_camera<double>> const & cams,vgl_box_2d<double> & bbox,double z_plane)280 bool vsph_camera_bounds::planar_bounding_box(std::vector<vpgl_perspective_camera<double> > const& cams,
281                                              vgl_box_2d<double>& bbox,
282                                              double z_plane)
283 {
284   bool good = true;
285   for (const auto & cam : cams) {
286     vgl_box_2d<double> b;
287     if ( planar_bounding_box( cam, b, z_plane ) )
288       bbox.add(b);
289     else
290       good = false;
291   }
292   return good;
293 }
294