ErrorMinimizersImpl.cpp
Go to the documentation of this file.
00001 // kate: replace-tabs off; indent-width 4; indent-mode normal
00002 // vim: ts=4:sw=4:noexpandtab
00003 /*
00004 
00005 Copyright (c) 2010--2012,
00006 François Pomerleau and Stephane Magnenat, ASL, ETHZ, Switzerland
00007 You can contact the authors at <f dot pomerleau at gmail dot com> and
00008 <stephane at magnenat dot net>
00009 
00010 All rights reserved.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014     * Redistributions of source code must retain the above copyright
00015       notice, this list of conditions and the following disclaimer.
00016     * Redistributions in binary form must reproduce the above copyright
00017       notice, this list of conditions and the following disclaimer in the
00018       documentation and/or other materials provided with the distribution.
00019     * Neither the name of the <organization> nor the
00020       names of its contributors may be used to endorse or promote products
00021       derived from this software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00024 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00025 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00026 DISCLAIMED. IN NO EVENT SHALL ETH-ASL BE LIABLE FOR ANY
00027 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00028 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00030 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00031 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034 */
00035 
00036 #include "ErrorMinimizersImpl.h"
00037 #include "PointMatcherPrivate.h"
00038 #include "Functions.h"
00039 
00040 #include "Eigen/SVD"
00041 #include <iostream>
00042 
00043 using namespace Eigen;
00044 using namespace std;
00045 using namespace PointMatcherSupport;
00046 
00047 // Identity Error Minimizer
00048 template<typename T>
00049 typename PointMatcher<T>::TransformationParameters ErrorMinimizersImpl<T>::IdentityErrorMinimizer::compute(
00050         const DataPoints& filteredReading,
00051         const DataPoints& filteredReference,
00052         const OutlierWeights& outlierWeights,
00053         const Matches& matches)
00054 {
00055         return TransformationParameters::Identity(filteredReading.features.rows(), filteredReading.features.rows());
00056 }
00057 
00058 template struct ErrorMinimizersImpl<float>::IdentityErrorMinimizer;
00059 template struct ErrorMinimizersImpl<double>::IdentityErrorMinimizer;
00060 
00061 
00062 // Point To POINT ErrorMinimizer
00063 template<typename T>
00064 typename PointMatcher<T>::TransformationParameters ErrorMinimizersImpl<T>::PointToPointErrorMinimizer::compute(
00065         const DataPoints& filteredReading,
00066         const DataPoints& filteredReference,
00067         const OutlierWeights& outlierWeights,
00068         const Matches& matches)
00069 {
00070         typedef typename Matches::Ids Ids;
00071         
00072         assert(matches.ids.rows() > 0);
00073 
00074         typename ErrorMinimizer::ErrorElements& mPts = this->getMatchedPoints(filteredReading, filteredReference, matches, outlierWeights);
00075         
00076         // now minimize on kept points
00077         const int dimCount(mPts.reading.features.rows());
00078         const int ptsCount(mPts.reading.features.cols()); //But point cloud have now the same number of (matched) point
00079 
00080         // Compute the mean of each point cloud
00081         const Vector meanReading = mPts.reading.features.rowwise().sum() / ptsCount;
00082         const Vector meanReference = mPts.reference.features.rowwise().sum() / ptsCount;
00083         
00084         // Remove the mean from the point clouds
00085         mPts.reading.features.colwise() -= meanReading;
00086         mPts.reference.features.colwise() -= meanReference;
00087 
00088         // Singular Value Decomposition
00089         const Matrix m(mPts.reference.features.topRows(dimCount-1) * mPts.reading.features.topRows(dimCount-1).transpose());
00090         const JacobiSVD<Matrix> svd(m, ComputeThinU | ComputeThinV);
00091         const Matrix rotMatrix(svd.matrixU() * svd.matrixV().transpose());
00092         const Vector trVector(meanReference.head(dimCount-1)- rotMatrix * meanReading.head(dimCount-1));
00093         
00094         Matrix result(Matrix::Identity(dimCount, dimCount));
00095         result.corner(TopLeft, dimCount-1, dimCount-1) = rotMatrix;
00096         result.corner(TopRight, dimCount-1, 1) = trVector;
00097         
00098         return result;
00099 }
00100 
00101 template<typename T>
00102 T ErrorMinimizersImpl<T>::PointToPointErrorMinimizer::getOverlap() const
00103 {
00104         //NOTE: computing overlap of 2 point clouds can be complicated due to
00105         // the sparse nature of the representation. Here is only an estimate 
00106         // of the true overlap.
00107         const int nbPoints = this->lastErrorElements.reading.features.cols();
00108         if(nbPoints == 0)
00109         {
00110                 throw std::runtime_error("Error, last error element empty. Error minimizer needs to be called at least once before using this method.");
00111         }
00112 
00113         if (!this->lastErrorElements.reading.descriptorExists("simpleSensorNoise"))
00114         {
00115                 LOG_INFO_STREAM("PointToPointErrorMinimizer - warning, no sensor noise found. Using best estimate given outlier rejection instead.");
00116                 return this->weightedPointUsedRatio;
00117         }
00118 
00119         const BOOST_AUTO(noises, this->lastErrorElements.reading.getDescriptorViewByName("simpleSensorNoise"));
00120 
00121         const Vector dists = (this->lastErrorElements.reading.features - this->lastErrorElements.reference.features).colwise().norm();
00122         const T mean = dists.sum()/nbPoints;
00123 
00124         int count = 0;
00125         for(int i=0; i < nbPoints; i++)
00126         {
00127                 if(dists(i) < (mean + noises(0,i)))
00128                 {
00129                         count++;
00130                 }
00131         }
00132 
00133         return (T)count/(T)nbPoints;
00134 }
00135 
00136 template struct ErrorMinimizersImpl<float>::PointToPointErrorMinimizer;
00137 template struct ErrorMinimizersImpl<double>::PointToPointErrorMinimizer;
00138 
00139 
00140 // Point To PLANE ErrorMinimizer
00141 template<typename T>
00142 ErrorMinimizersImpl<T>::PointToPlaneErrorMinimizer::PointToPlaneErrorMinimizer(const Parameters& params):
00143         ErrorMinimizer("PointToPlaneErrorMinimizer", PointToPlaneErrorMinimizer::availableParameters(), params),
00144         force2D(Parametrizable::get<T>("force2D"))
00145 {
00146 }
00147 
00148 
00149 template<typename T>
00150 typename PointMatcher<T>::TransformationParameters ErrorMinimizersImpl<T>::PointToPlaneErrorMinimizer::compute(
00151         const DataPoints& filteredReading,
00152         const DataPoints& filteredReference,
00153         const OutlierWeights& outlierWeights,
00154         const Matches& matches)
00155 {
00156         typedef typename PointMatcher<T>::ConvergenceError ConvergenceError;
00157         typedef typename Matches::Ids Ids;
00158 
00159         assert(matches.ids.rows() > 0);
00160 
00161         // Fetch paired points
00162         typename ErrorMinimizer::ErrorElements& mPts = this->getMatchedPoints(filteredReading, filteredReference, matches, outlierWeights);
00163 
00164         const int dim = mPts.reading.features.rows();
00165         const int nbPts = mPts.reading.features.cols();
00166 
00167         // Adjust if the user forces 2D minimization on XY-plane
00168         int forcedDim = dim - 1;
00169         if(force2D && dim == 4)
00170         {
00171                 mPts.reading.features.conservativeResize(3, Eigen::NoChange);
00172                 mPts.reading.features.row(2) = Matrix::Ones(1, nbPts);
00173                 mPts.reference.features.conservativeResize(3, Eigen::NoChange);
00174                 mPts.reference.features.row(2) = Matrix::Ones(1, nbPts);
00175                 forcedDim = dim - 2;
00176         }
00177 
00178         // Fetch normal vectors of the reference point cloud (with adjustment if needed)
00179         const BOOST_AUTO(normalRef, mPts.reference.getDescriptorViewByName("normals").topRows(forcedDim));
00180 
00181         // Note: Normal vector must be precalculated to use this error. Use appropriate input filter.
00182         assert(normalRef.rows() > 0);
00183 
00184         // Compute cross product of cross = cross(reading X normalRef)
00185         const Matrix cross = this->crossProduct(mPts.reading.features, normalRef);
00186 
00187         // wF = [weights*cross, normals]
00188         // F  = [cross, normals]
00189         Matrix wF(normalRef.rows()+ cross.rows(), normalRef.cols());
00190         Matrix F(normalRef.rows()+ cross.rows(), normalRef.cols());
00191         
00192         for(int i=0; i < cross.rows(); i++)
00193         {
00194                 wF.row(i) = mPts.weights.cwise() * cross.row(i);
00195                 F.row(i) = cross.row(i);
00196         }
00197         for(int i=0; i < normalRef.rows(); i++)
00198         {
00199                 wF.row(i + cross.rows()) = normalRef.row(i);
00200                 F.row(i + cross.rows()) = normalRef.row(i);
00201         }
00202 
00203         // Unadjust covariance A = wF * F'
00204         const Matrix A = wF * F.transpose();
00205         if (A.fullPivHouseholderQr().rank() != A.rows())
00206         {
00207                 // TODO: handle that properly
00208                 //throw ConvergenceError("encountered singular while minimizing point to plane distance");
00209         }
00210 
00211         const Matrix deltas = mPts.reading.features - mPts.reference.features;
00212 
00213         // dot product of dot = dot(deltas, normals)
00214         Matrix dotProd = Matrix::Zero(1, normalRef.cols());
00215         
00216         for(int i=0; i<normalRef.rows(); i++)
00217         {
00218                 dotProd += (deltas.row(i).cwise() * normalRef.row(i));
00219         }
00220 
00221         // b = -(wF' * dot)
00222         const Vector b = -(wF * dotProd.transpose());
00223 
00224         // Cholesky decomposition
00225         Vector x(A.rows());
00226         A.llt().solve(b, &x);
00227         
00228         // Transform parameters to matrix
00229         Matrix mOut;
00230         if(dim == 4 && !force2D)
00231         {
00232                 Eigen::Transform<T, 3, Eigen::Affine> transform;
00233                 // Rotation in Eular angles follow roll-pitch-yaw (1-2-3) rule
00234                 transform = Eigen::AngleAxis<T>(x(0), Eigen::Matrix<T,1,3>::UnitX())
00235                                 * Eigen::AngleAxis<T>(x(1), Eigen::Matrix<T,1,3>::UnitY())
00236                                 * Eigen::AngleAxis<T>(x(2), Eigen::Matrix<T,1,3>::UnitZ());
00237                 // Reverse roll-pitch-yaw conversion, very useful piece of knowledge, keep it with you all time!
00238                 /*const T pitch = -asin(transform(2,0));
00239                 const T roll = atan2(transform(2,1), transform(2,2));
00240                 const T yaw = atan2(transform(1,0) / cos(pitch), transform(0,0) / cos(pitch));
00241                 std::cerr << "d angles" << x(0) - roll << ", " << x(1) - pitch << "," << x(2) - yaw << std::endl;*/
00242                 transform.translation() = x.segment(3, 3);
00243                 mOut = transform.matrix();
00244         }
00245         else
00246         {
00247                 Eigen::Transform<T, 2, Eigen::Affine> transform;
00248                 transform = Eigen::Rotation2D<T> (x(0));
00249                 transform.translation() = x.segment(1, 2);
00250 
00251                 if(force2D)
00252                 {
00253                         mOut = Matrix::Identity(dim, dim);
00254                         mOut.corner(TopLeft, 2, 2) = transform.matrix().corner(TopLeft, 2, 2);
00255                         mOut.corner(TopRight, 2, 1) = transform.matrix().corner(TopRight, 2, 1);
00256                 }
00257                 else
00258                 {
00259                         mOut = transform.matrix();
00260                 }
00261         }
00262         return mOut; 
00263 }
00264 
00265 template<typename T>
00266 T ErrorMinimizersImpl<T>::PointToPlaneErrorMinimizer::getOverlap() const
00267 {
00268         const int nbPoints = this->lastErrorElements.reading.features.cols();
00269         const int dim = this->lastErrorElements.reading.features.rows();
00270         if(nbPoints == 0)
00271         {
00272                 throw std::runtime_error("Error, last error element empty. Error minimizer needs to be called at least once before using this method.");
00273         }
00274         
00275         if (!this->lastErrorElements.reading.descriptorExists("simpleSensorNoise") ||
00276                 !this->lastErrorElements.reading.descriptorExists("normals"))
00277         {
00278                 LOG_INFO_STREAM("PointToPlaneErrorMinimizer - warning, no sensor noise or normals found. Using best estimate given outlier rejection instead.");
00279                 return this->weightedPointUsedRatio;
00280         }
00281 
00282         const BOOST_AUTO(noises, this->lastErrorElements.reading.getDescriptorViewByName("simpleSensorNoise"));
00283         const BOOST_AUTO(normals, this->lastErrorElements.reading.getDescriptorViewByName("normals"));
00284         int count = 0;
00285         for(int i=0; i < nbPoints; i++)
00286         {
00287                 if(this->lastErrorElements.matches.dists(0, i) != numeric_limits<T>::infinity())
00288                 {
00289                         const Vector d = this->lastErrorElements.reading.features.col(i) - this->lastErrorElements.reference.features.col(i);
00290                         const Vector n = normals.col(i);
00291                         const T projectionDist = d.head(dim-1).dot(n.normalized());
00292                         if(anyabs(projectionDist) < noises(0,i))
00293                                 count++;
00294                 }
00295         }
00296 
00297         return (T)count/(T)nbPoints;
00298 }
00299 
00300 template struct ErrorMinimizersImpl<float>::PointToPlaneErrorMinimizer;
00301 template struct ErrorMinimizersImpl<double>::PointToPlaneErrorMinimizer;
00302 
00303 
00304 
00305 // Point To POINT WITH COV ErrorMinimizer
00306 template<typename T>
00307 ErrorMinimizersImpl<T>::PointToPointWithCovErrorMinimizer::PointToPointWithCovErrorMinimizer(const Parameters& params):
00308         ErrorMinimizer("PointToPointWithCovErrorMinimizer", PointToPointWithCovErrorMinimizer::availableParameters(), params),
00309         sensorStdDev(Parametrizable::get<T>("sensorStdDev"))
00310 {
00311 }
00312 
00313 template<typename T>
00314 typename PointMatcher<T>::TransformationParameters ErrorMinimizersImpl<T>::PointToPointWithCovErrorMinimizer::compute(
00315         const DataPoints& filteredReading,
00316         const DataPoints& filteredReference,
00317         const OutlierWeights& outlierWeights,
00318         const Matches& matches)
00319 {
00320         typedef typename Matches::Ids Ids;
00321         
00322         assert(matches.ids.rows() > 0);
00323 
00324         typename ErrorMinimizer::ErrorElements& mPts = this->getMatchedPoints(filteredReading, filteredReference, matches, outlierWeights);
00325         
00326         // now minimize on kept points
00327         const int dimCount(mPts.reading.features.rows());
00328         const int ptsCount(mPts.reading.features.cols()); //But point cloud have now the same number of (matched) point
00329 
00330         // Compute the mean of each point cloud
00331         const Vector meanReading = mPts.reading.features.rowwise().sum() / ptsCount;
00332         const Vector meanReference = mPts.reference.features.rowwise().sum() / ptsCount;
00333         
00334         // Remove the mean from the point clouds
00335         mPts.reading.features.colwise() -= meanReading;
00336         mPts.reference.features.colwise() -= meanReference;
00337 
00338         // Singular Value Decomposition
00339         const Matrix m(mPts.reference.features.topRows(dimCount-1) * mPts.reading.features.topRows(dimCount-1).transpose());
00340         const JacobiSVD<Matrix> svd(m, ComputeThinU | ComputeThinV);
00341         const Matrix rotMatrix(svd.matrixU() * svd.matrixV().transpose());
00342         const Vector trVector(meanReference.head(dimCount-1)- rotMatrix * meanReading.head(dimCount-1));
00343         
00344         Matrix result(Matrix::Identity(dimCount, dimCount));
00345         result.corner(TopLeft, dimCount-1, dimCount-1) = rotMatrix;
00346         result.corner(TopRight, dimCount-1, 1) = trVector;
00347 
00348         this->covMatrix = this->estimateCovariance(filteredReading, filteredReference, matches, outlierWeights, result);
00349 
00350         return result;
00351 }
00352 
00353 template<typename T>
00354 typename ErrorMinimizersImpl<T>::Matrix
00355 ErrorMinimizersImpl<T>::PointToPointWithCovErrorMinimizer::estimateCovariance(const DataPoints& reading, const DataPoints& reference, const Matches& matches, const OutlierWeights& outlierWeights, const TransformationParameters& transformation)
00356 {
00357         int max_nbr_point = outlierWeights.cols();
00358 
00359         Matrix covariance(Matrix::Zero(6,6));
00360         Matrix J_hessian(Matrix::Zero(6,6));
00361         Matrix d2J_dReadingdX(Matrix::Zero(6, max_nbr_point));
00362         Matrix d2J_dReferencedX(Matrix::Zero(6, max_nbr_point));
00363 
00364         Vector reading_point(Vector::Zero(3));
00365         Vector reference_point(Vector::Zero(3));
00366         Vector normal(3);
00367         Vector reading_direction(Vector::Zero(3));
00368         Vector reference_direction(Vector::Zero(3));
00369 
00370         normal << 1.0, 1.0, 1.0;    // Used for point-to-point computation
00371 
00372         T beta = -asin(transformation(2,0));
00373         T alpha = atan2(transformation(2,1), transformation(2,2));
00374         T gamma = atan2(transformation(1,0)/cos(beta), transformation(0,0)/cos(beta));
00375         T t_x = transformation(0,3);
00376         T t_y = transformation(1,3);
00377         T t_z = transformation(2,3);
00378 
00379         Vector tmp_vector_6(Vector::Zero(6));
00380 
00381         int valid_points_count = 0;
00382 
00383         for(int i = 0; i < max_nbr_point; ++i)
00384         {
00385                 if (outlierWeights(0,i) > 0.0)
00386                 {
00387                         reading_point = reading.features.block(0,i,3,1);
00388                         int reference_idx = matches.ids(0,i);
00389                         reference_point = reference.features.block(0,reference_idx,3,1);
00390 
00391                         T reading_range = reading_point.norm();
00392                         reading_direction = reading_point / reading_range;
00393                         T reference_range = reference_point.norm();
00394                         reference_direction = reference_point / reference_range;
00395 
00396                         T n_alpha = normal(2)*reading_direction(1) - normal(1)*reading_direction(2);
00397                         T n_beta = normal(0)*reading_direction(2) - normal(2)*reading_direction(0);
00398                         T n_gamma = normal(1)*reading_direction(0) - normal(0)*reading_direction(1);
00399 
00400                         T E = normal(0)*(reading_point(0) - gamma*reading_point(1) + beta*reading_point(2) + t_x - reference_point(0));
00401                         E +=  normal(1)*(gamma*reading_point(0) + reading_point(1) - alpha*reading_point(2) + t_y - reference_point(1));
00402                         E +=  normal(2)*(-beta*reading_point(0) + alpha*reading_point(1) + reading_point(2) + t_z - reference_point(2));
00403 
00404                         T N_reading = normal(0)*(reading_direction(0) - gamma*reading_direction(1) + beta*reading_direction(2));
00405                         N_reading +=  normal(1)*(gamma*reading_direction(0) + reading_direction(1) - alpha*reading_direction(2));
00406                         N_reading +=  normal(2)*(-beta*reading_direction(0) + alpha*reading_direction(1) + reading_direction(2));
00407 
00408                         T N_reference = -(normal(0)*reference_direction(0) + normal(1)*reference_direction(1) + normal(2)*reference_direction(2));
00409 
00410                         // update the hessian and d2J/dzdx
00411                         tmp_vector_6 << normal(0), normal(1), normal(2), reading_range * n_alpha, reading_range * n_beta, reading_range * n_gamma;
00412 
00413                         J_hessian += tmp_vector_6 * tmp_vector_6.transpose();
00414 
00415                         tmp_vector_6 << normal(0) * N_reading, normal(1) * N_reading, normal(2) * N_reading, n_alpha * (E + reading_range * N_reading), n_beta * (E + reading_range * N_reading), n_gamma * (E + reading_range * N_reading);
00416 
00417                         d2J_dReadingdX.block(0,valid_points_count,6,1) = tmp_vector_6;
00418 
00419                         tmp_vector_6 << normal(0) * N_reference, normal(1) * N_reference, normal(2) * N_reference, reference_range * n_alpha * N_reference, reference_range * n_beta * N_reference, reference_range * n_gamma * N_reference;
00420 
00421                         d2J_dReferencedX.block(0,valid_points_count,6,1) = tmp_vector_6;
00422 
00423                         valid_points_count++;
00424                 } // if (outlierWeights(0,i) > 0.0)
00425         }
00426 
00427         Matrix d2J_dZdX(Matrix::Zero(6, 2 * valid_points_count));
00428         d2J_dZdX.block(0,0,6,valid_points_count) = d2J_dReadingdX.block(0,0,6,valid_points_count);
00429         d2J_dZdX.block(0,valid_points_count,6,valid_points_count) = d2J_dReferencedX.block(0,0,6,valid_points_count);
00430 
00431         Matrix inv_J_hessian = J_hessian.inverse();
00432 
00433         covariance = d2J_dZdX * d2J_dZdX.transpose();
00434         covariance = inv_J_hessian * covariance * inv_J_hessian;
00435 
00436         return (sensorStdDev * sensorStdDev) * covariance;
00437 }
00438 
00439 template<typename T>
00440 T ErrorMinimizersImpl<T>::PointToPointWithCovErrorMinimizer::getOverlap() const
00441 {
00442         const int nbPoints = this->lastErrorElements.reading.features.cols();
00443         if(nbPoints == 0)
00444         {
00445                 throw std::runtime_error("Error, last error element empty. Error minimizer needs to be called at least once before using this method.");
00446         }
00447 
00448         if (!this->lastErrorElements.reading.descriptorExists("simpleSensorNoise"))
00449         {
00450                 LOG_INFO_STREAM("PointToPointErrorMinimizer - warning, no sensor noise found. Using best estimate given outlier rejection instead.");
00451                 return this->weightedPointUsedRatio;
00452         }
00453 
00454         const BOOST_AUTO(noises, this->lastErrorElements.reading.getDescriptorViewByName("simpleSensorNoise"));
00455         int count = 0;
00456         for(int i=0; i < nbPoints; i++)
00457         {
00458                 const T dist = (this->lastErrorElements.reading.features.col(i) - this->lastErrorElements.reference.features.col(i)).norm();
00459                 if(dist < noises(0,i))
00460                         count++;
00461 
00462         }
00463 
00464         return (T)count/(T)nbPoints;
00465 }
00466 
00467 template<typename T>
00468 typename ErrorMinimizersImpl<T>::Matrix ErrorMinimizersImpl<T>::PointToPointWithCovErrorMinimizer::getCovariance() const
00469 {
00470   return covMatrix;
00471 }
00472 
00473 template struct ErrorMinimizersImpl<float>::PointToPointWithCovErrorMinimizer;
00474 template struct ErrorMinimizersImpl<double>::PointToPointWithCovErrorMinimizer;
00475 
00476 
00477 // Point To PLANE WITH COV ErrorMinimizer
00478 template<typename T>
00479 ErrorMinimizersImpl<T>::PointToPlaneWithCovErrorMinimizer::PointToPlaneWithCovErrorMinimizer(const Parameters& params):
00480         ErrorMinimizer("PointToPlaneWithCovErrorMinimizer", PointToPlaneWithCovErrorMinimizer::availableParameters(), params),
00481         force2D(Parametrizable::get<T>("force2D")),
00482         sensorStdDev(Parametrizable::get<T>("sensorStdDev"))
00483 {
00484 }
00485 
00486 
00487 template<typename T>
00488 typename PointMatcher<T>::TransformationParameters ErrorMinimizersImpl<T>::PointToPlaneWithCovErrorMinimizer::compute(
00489         const DataPoints& filteredReading,
00490         const DataPoints& filteredReference,
00491         const OutlierWeights& outlierWeights,
00492         const Matches& matches)
00493 {
00494         typedef typename PointMatcher<T>::ConvergenceError ConvergenceError;
00495         typedef typename Matches::Ids Ids;
00496 
00497         assert(matches.ids.rows() > 0);
00498 
00499         // Fetch paired points
00500         typename ErrorMinimizer::ErrorElements& mPts = this->getMatchedPoints(filteredReading, filteredReference, matches, outlierWeights);
00501 
00502         const int dim = mPts.reading.features.rows();
00503         const int nbPts = mPts.reading.features.cols();
00504 
00505         // Adjust if the user forces 2D minimization on XY-plane
00506         int forcedDim = dim - 1;
00507         if(force2D && dim == 4)
00508         {
00509                 mPts.reading.features.conservativeResize(3, Eigen::NoChange);
00510                 mPts.reading.features.row(2) = Matrix::Ones(1, nbPts);
00511                 mPts.reference.features.conservativeResize(3, Eigen::NoChange);
00512                 mPts.reference.features.row(2) = Matrix::Ones(1, nbPts);
00513                 forcedDim = dim - 2;
00514         }
00515 
00516         // Fetch normal vectors of the reference point cloud (with adjustment if needed)
00517         const BOOST_AUTO(normalRef, mPts.reference.getDescriptorViewByName("normals").topRows(forcedDim));
00518 
00519         // Note: Normal vector must be precalculated to use this error. Use appropriate input filter.
00520         assert(normalRef.rows() > 0);
00521 
00522         // Compute cross product of cross = cross(reading X normalRef)
00523         const Matrix cross = this->crossProduct(mPts.reading.features, normalRef);
00524 
00525         // wF = [weights*cross, normals]
00526         // F  = [cross, normals]
00527         Matrix wF(normalRef.rows()+ cross.rows(), normalRef.cols());
00528         Matrix F(normalRef.rows()+ cross.rows(), normalRef.cols());
00529         
00530         for(int i=0; i < cross.rows(); i++)
00531         {
00532                 wF.row(i) = mPts.weights.cwise() * cross.row(i);
00533                 F.row(i) = cross.row(i);
00534         }
00535         for(int i=0; i < normalRef.rows(); i++)
00536         {
00537                 wF.row(i + cross.rows()) = normalRef.row(i);
00538                 F.row(i + cross.rows()) = normalRef.row(i);
00539         }
00540 
00541         // Unadjust covariance A = wF * F'
00542         const Matrix A = wF * F.transpose();
00543         if (A.fullPivHouseholderQr().rank() != A.rows())
00544         {
00545                 // TODO: handle that properly
00546                 //throw ConvergenceError("encountered singular while minimizing point to plane distance");
00547         }
00548 
00549         const Matrix deltas = mPts.reading.features - mPts.reference.features;
00550 
00551         // dot product of dot = dot(deltas, normals)
00552         Matrix dotProd = Matrix::Zero(1, normalRef.cols());
00553         
00554         for(int i=0; i<normalRef.rows(); i++)
00555         {
00556                 dotProd += (deltas.row(i).cwise() * normalRef.row(i));
00557         }
00558 
00559         // b = -(wF' * dot)
00560         const Vector b = -(wF * dotProd.transpose());
00561 
00562         // Cholesky decomposition
00563         Vector x(A.rows());
00564         A.llt().solve(b, &x);
00565         
00566         // Transform parameters to matrix
00567         Matrix mOut;
00568         if(dim == 4 && !force2D)
00569         {
00570                 Eigen::Transform<T, 3, Eigen::Affine> transform;
00571                 // Rotation in Eular angles follow roll-pitch-yaw (1-2-3) rule
00572                 transform = Eigen::AngleAxis<T>(x(0), Eigen::Matrix<T,1,3>::UnitX())
00573                                 * Eigen::AngleAxis<T>(x(1), Eigen::Matrix<T,1,3>::UnitY())
00574                                 * Eigen::AngleAxis<T>(x(2), Eigen::Matrix<T,1,3>::UnitZ());
00575                 // Reverse roll-pitch-yaw conversion, very useful piece of knowledge, keep it with you all time!
00576                 /*const T pitch = -asin(transform(2,0));
00577                 const T roll = atan2(transform(2,1), transform(2,2));
00578                 const T yaw = atan2(transform(1,0) / cos(pitch), transform(0,0) / cos(pitch));
00579                 std::cerr << "d angles" << x(0) - roll << ", " << x(1) - pitch << "," << x(2) - yaw << std::endl;*/
00580                 transform.translation() = x.segment(3, 3);
00581                 mOut = transform.matrix();
00582         }
00583         else
00584         {
00585                 Eigen::Transform<T, 2, Eigen::Affine> transform;
00586                 transform = Eigen::Rotation2D<T> (x(0));
00587                 transform.translation() = x.segment(1, 2);
00588 
00589                 if(force2D)
00590                 {
00591                         mOut = Matrix::Identity(dim, dim);
00592                         mOut.corner(TopLeft, 2, 2) = transform.matrix().corner(TopLeft, 2, 2);
00593                         mOut.corner(TopRight, 2, 1) = transform.matrix().corner(TopRight, 2, 1);
00594                 }
00595                 else
00596                 {
00597                         mOut = transform.matrix();
00598                 }
00599         }
00600 
00601         this->covMatrix = this->estimateCovariance(filteredReading, filteredReference, matches, outlierWeights, mOut);
00602 
00603         return mOut; 
00604 }
00605 
00606 template<typename T>
00607 typename ErrorMinimizersImpl<T>::Matrix
00608 ErrorMinimizersImpl<T>::PointToPlaneWithCovErrorMinimizer::estimateCovariance(const DataPoints& reading, const DataPoints& reference, const Matches& matches, const OutlierWeights& outlierWeights, const TransformationParameters& transformation)
00609 {
00610         int max_nbr_point = outlierWeights.cols();
00611 
00612         Matrix covariance(Matrix::Zero(6,6));
00613         Matrix J_hessian(Matrix::Zero(6,6));
00614         Matrix d2J_dReadingdX(Matrix::Zero(6, max_nbr_point));
00615         Matrix d2J_dReferencedX(Matrix::Zero(6, max_nbr_point));
00616 
00617         Vector reading_point(Vector::Zero(3));
00618         Vector reference_point(Vector::Zero(3));
00619         Vector normal(3);
00620         Vector reading_direction(Vector::Zero(3));
00621         Vector reference_direction(Vector::Zero(3));
00622 
00623         Matrix normals = reference.getDescriptorViewByName("normals");
00624 
00625         if (normals.rows() < 3)    // Make sure there are normals in DataPoints
00626                 return Matrix::Zero(6,6);
00627 
00628         T beta = -asin(transformation(2,0));
00629         T alpha = atan2(transformation(2,1), transformation(2,2));
00630         T gamma = atan2(transformation(1,0)/cos(beta), transformation(0,0)/cos(beta));
00631         T t_x = transformation(0,3);
00632         T t_y = transformation(1,3);
00633         T t_z = transformation(2,3);
00634 
00635         Vector tmp_vector_6(Vector::Zero(6));
00636 
00637         int valid_points_count = 0;
00638 
00639         for(int i = 0; i < max_nbr_point; ++i)
00640         {
00641                 if (outlierWeights(0,i) > 0.0)
00642                 {
00643                         reading_point = reading.features.block(0,i,3,1);
00644                         int reference_idx = matches.ids(0,i);
00645                         reference_point = reference.features.block(0,reference_idx,3,1);
00646 
00647                         normal = normals.block(0,reference_idx,3,1);
00648 
00649                         T reading_range = reading_point.norm();
00650                         reading_direction = reading_point / reading_range;
00651                         T reference_range = reference_point.norm();
00652                         reference_direction = reference_point / reference_range;
00653 
00654                         T n_alpha = normal(2)*reading_direction(1) - normal(1)*reading_direction(2);
00655                         T n_beta = normal(0)*reading_direction(2) - normal(2)*reading_direction(0);
00656                         T n_gamma = normal(1)*reading_direction(0) - normal(0)*reading_direction(1);
00657 
00658                         T E = normal(0)*(reading_point(0) - gamma*reading_point(1) + beta*reading_point(2) + t_x - reference_point(0));
00659                         E +=  normal(1)*(gamma*reading_point(0) + reading_point(1) - alpha*reading_point(2) + t_y - reference_point(1));
00660                         E +=  normal(2)*(-beta*reading_point(0) + alpha*reading_point(1) + reading_point(2) + t_z - reference_point(2));
00661 
00662                         T N_reading = normal(0)*(reading_direction(0) - gamma*reading_direction(1) + beta*reading_direction(2));
00663                         N_reading +=  normal(1)*(gamma*reading_direction(0) + reading_direction(1) - alpha*reading_direction(2));
00664                         N_reading +=  normal(2)*(-beta*reading_direction(0) + alpha*reading_direction(1) + reading_direction(2));
00665 
00666                         T N_reference = -(normal(0)*reference_direction(0) + normal(1)*reference_direction(1) + normal(2)*reference_direction(2));
00667 
00668                         // update the hessian and d2J/dzdx
00669                         tmp_vector_6 << normal(0), normal(1), normal(2), reading_range * n_alpha, reading_range * n_beta, reading_range * n_gamma;
00670 
00671                         J_hessian += tmp_vector_6 * tmp_vector_6.transpose();
00672 
00673                         tmp_vector_6 << normal(0) * N_reading, normal(1) * N_reading, normal(2) * N_reading, n_alpha * (E + reading_range * N_reading), n_beta * (E + reading_range * N_reading), n_gamma * (E + reading_range * N_reading);
00674 
00675                         d2J_dReadingdX.block(0,valid_points_count,6,1) = tmp_vector_6;
00676 
00677                         tmp_vector_6 << normal(0) * N_reference, normal(1) * N_reference, normal(2) * N_reference, reference_range * n_alpha * N_reference, reference_range * n_beta * N_reference, reference_range * n_gamma * N_reference;
00678 
00679                         d2J_dReferencedX.block(0,valid_points_count,6,1) = tmp_vector_6;
00680 
00681                         valid_points_count++;
00682                 } // if (outlierWeights(0,i) > 0.0)
00683         }
00684 
00685         Matrix d2J_dZdX(Matrix::Zero(6, 2 * valid_points_count));
00686         d2J_dZdX.block(0,0,6,valid_points_count) = d2J_dReadingdX.block(0,0,6,valid_points_count);
00687         d2J_dZdX.block(0,valid_points_count,6,valid_points_count) = d2J_dReferencedX.block(0,0,6,valid_points_count);
00688 
00689         Matrix inv_J_hessian = J_hessian.inverse();
00690 
00691         covariance = d2J_dZdX * d2J_dZdX.transpose();
00692         covariance = inv_J_hessian * covariance * inv_J_hessian;
00693 
00694         return (sensorStdDev * sensorStdDev) * covariance;
00695 }
00696 
00697 
00698 
00699 template<typename T>
00700 T ErrorMinimizersImpl<T>::PointToPlaneWithCovErrorMinimizer::getOverlap() const
00701 {
00702         const int nbPoints = this->lastErrorElements.reading.features.cols();
00703         const int dim = this->lastErrorElements.reading.features.rows();
00704         if(nbPoints == 0)
00705         {
00706                 throw std::runtime_error("Error, last error element empty. Error minimizer needs to be called at least once before using this method.");
00707         }
00708         
00709         if (!this->lastErrorElements.reading.descriptorExists("simpleSensorNoise") ||
00710                 !this->lastErrorElements.reading.descriptorExists("normals"))
00711         {
00712                 LOG_INFO_STREAM("PointToPlaneErrorMinimizer - warning, no sensor noise or normals found. Using best estimate given outlier rejection instead.");
00713                 return this->weightedPointUsedRatio;
00714         }
00715 
00716         const BOOST_AUTO(noises, this->lastErrorElements.reading.getDescriptorViewByName("simpleSensorNoise"));
00717         const BOOST_AUTO(normals, this->lastErrorElements.reading.getDescriptorViewByName("normals"));
00718         int count = 0;
00719         for(int i=0; i < nbPoints; i++)
00720         {
00721                 if(this->lastErrorElements.matches.dists(0, i) != numeric_limits<T>::infinity())
00722                 {
00723                         const Vector d = this->lastErrorElements.reading.features.col(i) - this->lastErrorElements.reference.features.col(i);
00724                         const Vector n = normals.col(i);
00725                         const T projectionDist = d.head(dim-1).dot(n.normalized());
00726                         if(anyabs(projectionDist) < noises(0,i))
00727                                 count++;
00728                 }
00729         }
00730 
00731         return (T)count/(T)nbPoints;
00732 }
00733 
00734 template<typename T>
00735 typename ErrorMinimizersImpl<T>::Matrix ErrorMinimizersImpl<T>::PointToPlaneWithCovErrorMinimizer::getCovariance() const
00736 {
00737   return covMatrix;
00738 }
00739 
00740 template struct ErrorMinimizersImpl<float>::PointToPlaneWithCovErrorMinimizer;
00741 template struct ErrorMinimizersImpl<double>::PointToPlaneWithCovErrorMinimizer;
00742 


upstream_src
Author(s):
autogenerated on Wed Sep 24 2014 10:41:58