vpgl_generic_camera.hxx
Go to the documentation of this file.
1 // This is core/vpgl/vpgl_generic_camera.hxx
2 #ifndef vpgl_generic_camera_hxx_
3 #define vpgl_generic_camera_hxx_
4 //:
5 // \file
6 
7 #include <cmath>
8 #include <iostream>
9 #include "vpgl_generic_camera.h"
10 #include <vnl/vnl_numeric_traits.h>
11 #include <cassert>
12 #ifdef _MSC_VER
13 # include <vcl_msvc_warnings.h>
14 #endif
15 #include <vgl/vgl_distance.h>
16 #include <vgl/vgl_closest_point.h>
17 #include <vgl/vgl_intersection.h>
18 #include <vgl/vgl_vector_2d.h>
19 #include <vgl/vgl_point_2d.h>
20 #include <vgl/vgl_plane_3d.h>
21 #include <vnl/vnl_math.h>
22 
23 //-------------------------------------------
24 template <class T>
26 {
27  // rays_ is empty and min ray and max ray origins are (0 0 0)
28 }
29 
30 //------------------------------------------
31 template <class T>
34 {
35  unsigned long nc = rays.cols();
36  unsigned long nr = rays.rows();
37  assert(nc>0&&nr>0);
38  //compute bounds on ray origins
39  double min_dist = vnl_numeric_traits<double>::maxval;
40  double max_dist = 0.0;
41  vgl_point_3d<T> datum(T(0), T(0), T(0));
42  for (int v = 0; v<nr; ++v)
43  for (int u = 0; u<nc; ++u) {
44  vgl_point_3d<T> org = rays[v][u].origin();
45  double d = vgl_distance(datum, org);
46  if (d>max_dist) {
47  max_dist = d;
48  max_ray_origin_ = org;
49  max_ray_direction_ = rays[v][u].direction();
50  }
51  if (d<min_dist) {
52  min_dist = d;
53  min_ray_origin_ = org;
54  min_ray_direction_ = rays[v][u].direction();
55  }
56  }
57  // form the pyramid for efficient projection
58  // find the number of levels
59  double dim = nc;
60  if (nr<nc)
61  dim = nr;
62  double lv = std::log(dim)/std::log(2.0);
63  n_levels_ = static_cast<int>(lv);// round down
64  if (dim*std::pow(0.5, static_cast<double>(n_levels_-1)) < 32.0) n_levels_--;
65  if (n_levels_<=0) n_levels_ = 1;
66  rays_.resize(n_levels_);
67  nr_.resize(n_levels_);
68  nc_.resize(n_levels_);
69  rays_[0]=rays;
70  nr_[0]=nr; nc_[0]=nc;
71  int nrlv = (nr)/2, nclv = (nc)/2;
72  for (int lev = 1; lev<n_levels_; ++lev) {
73  rays_[lev].resize(nrlv, nclv);
74  nr_[lev]=nrlv; nc_[lev]=nclv;
75  for (int r = 0; r<nrlv; ++r)
76  for (int c = 0; c<nclv; ++c)// nearest neighbor downsampling
77  rays_[lev][r][c] = rays_[lev-1][2*r][2*c];
78  //next level
79  nrlv =(nrlv) / 2; nclv = (nclv) / 2;
80  }
81 }
82 //------------------------------------------
83 template <class T>
85  vpgl_generic_camera( std::vector<vbl_array_2d<vgl_ray_3d<T> > > const& rays,
86  std::vector<int> nrs, std::vector<int> ncs )
87 {
88  assert(rays.size()>0 && nrs.size()>0 && ncs.size()>0);
89  //compute bounds on ray origins
90  double min_dist = vnl_numeric_traits<double>::maxval;
91  double max_dist = 0.0;
92  vgl_point_3d<T> datum(T(0), T(0), T(0));
93  for (int v = 0; v<nrs[0]; ++v) {
94  for (int u = 0; u<ncs[0]; ++u) {
95  vgl_point_3d<T> org = rays[0][v][u].origin();
96  double d = vgl_distance(datum, org);
97  if (d>max_dist) {
98  max_dist = d;
99  max_ray_origin_ = org;
100  max_ray_direction_ = rays[0][v][u].direction();
101  }
102  if (d<min_dist) {
103  min_dist = d;
104  min_ray_origin_ = org;
105  min_ray_direction_ = rays[0][v][u].direction();
106  }
107  }
108  }
109  rays_ = rays;
110  nr_ = nrs;
111  nc_ = ncs;
112  n_levels_ = rays.size();
113 }
114 // the ray closest to the given 3-d point is selected
115 // note that the ray is taken to be an infinite 3-d line
116 // and so the bound of the ray origin is not taken into account
117 //
118 template <class T>
120  vgl_point_3d<T> const& p,
121  int start_r, int end_r,
122  int start_c, int end_c,
123  int& nearest_r, int& nearest_c) const
124 {
125  assert(level>=0 && level<n_levels_);
126  assert(start_r>=0 && end_r < nr_[level]);
127  assert(start_c>=0 && end_c < nc_[level]);
128  nearest_r = 0, nearest_c = 0;
129  double min_d = vnl_numeric_traits<double>::maxval;
130  for (int r = start_r; r<=end_r; ++r)
131  for (int c = start_c; c<=end_c; ++c) {
132  double d = vgl_distance(rays_[level][r][c], p);
133  if (d<min_d) {
134  min_d=d;
135  nearest_r = r;
136  nearest_c = c;
137  }
138  }
139 }
140 
141 template <class T>
144  int& nearest_r, int& nearest_c) const
145 {
146  int lev = n_levels_-1;
147  int start_r = 0, end_r = nr_[lev];
148  int start_c = 0, end_c = nc_[lev];
149  for (; lev >= 0; --lev) {
150  if (start_r<0) start_r = 0;
151  if (start_c<0) start_c = 0;
152  if (end_r>=nr_[lev]) end_r = nr_[lev]-1;
153  if (end_c>=nc_[lev]) end_c = nc_[lev]-1;
154  nearest_ray(lev, p, start_r, end_r, start_c, end_c,
155  nearest_r, nearest_c);
156  // compute new bounds
157  start_r = 2*nearest_r-1; start_c = 2*nearest_c-1;
158  end_r = start_r + 2; end_c = start_c +2;
159  // check if the image sizes are odd, so search range is extended
160  if ( (lev > 0) && (nr_[lev-1]%2 != 0) ) end_r++;
161  if ( (lev > 0) && (nc_[lev-1]%2 != 0) ) end_c++;
162  }
163 }
164 
165 template <class T>
167  refine_ray_at_point(int nearest_c, int nearest_r,
168  vgl_point_3d<T> const& p,
169  vgl_ray_3d<T>& ray) const
170 {
171  T u = static_cast<T>(nearest_c), v = static_cast<T>(nearest_r);
172  ray = this->ray(u, v);
173  vgl_point_3d<T> cp = vgl_closest_point(p, ray);
174  vgl_vector_3d<T> t = p-cp;
175  vgl_point_3d<T> org = ray.origin();
176  org += t; //shift origin by vector from closest point, cp to p
177  ray.set(org, ray.direction());
178 }
179 
180 //: a ray passing through a given 3-d point
181 template <class T>
183  ray(vgl_point_3d<T> const& p) const
184 {
185  int nearest_c = -1, nearest_r = -1;
186  this->nearest_ray_to_point(p, nearest_r, nearest_c);
187  vgl_ray_3d<T> r;
188  this->refine_ray_at_point(nearest_c, nearest_r, p, r);
189  return r;
190 }
191 
192 // refine the projection to sub-pixel accuracy
193 // use an affine invariant map between the plane passing through p and the
194 // image plane. The plane normal is given by the direction of the
195 // nearest ray to p, but negated, so as to point upward.
196 template <class T>
198  refine_projection(int nearest_c, int nearest_r, vgl_point_3d<T> const& p,
199  T& u, T& v) const
200 {
201  // the ray closest to the projected 3-d point
202  vgl_ray_3d<T> nr = rays_[0][nearest_r][nearest_c];
203  // construct plane with normal given by -nr.direction() through p
204  vgl_plane_3d<T> pl(-nr.direction(), p);
205  bool valid_inter = true;
206  // find intersection of nearest ray with the plane
207  std::vector<vgl_point_3d<T> > inter_pts;
208  std::vector<vgl_point_2d<T> > img_pts;
209  vgl_point_3d<T> ipt;
210  valid_inter = vgl_intersection(nr, pl, ipt);
211  inter_pts.push_back(ipt);
212  //find intersections of neighboring rays with the plane
213  //need at least two neighbors
214  img_pts.push_back(vgl_point_2d<T>(0.0, 0.0));
215  bool horiz = false;
216  bool vert = false;
217  if (nearest_r>0 && !horiz) {
218  vgl_ray_3d<T> r = rays_[0][nearest_r-1][nearest_c];
219  valid_inter = vgl_intersection(r, pl, ipt);
220  if(std::fabs((ipt-inter_pts[0]).length()) > vnl_math::eps)
221  {
222  inter_pts.push_back(ipt);
223  img_pts.push_back(vgl_point_2d<T>(0.0, -1.0));
224  horiz = true;
225  }
226  }
227  if (nearest_c>0 && !vert) {
228  vgl_ray_3d<T> r = rays_[0][nearest_r][nearest_c-1];
229  valid_inter = vgl_intersection(r, pl, ipt);
230  if(std::fabs((ipt-inter_pts[0]).length()) > vnl_math::eps)
231  {
232  inter_pts.push_back(ipt);
233  img_pts.push_back(vgl_point_2d<T>(-1.0, 0.0));
234  vert = true;
235  }
236  }
237  int nrght = static_cast<int>(cols())-1;
238  if (nearest_c<nrght && !vert ) {
239  vgl_ray_3d<T> r = rays_[0][nearest_r][nearest_c+1];
240  valid_inter = vgl_intersection(r, pl, ipt);
241  if(std::fabs((ipt-inter_pts[0]).length()) > vnl_math::eps)
242  {
243  inter_pts.push_back(ipt);
244  img_pts.push_back(vgl_point_2d<T>(1.0, 0.0));
245  vert = true;
246  }
247  }
248  int nbl = static_cast<int>(rows())-1;
249  if (nearest_r<nbl && !horiz ) {
250  vgl_ray_3d<T> r = rays_[0][nearest_r+1][nearest_c];
251  valid_inter = vgl_intersection(r, pl, ipt);
252  if(std::fabs((ipt-inter_pts[0]).length()) > vnl_math::eps)
253  {
254  inter_pts.push_back(ipt);
255  img_pts.push_back(vgl_point_2d<T>(0.0, 1.0));
256  horiz = true;
257  }
258  }
259  //less than two neighbors, shouldn't happen!
260  if (!valid_inter||inter_pts.size()<3) {
261  u = static_cast<T>(nearest_c);
262  v = static_cast<T>(nearest_r);
263  return;
264  }
265  // compute 2-d plane coordinates for points
266  vgl_point_3d<T> p0 = inter_pts[0];// origin
267  // coordinate axes
268  vgl_vector_3d<T> v0 = inter_pts[1]- p0;
269  vgl_vector_3d<T> v1 = inter_pts[2]- p0;
270  vgl_vector_3d<T> vp = p-p0;
271  // compute coordinates of p in the plane
272  T v0v0 = dot_product(v0,v0);
273  T v0v1 = dot_product(v0,v1);
274  T v1v1 = dot_product(v1,v1);
275  T one_over_det = static_cast<T>(1)/(v0v0*v1v1 - v0v1*v0v1);
276  // b0 and b1 are rows of coordinate transformation matrix
277  vgl_vector_3d<T> b0 = one_over_det * (v1v1*v0 - v0v1*v1);
278  vgl_vector_3d<T> b1 = one_over_det * (v0v0*v1 - v0v1*v0);
279  // x0,x1 are coordinates of p in the plane
280  T x0 = dot_product(b0, vp), x1 = dot_product(b1, vp);
281  // in image space
282  vgl_point_2d<T> ip0 = img_pts[0];
283  vgl_vector_2d<T> iv0 = img_pts[1]-ip0;
284  vgl_vector_2d<T> iv1 = img_pts[2]-ip0;
285  vgl_vector_2d<T> del = x0*iv0 + x1*iv1;
286  u = nearest_c + del.x();
287  v = nearest_r + del.y();
288 }
289 
290 // projects by exhaustive search in a pyramid.
291 template <class T>
292 void vpgl_generic_camera<T>::project(const T x, const T y, const T z,
293  T& u, T& v) const
294 {
295  vgl_point_3d<T> p(x, y, z);
296  int nearest_c=-1, nearest_r=-1;
297  this->nearest_ray_to_point(p, nearest_r, nearest_c);
298  // refine to sub-pixel accuracy using a Taylor series approximation
299  this->refine_projection(nearest_c, nearest_r, p, u, v);
300 }
301 
302 
303 // a ray specified by an image location (can be sub-pixel)
304 template <class T>
305 vgl_ray_3d<T> vpgl_generic_camera<T>::ray(const T u, const T v) const
306 {
307  double du = static_cast<double>(u);
308  double dv = static_cast<double>(v);
309  int nright = static_cast<int>(cols())-1;
310  int nbelow = static_cast<int>(rows())-1;
311  if( ! (du>=-0.5 && dv>=-0.5 && du<=nright+0.5 && dv<=nbelow+0.5) ) {
312  assert(false);
313  return vgl_ray_3d<T>();
314  }
315  int iu, iv;
316  iu = du<nright ? static_cast<int>(du) : nright-1;
317  iv = dv<nbelow ? static_cast<int>(dv) : nbelow-1;
318  //check for integer pixel coordinates
319  if ((du-iu) == 0.0 && (dv-iv) == 0.0)
320  return rays_[0][iv][iu];
321  // u or v is sub-pixel so find interpolated ray
322  //find neighboring rays and pixel distances to the sub-pixel location
323  std::vector<double> dist;
324  std::vector<vgl_ray_3d<T> > nrays;
325  // closest ray (the lower left corner pixel in this case)
326  vgl_ray_3d<T> ru = rays_[0][iv][iu];
327  nrays.push_back(ru);
328  double d = (1 - (dv-iv))*(1 - (du-iu));
329  dist.push_back(d);
330  if(iu<nright) {
331  // ray to the right
332  vgl_ray_3d<T> rr = rays_[0][iv][iu+1];
333  nrays.push_back(rr);
334  double d = (1 - (dv-iv))*(1 - (iu+1-du));
335  dist.push_back(d);
336  }
337  if(iv<nbelow) {
338  // ray below
339  vgl_ray_3d<T> rd = rays_[0][iv+1][iu];
340  nrays.push_back(rd);
341  double d = (1 - (iv+1-dv))*(1 - (du-iu));
342  dist.push_back(d);
343  }
344  if(iu<nright && iv<nbelow) {
345  // ray to the lower-right diagonal
346  vgl_ray_3d<T> rlr = rays_[0][iv+1][iu+1];
347  nrays.push_back(rlr);
348  double d = (1 - (iv+1-dv))*(1 - (iu+1-du));
349  dist.push_back(d);
350  }
351  assert(dist.size() == 4);
352  // compute the interpolated ray
353  double ox = 0.0, oy = 0.0, oz = 0.0, dx = 0.0, dy = 0.0, dz = 0.0;
354  for (unsigned i = 0; i<nrays.size(); ++i) {
355  vgl_ray_3d<T> r = nrays[i];
356  vgl_point_3d<T> org = r.origin();
357  vgl_vector_3d<T> dir = r.direction();
358  double w = dist[i];
359  ox += w*org.x(); oy += w*org.y(); oz += w*org.z();
360  dx += w*dir.x(); dy += w*dir.y(); dz += w*dir.z();
361  }
362  vgl_point_3d<T> avg_org(static_cast<T>(ox),
363  static_cast<T>(oy),
364  static_cast<T>(oz));
365  vgl_vector_3d<T> avg_dir(static_cast<T>(dx),
366  static_cast<T>(dy),
367  static_cast<T>(dz));
368  return vgl_ray_3d<T>(avg_org, avg_dir);
369 }
370 
371 
372 template <class T>
374 {
375  for (int r = 0; r<nr_[level]; ++r) {
376  for (int c = 0; c<nc_[level]; ++c) {
377  vgl_point_3d<T> o = rays_[level][r][c].origin();
378  std::cout << '(' << o.x() << ' ' << o.y() << ") ";
379  }
380  std::cout << '\n';
381  }
382 }
383 
384 template <class T>
385 void vpgl_generic_camera<T>::print_to_vrml(int level, std::ostream& os)
386 {
387  for (int r = 0; r<nr_[level]; ++r) {
388  for (int c = 0; c<nc_[level]; ++c) {
389  vgl_point_3d<T> o = rays_[level][r][c].origin();
390  os<< "Transform {\n"
391  << "translation " << o.x() << ' ' << o.y() << ' '
392  << ' ' << o.z() << '\n'
393  << "children [\n"
394  << "Shape {\n"
395  << " appearance DEF A1 Appearance {\n"
396  << " material Material\n"
397  << " {\n"
398  << " diffuseColor " << 1 << ' ' << 0 << ' ' << 0 << '\n'
399  << " emissiveColor " << .3 << ' ' << 0 << ' ' << 0 << '\n'
400  << " }\n"
401  << " }\n"
402  << " geometry Sphere\n"
403  << "{\n"
404  << " radius " << 20 << '\n'
405  << " }\n"
406  << " }\n"
407  << " ]\n"
408  << "}\n";
409  }
410  }
411 }
412 
413 // Code for easy instantiation.
414 #undef vpgl_GENERIC_CAMERA_INSTANTIATE
415 #define vpgl_GENERIC_CAMERA_INSTANTIATE(T) \
416  template class vpgl_generic_camera<T >
417 
418 
419 #endif // vpgl_generic_camera_hxx_
void refine_ray_at_point(int nearest_c, int nearest_r, vgl_point_3d< T > const &p, vgl_ray_3d< T > &ray) const
refine ray.
double vgl_distance(vgl_point_2d< T >const &p1, vgl_point_2d< T >const &p2)
void refine_projection(int nearest_c, int nearest_r, vgl_point_3d< T > const &p, T &u, T &v) const
refine the projection to sub pixel.
void nearest_ray_to_point(vgl_point_3d< T > const &p, int &nearest_r, int &nearest_c) const
vgl_point_2d< T > vgl_closest_point(vgl_line_2d< T > const &l, vgl_point_2d< T > const &p)
T y() const
vgl_point_3d< T > vgl_intersection(const std::vector< vgl_plane_3d< T > > &p)
#define v
#define vp(os, v, s)
void print_to_vrml(int level, std::ostream &os)
visualization.
void print_orig(int level)
debug function.
T y() const
T x() const
T z() const
vgl_point_3d< Type > origin() const
vgl_ray_3d< T > ray(const T u, const T v) const
the ray corresponding to a given pixel.
void set(vgl_point_3d< Type > const &p0, vgl_vector_3d< Type > const &direction)
T dot_product(v const &a, v const &b)
T x() const
The generic camera.
double length(v const &a)
vgl_vector_3d< Type > direction() const
void nearest_ray(int level, vgl_point_3d< T > const &p, int start_r, int end_r, int start_c, int end_c, int &nearest_r, int &nearest_c) const
nearest ray at level.
void project(const T x, const T y, const T z, T &u, T &v) const override
The generic camera interface. u represents image column, v image row. Finds projection using a pyrami...