contrib/gel/vmal/vmal_rectifier.cxx
Go to the documentation of this file.
00001 // This is gel/vmal/vmal_rectifier.cxx
00002 #include "vmal_rectifier.h"
00003 //:
00004 // \file
00005 
00006 #include <vmal/vmal_convert_vtol.h>
00007 #include <vcl_cmath.h> // atan2()
00008 #include <vbl/vbl_bounding_box.h>
00009 #include <vnl/algo/vnl_svd.h>
00010 #include <vnl/vnl_inverse.h>
00011 #include <vnl/algo/vnl_determinant.h>
00012 #include <vnl/vnl_vector.h>
00013 #include <mvl/FMatrix.h>
00014 #include <mvl/FMatrixComputeLinear.h>
00015 #include <mvl/FMatrixComputeRANSAC.h>
00016 #include <mvl/FMatrixComputeMLESAC.h>
00017 #include <mvl/FMatrixComputeRobust.h>
00018 #include <vil/vil_bilin_interp.h>
00019 
00020 vmal_rectifier::vmal_rectifier()
00021 {
00022   lines0_p_=NULL;
00023   lines0_q_=NULL;
00024   lines1_p_=NULL;
00025   lines1_q_=NULL;
00026   points0_=NULL;
00027   points1_=NULL;
00028   rectL =  new vil_image_view<vxl_byte>(1,1,1);
00029   rectR =  new vil_image_view<vxl_byte>(1,1,1);
00030   //  rectL =  NULL;
00031   //  rectR =  NULL;
00032 }
00033 
00034 vmal_rectifier::vmal_rectifier(vmal_multi_view_data_vertex_sptr mvd_vertex,
00035                                vmal_multi_view_data_edge_sptr mvd_edge,
00036                                int ima_height, int ima_width) :
00037   is_f_compute_(false)
00038 {
00039   lines0_p_=NULL;
00040   lines0_q_=NULL;
00041   lines1_p_=NULL;
00042   lines1_q_=NULL;
00043   points0_=NULL;
00044   rectL =  new vil_image_view<vxl_byte>(1,1,1);
00045   rectR =  new vil_image_view<vxl_byte>(1,1,1);
00046 
00047   if ((mvd_vertex->get_nb_views()>1) && (mvd_edge->get_nb_views()>1))
00048   {
00049     // Prescale the points
00050     vcl_vector<vtol_edge_2d_sptr> tmp_lines0;
00051     vcl_vector<vtol_edge_2d_sptr> tmp_lines1;
00052 
00053     mvd_edge->get(0,1,tmp_lines0,tmp_lines1);
00054     numpoints_=tmp_lines0.size();
00055 
00056     convert_lines_double_3(tmp_lines0, lines0_p_, lines0_q_);
00057     convert_lines_double_3(tmp_lines1, lines1_p_, lines1_q_);
00058 
00059     vcl_vector<vtol_vertex_2d_sptr> tmp_points0;
00060     vcl_vector<vtol_vertex_2d_sptr> tmp_points1;
00061 
00062     mvd_vertex->get(0,1,tmp_points0,tmp_points1);
00063 
00064     convert_points_double_3(tmp_points0, points0_);
00065     convert_points_double_3(tmp_points1, points1_);
00066     height_=ima_height;
00067     width_=ima_width;
00068   }
00069 }
00070 
00071 // Constructor for dealing with just matched points
00072 
00073 vmal_rectifier::vmal_rectifier(vcl_vector< vnl_vector<double> >* pts0,
00074                                vcl_vector< vnl_vector<double> >* pts1,
00075                                int ima_height, int ima_width) :
00076   lines0_p_(0), lines0_q_(0), lines1_p_(0), lines1_q_(0),
00077   numpoints_(pts0->size()), height_(ima_height), width_(ima_width),
00078   is_f_compute_(false)
00079 {
00080   rectL = new vil_image_view<vxl_byte>(1,1,1);
00081   rectR = new vil_image_view<vxl_byte>(1,1,1);
00082 
00083   // put the points in the proper buffers...
00084   points0_ = new vnl_double_3[numpoints_];
00085   points1_ = new vnl_double_3[numpoints_];
00086   vcl_vector< vnl_vector<double> >::iterator vit0 = pts0->begin();
00087   vcl_vector< vnl_vector<double> >::iterator vit1 = pts1->begin();
00088   for (int i=0; i<numpoints_; ++i,++vit0,++vit1)
00089   {
00090     points0_[i][0] = (*vit0)[0]; // [1]
00091     points0_[i][1] = (*vit0)[1]; // [0]
00092     points0_[i][2] = 1.0;
00093     points1_[i][0] = (*vit1)[0]; // [1]
00094     points1_[i][1] = (*vit1)[1]; // [0]
00095     points1_[i][2] = 1.0;
00096   }
00097 }
00098 
00099 
00100 vmal_rectifier::~vmal_rectifier()
00101 {
00102   delete [] points0_;
00103   delete [] points1_;
00104   delete [] lines0_p_;
00105   delete [] lines0_q_;
00106   delete [] lines1_p_;
00107   delete [] lines1_q_;
00108   delete rectL;
00109   delete rectR;
00110 }
00111 
00112 void vmal_rectifier::rectification_matrix(vnl_double_3x3& H0,
00113                                           vnl_double_3x3& H1)
00114 {
00115   if (!is_f_compute_)
00116   {
00117     is_f_compute_=true;
00118     vcl_vector<HomgPoint2D> v_points0;
00119     vcl_vector<HomgPoint2D> v_points1;
00120     for (int i=0;i<numpoints_;i++)
00121     {
00122       HomgPoint2D tmp_point0(points0_[i][0],points0_[i][1]);
00123       HomgPoint2D tmp_point1(points1_[i][0],points1_[i][1]);
00124       v_points0.push_back(tmp_point0);
00125       v_points1.push_back(tmp_point1);
00126     }
00127 
00128     FMatrixComputeLinear tmp_fcom(true,true);
00129     FMatrix tmp_f;
00130     tmp_fcom.compute(v_points0,v_points1,& tmp_f);
00131     tmp_f.get(&F12_.as_ref().non_const());
00132 
00133     vcl_cout << "Fundamental Matrix:\n" << tmp_f << vcl_endl;
00134 
00135     HomgPoint2D epi1;
00136     HomgPoint2D epi2;
00137 
00138     tmp_f.get_epipoles (&epi1, &epi2);
00139     double x1,y1;
00140     double x2,y2;
00141     epi1.get_nonhomogeneous(x1,y1);
00142     epi2.get_nonhomogeneous(x2,y2);
00143 
00144     vnl_double_3 tmp_epi1;
00145     tmp_epi1[0]=x1;
00146     tmp_epi1[1]=y1;
00147     tmp_epi1[2]=1.0;
00148 
00149     vnl_double_3 tmp_epi2;
00150     tmp_epi2[0]=x2;
00151     tmp_epi2[1]=y2;
00152     tmp_epi2[2]=1.0;
00153 
00154     epipoles_.push_back(tmp_epi1);
00155     epipoles_.push_back(tmp_epi2);
00156   }
00157   bool affine=false;
00158   int out_height;
00159   int out_width;
00160   int sweeti=(int)(width_/2);  // sweetx in RH parlance
00161   int sweetj=(int)(height_/2);  // sweety in RH parlance
00162 
00163   compute_joint_epipolar_transform_new(
00164     points0_,       // Points in one view
00165     points1_,       // Points in the other view
00166     numpoints_,     // Number of matched points
00167     H0_, H1_,       // The matrices to be returned
00168     height_, width_,// Dimensions of the input images
00169     out_height, out_width,
00170     sweeti, sweetj, // Sweet spot in the first image
00171     affine);
00172   H0=H0_;
00173   H1=H1_;
00174   // vnl_double_3 p1=H0*epipoles_[0], p2=H1*epipoles_[1], p3=H0*points0_[0], p4=H1*points1_[0];
00175 }
00176 
00177 //In this case (3 cameras), we assume that the first camera matrix is equal
00178 //to P=[I|0]. So epi1 corresponds to e', i.e. the epipole of the second image and
00179 //epi2 corresponds to e", i.e. the epipole of the third image in relation to the
00180 //first image.
00181 void vmal_rectifier::set_tritensor(TriTensor &tri)
00182 {
00183   //this method is helpful when using the vmal_projective_reconstruction class
00184   //that compute the tritensor.
00185   is_f_compute_=true;
00186   tritensor_=tri;
00187 
00188   HomgPoint2D epi12=tri.get_epipole_12();
00189   FMatrix F12(tri.get_fmatrix_12());
00190 
00191   double x,y;
00192   epi12.get_nonhomogeneous(x,y);
00193 
00194   vnl_double_3 tmp_epi;
00195   tmp_epi[0]=x;
00196   tmp_epi[1]=y;
00197   tmp_epi[2]=1.0;
00198 
00199   epipoles_.push_back(tmp_epi);
00200   F12.get(&F12_.as_ref().non_const());
00201 }
00202 
00203 void vmal_rectifier::compute_joint_epipolar_transform_new (
00204   vnl_double_3* points0,  // Points in one view
00205   vnl_double_3* points1,  // Points in the other view
00206   int numpoints,          // Number of matched points
00207   vnl_double_3x3 &H0, vnl_double_3x3 &H1,  // The matrices to be returned
00208   int /* in_height */, int /* in_width */, // Dimensions of the input images
00209   int & /* out_height */, int & /* out_width */,
00210   double sweeti, double sweetj,// Sweet spot in the first image
00211   bool affine // = FALSE
00212 )
00213 {
00214    // Compute the pair of epipolar transforms to rectify matched points
00215    compute_initial_joint_epipolar_transforms (points0, points1, numpoints,
00216                                               H0, H1, sweeti, sweetj, affine);
00217 
00218    // Apply the affine correction
00219    apply_affine_correction (points0, points1, numpoints, H0, H1);
00220 #if 0
00221    // Next set the range so that the overlap is properly placed
00222    center_overlap_region (H0, im1_roi, H1, im2_roi, output_roi);
00223 #endif
00224    // Rotate through 90 degrees
00225    // NEED TO SET out_height & out_width!!! They're not set yet in this version!!! GWB - 06/25/03
00226    // use of out_height is currently removed in rectify_rotate90, so this is safe for now...
00227    //   rectify_rotate90 (out_height,out_width, H0, H1);
00228    // We do this twice because the images come out upside down!
00229    //   rectify_rotate90 (out_height,out_width, H0, H1);
00230    conditional_rectify_rotate180 (H0, H1);
00231 #if 0
00232    // Also, rotate a further 180 degrees if necessary
00233    conditional_rectify_rotate180 (output_roi, H0, H1);
00234 #endif
00235    vcl_cerr << "vmal_rectifier::compute_joint_epipolar_transform_new() not yet fully implemented\n";
00236 }
00237 
00238 void vmal_rectifier::compute_initial_joint_epipolar_transforms (
00239    vnl_double_3* /* points0 */,       // Points in one view
00240    vnl_double_3* /* points1 */,       // Points in the other view
00241    int /* numpoints */,               // Number of matched points
00242    vnl_double_3x3 &H0, vnl_double_3x3 &H1,  // The matrices to be returned
00243    double sweeti, double sweetj,// Sweet spot in the first image
00244    bool /* affine */ // = FALSE
00245    )
00246 {
00247    // Compute the pair of epipolar transforms to rectify matched points
00248    // This does a minimally distorting correction to H0 and matches it with H1.
00249 
00250   if (is_f_compute_)
00251   {
00252     //the epipoles are already set, we don't have to compute the
00253     //the fundamental matrix.
00254     if ( !compute_initial_joint_epipolar_transforms (F12_,sweeti, sweetj, H0, H1))
00255     {
00256       // Error message and exit
00257       vcl_cerr<<"Computation of epipolar transform failed\n";
00258     }
00259   }
00260   else // TODO
00261   {
00262     vcl_cerr << "vmal_rectifier::compute_initial_joint_epipolar_transforms() not yet fully implemented\n";
00263 #if 0
00264     // First of all compute the Q matrix
00265     rhMatrix Q(3, 3);
00266 
00267     // Compute and refine the estimate of the Q matrix
00268     if (affine)
00269     {
00270     // We do not call affine_optimum_solveQmatrix right now,
00271     // since it is faulty, because the triangulation routine is bad.
00272       affine_solveQmatrix (u1, v1, u2, v2, numpoints, Q);
00273       checkQmatrix (u1, v1, u2, v2, numpoints, Q);
00274     }
00275     else
00276     {
00277       solveQmatrix (u1, v1, u2, v2, numpoints, Q);
00278       refine_Q_matrix (u1, v1, u2, v2, numpoints, Q);
00279       checkQmatrix (u1, v1, u2, v2, numpoints, Q);
00280     }
00281 
00282     // Compute the epipolar transforms.
00283     if ( !compute_initial_joint_epipolar_transforms (Q, sweeti, sweetj, H0, H1))
00284     {
00285       // Error message and exit
00286       error_message ("Computation of epipolar transform failed\n");
00287       bail_out (2);
00288     }
00289 #endif
00290   }
00291 }
00292 
00293 //: Computes a pair of transformation matrices for an image pair that will define the joint epipolar projection.
00294 // This does a minimally distortion-free correction to H0 and then
00295 // gets a matching H0.
00296 
00297 int vmal_rectifier::compute_initial_joint_epipolar_transforms (
00298   const vnl_double_3x3 &Q,
00299   double ci, double cj,   // Position of reference point in first image
00300   vnl_double_3x3 &H0,     // The first transformation matrix computed
00301   vnl_double_3x3 &H1      // second transformation matrix to be computed
00302   )
00303 {
00304   // First get the epipole e'
00305   vnl_double_3 p1 = epipoles_[0];
00306 
00307   // Next, compute the mapping H' for the second image
00308 
00309   // First of all, translate the centre pixel to 0, 0
00310   H0.set_identity().set(0,2,-ci).set(1,2,-cj);
00311 
00312   // Translate the epipole as well
00313   p1 = H0 * p1;
00314 
00315   // Make sure that the epipole is not at the origin
00316   if (p1[0] == 0.0 && p1[1] == 0.0)
00317   {
00318     vcl_cerr<<"Error : Epipole is at image center\n";
00319     return 0;
00320   }
00321 
00322   // Next determine a rotation that will send the epipole to (1, 0, x)
00323   double theta = vcl_atan2 (p1[1], p1[0]);
00324   double c = vcl_cos (theta);
00325   double s = vcl_sin (theta);
00326 
00327   double t[] = { c,   s, 0.0,
00328                 -s,   c, 0.0,
00329                0.0, 0.0, 1.0 };
00330   vnl_double_3x3 T(t);
00331 
00332   // Multiply things out
00333   H0 =  T * H0;
00334   p1 = T * p1;
00335   vnl_double_3 ep1=H0*epipoles_[0];
00336 
00337   // Now send the epipole to infinity
00338   vnl_double_3x3 E; E.set_identity().set(2,0, -p1[2]/p1[0]);
00339 
00340   // Multiply things out.  Put the result in H0
00341   H0 = E * H0;
00342   ep1=H0*epipoles_[0];
00343   vcl_cout << "vmal_rectifier::c_i_j_e_t: epipole[0] at infinity = " << ep1 << vcl_endl;
00344   // Next compute the initial 2x
00345   H1 = matching_transform (Q.transpose(), H0);
00346 
00347   vnl_double_3 ep2=H1*epipoles_[1];
00348   vcl_cout << "vmal_rectifier::c_i_j_e_t: epipole[1] at infinity = " << ep2 << vcl_endl;
00349 
00350   // Return 1 value
00351   return 1;
00352 }
00353 
00354 vnl_double_3x3 vmal_rectifier::matching_transform (
00355   const vnl_double_3x3 &Q,
00356   const vnl_double_3x3 &H1
00357   )
00358    {
00359    // Computes a transform compatible with H1.  It is assumed that
00360    // H1 maps the epipole p2 to infinity (1, 0, 0)
00361    vnl_double_3x3 S, M;
00362    factor_Q_matrix_SR (Q, M, S);
00363 
00364    // Compute the matching transform
00365    vnl_double_3x3 H0 = H1 * M;
00366    return H0;
00367 }
00368 
00369 //: This routine comes up with a factorization of Q into R S
00370 void vmal_rectifier::factor_Q_matrix_SR (
00371         const vnl_double_3x3 &Q,// 3 x 3 matrix
00372         vnl_double_3x3 &R,      // non-singular matrix
00373         vnl_double_3x3 &S)      // Skew-symmetric part
00374 {
00375   double r, s;            // The two singular values
00376 
00377   // First do the singular value decomposition of Q
00378 
00379   vnl_svd<double> SVD(Q.as_ref());
00380   vnl_double_3x3 U(SVD.U());
00381   vnl_double_3x3 V(SVD.V());
00382 
00383   vnl_double_3x3 E(0.0);
00384   vnl_double_3x3 Z(0.0);
00385   E[1][0]=E[2][2]=1;
00386   E[0][1]=-1;
00387   Z[0][1]=1;Z[1][0]=-1;
00388 
00389   r = SVD.W(0);
00390   s = SVD.W(1);
00391 
00392   // Transpose V, since we will need to multiply by it
00393   vnl_double_3x3 Vt = V.transpose();
00394   vnl_double_3x3 Ut = U.transpose();
00395 
00396   // Define RS
00397   vnl_double_3x3 RS;
00398   RS[0][0] =  r; RS[0][1] =  0; RS[0][2] =  0;
00399   RS[1][0] =  0; RS[1][1] =  s; RS[1][2] =  0;
00400   RS[2][0] =  0; RS[2][1] =  0; RS[2][2] =  s;
00401 
00402   // Construct the result
00403   // First of all the R matrix
00404   R=(U * E * RS * Vt);
00405 
00406   // Next the S matrix
00407   S=(U * Z * Ut);
00408 }
00409 
00410 vnl_double_3x3 vmal_rectifier::affine_correction (
00411    vnl_double_3* points0,
00412    vnl_double_3* points1,
00413    int numpoints,
00414    const vnl_double_3x3 &H0,
00415    const vnl_double_3x3 &H1)
00416 {
00417   // Correct according to an affine transformation
00418   // Finds the correction to H1 that would make it closest to H0
00419 
00420   // Matrices for a linear least-squares problem
00421   vnl_matrix<double> E(numpoints, 3);
00422   vnl_vector<double> x(numpoints);
00423   // Fill out the equations
00424   for (int i=0; i<numpoints; i++)
00425   {
00426     // Compute the transformed points
00427     vnl_double_3 u2hat = H1 * points1[i];
00428     double uu2 = u2hat[0]/u2hat[2];
00429     double vv2 = u2hat[1]/u2hat[2];
00430 
00431     vnl_double_3 u1hat = H0 * points0[i];
00432     double uu1 = u1hat[0]/u1hat[2];
00433 
00434     // Fill out the equation
00435     E.set_row(i, vnl_double_3(uu2,vv2,1.0).as_ref());
00436     x[i] = uu1;
00437   }
00438 
00439   // Now solve the equations
00440   vnl_svd<double> SVD(E);
00441   vnl_double_3 a=SVD.solve(x);
00442   // rhVector a = lin_solve(E, x);
00443 
00444   // Now, make up a matrix to do the transformation
00445   // This is the affine correction matrix to be returned
00446   return vnl_double_3x3().set_identity().set_row(0,a);
00447 }
00448 
00449 void vmal_rectifier::apply_affine_correction (
00450    vnl_double_3* points0,       // Points in one view
00451    vnl_double_3* points1,       // Points in the other view
00452    int numpoints,               // Number of matched points
00453    vnl_double_3x3 &H0, vnl_double_3x3 &H1   // The matrices to be returned
00454    )
00455 {
00456    // Correct H0 so that it matches with H1 best on the points given
00457    vnl_double_3x3 A = affine_correction (points0, points1, numpoints, H0, H1);
00458 
00459    // Now correct H0
00460    H1 = A * H1;
00461 
00462    // Make both H0 and H1 have positive determinant)
00463    if (vnl_determinant (H1) < 0.0) H1 = -H1;
00464 }
00465 
00466 void vmal_rectifier::rectify_rotate90 (
00467   int &height, int &width,
00468   vnl_double_3x3 &H0,
00469   vnl_double_3x3 &H1
00470   )
00471 {
00472    // Make a correction so that the horizontal lines come out horizontal
00473    // instead of vertical
00474 
00475    // Define a matrix for rotating the images
00476    vnl_double_3x3 R;
00477 
00478    // This line is commented out because height is not defined yet coming into
00479    // this routine. Hence, it's garbage.
00480    //   R[0][0] = 0.0;  R[0][1] = -1.0; R[0][2] = height;
00481    R[0][0] = 0.0;  R[0][1] = -1.0; R[0][2] = 0.0;
00482    R[1][0] = 1.0;  R[1][1] =  0.0; R[1][2] = 0.0;
00483    R[2][0] = 0.0;  R[2][1] =  0.0; R[2][2] = 1.0;
00484 
00485    // Multiply the matrices
00486    H0 = R * H0;
00487    H1 = R * H1;
00488 
00489    // Now return the output image dimensions
00490    int t = width; width = height; height = t;
00491 }
00492 
00493 // Not quite R. Hartley's implementation, but it works for me for now...
00494 // G.W. Brooksby
00495 void vmal_rectifier::conditional_rectify_rotate180 (
00496   vnl_double_3x3 &H0,
00497   vnl_double_3x3 &H1
00498   )
00499 {
00500    // Rotate by 180 degrees if needed
00501 
00502   if ( H1[0][0]/H1[2][2] > 0)
00503     return;
00504 
00505 
00506    // Define a matrix for rotating the images
00507    vnl_double_3x3 R;
00508 
00509    R[0][0] = -1.0;  R[0][1] = 0.0; R[0][2] = 0.0;
00510    R[1][0] = 0.0;  R[1][1] =  -1.0; R[1][2] = 0.0;
00511    R[2][0] = 0.0;  R[2][1] =  0.0; R[2][2] = 1.0;
00512 
00513    // Multiply the matrices
00514    H0 = R * H0;
00515    H1 = R * H1;
00516 }
00517 
00518 // vmal_rectifier::resample -
00519 // This routine takes two input transforms and images and resamples
00520 // the images according to the transforms.  The resultant images are returned
00521 // through the provided pointers.
00522 
00523 void vmal_rectifier::resample(vnl_double_3x3 H0, vnl_double_3x3 H1,
00524                               vil_image_view<vxl_byte> imgL,
00525                               vil_image_view<vxl_byte> imgR)
00526 {
00527   // Find the bound of the image to be resampled
00528   vnl_double_3 ipointL; // input and output image points
00529   vnl_double_3 opointL; // "input" = non-rectified image
00530   vnl_double_3 ipointR; // "output" = rectified image
00531   vnl_double_3 opointR;
00532   vbl_bounding_box<double,2> boxL;
00533   // Here we're presuming the two images are the same dimensions!
00534   int img_h = imgL.nj();
00535   int img_w = imgL.ni();
00536   for (int i=0; i<img_h; i++)
00537   {
00538     for (int j=0; j<img_w; j++)
00539     {
00540       ipointL[0]=j; ipointL[1]=i; ipointL[2]=1.0;
00541       ipointR[0]=j; ipointR[1]=i; ipointR[2]=1.0;
00542       opointL = H0 * ipointL;
00543       opointR = H1 * ipointR;
00544       opointL /= opointL[2]; // inhomogenize the point... [x,y,1.0]
00545       opointR /= opointR[2];
00546       // save the extremes for image sizing and bounds checking
00547       boxL.update(opointL[0],opointL[1]);
00548 //    boxR.update(opointR[0],opointR[1]);
00549     }
00550   }
00551 
00552   // Now we know the size of the image
00553   // Now do the warping
00554 
00555   // Find the mapping from rectified space back to the original
00556   // using the inverse of the transforms
00557   vnl_double_3x3 H0inv = vnl_inverse(H0);
00558   vnl_double_3x3 H1inv = vnl_inverse(H1);
00559 
00560   int img2_h = int(boxL.max()[1] - boxL.min()[1] + 1); // output image size
00561   int img2_w = int(boxL.max()[0] - boxL.min()[0] + 1); // (make them both the same)
00562   int planes=1;
00563 
00564   //re-size the images for the next iteration...
00565   rectL->set_size(img2_w,img2_h,planes);
00566   rectR->set_size(img2_w,img2_h,planes);
00567 
00568   for (int i=0; i<img2_h; i++)
00569   {
00570     for (int j=0; j<img2_w; j++)
00571     {
00572       // create the homogeneous point in rectified image(s)
00573       opointL[0]=j+boxL.min()[0]; opointL[1]=i+boxL.min()[1]; opointL[2]=1.0;
00574 
00575       // map it into the original image(s)
00576       ipointL = H0inv * opointL; ipointL /= ipointL[2]; // inhomogenize
00577       ipointR = H1inv * opointL; ipointR /= ipointR[2];
00578 
00579       // Bounds checking...
00580       if (ipointL[0] >= 0 && ipointL[0] < img_w-1 &&
00581           ipointL[1] >= 0 && ipointL[1] < img_h-1)
00582       {
00583         // interpolate the rectified image point from the original.
00584         (*rectL)(j,i) = (vxl_byte)vil_bilin_interp(imgL,ipointL[0],ipointL[1],0);
00585       }
00586       else
00587         (*rectL)(j,i) = vxl_byte(0);
00588 
00589       if (ipointR[0] >= 0 && ipointR[0] < img_w-1 &&
00590           ipointR[1] >= 0 && ipointR[1] < img_h-1)
00591       {
00592         // interpolate the rectified image point from the original.
00593         (*rectR)(j,i) = (vxl_byte)vil_bilin_interp(imgR,ipointR[0],ipointR[1],0);
00594       }
00595       else
00596         (*rectR)(j,i) = vxl_byte(0);
00597     }
00598   }
00599 }