registerPoints3D.h

Go to the documentation of this file.
00001 
00016 #ifndef _DLRCOMPUTERVISION_REGISTERPOINTS3D_H_
00017 #define _DLRCOMPUTERVISION_REGISTERPOINTS3D_H_
00018 
00019 #include <dlrNumeric/transform3D.h>
00020 
00021 namespace dlr {
00022 
00023   namespace computerVision {
00024   
00054     template <class InIter0, class InIter1>
00055     Transform3D
00056     registerPoints3D(InIter0 fromPointsBegin, InIter0 fromPointsEnd,
00057                      InIter1 toPointsBegin);
00058 
00059 
00096     template <class InIter0, class InIter1, class InIter2>
00097     Transform3D
00098     registerPoints3D(InIter0 fromPointsBegin, InIter0 fromPointsEnd,
00099                      InIter1 toPointsBegin, InIter2 selectedFlagsBegin);
00100 
00101 
00102 
00166     template <class InIter0, class InIter1, class OutIter0>
00167     Transform3D
00168     registerPoints3D(InIter0 fromPointsBegin, InIter0 fromPointsEnd,
00169                      InIter1 toPointsBegin, OutIter0 selectedFlagsBegin,
00170                      double inclusion, double maximumResidual = -1.0,
00171                      size_t maximumIterations = 5);
00172   
00173   
00174   } // namespace computerVision    
00175 
00176 } // namespace dlr
00177 
00178 /* ========== Definitions of inline and template functions follow ========== */
00179 
00180 #include <limits>
00181 #include <dlrCommon/functional.h>
00182 #include <dlrCommon/types.h>
00183 #include <dlrNumeric/array1D.h>
00184 #include <dlrNumeric/array2D.h>
00185 #include <dlrNumeric/quaternion.h>
00186 #include <dlrNumeric/rotations.h>
00187 #include <dlrNumeric/utilities.h>
00188 #include <dlrNumeric/vector3D.h>
00189 #include <dlrLinearAlgebra/linearAlgebra.h>
00190 
00191 namespace dlr {
00192 
00193   namespace computerVision {
00194     
00196     namespace privateCode {
00197 
00198       const Array2D<double>&
00199       getHornNCoefficientMatrix()
00200       {
00201         static Array2D<double> _NCoefficients(
00202           "[[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0], "
00203           " [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0], "
00204           " [0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], "
00205           " [0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0], "
00206           " [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0], "
00207           " [1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0], "
00208           " [0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], "
00209           " [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], "
00210           " [0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], "
00211           " [0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], "
00212           " [-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0], "
00213           " [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0], "
00214           " [0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0], "
00215           " [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], "
00216           " [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0], "
00217           " [-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0]]");
00218         return _NCoefficients;
00219       }
00220 
00221     } // namespace privateCode
00223   
00224 
00225     template <class InIter0, class InIter1>
00226     Transform3D
00227     registerPoints3D(InIter0 fromPointsBegin, InIter0 fromPointsEnd,
00228                      InIter1 toPointsBegin)
00229     {
00230       std::vector<bool> flags(fromPointsEnd - fromPointsBegin, true);
00231       return registerPoints3D(
00232         fromPointsBegin, fromPointsEnd, toPointsBegin, flags.begin());
00233     }
00234 
00235   
00236     template <class InIter0, class InIter1, class InIter2>
00237     Transform3D
00238     registerPoints3D(InIter0 fromPointsBegin, InIter0 fromPointsEnd,
00239                      InIter1 toPointsBegin, InIter2 flagsBegin)
00240     {
00241       // Compute the mean of each point cloud, so we can translate its
00242       // center to the origin.
00243       InIter0 fromIter = fromPointsBegin;
00244       InIter1 toIter = toPointsBegin;
00245       InIter2 flagsIter = flagsBegin;
00246       Vector3D fromMean(0.0, 0.0, 0.0);
00247       Vector3D toMean(0.0, 0.0, 0.0);
00248       size_t count = 0;
00249       while(fromIter != fromPointsEnd) {
00250         if(*flagsIter) {
00251           fromMean += *fromIter;
00252           toMean += *toIter;
00253           ++count;
00254         }
00255         ++fromIter;
00256         ++toIter;
00257         ++flagsIter;
00258       }
00259       if(count == 0) {
00260         DLR_THROW(ValueException, "registerPoints3D()",
00261                   "No points to register!");
00262       }
00263       fromMean /= static_cast<double>(count);
00264       toMean /= static_cast<double>(count);
00265 
00266       // Now translate each point cloud so that its center of mass is at
00267       // the origin.  We'll use these translated point clouds to compute
00268       // the best fit rotation.  We use Nx3 arrays to represent these
00269       // translated ponits so that we'll be able to conveniently do
00270       // linear algebra later.
00271       fromIter = fromPointsBegin;
00272       toIter = toPointsBegin;
00273       flagsIter = flagsBegin;
00274       Array2D<double> translatedFromPoints(count, 3);
00275       Array2D<double> translatedToPoints(count, 3);
00276       size_t arrayIndex = 0;
00277       while(fromIter != fromPointsEnd) {
00278         if(*flagsIter) {
00279           Vector3D& fromPoint = *fromIter;
00280           Vector3D& toPoint = *toIter;
00281         
00282           // Copy the first element in the current row.
00283           translatedFromPoints[arrayIndex] = fromPoint.x() - fromMean.x();
00284           translatedToPoints[arrayIndex] = toPoint.x() - toMean.x();
00285         
00286           // Advance to next element in the current row.
00287           ++arrayIndex;
00288           translatedFromPoints[arrayIndex] = fromPoint.y() - fromMean.y();
00289           translatedToPoints[arrayIndex] = toPoint.y() - toMean.y();
00290         
00291           // Advance to next element in the current row.
00292           ++arrayIndex;
00293           translatedFromPoints[arrayIndex] = fromPoint.z() - fromMean.z();
00294           translatedToPoints[arrayIndex] = toPoint.z() - toMean.z();
00295         
00296           // Wrap around to next row.
00297           ++arrayIndex;
00298         }
00299         // Advance to next point.
00300         ++fromIter;
00301         ++toIter;
00302         ++flagsIter;
00303       }
00304 
00305       // Compute the (not quite) covariance matrix between the two point
00306       // clouds.  This is a  3x3 matrix.
00307       Array2D<double> matrixM = matrixMultiply(
00308         translatedFromPoints.transpose(), translatedToPoints);
00309 
00310       // Select elements as describe by Horn.  The member function
00311       // ravel() simply returns a flattened (1D) array referencing the
00312       // elements of the Array2D instance in row-major order.
00313       Array1D<double> vectorN = matrixMultiply(
00314         privateCode::getHornNCoefficientMatrix(), matrixM.ravel());
00315       Array2D<double> matrixN(4, 4, vectorN.data(), vectorN.refCountPtr());
00316 
00317       // Find the largest eigenvector of matrixN.  This is a unit
00318       // quaternion describing the best fit rotation.
00319       Array1D<Float64> eValues;
00320       Array2D<Float64> eVectors;
00321       if(sizeof(double) == sizeof(Float64)) {
00322         eigenvectorsSymmetric(matrixN, eValues, eVectors);
00323       } else {
00324         Array2D<Float64> tempArray(matrixN.rows(), matrixN.columns());
00325         tempArray.copy(matrixN);
00326         eigenvectorsSymmetric(matrixN, eValues, eVectors);
00327       }
00328 
00329       // The function argmax returns the index of the largest element of
00330       // eValues.
00331       size_t index0 = argmax(eValues);
00332       Quaternion q0(static_cast<double>(eVectors(0, index0)),
00333                     static_cast<double>(eVectors(1, index0)),
00334                     static_cast<double>(eVectors(2, index0)),
00335                     static_cast<double>(eVectors(3, index0)));
00336 
00337       // Convert the unit quaternion to a rotation matrix.
00338       Transform3D xf = quaternionToTransform3D(q0);
00339 
00340       // And add in best fit translation.
00341       Vector3D bestFitTranslation = toMean - xf * fromMean;
00342       xf.setValue<0, 3>(bestFitTranslation.x());
00343       xf.setValue<1, 3>(bestFitTranslation.y());
00344       xf.setValue<2, 3>(bestFitTranslation.z());
00345       return xf;
00346     }
00347   
00348 
00349     template <class InIter0, class InIter1, class OutIter0>
00350     Transform3D
00351     registerPoints3D(InIter0 fromPointsBegin, InIter0 fromPointsEnd,
00352                      InIter1 toPointsBegin, OutIter0 selectedFlagsBegin,
00353                      double inclusion, double maximumResidual,
00354                      size_t maximumIterations)
00355     {
00356       // Sort out arguments, array sizes, etc.
00357       size_t numberOfPoints = fromPointsEnd - fromPointsBegin;
00358       size_t numberToExclude = 0;
00359       if(inclusion > 0.0 && inclusion < 1.0) {
00360         size_t numberToInclude = static_cast<size_t>(
00361           std::floor(inclusion * numberOfPoints));
00362         if(numberToInclude >= numberOfPoints) {
00363           DLR_THROW(ValueException, "registerPoints3D()",
00364                     "Inclusion percentage is too low... all points excluded.");
00365         }
00366         numberToExclude = numberOfPoints - numberToInclude;
00367       }
00368 
00369       // Register repeatedly until we converge.
00370       Transform3D resultTransform;
00371       std::vector<bool> flags(numberOfPoints, true);
00372       for(size_t iterationIndex = 0;
00373           iterationIndex < maximumIterations;
00374           ++iterationIndex) {
00375         // Do a sub-registration.
00376         resultTransform = registerPoints3D(
00377           fromPointsBegin, fromPointsEnd, toPointsBegin, flags.begin());
00378 
00379         // Compute residuals.
00380         std::vector<Vector3D> transformedPoints(numberOfPoints);
00381         std::transform(fromPointsBegin, fromPointsEnd,
00382                        transformedPoints.begin(), resultTransform.getFunctor());
00383 
00384         std::vector<double> squaredResiduals(numberOfPoints);
00385         typedef double (*magVec3D)(const Vector3D&);
00386         std::transform(
00387           transformedPoints.begin(), transformedPoints.end(),
00388           toPointsBegin, squaredResiduals.begin(),
00389           composeFunctor_1_2(
00390             std::ptr_fun(static_cast<magVec3D>(magnitudeSquared)),
00391             std::minus<Vector3D>()));
00392 
00393         // Figure out how big a residual has to be before we ignore the
00394         // associated point.
00395         double residualThreshold =
00396           (maximumResidual > 0.0)
00397           ? maximumResidual : std::numeric_limits<double>::max();
00398         if(numberToExclude != 0) {
00399           // Find the numberToExclude largest residuals.
00400           std::vector<double> sortedSquaredResiduals(numberToExclude);
00401           std::partial_sort_copy(
00402             squaredResiduals.begin(), squaredResiduals.end(),
00403             sortedSquaredResiduals.begin(), sortedSquaredResiduals.end(),
00404             std::greater<double>());
00405           double threshold = sortedSquaredResiduals[numberToExclude - 1];
00406           if(threshold < residualThreshold) {
00407             residualThreshold = threshold;
00408           }
00409         }
00410 
00411         // Check which points exceed the residual threshold and should
00412         // be counted as outliers.
00413         std::vector<bool> newFlags(numberOfPoints);
00414         std::transform(
00415           squaredResiduals.begin(), squaredResiduals.end(), newFlags.begin(),
00416           std::bind2nd(std::less<double>(), residualThreshold));
00417 
00418         // Any change from last iteration?
00419         if(std::equal(flags.begin(), flags.end(), newFlags.begin())) {
00420           // No change.  We're done.
00421           break;
00422         }
00423       
00424         // Reset flags so the next iteration will exclude points which
00425         // exceed the threshold.
00426         std::copy(newFlags.begin(), newFlags.end(), flags.begin());
00427       } // for
00428 
00429       std::copy(flags.begin(), flags.end(), selectedFlagsBegin);
00430       return resultTransform;
00431     }
00432   
00433   } // namespace computerVision    
00434 
00435 } // namespace dlr
00436 
00437 #endif /* #ifndef _DLRCOMPUTERVISION_REGISTERPOINTS3D_H_ */

Generated on Mon Jul 9 20:34:03 2007 for dlrLibs Utility Libraries by  doxygen 1.5.2