TEX_NAMESPACE_BEGIN /** * Dampens the quality of all views in which the face's projection * has a much different color than in the majority of views. * Returns whether the outlier removal was successfull. * * @param infos contains information about one face seen from several views * @param settings runtime configuration. */ bool photometric_outlier_detection(std::vector<ProjectedFaceInfo> * infos, Settings const & settings) { if (infos->size() == 0) return true; /* Configuration variables. */ double const gauss_rejection_threshold = 6e-3; /* If all covariances drop below this we stop outlier detection. */ double const minimal_covariance = 5e-4; int const outlier_detection_iterations = 10; int const minimal_num_inliers = 4; float outlier_removal_factor = std::numeric_limits<float>::signaling_NaN(); switch (settings.outlier_removal) { case NONE: return true; case GAUSS_CLAMPING: outlier_removal_factor = 1.0f; break; case GAUSS_DAMPING: outlier_removal_factor = 0.2f; break; } Eigen::MatrixX3d inliers(infos->size(), 3); std::vector<std::uint32_t> is_inlier(infos->size(), 1); for (std::size_t row = 0; row < infos->size(); ++row) { inliers.row(row) = mve_to_eigen(infos->at(row).mean_color).cast<double>(); } Eigen::RowVector3d var_mean; Eigen::Matrix3d covariance; Eigen::Matrix3d covariance_inv; for (int i = 0; i < outlier_detection_iterations; ++i) { if (inliers.rows() < minimal_num_inliers) { return false; } /* Calculate the inliers' mean color and color covariance. */ var_mean = inliers.colwise().mean(); Eigen::MatrixX3d centered = inliers.rowwise() - var_mean; covariance = (centered.adjoint() * centered) / double(inliers.rows() - 1); /* If all covariances are very small we stop outlier detection * and only keep the inliers (set quality of outliers to zero). */ if (covariance.array().abs().maxCoeff() < minimal_covariance) { for (std::size_t row = 0; row < infos->size(); ++row) { if (!is_inlier[row]) infos->at(row).quality = 0.0f; } return true; } /* Invert the covariance. FullPivLU is not the fastest way but * it gives feedback about numerical stability during inversion. */ Eigen::FullPivLU<Eigen::Matrix3d> lu(covariance); if (!lu.isInvertible()) { return false; } covariance_inv = lu.inverse(); /* Compute new number of inliers (all views with a gauss value above a threshold). */ for (std::size_t row = 0; row < infos->size(); ++row) { Eigen::RowVector3d color = mve_to_eigen(infos->at(row).mean_color).cast<double>(); double gauss_value = multi_gauss_unnormalized(color, var_mean, covariance_inv); is_inlier[row] = (gauss_value >= gauss_rejection_threshold ? 1 : 0); } /* Resize Eigen matrix accordingly and fill with new inliers. */ inliers.resize(std::accumulate(is_inlier.begin(), is_inlier.end(), 0), Eigen::NoChange); for (std::size_t row = 0, inlier_row = 0; row < infos->size(); ++row) { if (is_inlier[row]) { inliers.row(inlier_row++) = mve_to_eigen(infos->at(row).mean_color).cast<double>(); } } } covariance_inv *= outlier_removal_factor; for (ProjectedFaceInfo & info : *infos) { Eigen::RowVector3d color = mve_to_eigen(info.mean_color).cast<double>(); double gauss_value = multi_gauss_unnormalized(color, var_mean, covariance_inv); assert(0.0 <= gauss_value && gauss_value <= 1.0); switch(settings.outlier_removal) { case NONE: return true; case GAUSS_DAMPING: info.quality *= gauss_value; break; case GAUSS_CLAMPING: if (gauss_value < gauss_rejection_threshold) info.quality = 0.0f; break; } } return true; }