add postprocess function to the API (code related changes)

This commit is contained in:
iyaz 2013-08-08 02:33:20 +03:00
parent 36d50d9477
commit dd5bf0ec3e
3 changed files with 186 additions and 125 deletions

View File

@ -197,8 +197,7 @@ public:
if(sdf_value) {
sdf_values[facet_begin] = *sdf_value;
} else {
sdf_values[facet_begin] =
0.0; // no intersection is found, for now we are returning 0 not a special indicator
sdf_values[facet_begin] = -1.0;
}
}
}

View File

@ -25,6 +25,139 @@ namespace CGAL
/// @cond CGAL_DOCUMENT_INTERNAL
namespace internal
{
// Postprocess functions for sdf values
template<class Polyhedron>
class Postprocess_sdf_values
{
typedef typename Polyhedron::Facet Facet;
typedef typename Polyhedron::Facet_const_handle Facet_const_handle;
typedef typename Polyhedron::Facet_const_iterator Facet_const_iterator;
typedef Bilateral_filtering<Polyhedron> Default_filter;
public:
template <class Filter, class SDFPropertyMap>
std::pair<double, double>
postprocess(const Polyhedron& mesh, SDFPropertyMap sdf_pmap) {
check_missing_sdf_values(mesh, sdf_pmap);
Filter()(mesh, get_window_size(mesh), sdf_pmap);
return linear_normalize_sdf_values(mesh, sdf_pmap);
}
template <class SDFPropertyMap>
std::pair<double, double>
postprocess(const Polyhedron& mesh, SDFPropertyMap sdf_pmap) {
return postprocess<Default_filter>(mesh, sdf_pmap);
}
/**
* Finds facets which have missing (indicated by -1) sdf values.
* Sdf values on these facets are assigned to average sdf value of its neighbors.
* If still there is any facet which has no sdf value, assigns minimum sdf value to it.
* This is meaningful since (being an outlier) zero sdf values might effect normalization & log extremely.
* @param[in, out] sdf_values `ReadWritePropertyMap` with `Polyhedron::Facet_const_handle` as key and `double` as value type
*/
template<class SDFPropertyMap>
void check_missing_sdf_values(const Polyhedron& mesh,
SDFPropertyMap sdf_values) {
std::vector<Facet_const_handle> still_missing_facets;
double min_sdf = (std::numeric_limits<double>::max)();
// If there is any facet which has no sdf value, assign average sdf value of its neighbors
for(Facet_const_iterator facet_it = mesh.facets_begin();
facet_it != mesh.facets_end(); ++facet_it) {
double sdf_value = sdf_values[facet_it];
CGAL_assertion(sdf_value == -1 || sdf_value >= 0); // validity check
if(sdf_value != -1.0) {
min_sdf = (std::min)(sdf_value, min_sdf);
continue;
}
typename Facet::Halfedge_around_facet_const_circulator facet_circulator =
facet_it->facet_begin();
double total_neighbor_sdf = 0.0;
int nb_valid_neighbors = 0;
do {
if(!facet_circulator->opposite()->is_border()) {
double neighbor_sdf = sdf_values[facet_circulator->opposite()->facet()];
if(neighbor_sdf != -1) {
total_neighbor_sdf += neighbor_sdf;
++nb_valid_neighbors;
}
}
} while( ++facet_circulator != facet_it->facet_begin());
if(nb_valid_neighbors == 0) {
still_missing_facets.push_back(facet_it);
} else {
sdf_value = total_neighbor_sdf / nb_valid_neighbors;
sdf_values[facet_it] = sdf_value;
// trying to update min_sdf is pointless, since it is interpolated one of the neighbors sdf will be smaller than it
}
}
// If still there is any facet which has no sdf value, assign minimum sdf value.
// This is meaningful since (being an outlier) 0 sdf values might effect normalization & log extremely.
for(typename std::vector<Facet_const_handle>::iterator it =
still_missing_facets.begin();
it != still_missing_facets.end(); ++it) {
sdf_values[*it] = min_sdf;
}
}
template<class SDFPropertyMap>
std::pair<double, double> min_max_value(const Polyhedron& mesh,
SDFPropertyMap sdf_values) {
double max_sdf = -(std::numeric_limits<double>::max)();
double min_sdf = (std::numeric_limits<double>::max)();
for(Facet_const_iterator facet_it = mesh.facets_begin();
facet_it != mesh.facets_end(); ++facet_it) {
double sdf_value = sdf_values[facet_it];
max_sdf = (std::max)(sdf_value, max_sdf);
min_sdf = (std::min)(sdf_value, min_sdf);
}
return std::make_pair(min_sdf, max_sdf);
}
/**
* Normalize sdf values between [0-1].
* @param sdf_values `ReadWritePropertyMap` with `Polyhedron::Facet_const_handle` as key and `double` as value type
* @return minimum and maximum SDF values before normalization
*/
template<class SDFPropertyMap>
std::pair<double, double> linear_normalize_sdf_values(const Polyhedron& mesh,
SDFPropertyMap sdf_values) {
double min_sdf, max_sdf;
boost::tie(min_sdf, max_sdf) = min_max_value(mesh, sdf_values);
if(min_sdf == max_sdf) {
CGAL_warning(min_sdf == max_sdf && !"Linear normalization is not applicable!");
return std::make_pair(min_sdf, max_sdf);
}
const double max_min_dif = max_sdf - min_sdf;
for(Facet_const_iterator facet_it = mesh.facets_begin();
facet_it != mesh.facets_end(); ++facet_it) {
sdf_values[facet_it] = (sdf_values[facet_it] - min_sdf) / max_min_dif;
}
return std::make_pair(min_sdf, max_sdf);
}
/**
* Simple window-size determination function for smoothing.
* It is proportional to square root of size of facets in polyhedron.
* @return size of the window
* - 0-2000 -> 1
* - 2000-8000 -> 2
* - 8000-18000 -> 3
* - ...
*/
int get_window_size(const Polyhedron& mesh) {
double facet_sqrt = std::sqrt(mesh.size_of_facets() / 2000.0);
return static_cast<int>(facet_sqrt) + 1;
}
};
/**
* @brief Main entry point for mesh segmentation algorithm.
*
@ -80,7 +213,6 @@ private:
private:
const Polyhedron& mesh;
GeomTraits traits;
int m_window_size;
// member functions
public:
/**
@ -88,7 +220,7 @@ public:
* @param mesh `CGAL Polyhedron` on which other functions operate.
*/
Surface_mesh_segmentation(const Polyhedron& mesh, GeomTraits traits)
: mesh(mesh), traits(traits), m_window_size( get_default_window_size() ) {
: mesh(mesh), traits(traits) {
CGAL_precondition(mesh.is_pure_triangle());
}
@ -96,19 +228,16 @@ public:
template <class SDFPropertyMap>
std::pair<double, double>
calculate_sdf_values(double cone_angle, int number_of_rays,
SDFPropertyMap sdf_pmap) {
SDFPropertyMap sdf_pmap, bool postprocess_req) {
// calculate sdf values
SDF_calculation_class sdf_calculator(mesh, false, true, traits);
sdf_calculator.calculate_sdf_values(mesh.facets_begin(), mesh.facets_end(),
cone_angle, number_of_rays, sdf_pmap);
// apply post-processing steps
check_zero_sdf_values(sdf_pmap);
Filter()(mesh, get_window_size(), sdf_pmap);
std::pair<double, double> min_max_sdf_values = linear_normalize_sdf_values(
sdf_pmap);
// return minimum and maximum sdf values before normalization
return min_max_sdf_values;
Postprocess_sdf_values<Polyhedron> p;
return postprocess_req ? p.template postprocess<Filter>(mesh,
sdf_pmap) : // return minimum and maximum sdf values before normalization
p.min_max_value(mesh, sdf_pmap);
}
template <class FacetSegmentMap, class SDFPropertyMap>
@ -157,21 +286,6 @@ public:
return number_of_centers;
}
/**
* Return the current window size. It is used when calling Filter::operator().
* By default it is the value returned by `get_default_window_size()`.
*/
int get_window_size() const {
return m_window_size;
}
/**
* Set the window size used to call `Filter::operator()` to `s`
*/
void set_window_size(int s) {
m_window_size=s;
}
private:
/**
@ -220,93 +334,6 @@ private:
}
}
/**
* Normalize sdf values between [0-1].
* @param sdf_values `ReadWritePropertyMap` with `Polyhedron::Facet_const_handle` as key and `double` as value type
* @return minimum and maximum SDF values before normalization
*/
template<class SDFPropertyMap>
std::pair<double, double> linear_normalize_sdf_values(SDFPropertyMap
sdf_values) {
double max_sdf = -(std::numeric_limits<double>::max)();
double min_sdf = (std::numeric_limits<double>::max)();
for(Facet_const_iterator facet_it = mesh.facets_begin();
facet_it != mesh.facets_end(); ++facet_it) {
double sdf_value = sdf_values[facet_it];
max_sdf = (std::max)(sdf_value, max_sdf);
min_sdf = (std::min)(sdf_value, min_sdf);
}
const double max_min_dif = max_sdf - min_sdf;
for(Facet_const_iterator facet_it = mesh.facets_begin();
facet_it != mesh.facets_end(); ++facet_it) {
sdf_values[facet_it] = (sdf_values[facet_it] - min_sdf) / max_min_dif;
}
return std::make_pair(min_sdf, max_sdf);
}
/**
* Simple window-size determination function for smoothing.
* It is proportional to square root of size of facets in polyhedron.
* @return size of the window
* - 0-2000 -> 1
* - 2000-8000 -> 2
* - 8000-18000 -> 3
* - ...
*/
int get_default_window_size() {
double facet_sqrt = std::sqrt(mesh.size_of_facets() / 2000.0);
return static_cast<int>(facet_sqrt) + 1;
}
/**
* Finds facets which have zero sdf values.
* Sdf values on these facets are assigned to average sdf value of its neighbors.
* If still there is any facet which has no sdf value, assigns minimum sdf value to it.
* This is meaningful since (being an outlier) zero sdf values might effect normalization & log extremely.
* @param[in, out] sdf_values `ReadWritePropertyMap` with `Polyhedron::Facet_const_handle` as key and `double` as value type
*/
template<class SDFPropertyMap>
void check_zero_sdf_values(SDFPropertyMap sdf_values) {
std::vector<Facet_const_handle> still_zero_facets;
double min_sdf = (std::numeric_limits<double>::max)();
// If there is any facet which has no sdf value, assign average sdf value of its neighbors
for(Facet_const_iterator facet_it = mesh.facets_begin();
facet_it != mesh.facets_end(); ++facet_it) {
double sdf_value = sdf_values[facet_it];
if(sdf_value == 0.0) {
typename Facet::Halfedge_around_facet_const_circulator facet_circulator =
facet_it->facet_begin();
double total_neighbor_sdf = 0.0;
do {
if(!facet_circulator->opposite()->is_border()) {
total_neighbor_sdf += sdf_values[facet_circulator->opposite()->facet()];
}
} while( ++facet_circulator != facet_it->facet_begin());
sdf_value = total_neighbor_sdf / 3.0;
sdf_values[facet_it] = sdf_value;
if(sdf_value == 0.0) {
still_zero_facets.push_back(
facet_it); // if sdf_value is still zero, put facet to zero-list
} else {
min_sdf = (std::min)(sdf_value,
min_sdf); // find min_sdf other than zero
}
} else {
min_sdf = (std::min)(sdf_value, min_sdf); // find min_sdf other than zero
}
}
// If still there is any facet which has no sdf value, assign minimum sdf value.
// This is meaningful since (being an outlier) 0 sdf values might effect normalization & log extremely.
for(typename std::vector<Facet_const_handle>::iterator it =
still_zero_facets.begin();
it != still_zero_facets.end(); ++it) {
sdf_values[*it] = min_sdf;
}
}
/**
* Receives probability-matrix with probabilities betwen [0-1], and returns log-normalized probabilities
* which are suitable to use in graph-cut.

View File

@ -17,12 +17,8 @@ namespace CGAL
* @brief Function computing the Shape Diameter Function over a surface mesh.
*
* This function implements the Shape Diameter Function (SDF) as described in \cite shapira2008consistent.
* After the computation of SDF values for each facet, the following post-processing steps are applied:
* - Facets with no SDF values (i.e. zero) are assigned the average SDF value of their one-ring edge-adjacent neighbors.
* If there is still a facet having a zero SDF value, the minimum SDF value greater than zero is assigned to it. Note that this step is not inherited from the paper.
* The main reason for avoiding zero SDF values is that it can obstruct log-normalization process which takes place at the beginning of segment_from_sdf_values()`.
* - SDF values are smoothed with bilateral filtering.
* - SDF values are linearly normalized between [0,1].
* It is possible to compute raw SDF values (see \ref Surface_mesh_segmentationRawSDF) and apply post-processing steps (see \ref Surface_mesh_segmentationPostprocessing).
* For raw SDF values, -1.0 is used as an indicator for no SDF value.
*
* @pre @a polyhedron.is_pure_triangle()
* @tparam Fast_sdf_calculation_mode regardless of `GeomTraits`, use inexact predicates while traversing AABB tree nodes.
@ -34,8 +30,9 @@ namespace CGAL
* @param[out] sdf_values the SDF value of each facet
* @param cone_angle opening angle for cone, expressed in radians
* @param number_of_rays number of rays picked from cone for each facet. In general, increasing the number of rays beyond the default value has little influence upon the resulting segmentation.
* @param postprocess if true apply post-processing steps in `CGAL::postprocess_sdf_values`, otherwise return raw SDF values.
* @param traits traits object
* @return minimum and maximum SDF values before linear normalization
* @return minimum and maximum SDF values if @a postprocess is true, otherwise minimum and maximum SDF values before linear normalization
*/
template <bool Fast_sdf_calculation_mode, class Polyhedron,
class SDFPropertyMap, class GeomTraits
@ -48,11 +45,13 @@ compute_sdf_values(const Polyhedron& polyhedron,
SDFPropertyMap sdf_values,
double cone_angle = 2.0 / 3.0 * CGAL_PI,
int number_of_rays = 25,
bool postprocess = true,
GeomTraits traits = GeomTraits())
{
internal::Surface_mesh_segmentation<Polyhedron, GeomTraits, Fast_sdf_calculation_mode>
algorithm(polyhedron, traits);
return algorithm.calculate_sdf_values(cone_angle, number_of_rays, sdf_values);
return algorithm.calculate_sdf_values(cone_angle, number_of_rays, sdf_values,
postprocess);
}
/// @cond SKIP_IN_MANUAL
@ -66,13 +65,47 @@ compute_sdf_values(const Polyhedron& polyhedron,
SDFPropertyMap sdf_values,
double cone_angle = 2.0 / 3.0 * CGAL_PI,
int number_of_rays = 25,
bool postprocess = true,
GeomTraits traits = GeomTraits())
{
return compute_sdf_values<true, Polyhedron, SDFPropertyMap, GeomTraits>
(polyhedron, sdf_values, cone_angle, number_of_rays, traits);
(polyhedron, sdf_values, cone_angle, number_of_rays, postprocess, traits);
}
/// @endcond
/*!
* \ingroup PkgSurfaceSegmentation
* @brief Function post-processing raw SDF values computed per facet.
*
* Post-processing steps applied are:
* - Facets with -1.0 SDF values are assigned the average SDF value of their one-ring edge-adjacent neighbors.
* If there is still a facet having -1.0 SDF value, the minimum valid SDF value assigned to it. Note that this step is not inherited from the paper.
* The main reason for not assigning 0 to facets with no SDF values (i.e. -1.0) is that it can obstruct log-normalization process which takes place at the beginning of `CGAL::segment_from_sdf_values`.
* - SDF values are smoothed with bilateral filtering.
* - SDF values are linearly normalized between [0,1].
*
* See the section \ref Surface_mesh_segmentationPostprocessing for more details.
*
* @pre @a polyhedron.is_pure_triangle()
* @pre Raw values should be either equal to -1.0 or, greater or equal than 0.0
* @tparam Polyhedron a %CGAL polyhedron
* @tparam SDFPropertyMap a `ReadWritePropertyMap` with `Polyhedron::Facet_const_handle` as key and `double` as value type
*
* @param polyhedron surface mesh on which SDF values are computed
* @param[in, out] sdf_values the SDF value of each facet
*
* @return minimum and maximum SDF values before linear normalization
*/
template<class Polyhedron, class SDFPropertyMap>
std::pair<double, double>
postprocess_sdf_values(const Polyhedron& polyhedron, SDFPropertyMap sdf_values)
{
CGAL_precondition(polyhedron.is_pure_triangle());
return internal::Postprocess_sdf_values<Polyhedron>().postprocess(polyhedron,
sdf_values);
}
/*!
* \ingroup PkgSurfaceSegmentation
* @brief Function computing the segmentation of a surface mesh given an SDF value per facet.
@ -206,10 +239,11 @@ compute_sdf_values(const Polyhedron& polyhedron,
SDFPropertyMap sdf_values,
double cone_angle = 2.0 / 3.0 * CGAL_PI,
int number_of_rays = 25,
bool postprocess = true,
typename Polyhedron::Traits traits = typename Polyhedron::Traits())
{
return compute_sdf_values<Fast_sdf_calculation_mode, Polyhedron, SDFPropertyMap, typename Polyhedron::Traits>
(polyhedron, sdf_values, cone_angle, number_of_rays, traits);
(polyhedron, sdf_values, cone_angle, number_of_rays, postprocess, traits);
}
template < class Polyhedron, class SDFPropertyMap>
@ -218,10 +252,11 @@ compute_sdf_values(const Polyhedron& polyhedron,
SDFPropertyMap sdf_values,
double cone_angle = 2.0 / 3.0 * CGAL_PI,
int number_of_rays = 25,
bool postprocess = true,
typename Polyhedron::Traits traits = typename Polyhedron::Traits())
{
return compute_sdf_values<true, Polyhedron, SDFPropertyMap, typename Polyhedron::Traits>
(polyhedron, sdf_values, cone_angle, number_of_rays, traits);
(polyhedron, sdf_values, cone_angle, number_of_rays, postprocess, traits);
}
template <class Polyhedron, class SDFPropertyMap, class SegmentPropertyMap>