cgal/Classification/include/CGAL/Point_set_classifier.h

943 lines
35 KiB
C++

// Copyright (c) 2016 INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s) : Simon Giraudot
#ifndef CGAL_POINT_SET_CLASSIFIER_H
#define CGAL_POINT_SET_CLASSIFIER_H
#include <CGAL/property_map.h>
#include <CGAL/Classifier.h>
#include <CGAL/Classification/Point_set_neighborhood.h>
#include <CGAL/Classification/Planimetric_grid.h>
#include <CGAL/Classification/Local_eigen_analysis.h>
#include <CGAL/Classification/Attribute_base.h>
#include <CGAL/Classification/Attribute/Hsv.h>
#include <CGAL/Classification/Attribute/Distance_to_plane.h>
#include <CGAL/Classification/Attribute/Echo_scatter.h>
#include <CGAL/Classification/Attribute/Elevation.h>
#include <CGAL/Classification/Attribute/Vertical_dispersion.h>
#include <CGAL/Classification/Attribute/Verticality.h>
#include <CGAL/Classification/Attribute/Eigen.h>
#include <CGAL/Classification/Type.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <CGAL/Timer.h>
#include <CGAL/demangle.h>
namespace CGAL {
/*!
\ingroup PkgClassification
\brief Classifies a point set based on a set of attributes and a set
of classification types.
This class specializes `Classifier` to point sets. It takes care of
generating necessary data structures and automatically generate a
set of generic attributes. Attributes can be generated at multiple
scales to increase the reliability of the classification.
\tparam Kernel model of \cgal Kernel.
\tparam Range range of items, model of `ConstRange`. Its iterator type
is `RandomAccessIterator`.
\tparam PointMap model of `ReadablePropertyMap` whose key
type is the value type of the iterator of `Range` and value type
is `Point_3<Kernel>`.
\tparam DiagonalizeTraits model of `DiagonalizeTraits` used
for matrix diagonalization.
*/
template <typename Kernel,
typename Range,
typename PointMap,
typename DiagonalizeTraits = CGAL::Default_diagonalize_traits<double,3> >
class Point_set_classifier : public Classifier<Range, PointMap>
{
public:
typedef typename Kernel::Iso_cuboid_3 Iso_cuboid_3;
typedef Classifier<Range, PointMap> Base;
typedef typename Range::const_iterator Iterator;
using Base::m_input;
using Base::m_item_map;
typedef Classification::Planimetric_grid
<Kernel, Range, PointMap> Planimetric_grid;
typedef Classification::Point_set_neighborhood
<Kernel, Range, PointMap> Neighborhood;
typedef Classification::Local_eigen_analysis
<Kernel, Range, PointMap, DiagonalizeTraits> Local_eigen_analysis;
typedef Classification::Attribute_handle Attribute_handle;
typedef Classification::Type Type;
typedef Classification::Type_handle Type_handle;
/// \cond SKIP_IN_MANUAL
typedef typename Kernel::Point_3 Point;
typedef Classification::Attribute::Anisotropy
<Kernel, Range, PointMap, DiagonalizeTraits> Anisotropy;
typedef Classification::Attribute::Distance_to_plane
<Kernel, Range, PointMap, DiagonalizeTraits> Distance_to_plane;
typedef Classification::Attribute::Eigentropy
<Kernel, Range, PointMap, DiagonalizeTraits> Eigentropy;
typedef Classification::Attribute::Elevation
<Kernel, Range, PointMap> Elevation;
typedef Classification::Attribute::Linearity
<Kernel, Range, PointMap, DiagonalizeTraits> Linearity;
typedef Classification::Attribute::Omnivariance
<Kernel, Range, PointMap, DiagonalizeTraits> Omnivariance;
typedef Classification::Attribute::Planarity
<Kernel, Range, PointMap, DiagonalizeTraits> Planarity;
typedef Classification::Attribute::Sphericity
<Kernel, Range, PointMap, DiagonalizeTraits> Sphericity;
typedef Classification::Attribute::Sum_eigenvalues
<Kernel, Range, PointMap, DiagonalizeTraits> Sum_eigen;
typedef Classification::Attribute::Surface_variation
<Kernel, Range, PointMap, DiagonalizeTraits> Surface_variation;
typedef Classification::Attribute::Vertical_dispersion
<Kernel, Range, PointMap> Dispersion;
typedef Classification::Attribute::Verticality
<Kernel, Range, PointMap, DiagonalizeTraits> Verticality;
/// \endcond
typedef typename Classification::RGB_Color RGB_Color;
private:
struct Scale
{
Neighborhood* neighborhood;
Planimetric_grid* grid;
Local_eigen_analysis* eigen;
double voxel_size;
std::vector<Attribute_handle> attributes;
Scale (const Range& input, PointMap point_map,
const Iso_cuboid_3& bbox, double voxel_size)
: voxel_size (voxel_size)
{
CGAL::Timer t;
t.start();
if (voxel_size < 0.)
neighborhood = new Neighborhood (input, point_map);
else
neighborhood = new Neighborhood (input, point_map, voxel_size);
t.stop();
if (voxel_size < 0.)
std::cerr << "Neighborhood computed in " << t.time() << " second(s)" << std::endl;
else
std::cerr << "Neighborhood with voxel size " << voxel_size
<< " computed in " << t.time() << " second(s)" << std::endl;
t.reset();
t.start();
eigen = new Local_eigen_analysis (input, point_map, neighborhood->k_neighbor_query(6));
double range = eigen->mean_range();
if (this->voxel_size < 0)
this->voxel_size = range / 3;
t.stop();
std::cerr << "Eigen values computed in " << t.time() << " second(s)" << std::endl;
std::cerr << "Range = " << range << std::endl;
t.reset();
t.start();
grid = new Planimetric_grid (input, point_map, bbox, this->voxel_size);
t.stop();
std::cerr << "Planimetric grid computed in " << t.time() << " second(s)" << std::endl;
t.reset();
}
~Scale()
{
delete neighborhood;
delete grid;
delete eigen;
}
double grid_resolution() const { return voxel_size; }
double radius_neighbors() const { return voxel_size * 5; }
double radius_dtm() const { return voxel_size * 100; }
};
Iso_cuboid_3 m_bbox;
std::vector<Scale*> m_scales;
public:
/// \name Constructor
/// @{
/*!
\brief Initializes a classification object.
\param input input range.
\param item_map property map to access the input points.
*/
Point_set_classifier(const Range& input, PointMap point_map) : Base (input, point_map)
{
m_bbox = CGAL::bounding_box
(boost::make_transform_iterator (m_input.begin(), CGAL::Property_map_to_unary_function<PointMap>(m_item_map)),
boost::make_transform_iterator (m_input.end(), CGAL::Property_map_to_unary_function<PointMap>(m_item_map)));
}
/// @}
/// \cond SKIP_IN_MANUAL
virtual ~Point_set_classifier()
{
clear();
}
/// \endcond
/// \name Attributes
/// @{
/*!
\brief Generate all possible attributes from an input range.
The smallest scale is automatically estimated and the data
structures needed (`Neighborhood`, `Planimetric_grid` and
`Local_eigen_analysis`) are computed at `nb_scales` recursively
larger scales. At each scale, the following attributes are
generated:
- `CGAL::Classification::Attribute::Anisotropy`
- `CGAL::Classification::Attribute::Distance_to_plane`
- `CGAL::Classification::Attribute::Eigentropy`
- `CGAL::Classification::Attribute::Elevation`
- `CGAL::Classification::Attribute::Linearity`
- `CGAL::Classification::Attribute::Omnivariance`
- `CGAL::Classification::Attribute::Planarity`
- `CGAL::Classification::Attribute::Sphericity`
- `CGAL::Classification::Attribute::Sum_eigenvalues`
- `CGAL::Classification::Attribute::Surface_variation`
- `CGAL::Classification::Attribute::Vertical_dispersion`
If normal vectors are provided (if `VectorMap` is different from
`CGAL::Default`), the following attribute is generated at each
scale:
- `CGAL::Classification::Attribute::Vertical_dispersion`
If colors are provided (if `ColorMap` is different from
`CGAL::Default`), the following attributes are generated at each
scale:
- 9 attributes `CGAL::Classification::Attribute::Hsv` on
channel 0 (hue) with mean ranging from 0° to 360° and standard
deviation of 22.5.
- 5 attributes `CGAL::Classification::Attribute::Hsv` on
channel 1 (saturation) with mean ranging from 0 to 100 and standard
deviation of 12.5.
- 5 attributes `CGAL::Classification::Attribute::Hsv` on channel 2
(value) with mean ranging from 0 to 100 and standard deviation
of 12.5.
If echo numbers are provided (if `EchoMap` is different from
`CGAL::Default`), the following attribute is computed at each
scale:
- `CGAL::Classification::Attribute::Echo_scatter`
\tparam VectorMap model of `ReadablePropertyMap` with value type `Vector_3<Kernel>`.
\tparam ColorMap model of `ReadablePropertyMap` with value type `CGAL::Classification::RGB_Color`.
\tparam EchoMap model of `ReadablePropertyMap` with value type `std::size_t`.
\param nb_scales number of scales to compute.
\param normal_map property map to access the normal vectors of the input points (if any).
\param color_map property map to access the colors of the input points (if any).
\param echo_map property map to access the echo values of the input points (if any).
*/
template <typename VectorMap = Default,
typename ColorMap = Default,
typename EchoMap = Default>
void generate_attributes (std::size_t nb_scales,
VectorMap normal_map = VectorMap(),
ColorMap color_map = ColorMap(),
EchoMap echo_map = EchoMap())
{
typedef typename Default::Get<VectorMap, typename Kernel::Vector_3 >::type
Vmap;
typedef typename Default::Get<ColorMap, RGB_Color >::type
Cmap;
typedef typename Default::Get<EchoMap, std::size_t >::type
Emap;
generate_attributes_impl (nb_scales,
get_parameter<Vmap>(normal_map),
get_parameter<Cmap>(color_map),
get_parameter<Emap>(echo_map));
}
/// @}
/// \name Data Structures and Parameters
/// @{
/*!
\brief Returns the bounding box of the input point set.
*/
const Iso_cuboid_3& bbox() const { return m_bbox; }
/*!
\brief Returns the neighborhood structure at scale `scale`.
\note `generate_attributes()` must have been called before calling
this method.
*/
const Neighborhood& neighborhood(std::size_t scale = 0) const { return (*m_scales[scale]->neighborhood); }
/*!
\brief Returns the planimetric grid structure at scale `scale`.
\note `generate_attributes()` must have been called before calling
this method.
*/
const Planimetric_grid& grid(std::size_t scale = 0) const { return *(m_scales[scale]->grid); }
/*!
\brief Returns the local eigen analysis structure at scale `scale`.
\note `generate_attributes()` must have been called before calling
this method.
*/
const Local_eigen_analysis& eigen(std::size_t scale = 0) const { return *(m_scales[scale]->eigen); }
/*!
\brief Returns the grid resolution at scale `scale`.
\note `generate_attributes()` must have been called before calling
this method.
*/
double grid_resolution(std::size_t scale = 0) const { return m_scales[scale]->grid_resolution(); }
/*!
\brief Returns the radius used for neighborhood queries at scale `scale`.
\note `generate_attributes()` must have been called before calling
this method.
*/
double radius_neighbors(std::size_t scale = 0) const { return m_scales[scale]->radius_neighbors(); }
/*!
\brief Returns the radius used for digital terrain modeling at scale `scale`.
\note `generate_attributes()` must have been called before calling
this method.
*/
double radius_dtm(std::size_t scale = 0) const { return m_scales[scale]->radius_dtm(); }
/// @}
/*!
\brief Clears all computed data structures.
*/
void clear()
{
for (std::size_t i = 0; i < m_scales.size(); ++ i)
delete m_scales[i];
m_scales.clear();
this->clear_classification_types();
this->clear_attributes();
}
/// @}
/// \cond SKIP_IN_MANUAL
void info() const
{
std::cerr << m_scales.size() << " scale(s) used:" << std::endl;
for (std::size_t i = 0; i < m_scales.size(); ++ i)
{
std::size_t nb_useful = 0;
for (std::size_t j = 0; j < m_scales[i]->attributes.size(); ++ j)
if (m_scales[i]->attributes[j]->weight() != 0.)
nb_useful ++;
std::cerr << " * scale " << i << " with size " << m_scales[i]->voxel_size
<< ", " << nb_useful << " useful attribute(s)";
if (nb_useful != 0) std::cerr << ":" << std::endl;
else std::cerr << std::endl;
for (std::size_t j = 0; j < m_scales[i]->attributes.size(); ++ j)
if (m_scales[i]->attributes[j]->weight() != 0.)
std::cerr << " - " << m_scales[i]->attributes[j]->name()
<< " (weight = " << m_scales[i]->attributes[j]->weight() << ")" << std::endl;
}
}
void generate_point_based_attributes ()
{
CGAL::Timer teigen, tpoint;
teigen.start();
generate_multiscale_attribute_variant_0<Anisotropy> ();
generate_multiscale_attribute_variant_1<Distance_to_plane> ();
generate_multiscale_attribute_variant_0<Eigentropy> ();
generate_multiscale_attribute_variant_0<Linearity> ();
generate_multiscale_attribute_variant_0<Omnivariance> ();
generate_multiscale_attribute_variant_0<Planarity> ();
generate_multiscale_attribute_variant_0<Sphericity> ();
generate_multiscale_attribute_variant_0<Sum_eigen> ();
generate_multiscale_attribute_variant_0<Surface_variation> ();
teigen.stop();
tpoint.start();
generate_multiscale_attribute_variant_2<Dispersion> ();
generate_multiscale_attribute_variant_3<Elevation> ();
tpoint.stop();
std::cerr << "Point based attributes computed in " << tpoint.time() << " second(s)" << std::endl;
std::cerr << "Eigen based attributes computed in " << teigen.time() << " second(s)" << std::endl;
}
template <typename VectorMap>
void generate_normal_based_attributes(VectorMap normal_map)
{
CGAL::Timer t; t.start();
this->template add_attribute<Verticality> (normal_map);
m_scales[0]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
t.stop();
std::cerr << "Normal based attributes computed in " << t.time() << " second(s)" << std::endl;
}
void generate_normal_based_attributes(const CGAL::Default_property_map<Iterator, typename Kernel::Vector_3>&)
{
CGAL::Timer t; t.start();
generate_multiscale_attribute_variant_0<Verticality> ();
std::cerr << "Normal based attributes computed in " << t.time() << " second(s)" << std::endl;
}
template <typename ColorMap>
void generate_color_based_attributes(ColorMap color_map)
{
typedef Classification::Attribute::Hsv<Kernel, Range, ColorMap> Hsv;
CGAL::Timer t; t.start();
for (std::size_t i = 0; i <= 8; ++ i)
{
this->template add_attribute<Hsv> (color_map, 0, 45 * i, 22.5);
m_scales[0]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
}
for (std::size_t i = 0; i <= 4; ++ i)
{
this->template add_attribute<Hsv> (color_map, 1, 25 * i, 12.5);
m_scales[0]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
}
for (std::size_t i = 0; i <= 4; ++ i)
{
this->template add_attribute<Hsv> (color_map, 2, 25 * i, 12.5);
m_scales[0]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
}
t.stop();
std::cerr << "Color based attributes computed in " << t.time() << " second(s)" << std::endl;
}
void generate_color_based_attributes(const CGAL::Default_property_map<Iterator, RGB_Color>&)
{
}
template <typename EchoMap>
void generate_echo_based_attributes(EchoMap echo_map)
{
typedef Classification::Attribute::Echo_scatter<Kernel, Range, PointMap, EchoMap> Echo_scatter;
CGAL::Timer t; t.start();
for (std::size_t i = 0; i < m_scales.size(); ++ i)
{
this->template add_attribute<Echo_scatter>(echo_map, *(m_scales[i]->grid),
m_scales[i]->grid_resolution(),
m_scales[i]->radius_neighbors());
m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
}
t.stop();
std::cerr << "Echo based attributes computed in " << t.time() << " second(s)" << std::endl;
}
void generate_echo_based_attributes(const CGAL::Default_property_map<Iterator, std::size_t>&)
{
}
void get_map_scale (std::map<Attribute_handle, std::size_t>& map_scale)
{
for (std::size_t i = 0; i < m_scales.size(); ++ i)
for (std::size_t j = 0; j < m_scales[i]->attributes.size(); ++ j)
map_scale[m_scales[i]->attributes[j]] = i;
}
std::size_t scale_of_attribute (Attribute_handle att)
{
for (std::size_t i = 0; i < m_scales.size(); ++ i)
for (std::size_t j = 0; j < m_scales[i]->attributes.size(); ++ j)
if (m_scales[i]->attributes[j] == att)
return i;
return (std::size_t)(-1);
}
std::string name_att (Attribute_handle att,
std::map<Attribute_handle, std::size_t>& map_scale)
{
std::ostringstream oss;
oss << att->name() << "_" << map_scale[att];
return oss.str();
}
/// \endcond
/// \name Input/Output
/// @{
/*!
\brief Saves the current configuration in the stream `output`.
This allows to easily save and recover a specific classification
configuration, that is to say:
- The computed scales
- The attributes and their respective weights
- The classification types and the effect the attributes have on them
The output file is written in an XML format that is readable by
the `load_configuration()` method.
*/
void save_configuration (std::ostream& output)
{
boost::property_tree::ptree tree;
// tree.put("classification.parameters.grid_resolution", m_grid_resolution);
// tree.put("classification.parameters.radius_neighbors", m_radius_neighbors);
tree.put("classification.parameters.voxel_size", m_scales[0]->voxel_size);
std::map<Attribute_handle, std::size_t> map_scale;
get_map_scale (map_scale);
for (std::size_t i = 0; i < this->number_of_attributes(); ++ i)
{
Attribute_handle att = this->get_attribute(i);
if (att->weight() == 0)
continue;
boost::property_tree::ptree ptr;
ptr.put("id", name_att (att, map_scale));
ptr.put("weight", att->weight());
tree.add_child("classification.attributes.attribute", ptr);
}
for (std::size_t i = 0; i < this->number_of_classification_types(); ++ i)
{
Type_handle type = this->get_classification_type(i);
boost::property_tree::ptree ptr;
ptr.put("id", type->name());
for (std::size_t j = 0; j < this->number_of_attributes(); ++ j)
{
Attribute_handle att = this->get_attribute(j);
if (att->weight() == 0)
continue;
boost::property_tree::ptree ptr2;
ptr2.put("id", name_att (att, map_scale));
Classification::Attribute::Effect effect = type->attribute_effect(att);
if (effect == Classification::Attribute::PENALIZING)
ptr2.put("effect", "penalized");
else if (effect == Classification::Attribute::NEUTRAL)
ptr2.put("effect", "neutral");
else if (effect == Classification::Attribute::FAVORING)
ptr2.put("effect", "favored");
ptr.add_child("attribute", ptr2);
}
tree.add_child("classification.types.type", ptr);
}
// Write property tree to XML file
boost::property_tree::xml_writer_settings<std::string> settings(' ', 3);
boost::property_tree::write_xml(output, tree, settings);
}
/*!
\brief Loads a configuration from the stream `input`.
All data structures, attributes and types specified in the input
stream `input` are instantiated if possible (in particular,
property maps needed should be provided), similarly to what is
done in `generate_attributes()`.
The input file should be in the XML format written by the
`save_configuration()` method.
\tparam VectorMap model of `ReadablePropertyMap` with value type `Vector_3<Kernel>`.
\tparam ColorMap model of `ReadablePropertyMap` with value type `CGAL::Classification::RGB_Color`.
\tparam EchoMap model of `ReadablePropertyMap` with value type `std::size_t`.
\param input input stream.
\param normal_map property map to access the normal vectors of the input points (if any).
\param color_map property map to access the colors of the input points (if any).
\param echo_map property map to access the echo values of the input points (if any).
*/
template<typename VectorMap = Default,
typename ColorMap = Default,
typename EchoMap = Default>
bool load_configuration (std::istream& input,
VectorMap normal_map = VectorMap(),
ColorMap color_map = ColorMap(),
EchoMap echo_map = EchoMap())
{
typedef typename Default::Get<VectorMap, typename Kernel::Vector_3 >::type
Vmap;
typedef typename Default::Get<ColorMap, RGB_Color >::type
Cmap;
typedef typename Default::Get<EchoMap, std::size_t >::type
Emap;
return load_configuration_impl (input,
get_parameter<Vmap>(normal_map),
get_parameter<Cmap>(color_map),
get_parameter<Emap>(echo_map));
}
/*!
\brief Writes a classification in a colored and labeled PLY format
in the stream `output`.
The input point set is written in a PLY format with the addition
of several PLY properties:
- a property `label` that indicates which classification type is
assigned to the point. The types are indexed from 0 to N (the
correspondancy is given as comments in the PLY header).
- 3 properties `red`, `green` and `blue` to associate each label
to a color (this is useful to visualize the classification in a
viewer that supports PLY colors). Colors are picked randomly.
\param stream The output stream where to write the content
\param begin Iterator to the first input object
\param end Past-the-end iterator
\param point_map Property map to access the input points
\param psc The classification object to write from
\param colors A set of colors to be used to represent the
different classification types. If none is given, random colors
are picked.
*/
void write_classification_to_ply (std::ostream& output)
{
output << "ply" << std::endl
<< "format ascii 1.0" << std::endl
<< "comment Generated by the CGAL library www.cgal.org" << std::endl
<< "element vertex " << m_input.size() << std::endl
<< "property double x" << std::endl
<< "property double y" << std::endl
<< "property double z" << std::endl
<< "property uchar red" << std::endl
<< "property uchar green" << std::endl
<< "property uchar blue" << std::endl
<< "property int label" << std::endl;
std::vector<RGB_Color> colors;
std::map<Type_handle, std::size_t> map_types;
output << "comment label -1 is (unclassified)" << std::endl;
for (std::size_t i = 0; i < this->number_of_classification_types(); ++ i)
{
map_types.insert (std::make_pair (this->get_classification_type(i), i));
output << "comment label " << i << " is " << this->get_classification_type(i)->name() << std::endl;
RGB_Color c = {{ (unsigned char)(64 + rand() % 128),
(unsigned char)(64 + rand() % 128),
(unsigned char)(64 + rand() % 128) }};
colors.push_back (c);
}
map_types.insert (std::make_pair (Type_handle(), this->number_of_classification_types()));
output << "end_header" << std::endl;
std::size_t i = 0;
for (Iterator it = m_input.begin(); it != m_input.end(); ++ it)
{
Type_handle t = this->classification_type_of(i);
std::size_t idx = map_types[t];
if (idx == this->number_of_classification_types())
output << get(m_item_map, *it) << " 0 0 0 -1" << std::endl;
else
output << get(m_item_map, *it) << " "
<< (int)(colors[idx][0]) << " "
<< (int)(colors[idx][1]) << " "
<< (int)(colors[idx][2]) << " "
<< idx << std::endl;
++ i;
}
}
/// @}
private:
template <typename T>
const T& get_parameter (const T& t)
{
return t;
}
template <typename T>
Default_property_map<Iterator, T>
get_parameter (const Default&)
{
return Default_property_map<Iterator, T>();
}
template<typename VectorMap, typename ColorMap, typename EchoMap>
void generate_attributes_impl (std::size_t nb_scales,
VectorMap normal_map,
ColorMap color_map,
EchoMap echo_map)
{
CGAL::Timer t; t.start();
m_scales.reserve (nb_scales);
double voxel_size = - 1.;
m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size));
voxel_size = m_scales[0]->grid_resolution();
for (std::size_t i = 1; i < nb_scales; ++ i)
{
voxel_size *= 2;
m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size));
}
generate_point_based_attributes ();
generate_normal_based_attributes (normal_map);
generate_color_based_attributes (color_map);
generate_echo_based_attributes (echo_map);
}
template <typename Attribute_type>
void generate_multiscale_attribute_variant_0 ()
{
for (std::size_t i = 0; i < m_scales.size(); ++ i)
{
this->template add_attribute<Attribute_type>(*(m_scales[i]->eigen));
m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
}
}
template <typename Attribute_type>
void generate_multiscale_attribute_variant_1 ()
{
for (std::size_t i = 0; i < m_scales.size(); ++ i)
{
this->template add_attribute<Attribute_type>(m_item_map, *(m_scales[i]->eigen));
m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
}
}
template <typename Attribute_type>
void generate_multiscale_attribute_variant_2 ()
{
for (std::size_t i = 0; i < m_scales.size(); ++ i)
{
this->template add_attribute<Attribute_type>(m_item_map,
*(m_scales[i]->grid),
m_scales[i]->grid_resolution(),
m_scales[i]->radius_neighbors());
m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
}
}
template <typename Attribute_type>
void generate_multiscale_attribute_variant_3 ()
{
for (std::size_t i = 0; i < m_scales.size(); ++ i)
{
this->template add_attribute<Attribute_type>(m_item_map,
*(m_scales[i]->grid),
m_scales[i]->grid_resolution(),
m_scales[i]->radius_dtm());
m_scales[i]->attributes.push_back (this->get_attribute (this->number_of_attributes() - 1));
}
}
template<typename VectorMap,typename ColorMap, typename EchoMap>
bool load_configuration_impl (std::istream& input,
VectorMap normal_map,
ColorMap color_map,
EchoMap echo_map)
{
typedef Classification::Attribute::Echo_scatter<Kernel, Range, PointMap, EchoMap> Echo_scatter;
typedef Classification::Attribute::Hsv<Kernel, Range, ColorMap> Hsv;
clear();
boost::property_tree::ptree tree;
boost::property_tree::read_xml(input, tree);
double voxel_size = tree.get<double>("classification.parameters.voxel_size");
m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size));
CGAL::Timer t;
std::map<std::string, Attribute_handle> att_map;
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, tree.get_child("classification.attributes"))
{
std::string full_id = v.second.get<std::string>("id");
std::vector<std::string> splitted_id;
boost::split(splitted_id, full_id, boost::is_any_of("_"));
std::string id = splitted_id[0];
for (std::size_t i = 1; i < splitted_id.size() - 1; ++ i)
id = id + "_" + splitted_id[i];
std::size_t scale = std::atoi (splitted_id.back().c_str());
while (m_scales.size() <= scale)
{
voxel_size *= 2;
m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size));
}
double weight = v.second.get<double>("weight");
// Generate the right attribute if possible
if (id == "anisotropy")
this->template add_attribute<Anisotropy>(*(m_scales[scale]->eigen));
else if (id == "distance_to_plane")
this->template add_attribute<Distance_to_plane>(m_item_map, *(m_scales[scale]->eigen));
else if (id == "eigentropy")
this->template add_attribute<Eigentropy>(*(m_scales[scale]->eigen));
else if (id == "elevation")
{
t.start();
this->template add_attribute<Elevation>(m_item_map,
*(m_scales[scale]->grid),
m_scales[scale]->grid_resolution(),
m_scales[scale]->radius_dtm());
t.stop();
}
else if (id == "linearity")
this->template add_attribute<Linearity>(*(m_scales[scale]->eigen));
else if (id == "omnivariance")
this->template add_attribute<Omnivariance>(*(m_scales[scale]->eigen));
else if (id == "planarity")
this->template add_attribute<Planarity>(*(m_scales[scale]->eigen));
else if (id == "sphericity")
this->template add_attribute<Sphericity>(*(m_scales[scale]->eigen));
else if (id == "sum_eigen")
this->template add_attribute<Sum_eigen>(*(m_scales[scale]->eigen));
else if (id == "surface_variation")
this->template add_attribute<Surface_variation>(*(m_scales[scale]->eigen));
else if (id == "vertical_dispersion")
this->template add_attribute<Dispersion>(m_item_map,
*(m_scales[scale]->grid),
m_scales[scale]->grid_resolution(),
m_scales[scale]->radius_neighbors());
else if (id == "verticality")
{
if (boost::is_convertible<VectorMap,
typename CGAL::Default_property_map<Iterator, typename Kernel::Vector_3> >::value)
this->template add_attribute<Verticality>(*(m_scales[scale]->eigen));
else
this->template add_attribute<Verticality>(normal_map);
}
else if (id == "echo_scatter")
{
if (boost::is_convertible<EchoMap,
typename CGAL::Default_property_map<Iterator, std::size_t> >::value)
{
std::cerr << "Warning: echo_scatter required but no echo map given." << std::endl;
continue;
}
this->template add_attribute<Echo_scatter>(echo_map, *(m_scales[scale]->grid),
m_scales[scale]->grid_resolution(),
m_scales[scale]->radius_neighbors());
}
else if (boost::starts_with(id.c_str(), "hue")
|| boost::starts_with(id.c_str(), "saturation")
|| boost::starts_with(id.c_str(), "value"))
{
if (boost::is_convertible<ColorMap,
typename CGAL::Default_property_map<Iterator, RGB_Color> >::value)
{
std::cerr << "Warning: color attribute required but no color map given." << std::endl;
continue;
}
if (boost::starts_with(id.c_str(), "hue"))
{
double value = boost::lexical_cast<int>(id.c_str() + 4);
this->template add_attribute<Hsv>(color_map, 0, value, 22.5);
}
else if (boost::starts_with(id.c_str(), "saturation"))
{
double value = boost::lexical_cast<int>(id.c_str() + 11);
this->template add_attribute<Hsv>(color_map, 1, value, 12.5);
}
else if (boost::starts_with(id.c_str(), "value"))
{
double value = boost::lexical_cast<int>(id.c_str() + 6);
this->template add_attribute<Hsv>(color_map, 2, value, 12.5);
}
}
else
{
std::cerr << "Warning: unknown attribute \"" << id << "\"" << std::endl;
continue;
}
Attribute_handle att = this->get_attribute (this->number_of_attributes() - 1);
m_scales[scale]->attributes.push_back (att);
att->set_weight(weight);
att_map[full_id] = att;
}
std::cerr << "Elevation took " << t.time() << " second(s)" << std::endl;
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, tree.get_child("classification.types"))
{
std::string type_id = v.second.get<std::string>("id");
Type_handle new_type = this->add_classification_type (type_id.c_str());
BOOST_FOREACH(boost::property_tree::ptree::value_type &v2, v.second)
{
if (v2.first == "id")
continue;
std::string att_id = v2.second.get<std::string>("id");
std::map<std::string, Attribute_handle>::iterator it = att_map.find(att_id);
if (it == att_map.end())
continue;
Attribute_handle att = it->second;
std::string effect = v2.second.get<std::string>("effect");
if (effect == "penalized")
new_type->set_attribute_effect (att, Classification::Attribute::PENALIZING);
else if (effect == "neutral")
new_type->set_attribute_effect (att, Classification::Attribute::NEUTRAL);
else
new_type->set_attribute_effect (att, Classification::Attribute::FAVORING);
}
}
return true;
}
};
} // namespace CGAL
#endif // CGAL_POINT_SET_CLASSIFIER_H