core/vpgl/vpgl_generic_camera.txx
Go to the documentation of this file.
00001 // This is core/vpgl/vpgl_generic_camera.txx
00002 #ifndef vpgl_generic_camera_txx_
00003 #define vpgl_generic_camera_txx_
00004 //:
00005 // \file
00006 
00007 #include "vpgl_generic_camera.h"
00008 #include <vnl/vnl_numeric_traits.h>
00009 #include <vcl_cassert.h>
00010 #include <vcl_cmath.h>
00011 #include <vgl/vgl_distance.h>
00012 #include <vgl/vgl_closest_point.h>
00013 #include <vgl/vgl_intersection.h>
00014 #include <vgl/vgl_vector_2d.h>
00015 #include <vgl/vgl_point_2d.h>
00016 #include <vgl/vgl_plane_3d.h>
00017 #include <vcl_iostream.h>
00018 //-------------------------------------------
00019 template <class T>
00020 vpgl_generic_camera<T>::vpgl_generic_camera()
00021 {
00022   // rays_ is empty and min ray and max ray origins are (0 0 0)
00023 }
00024 
00025 //------------------------------------------
00026 template <class T>
00027 vpgl_generic_camera<T>::
00028 vpgl_generic_camera( vbl_array_2d<vgl_ray_3d<T> > const& rays)
00029 {
00030   int nc = rays.cols(), nr = rays.rows();
00031   assert(nc>0&&nr>0);
00032   //compute bounds on ray origins
00033   double min_dist = vnl_numeric_traits<double>::maxval;
00034   double max_dist = 0.0;
00035   vgl_point_3d<T> datum(T(0), T(0), T(0));
00036   for (int v = 0; v<nr; ++v)
00037     for (int u = 0; u<nc; ++u) {
00038       vgl_point_3d<T> org = rays[v][u].origin();
00039       double d = vgl_distance(datum, org);
00040       if (d>max_dist) {
00041         max_dist = d;
00042         max_ray_origin_ = org;
00043         max_ray_direction_ = rays[v][u].direction();
00044       }
00045       if (d<min_dist) {
00046         min_dist = d;
00047         min_ray_origin_ = org;
00048         min_ray_direction_ = rays[v][u].direction();
00049       }
00050     }
00051   // form the pyramid for efficient projection
00052   // find the number of levels
00053   double dim = nc;
00054   if (nr<nc)
00055     dim = nr;
00056   double lv = vcl_log(dim)/vcl_log(2.0);
00057   n_levels_ = static_cast<int>(lv);// round down
00058   if (dim/vcl_pow(2.0, static_cast<double>(n_levels_-1)) < 3.0) n_levels_--;
00059   if (n_levels_<=0) n_levels_ = 1;
00060   rays_.resize(n_levels_);
00061   nr_.resize(n_levels_);
00062   nc_.resize(n_levels_);
00063   rays_[0]=rays;
00064   nr_[0]=nr; nc_[0]=nc;
00065   int nrlv = nr/2, nclv = nc/2;
00066   for (int lev = 1; lev<n_levels_; ++lev) {
00067     rays_[lev].resize(nrlv, nclv);
00068     nr_[lev]=nrlv; nc_[lev]=nclv;
00069     for (int r = 0; r<nrlv; ++r)
00070       for (int c = 0; c<nclv; ++c)// nearest neighbor downsampling
00071         rays_[lev][r][c] = rays_[lev-1][2*r][2*c];
00072     //next level
00073     nrlv /= 2; nclv /= 2;
00074   }
00075 }
00076 
00077 // the ray closest to the given 3-d point is selected
00078 // note that the ray is taken to be an infinite 3-d line
00079 // and so the bound of the ray origin is not taken into account
00080 //
00081 template <class T>
00082 void vpgl_generic_camera<T>::nearest_ray(int level,
00083                                          vgl_point_3d<T> const& p,
00084                                          int start_r, int end_r,
00085                                          int start_c, int end_c,
00086                                          int& nearest_r, int& nearest_c) const
00087 {
00088   assert(level>=0 && level<n_levels_);
00089   assert(start_r>=0 && end_r < nr_[level]);
00090   assert(start_c>=0 && end_c < nc_[level]);
00091   nearest_r = 0, nearest_c = 0;
00092   double min_d = vnl_numeric_traits<double>::maxval;
00093   for (int r = start_r; r<=end_r; ++r)
00094     for (int c = start_c; c<=end_c; ++c) {
00095       double d = vgl_distance(rays_[level][r][c], p);
00096       if (d<min_d) {
00097         min_d=d;
00098         nearest_r = r;
00099         nearest_c = c;
00100       }
00101     }
00102 }
00103 
00104 template <class T>
00105 void vpgl_generic_camera<T>::
00106 nearest_ray_to_point(vgl_point_3d<T> const& p,
00107                      int& nearest_r, int& nearest_c) const
00108 {
00109   int lev = n_levels_-1;
00110   int start_r = 0, end_r = nr_[lev];
00111   int start_c = 0, end_c = nc_[lev];
00112   for (; lev >= 0; --lev) {
00113     if (start_r<0) start_r = 0;
00114     if (start_c<0) start_c = 0;
00115     if (end_r>=nr_[lev]) end_r = nr_[lev]-1;
00116     if (end_c>=nc_[lev]) end_c = nc_[lev]-1;
00117     nearest_ray(lev, p, start_r, end_r, start_c, end_c,
00118                 nearest_r, nearest_c);
00119     // compute new bounds
00120     start_r = 2*nearest_r-1; start_c = 2*nearest_c-1;
00121     end_r = start_r + 2; end_c = start_c +2;
00122     // check if the image sizes are odd, so search range is extended
00123     if (lev ==1&&nr_[0]%2!=0) end_r++;
00124     if (lev ==1&&nc_[0]%2!=0) end_c++;
00125   }
00126 }
00127 
00128 template <class T>
00129 void vpgl_generic_camera<T>::
00130 refine_ray_at_point(int nearest_c, int nearest_r,
00131                     vgl_point_3d<T> const& p,
00132                     vgl_ray_3d<T>& ray) const
00133 {
00134   T u = static_cast<T>(nearest_c), v = static_cast<T>(nearest_r);
00135   ray = this->ray(u, v);
00136   vgl_point_3d<T> cp = vgl_closest_point(p, ray);
00137   vgl_vector_3d<T> t = p-cp;
00138   vgl_point_3d<T> org = ray.origin();
00139   org += t; //shift origin by vector from closest point, cp to p
00140   ray.set(org, ray.direction());
00141 }
00142 
00143 //: a ray passing through a given 3-d point
00144 template <class T>
00145 vgl_ray_3d<T> vpgl_generic_camera<T>::
00146 ray(vgl_point_3d<T> const& p) const
00147 {
00148   int nearest_c = -1, nearest_r = -1;
00149   this->nearest_ray_to_point(p, nearest_r, nearest_c);
00150   vgl_ray_3d<T> r;
00151   this->refine_ray_at_point(nearest_c, nearest_r, p, r);
00152   return r;
00153 }
00154 
00155 // refine the projection to sub-pixel accuracy
00156 // use an affine invariant map between the plane passing through p and the
00157 // image plane. The plane normal is given by the direction of the
00158 // nearest ray to p, but negated, so as to point upward.
00159 template <class T>
00160 void vpgl_generic_camera<T>::
00161 refine_projection(int nearest_c, int nearest_r, vgl_point_3d<T> const& p,
00162                   T& u, T& v) const
00163 {
00164   // the ray closest to the projected 3-d point
00165   vgl_ray_3d<T> nr = rays_[0][nearest_r][nearest_c];
00166 
00167   // construct plane with normal given by -nr.direction() through p
00168   vgl_plane_3d<T> pl(-nr.direction(), p);
00169   bool valid_inter = true;
00170   // find intersection of nearest ray with the plane
00171   vcl_vector<vgl_point_3d<T> > inter_pts;
00172   vcl_vector<vgl_point_2d<T> > img_pts;
00173   vgl_point_3d<T> ipt;
00174   valid_inter = vgl_intersection(nr, pl, ipt);
00175   inter_pts.push_back(ipt);
00176   //find intersections of neighboring rays with the plane
00177   //need at least two neighbors
00178   img_pts.push_back(vgl_point_2d<T>(0.0, 0.0));
00179   if (nearest_r>0) {
00180     vgl_ray_3d<T> r = rays_[0][nearest_r-1][nearest_c];
00181     valid_inter = vgl_intersection(r, pl, ipt);
00182     inter_pts.push_back(ipt);
00183     img_pts.push_back(vgl_point_2d<T>(0.0, -1.0));
00184   }
00185   if (nearest_c>0) {
00186     vgl_ray_3d<T> r = rays_[0][nearest_r][nearest_c-1];
00187     valid_inter = vgl_intersection(r, pl, ipt);
00188     inter_pts.push_back(ipt);
00189     img_pts.push_back(vgl_point_2d<T>(-1.0, 0.0));
00190   }
00191   int nrght = static_cast<int>(cols())-1;
00192   if (nearest_c<nrght) {
00193     vgl_ray_3d<T> r = rays_[0][nearest_r][nearest_c+1];
00194     valid_inter = vgl_intersection(r, pl, ipt);
00195     inter_pts.push_back(ipt);
00196     img_pts.push_back(vgl_point_2d<T>(1.0, 0.0));
00197   }
00198   int nbl = static_cast<int>(rows())-1;
00199   if (nearest_r<nbl) {
00200     vgl_ray_3d<T> r = rays_[0][nearest_r+1][nearest_c];
00201     valid_inter = vgl_intersection(r, pl, ipt);
00202     inter_pts.push_back(ipt);
00203     img_pts.push_back(vgl_point_2d<T>(0.0, 1.0));
00204   }
00205   //less than two neighbors, shouldn't happen!
00206   if (!valid_inter||inter_pts.size()<3) {
00207     u = static_cast<T>(nearest_c);
00208     v = static_cast<T>(nearest_r);
00209     return;
00210   }
00211   // compute 2-d plane coordinates for points
00212   vgl_point_3d<T> p0 = inter_pts[0];// origin
00213   // coordinate axes
00214   vgl_vector_3d<T> v0 = inter_pts[1]- p0;
00215   vgl_vector_3d<T> v1 = inter_pts[2]- p0;
00216   vgl_vector_3d<T> vp = p-p0;
00217   // coordinates of p in the plane
00218   T x0 = dot_product(v0, vp), x1 = dot_product(v1, vp);
00219 
00220   // in image space
00221   vgl_point_2d<T> ip0 = img_pts[0];
00222   vgl_vector_2d<T> iv0 = img_pts[1]-ip0;
00223   vgl_vector_2d<T> iv1 = img_pts[2]-ip0;
00224   vgl_vector_2d<T>  del = x0*iv0 + x1*iv1;
00225   u = nearest_c + del.x();
00226   v = nearest_r + del.y();
00227 }
00228 
00229 // projects by exhaustive search in a pyramid.
00230 template <class T>
00231 void vpgl_generic_camera<T>::project(const T x, const T y, const T z,
00232                                      T& u, T& v) const
00233 {
00234   vgl_point_3d<T> p(x, y, z);
00235   int nearest_c=-1, nearest_r=-1;
00236   this->nearest_ray_to_point(p, nearest_r, nearest_c);
00237   // refine to sub-pixel accuracy using a Taylor series approximation
00238   this->refine_projection(nearest_c, nearest_r, p, u, v);
00239 }
00240 
00241 // a ray specified by an image location (can be sub-pixel)
00242 template <class T>
00243 vgl_ray_3d<T> vpgl_generic_camera<T>::ray(const T u, const T v) const
00244 {
00245   double du = static_cast<double>(u);
00246   double dv = static_cast<double>(v);
00247   assert(du>=0.0&&dv>=0.0);
00248   int iu = static_cast<int>(du);
00249   int iv = static_cast<int>(dv);
00250   assert(iu<static_cast<int>(cols()) && iv<static_cast<int>(rows()));
00251   //check for integer pixel coordinates
00252   if ((du-iu) == 0.0 && (dv-iv) == 0.0)
00253     return rays_[0][iv][iu];
00254   // u or v is sub-pixel so find interpolated ray
00255   //find neighboring rays and pixel distances to the sub-pixel location
00256   vcl_vector<double> dist;
00257   vcl_vector<vgl_ray_3d<T> > nrays;
00258   // ray above
00259   if (iv>0) {
00260     vgl_ray_3d<T> ru = rays_[0][iv-1][iu];
00261     nrays.push_back(ru);
00262     double d = vcl_sqrt((iv-1-dv)*(iv-1-dv) + (iu-du)*(iu-du));
00263     if (d==0.0)
00264       return ru;
00265     dist.push_back(1.0/d);
00266   }
00267   // ray to the left
00268   if (iu>0) {
00269     vgl_ray_3d<T> rl = rays_[0][iv][iu-1];
00270     nrays.push_back(rl);
00271     double d = vcl_sqrt((iv-dv)*(iv-dv) + (iu-1-du)*(iu-1-du));
00272     if (d==0.0)
00273       return rl;
00274     dist.push_back(1.0/d);
00275   }
00276   // ray to the right
00277   int nrght = static_cast<int>(cols())-1;
00278   if (iu<nrght) {
00279     vgl_ray_3d<T> rr = rays_[0][iv][iu+1];
00280     nrays.push_back(rr);
00281     double d = vcl_sqrt((iv-dv)*(iv-dv) + (iu+1-du)*(iu+1-du));
00282     if (d==0.0)
00283       return rr;
00284     dist.push_back(1.0/d);
00285   }
00286   // ray below
00287   int nbl = static_cast<int>(rows())-1;
00288   if (iv<nbl) {
00289     vgl_ray_3d<T> rd = rays_[0][iv+1][iu];
00290     nrays.push_back(rd);
00291     double d = vcl_sqrt((iv+1-dv)*(iv+1-dv) + (iu-du)*(iu-du));
00292     if (d==0.0)
00293       return rd;
00294     dist.push_back(1.0/d);
00295   }
00296   // compute the interpolated ray
00297   double ox = 0.0, oy = 0.0, oz = 0.0, dx = 0.0, dy = 0.0, dz = 0.0;
00298   double sumw = 0.0;
00299   for (unsigned i = 0; i<nrays.size(); ++i) {
00300     vgl_ray_3d<T> r = nrays[i];
00301     vgl_point_3d<T> org = r.origin();
00302     vgl_vector_3d<T> dir = r.direction();
00303     double w = dist[i];
00304     sumw += w;
00305     ox += w*org.x(); oy += w*org.y(); oz += w*org.z();
00306     dx += w*dir.x(); dy += w*dir.y(); dz += w*dir.z();
00307   }
00308   ox /= sumw;  oy /= sumw; oz /= sumw;
00309   dx /= sumw;  dy /= sumw; dz /= sumw;
00310   vgl_point_3d<T> avg_org(static_cast<T>(ox),
00311                           static_cast<T>(oy),
00312                           static_cast<T>(oz));
00313   vgl_vector_3d<T> avg_dir(static_cast<T>(dx),
00314                            static_cast<T>(dy),
00315                            static_cast<T>(dz));
00316   return vgl_ray_3d<T>(avg_org, avg_dir);
00317 }
00318 
00319 
00320 template <class T>
00321 void vpgl_generic_camera<T>::print_orig(int level)
00322 {
00323   for (int r = 0; r<nr_[level]; ++r) {
00324     for (int c = 0; c<nc_[level]; ++c) {
00325       vgl_point_3d<T> o = rays_[level][r][c].origin();
00326       vcl_cout << '(' << o.x() << ' ' << o.y() << ") ";
00327     }
00328     vcl_cout << '\n';
00329   }
00330 }
00331 
00332 template <class T>
00333 void vpgl_generic_camera<T>::print_to_vrml(int level, vcl_ostream& os)
00334 {
00335   for (int r = 0; r<nr_[level]; ++r) {
00336     for (int c = 0; c<nc_[level]; ++c) {
00337       vgl_point_3d<T> o = rays_[level][r][c].origin();
00338       os<< "Transform {\n"
00339         << "translation " << o.x() << ' ' << o.y() << ' '
00340         << ' ' << o.z() << '\n'
00341         << "children [\n"
00342         << "Shape {\n"
00343         << " appearance DEF A1 Appearance {\n"
00344         << "   material Material\n"
00345         << "    {\n"
00346         << "      diffuseColor " << 1 << ' ' << 0 << ' ' << 0 << '\n'
00347         << "      emissiveColor " << .3 << ' ' << 0 << ' ' << 0 << '\n'
00348         << "    }\n"
00349         << "  }\n"
00350         << " geometry Sphere\n"
00351         << "{\n"
00352         << "  radius " << 20 << '\n'
00353         << "   }\n"
00354         << "  }\n"
00355         << " ]\n"
00356         << "}\n";
00357     }
00358   }
00359 }
00360 
00361 // Code for easy instantiation.
00362 #undef vpgl_GENERIC_CAMERA_INSTANTIATE
00363 #define vpgl_GENERIC_CAMERA_INSTANTIATE(T) \
00364 template class vpgl_generic_camera<T >
00365 
00366 
00367 #endif // vpgl_generic_camera_txx_