calibrationTools.h

Go to the documentation of this file.
00001 
00015 #ifndef DLR_COMPUTERVISION_CALIBRATIONTOOLS_H
00016 #define DLR_COMPUTERVISION_CALIBRATIONTOOLS_H
00017 
00018 #include <dlrComputerVision/cameraIntrinsicsPinhole.h>
00019 
00020 namespace dlr {
00021 
00022   namespace computerVision {
00023 
00024 
00062     template <class Iter3D, class Iter2D>
00063     CameraIntrinsicsPinhole
00064     estimateCameraIntrinsicsPinhole(size_t numPixelsX, size_t numPixelsY,
00065                                     Iter3D points3DBegin, Iter3D points3DEnd,
00066                                     Iter2D points2DBegin);
00067     
00068   } // namespace computerVision
00069   
00070 } // namespace dlr
00071 
00072 
00073 /* ============ Definitions of inline & template functions ============ */
00074 
00075 
00076 #include <dlrNumeric/utilities.h>
00077 #include <dlrLinearAlgebra/linearAlgebra.h>
00078 
00079 namespace dlr {
00080 
00081   namespace computerVision {
00082     
00083     // This function estimates pinhole camera intrinsic parameters
00084     // based on corresponding points in 2D image coordinates and 3D
00085     // camera coordinates.
00086     template <class Iter3D, class Iter2D>
00087     CameraIntrinsicsPinhole
00088     estimateCameraIntrinsicsPinhole(size_t numPixelsX, size_t numPixelsY,
00089                                     Iter3D points3DBegin, Iter3D points3DEnd,
00090                                     Iter2D points2DBegin)
00091     {
00092       // Depending on the type of iterator, this might be O(N), but the
00093       // rest of the function will dominate regardless.
00094       size_t numberOfInputPoints = points3DEnd - points3DBegin;
00095 
00096       // For each 2D point, w_i = [u_i, v_i, 1]^T, expressed in 2D
00097       // homogeneous coordinates, and corresponding 3D point, d_i =
00098       // [x_i, y_i, z_i, 1]^T, expressed in 3D homogeneous coordinates,
00099       // the projection equation is
00100       //
00101       //   alpha * w_i = P * d_i
00102       // 
00103       // where alpha is an arbitrary scale factor, and P is the pinhole
00104       // projection matrix.
00105       // 
00106       //       | k_x, 0.0, u_0, 0.0 |
00107       //   P = | 0.0, k_y, v_0, 0.0 |
00108       //       | 0.0, 0.0, 1.0, 0.0 |
00109       //
00110       // This implies that w_i and (P * d_i) are parallel,
00111       // which implies that their cross product has zero magnitude:
00112       //
00113       //   cross(w_i, P * d_i) = [0.0, 0.0, 0.0]^T
00114       //
00115       // This cross product can be computed as
00116       //
00117       //   cross(w_i, P * d_i) = skewSymmetric(w_i) * (P * d_i)
00118       //
00119       // We can rearrange the matrix*vector product P * d_i to get
00120       // Equation 1:
00121       //
00122       //   cross(w_i, P * d_i)
00123       //     = skewSymmetric(w_i) * equivalentMatrix(d_i) * vec(P)
00124       //     = [0.0, 0.0, 0.0]^T
00125       //    
00126       // We get one of these sets of linear equations in the elements of
00127       // P for each pair of input points.  We can assemble the equations
00128       // into a linear system and solve for vec(P).
00129       // 
00130       //   A * vec(P) = [0.0, 0.0, ..., 0.0]
00131       //
00132       // In practice, though, we see that P is very sparse, and has one
00133       // element itentically equal to zero.  The first of these two
00134       // observations means that we must drop many of the columns of
00135       // equivalentMatrix(d_i).  Rather than use the prefab functino
00136       // "equivalentMatrix()" and ignore remove unused columns, we
00137       // hand-roll our own sparse equivalentMatrix below.
00138       //
00139       // The second observation (that the [2, 3] element of P is equal
00140       // to 1.0) lets us rearrange the matrix equation above.  Here is
00141       // the P * d_i product (the equivalentMatrix(d_i) * vec(P)
00142       // product), written out by individual elements:
00143       //
00144       //   | x_i, 0.0, z_i, 0.0 |   | k_x |   | 0.0 |
00145       //   | 0.0, y_i, 0.0, z_i | * | k_y | + | 0.0 | 
00146       //   | 0.0, 0.0, 0.0, 0.0 |   | u_0 |   | z_i |
00147       //                            | v_0 |
00148       //
00149       // Here is skewSymmetric(w_i) written out:
00150       //
00151       //                        |  0.0, -1.0,  v_i |
00152       //   skewSymmetric(w_i) = |  1.0,  0.0, -u_i |
00153       //                        | -v_i,  u_i,  0.0 |
00154       //
00155       // Substituting the previous two equations into Eq. 1, we have:
00156       //
00157       //   |      0.0,    -y_i,      0.0,   -z_i |   | k_x |   |  v_i * z_i |
00158       //   |      x_i,     0.0,      z_i,    0.0 | * | k_y | + | -u_i * z_i |
00159       //   | -v_i*x_i, u_i*y_i, -v_i*z_i, u_i*z_i|   | u_0 |   |     0.0    |
00160       //                                             | v_0 |
00161       //
00162       //          | 0.0 |
00163       //        = | 0.0 |
00164       //          | 0.0 |
00165       //
00166       // Subtracting from both sides the vector that does not depend on
00167       // our unknown parameters gives:
00168       //
00169       //   |      0.0,    -y_i,      0.0,   -z_i |   | k_x |   | -v_i * z_i |
00170       //   |      x_i,     0.0,      z_i,    0.0 | * | k_y | = |  u_i * z_i |
00171       //   | -v_i*x_i, u_i*y_i, -v_i*z_i, u_i*z_i|   | u_0 |   |     0.0    |
00172       //                                             | v_0 |
00173       // These are the equations implemented below.
00174       numeric::Array2D<double> AMatrix(3 * numberOfInputPoints, 4);
00175       numeric::Array1D<double> bVector(3 * numberOfInputPoints);
00176       unsigned int rowIndex = 0;
00177       while(points3DBegin != points3DEnd) {
00178         AMatrix(rowIndex, 0) = 0.0;
00179         AMatrix(rowIndex, 1) = -(points3DBegin->y());
00180         AMatrix(rowIndex, 2) = 0.0;
00181         AMatrix(rowIndex, 3) = -(points3DBegin->z());
00182         AMatrix(rowIndex + 1, 0) = points3DBegin->x();
00183         AMatrix(rowIndex + 1, 1) = 0.0;
00184         AMatrix(rowIndex + 1, 2) = points3DBegin->z();
00185         AMatrix(rowIndex + 1, 3) = 0.0;
00186         AMatrix(rowIndex + 2, 0) = -(points2DBegin->y()) * points3DBegin->x();
00187         AMatrix(rowIndex + 2, 1) = points2DBegin->x() * points3DBegin->y();
00188         AMatrix(rowIndex + 2, 2) = -(points2DBegin->y()) * points3DBegin->z();
00189         AMatrix(rowIndex + 2, 3) = points2DBegin->x() * points3DBegin->z();
00190         bVector(rowIndex) = AMatrix(rowIndex + 2, 2);
00191         bVector(rowIndex + 1) = AMatrix(rowIndex + 2, 3);
00192         bVector(rowIndex + 2) = 0.0;
00193 
00194         rowIndex += 3;
00195         ++points2DBegin;
00196         ++points3DBegin;
00197       }
00198 
00199       // Now we can solve for our unknow parameters.
00200       numeric::Array2D<double> ATranspose = AMatrix.transpose();
00201       numeric::Array2D<double> ATA = numeric::matrixMultiply(
00202         ATranspose, AMatrix);
00203       numeric::Array1D<double> ATb = numeric::matrixMultiply(
00204         ATranspose, bVector);
00205       linearAlgebra::linearSolveInPlace(ATA, ATb);
00206 
00207       // ...which are returned in ATb.
00208       double k_x = ATb[0];
00209       double k_y = ATb[1];
00210       double u_0 = ATb[2];
00211       double v_0 = ATb[3];
00212 
00213       return CameraIntrinsicsPinhole(
00214         numPixelsX, numPixelsY, 1, 1.0 / k_x, 1.0 / k_y, u_0, v_0);
00215     }
00216 
00217   } // namespace computerVision
00218   
00219 } // namespace dlr
00220 
00221 #endif /* #ifndef DLR_COMPUTERVISION_CALIBRATIONTOOLS_H */

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