template <typename PointT> bool pcl::MaximumLikelihoodSampleConsensus<PointT>::computeModel (int debug_verbosity_level) { // Warn and exit if no threshold was set if (threshold_ == std::numeric_limits<double>::max()) { PCL_ERROR ("[pcl::MaximumLikelihoodSampleConsensus::computeModel] No threshold set!\n"); return (false); } iterations_ = 0; double d_best_penalty = std::numeric_limits<double>::max(); double k = 1.0; std::vector<int> best_model; std::vector<int> selection; Eigen::VectorXf model_coefficients; std::vector<double> distances; // Compute sigma - remember to set threshold_ correctly ! sigma_ = computeMedianAbsoluteDeviation (sac_model_->getInputCloud (), sac_model_->getIndices (), threshold_); if (debug_verbosity_level > 1) PCL_DEBUG ("[pcl::MaximumLikelihoodSampleConsensus::computeModel] Estimated sigma value: %f.\n", sigma_); // Compute the bounding box diagonal: V = sqrt (sum (max(pointCloud) - min(pointCloud)^2)) Eigen::Vector4f min_pt, max_pt; getMinMax (sac_model_->getInputCloud (), sac_model_->getIndices (), min_pt, max_pt); max_pt -= min_pt; double v = sqrt (max_pt.dot (max_pt)); int n_inliers_count = 0; size_t indices_size; unsigned skipped_count = 0; // supress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters! const unsigned max_skip = max_iterations_ * 10; // Iterate while (iterations_ < k && skipped_count < max_skip) { // Get X samples which satisfy the model criteria sac_model_->getSamples (iterations_, selection); if (selection.empty ()) break; // Search for inliers in the point cloud for the current plane model M if (!sac_model_->computeModelCoefficients (selection, model_coefficients)) { //iterations_++; ++ skipped_count; continue; } // Iterate through the 3d points and calculate the distances from them to the model sac_model_->getDistancesToModel (model_coefficients, distances); // Use Expectiation-Maximization to find out the right value for d_cur_penalty // ---[ Initial estimate for the gamma mixing parameter = 1/2 double gamma = 0.5; double p_outlier_prob = 0; indices_size = sac_model_->getIndices ()->size (); std::vector<double> p_inlier_prob (indices_size); for (int j = 0; j < iterations_EM_; ++j) { // Likelihood of a datum given that it is an inlier for (size_t i = 0; i < indices_size; ++i) p_inlier_prob[i] = gamma * exp (- (distances[i] * distances[i] ) / 2 * (sigma_ * sigma_) ) / (sqrt (2 * M_PI) * sigma_); // Likelihood of a datum given that it is an outlier p_outlier_prob = (1 - gamma) / v; gamma = 0; for (size_t i = 0; i < indices_size; ++i) gamma += p_inlier_prob [i] / (p_inlier_prob[i] + p_outlier_prob); gamma /= static_cast<double>(sac_model_->getIndices ()->size ()); } // Find the log likelihood of the model -L = -sum [log (pInlierProb + pOutlierProb)] double d_cur_penalty = 0; for (size_t i = 0; i < indices_size; ++i) d_cur_penalty += log (p_inlier_prob[i] + p_outlier_prob); d_cur_penalty = - d_cur_penalty; // Better match ? if (d_cur_penalty < d_best_penalty) { d_best_penalty = d_cur_penalty; // Save the current model/coefficients selection as being the best so far model_ = selection; model_coefficients_ = model_coefficients; n_inliers_count = 0; // Need to compute the number of inliers for this model to adapt k for (size_t i = 0; i < distances.size (); ++i) if (distances[i] <= 2 * sigma_) n_inliers_count++; // Compute the k parameter (k=log(z)/log(1-w^n)) double w = static_cast<double> (n_inliers_count) / static_cast<double> (sac_model_->getIndices ()->size ()); double p_no_outliers = 1 - pow (w, static_cast<double> (selection.size ())); p_no_outliers = (std::max) (std::numeric_limits<double>::epsilon (), p_no_outliers); // Avoid division by -Inf p_no_outliers = (std::min) (1 - std::numeric_limits<double>::epsilon (), p_no_outliers); // Avoid division by 0. k = log (1 - probability_) / log (p_no_outliers); } ++iterations_; if (debug_verbosity_level > 1) PCL_DEBUG ("[pcl::MaximumLikelihoodSampleConsensus::computeModel] Trial %d out of %d. Best penalty is %f.\n", iterations_, static_cast<int> (ceil (k)), d_best_penalty); if (iterations_ > max_iterations_) { if (debug_verbosity_level > 0) PCL_DEBUG ("[pcl::MaximumLikelihoodSampleConsensus::computeModel] MLESAC reached the maximum number of trials.\n"); break; } } if (model_.empty ()) { if (debug_verbosity_level > 0) PCL_DEBUG ("[pcl::MaximumLikelihoodSampleConsensus::computeModel] Unable to find a solution!\n"); return (false); } // Iterate through the 3d points and calculate the distances from them to the model again sac_model_->getDistancesToModel (model_coefficients_, distances); std::vector<int> &indices = *sac_model_->getIndices (); if (distances.size () != indices.size ()) { PCL_ERROR ("[pcl::MaximumLikelihoodSampleConsensus::computeModel] Estimated distances (%zu) differs than the normal of indices (%zu).\n", distances.size (), indices.size ()); return (false); } inliers_.resize (distances.size ()); // Get the inliers for the best model found n_inliers_count = 0; for (size_t i = 0; i < distances.size (); ++i) if (distances[i] <= 2 * sigma_) inliers_[n_inliers_count++] = indices[i]; // Resize the inliers vector inliers_.resize (n_inliers_count); if (debug_verbosity_level > 0) PCL_DEBUG ("[pcl::MaximumLikelihoodSampleConsensus::computeModel] Model: %zu size, %d inliers.\n", model_.size (), n_inliers_count); return (true); }
/** \brief Compute the actual model and find the inliers * \param debug enable/disable on-screen debug information */ bool MLESAC::computeModel (int debug) { iterations_ = 0; double d_best_penalty = DBL_MAX; double k = 1.0; std::vector<int> best_model; std::vector<int> best_inliers, inliers; std::vector<int> selection; // Compute sigma - remember to set threshold_ correctly ! sigma_ = cloud_geometry::computeMedianAbsoluteDeviation (*sac_model_->getCloud (), *sac_model_->getIndices (), threshold_); if (debug) std::cerr << "[MLESAC::computeModel] Estimated sigma value : " << sigma_ << "." << std::endl; // Compute the bounding box diagonal: V = sqrt (sum (max(pointCloud) - min(pointCloud)^2)) std_msgs::Point32 min_pt, max_pt; cloud_geometry::getMinMax (*sac_model_->getCloud (), *sac_model_->getIndices (), min_pt, max_pt); double v = sqrt ( (max_pt.x - min_pt.x) * (max_pt.x - min_pt.x) + (max_pt.y - min_pt.y) * (max_pt.y - min_pt.y) + (max_pt.z - min_pt.z) * (max_pt.z - min_pt.z) ); // Iterate while (iterations_ < k) { // Get X samples which satisfy the model criteria selection = sac_model_->getSamples (iterations_); if (selection.size () == 0) break; // Search for inliers in the point cloud for the current plane model M sac_model_->computeModelCoefficients (selection); // Iterate through the 3d points and calculate the distances from them to the model std::vector<double> distances = sac_model_->getDistancesToModel (sac_model_->getModelCoefficients ()); // Use Expectiation-Maximization to find out the right value for d_cur_penalty // ---[ Initial estimate for the gamma mixing parameter = 1/2 double gamma = 0.5; double p_outlier_prob = 0; std::vector<double> p_inlier_prob (sac_model_->getIndices ()->size ()); for (int j = 0; j < iterations_EM_; j++) { // Likelihood of a datum given that it is an inlier for (unsigned int i = 0; i < sac_model_->getIndices ()->size (); i++) p_inlier_prob[i] = gamma * exp (- (distances.at (i) * distances.at (i) ) / 2 * (sigma_ * sigma_) ) / (sqrt (2 * M_PI) * sigma_); // Likelihood of a datum given that it is an outlier p_outlier_prob = (1 - gamma) / v; gamma = 0; for (unsigned int i = 0; i < sac_model_->getIndices ()->size (); i++) gamma += p_inlier_prob [i] / (p_inlier_prob[i] + p_outlier_prob); gamma /= sac_model_->getIndices ()->size (); } // Find the log likelihood of the model -L = -sum [log (pInlierProb + pOutlierProb)] double d_cur_penalty = 0; for (unsigned int i = 0; i < sac_model_->getIndices ()->size (); i++) d_cur_penalty += log (p_inlier_prob[i] + p_outlier_prob); d_cur_penalty = - d_cur_penalty; // Better match ? if (d_cur_penalty < d_best_penalty) { d_best_penalty = d_cur_penalty; best_model = selection; // Save the inliers best_inliers.resize (sac_model_->getIndices ()->size ()); int n_inliers_count = 0; for (unsigned int i = 0; i < sac_model_->getIndices ()->size (); i++) { if (distances[i] <= 2 * sigma_) { best_inliers[n_inliers_count] = sac_model_->getIndices ()->at (i); n_inliers_count++; } } best_inliers.resize (n_inliers_count); // Compute the k parameter (k=log(z)/log(1-w^n)) double w = (double)((double)n_inliers_count / (double)sac_model_->getIndices ()->size ()); double p_no_outliers = 1 - pow (w, (double)selection.size ()); p_no_outliers = std::max (std::numeric_limits<double>::epsilon (), p_no_outliers); // Avoid division by -Inf p_no_outliers = std::min (1 - std::numeric_limits<double>::epsilon (), p_no_outliers); // Avoid division by 0 k = log (1 - probability_) / log (p_no_outliers); } iterations_ += 1; if (debug > 1) std::cerr << "[MLESAC::computeModel] Trial " << iterations_ << " out of " << ceil (k) << ": best number of inliers so far is " << best_inliers.size () << "." << std::endl; if (iterations_ > max_iterations_) { if (debug > 0) std::cerr << "[MLESAC::computeModel] MSAC reached the maximum number of trials." << std::endl; break; } } if (best_model.size () != 0) { if (debug > 0) std::cerr << "[MLESAC::computeModel] Model found: " << best_inliers.size () << " inliers." << std::endl; sac_model_->setBestModel (best_model); sac_model_->setBestInliers (best_inliers); return (true); } else if (debug > 0) std::cerr << "[MLESAC::computeModel] Unable to find a solution!" << std::endl; return (false); }