stereoRectify.cpp

Go to the documentation of this file.
00001 
00016 #include <dlrComputerVision/stereoRectify.h>
00017 #include <dlrLinearAlgebra/linearAlgebra.h>
00018 #include <dlrNumeric/utilities.h>
00019 
00020 namespace linalg = dlr::linearAlgebra;
00021 namespace num = dlr::numeric;
00022 
00023 // Anonymous namespace for local functions.
00024 namespace {
00025 
00026   namespace cv = dlr::computerVision;
00027   
00028   num::Array2D<double>
00029   extractUpperLeftBlock(cv::CameraIntrinsicsPinhole const& intrinsics,
00030                         num::Transform3D const& cameraTworld)
00031   {
00032     num::Array2D<double> result(3, 3);
00033 #if 0
00034     result(0, 0) = (intrinsics.getKx() * cameraTworld(0, 0)
00035                     + intrinsics.getCenterU() * cameraTworld(2, 0));
00036     result(0, 1) = (intrinsics.getKx() * cameraTworld(0, 1)
00037                     + intrinsics.getCenterU() * cameraTworld(2, 1));
00038     result(0, 2) = (intrinsics.getKx() * cameraTworld(0, 2)
00039                     + intrinsics.getCenterU() * cameraTworld(2, 2));
00040     result(1, 0) = (intrinsics.getKy() * cameraTworld(1, 0)
00041                     + intrinsics.getCenterV() * cameraTworld(2, 0));
00042     result(1, 1) = (intrinsics.getKy() * cameraTworld(1, 1)
00043                     + intrinsics.getCenterV() * cameraTworld(2, 1));
00044     result(1, 2) = (intrinsics.getKy() * cameraTworld(1, 2)
00045                     + intrinsics.getCenterV() * cameraTworld(2, 2));
00046     result(2, 0) = cameraTworld(2, 0);
00047     result(2, 1) = cameraTworld(2, 1);
00048     result(2, 2) = cameraTworld(2, 2);
00049 #else
00050     result(0, 0) = (intrinsics.getKx() * cameraTworld(0, 0)
00051                     + intrinsics.getCenterU() * cameraTworld(2, 0));
00052     result(0, 1) = (intrinsics.getKx() * cameraTworld(0, 1)
00053                     + intrinsics.getCenterU() * cameraTworld(2, 1));
00054     result(0, 2) = (intrinsics.getKx() * cameraTworld(0, 2)
00055                     + intrinsics.getCenterU() * cameraTworld(2, 2));
00056 
00057     double ky = intrinsics.getKy();
00058     double c10 =  cameraTworld(1, 0);
00059     double dummy = ky * c10;
00060     dummy += intrinsics.getCenterV() * cameraTworld(2, 0);
00061     result(1, 0) = dummy;
00062     result(1, 1) = (intrinsics.getKy() * cameraTworld(1, 1)
00063                     + intrinsics.getCenterV() * cameraTworld(2, 1));
00064     result(1, 2) = (intrinsics.getKy() * cameraTworld(1, 2)
00065                     + intrinsics.getCenterV() * cameraTworld(2, 2));
00066     result(2, 0) = cameraTworld(2, 0);
00067     result(2, 1) = cameraTworld(2, 1);
00068     result(2, 2) = cameraTworld(2, 2);
00069 #endif
00070     return result;
00071   }
00072 
00073   
00074 }; // namespace
00075 
00076 
00077 namespace dlr {
00078 
00079   namespace computerVision {
00080     
00081     void
00082     stereoRectify(CameraIntrinsicsPinhole const& intrinsics0,
00083                   CameraIntrinsicsPinhole const& intrinsics1,
00084                   numeric::Transform3D const& camera0Tworld,
00085                   numeric::Transform3D const& camera1Tworld,
00086                   CameraIntrinsicsPinhole& rectifiedIntrinsics0,
00087                   CameraIntrinsicsPinhole& rectifiedIntrinsics1,
00088                   numeric::Transform3D& rcamera0Tworld,
00089                   numeric::Transform3D& rcamera1Tworld,
00090                   numeric::Transform2D& image0Trimage0,
00091                   numeric::Transform2D& image1Trimage1)
00092     {
00093       // Hack(xxx): find a principled way to set this threshold.
00094       double localEpsilon = 1.0E-12;
00095       
00096       // Recover optical centers (focal points) of the two cameras in
00097       // world coords.  These won't change with rectification.
00098       num::Transform3D worldTcamera0 = camera0Tworld.invert();
00099       num::Transform3D worldTcamera1 = camera1Tworld.invert();
00100       num::Vector3D focalPoint0(
00101         worldTcamera0(0, 3), worldTcamera0(1, 3), worldTcamera0(2, 3));
00102       num::Vector3D focalPoint1(
00103         worldTcamera1(0, 3), worldTcamera1(1, 3), worldTcamera1(2, 3));
00104 
00105       // Recover new X axis (expressed in world coordinates) for both
00106       // cameras.  This is parallel to the stereo baseline.
00107       num::Vector3D xAxis = focalPoint1 - focalPoint0;
00108       double xMagnitude = num::magnitude(xAxis);
00109       if(xMagnitude < localEpsilon) {
00110         DLR_THROW(ValueException, "rectify()",
00111                   "Optical centers of camera1 and camera0 are too close "
00112                   "together.");
00113       }
00114       xAxis /= xMagnitude;
00115 
00116       // New Y axis (expressed in world coordinates), again for both
00117       // cameras, is orthogonal to X, and orthogonal to Z axis of
00118       // unrectified camera0.  Note that this fails if camera1 lies on
00119       // the optical axis of camera0.
00120       num::Vector3D oldZ(
00121         camera0Tworld(2, 0), camera0Tworld(2, 1), camera0Tworld(2, 2));
00122       num::Vector3D yAxis = num::cross(oldZ, xAxis);
00123       double yMagnitude = num::magnitude(yAxis);
00124       if(yMagnitude < localEpsilon) {
00125         DLR_THROW(ValueException, "rectify()",
00126                   "Camera1 lies too close to the optical axis of camera0.");
00127       }
00128       yAxis /= yMagnitude;
00129 
00130       // New Z axis (expressed in world coordinates) follows directly
00131       // from X and Y.
00132       num::Vector3D zAxis = num::cross(xAxis, yAxis);
00133       double zMagnitude = num::magnitude(zAxis);
00134       if(zMagnitude < localEpsilon) {
00135         DLR_THROW(LogicException, "rectify()",
00136                   "Error in computation of z axis.");
00137       }
00138       zAxis /= zMagnitude;
00139       
00140       // The rectified cameras share the same orientation.  The
00141       // locations of the optical centers (i.e., the translation
00142       // component of the cameraTworld transforms) remain unchanged.
00143       num::Transform3D worldTrcamera0(
00144         xAxis.x(), yAxis.x(), zAxis.x(), worldTcamera0(0, 3),
00145         xAxis.y(), yAxis.y(), zAxis.y(), worldTcamera0(1, 3),
00146         xAxis.z(), yAxis.z(), zAxis.z(), worldTcamera0(2, 3),
00147         0.0, 0.0, 0.0, 1.0);
00148       num::Transform3D worldTrcamera1(
00149         xAxis.x(), yAxis.x(), zAxis.x(), worldTcamera1(0, 3),
00150         xAxis.y(), yAxis.y(), zAxis.y(), worldTcamera1(1, 3),
00151         xAxis.z(), yAxis.z(), zAxis.z(), worldTcamera1(2, 3),
00152         0.0, 0.0, 0.0, 1.0);
00153 
00154       rcamera0Tworld = worldTrcamera0.invert();
00155       rcamera1Tworld = worldTrcamera1.invert();
00156       
00157       // The rectified cameras will share the same intrinsic
00158       // parameters.  Note that these parameters are not uniquely
00159       // determined.  Choosing them poorly just means that the input
00160       // images will project into the rectified images suboptimally.
00161       //
00162       // Note(xxx): eventually we'll want to do something smart here,
00163       // but for now we just take the easy path of averaging values
00164       // from the two input cameras, and taking image size from
00165       // camera0.
00166       size_t numPixelsX = intrinsics0.getNumPixelsX();
00167       size_t numPixelsY = intrinsics0.getNumPixelsY();
00168       double focalLength =
00169         (intrinsics0.getFocalLength() + intrinsics1.getFocalLength()) / 2.0;
00170       double pixelSizeX = 
00171         (intrinsics0.getPixelSizeX() + intrinsics1.getPixelSizeX()) / 2.0;
00172       double pixelSizeY = 
00173         (intrinsics0.getPixelSizeY() + intrinsics1.getPixelSizeY()) / 2.0;
00174       double centerU = 
00175         (intrinsics0.getCenterU() + intrinsics1.getCenterU()) / 2.0;
00176       double centerV = 
00177         (intrinsics0.getCenterV() + intrinsics1.getCenterV()) / 2.0;
00178       rectifiedIntrinsics0 = CameraIntrinsicsPinhole(
00179         numPixelsX, numPixelsY, focalLength, pixelSizeX, pixelSizeY,
00180         centerU, centerV);
00181       rectifiedIntrinsics1 = rectifiedIntrinsics0;
00182 
00183       // Finally, 2D transformations to take coordinates in the
00184       // recified images and return matching coordinates in the
00185       // unrectified image.
00186       num::Array2D<double> oldQ0 = extractUpperLeftBlock(
00187         intrinsics0, camera0Tworld);
00188       num::Array2D<double> oldQ1 = extractUpperLeftBlock(
00189         intrinsics1, camera1Tworld);
00190       num::Array2D<double> newQ0 = extractUpperLeftBlock(
00191         rectifiedIntrinsics0, rcamera0Tworld);
00192       num::Array2D<double> newQ1 = extractUpperLeftBlock(
00193         rectifiedIntrinsics1, rcamera1Tworld);
00194 
00195       num::Array2D<double> rectArray0 =
00196         num::matrixMultiply(oldQ0, linalg::inverse(newQ0));
00197       num::Array2D<double> rectArray1 =
00198         num::matrixMultiply(oldQ1, linalg::inverse(newQ1));
00199       image0Trimage0.setTransform(
00200         rectArray0(0, 0), rectArray0(0, 1), rectArray0(0, 2),
00201         rectArray0(1, 0), rectArray0(1, 1), rectArray0(1, 2),
00202         rectArray0(2, 0), rectArray0(2, 1), rectArray0(2, 2));
00203       image1Trimage1.setTransform(
00204         rectArray1(0, 0), rectArray1(0, 1), rectArray1(0, 2),
00205         rectArray1(1, 0), rectArray1(1, 1), rectArray1(1, 2),
00206         rectArray1(2, 0), rectArray1(2, 1), rectArray1(2, 2));
00207     }
00208 
00209 
00210   } // namespace computerVision
00211 
00212 } // namespace dlr
00213 

Generated on Wed Nov 25 12:15:05 2009 for dlrComputerVision Utility Library by  doxygen 1.5.8