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