// Copyright (c) 2017 GeometryFactory Sarl (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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CGAL_LINKED_WITH_TBB #include #include #endif // CGAL_LINKED_WITH_TBB namespace CGAL { /*! \ingroup PkgClassification \brief Classifies a point set based on a set of features and a set of labels. This class specializes `Classifier` to point sets. It takes care of generating necessary data structures and automatically generate a set of generic features. Features can be generated at multiple scales to increase the reliability of the classification. \tparam Geom_traits model of \cgal Kernel. \tparam PointRange model of `ConstRange`. Its iterator type is `RandomAccessIterator`. \tparam PointMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `Geom_traits::Point_3`. \tparam ConcurrencyTag enables sequential versus parallel algorithm. Possible values are `Parallel_tag` (default value is %CGAL is linked with TBB) or `Sequential_tag` (default value otherwise). \tparam DiagonalizeTraits model of `DiagonalizeTraits` used for matrix diagonalization. */ template > class Point_set_classifier : public Classifier { public: typedef typename Geom_traits::Iso_cuboid_3 Iso_cuboid_3; /// \cond SKIP_IN_MANUAL typedef Classifier Base; typedef typename PointRange::const_iterator Iterator; using Base::m_input; using Base::m_item_map; /// \endcond typedef Classification::Planimetric_grid Planimetric_grid; typedef Classification::Point_set_neighborhood Neighborhood; typedef Classification::Local_eigen_analysis Local_eigen_analysis; /// \cond SKIP_IN_MANUAL typedef Classification::Feature_handle Feature_handle; typedef Classification::Label Label; typedef Classification::Label_handle Label_handle; typedef typename Geom_traits::Point_3 Point; typedef Classification::Feature::Anisotropy Anisotropy; typedef Classification::Feature::Distance_to_plane Distance_to_plane; typedef Classification::Feature::Eigentropy Eigentropy; typedef Classification::Feature::Elevation Elevation; typedef Classification::Feature::Linearity Linearity; typedef Classification::Feature::Omnivariance Omnivariance; typedef Classification::Feature::Planarity Planarity; typedef Classification::Feature::Sphericity Sphericity; typedef Classification::Feature::Sum_eigenvalues Sum_eigen; typedef Classification::Feature::Surface_variation Surface_variation; typedef Classification::Feature::Vertical_dispersion Dispersion; typedef Classification::Feature::Verticality Verticality; typedef typename Classification::RGB_Color RGB_Color; /// \endcond private: struct Scale { Neighborhood* neighborhood; Planimetric_grid* grid; Local_eigen_analysis* eigen; double voxel_size; std::vector features; Scale (const PointRange& input, PointMap point_map, const Iso_cuboid_3& bbox, double voxel_size) : voxel_size (voxel_size) { CGAL::Real_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.) CGAL_CLASSIFICATION_CERR << "Neighborhood computed in " << t.time() << " second(s)" << std::endl; else CGAL_CLASSIFICATION_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; t.stop(); CGAL_CLASSIFICATION_CERR << "Eigen values computed in " << t.time() << " second(s)" << std::endl; CGAL_CLASSIFICATION_CERR << "Range = " << range << std::endl; t.reset(); t.start(); grid = new Planimetric_grid (input, point_map, bbox, this->voxel_size); t.stop(); CGAL_CLASSIFICATION_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 m_scales; #ifdef CGAL_LINKED_WITH_TBB tbb::mutex m_scale_mutex; tbb::task_group* m_tasks; #endif struct Feature_adder { mutable Point_set_classifier* classifier; mutable Scale* scale; Feature_adder (Point_set_classifier* classifier, Scale* scale) : classifier (classifier), scale (scale) { } virtual ~Feature_adder() { } #ifdef CGAL_LINKED_WITH_TBB void mutex_lock() const { classifier->m_scale_mutex.lock(); } void mutex_unlock() const { classifier->m_scale_mutex.unlock(); } #else void mutex_lock() const { } void mutex_unlock() const { } #endif virtual void operator()() const = 0; }; friend Feature_adder; std::vector m_adders; public: /// \name Constructor /// @{ /*! \brief Initializes a classification object. \param input input range. \param point_map property map to access the input points. */ Point_set_classifier(const PointRange& 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(m_item_map)), boost::make_transform_iterator (m_input.end(), CGAL::Property_map_to_unary_function(m_item_map))); } /// @} /// \cond SKIP_IN_MANUAL virtual ~Point_set_classifier() { clear(); } /// \endcond /// \name Features /// @{ /*! \brief Generate all possible features from an input range. The size of 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 features are generated: - `CGAL::Classification::Feature::Anisotropy` - `CGAL::Classification::Feature::Distance_to_plane` - `CGAL::Classification::Feature::Eigentropy` - `CGAL::Classification::Feature::Elevation` - `CGAL::Classification::Feature::Linearity` - `CGAL::Classification::Feature::Omnivariance` - `CGAL::Classification::Feature::Planarity` - `CGAL::Classification::Feature::Sphericity` - `CGAL::Classification::Feature::Sum_eigenvalues` - `CGAL::Classification::Feature::Surface_variation` - `CGAL::Classification::Feature::Vertical_dispersion` based on eigenvalues If normal vectors are provided (if `VectorMap` is different from `CGAL::Default`), the following feature is generated at each scale: - `CGAL::Classification::Feature::Vertical_dispersion` based on normal vectors If colors are provided (if `ColorMap` is different from `CGAL::Default`), the following features are generated at each scale: - 9 features `CGAL::Classification::Feature::Hsv` on channel 0 (hue) with mean ranging from 0° to 360° and standard deviation of 22.5. - 5 features `CGAL::Classification::Feature::Hsv` on channel 1 (saturation) with mean ranging from 0 to 100 and standard deviation of 12.5. - 5 features `CGAL::Classification::Feature::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 feature is computed at each scale: - `CGAL::Classification::Feature::Echo_scatter` \tparam VectorMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `Geom_traits::Vector_3`. \tparam ColorMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `CGAL::Classification::RGB_Color`. \tparam EchoMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `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 void generate_features (std::size_t nb_scales, VectorMap normal_map = VectorMap(), ColorMap color_map = ColorMap(), EchoMap echo_map = EchoMap()) { typedef typename Default::Get::type Vmap; typedef typename Default::Get::type Cmap; typedef typename Default::Get::type Emap; generate_features_impl (nb_scales, get_parameter(normal_map), get_parameter(color_map), get_parameter(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_features()` 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_features()` 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_features()` 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 number of scales that were computed. */ std::size_t number_of_scales() const { return m_scales.size(); } /*! \brief Returns the grid resolution at scale `scale`. This resolution is the length and width of a cell of the `Planimetric_grid` defined at this scale. \note `generate_features()` 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`. This radius is the smallest radius that is relevant from a geometric point of view at this scale (that is to say that encloses a few cells of `Planimetric_grid`). \note `generate_features()` 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`. This radius represents the minimum size of a building at this scale. \note `generate_features()` 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_labels(); this->clear_features(); } /// @} /// @} /// \cond SKIP_IN_MANUAL void info() const { CGAL_CLASSIFICATION_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]->features.size(); ++ j) if (m_scales[i]->features[j]->weight() != 0.) nb_useful ++; CGAL_CLASSIFICATION_CERR << " * scale " << i << " with size " << m_scales[i]->voxel_size << ", " << nb_useful << " useful feature(s)"; if (nb_useful != 0) CGAL_CLASSIFICATION_CERR << ":" << std::endl; else CGAL_CLASSIFICATION_CERR << std::endl; for (std::size_t j = 0; j < m_scales[i]->features.size(); ++ j) if (m_scales[i]->features[j]->weight() != 0.) CGAL_CLASSIFICATION_CERR << " - " << m_scales[i]->features[j]->name() << " (weight = " << m_scales[i]->features[j]->weight() << ")" << std::endl; } } void generate_point_based_features () { generate_multiscale_feature_variant_0 (); generate_multiscale_feature_variant_0 (); generate_multiscale_feature_variant_0 (); generate_multiscale_feature_variant_0 (); generate_multiscale_feature_variant_0 (); generate_multiscale_feature_variant_0 (); generate_multiscale_feature_variant_0 (); generate_multiscale_feature_variant_0 (); generate_multiscale_feature_variant_1 (); generate_multiscale_feature_variant_2 (); generate_multiscale_feature_variant_3 (); } template void launch_feature_computation (FeatureAdder* adder) { m_adders.push_back (adder); #ifndef CGAL_LINKED_WITH_TBB CGAL_static_assertion_msg (!(boost::is_convertible::value), "Parallel_tag is enabled but TBB is unavailable."); #else if (boost::is_convertible::value) { m_tasks->run (*adder); } else #endif { (*adder)(); } } template struct Feature_adder_verticality : public Feature_adder { using Feature_adder::classifier; using Feature_adder::scale; VectorMap normal_map; // TODO! Feature_adder_verticality (Point_set_classifier* classifier, VectorMap normal_map, Scale* scale) : Feature_adder (classifier, scale), normal_map (normal_map) { } void operator() () const { Feature_handle result = classifier->template add_feature (normal_map); this->mutex_lock(); scale->features.push_back (result); this->mutex_unlock(); } }; template void generate_normal_based_features(VectorMap normal_map) { launch_feature_computation (new Feature_adder_verticality (this, normal_map, m_scales[0])); } void generate_normal_based_features(const CGAL::Default_property_map&) { generate_multiscale_feature_variant_0 (); } template struct Feature_adder_color : public Feature_adder { typedef Classification::Feature::Hsv Hsv; using Feature_adder::classifier; using Feature_adder::scale; ColorMap color_map; std::size_t channel; double mean; double sd; // TODO! Feature_adder_color (Point_set_classifier* classifier, ColorMap color_map, Scale* scale, std::size_t channel, double mean, double sd) : Feature_adder (classifier, scale), color_map (color_map), channel (channel), mean (mean), sd (sd) { } void operator() () const { Feature_handle result = classifier->template add_feature (color_map, channel, mean, sd); this->mutex_lock(); scale->features.push_back (result); this->mutex_unlock(); } }; template void generate_color_based_features(ColorMap color_map) { for (std::size_t i = 0; i <= 8; ++ i) launch_feature_computation (new Feature_adder_color (this, color_map, m_scales[0], 0, 45 * i, 22.5)); for (std::size_t i = 0; i <= 4; ++ i) launch_feature_computation (new Feature_adder_color (this, color_map, m_scales[0], 1, 25 * i, 12.5)); for (std::size_t i = 0; i <= 4; ++ i) launch_feature_computation (new Feature_adder_color (this, color_map, m_scales[0], 2, 25 * i, 12.5)); } void generate_color_based_features(const CGAL::Default_property_map&) { } template struct Feature_adder_echo : public Feature_adder { typedef Classification::Feature::Echo_scatter Echo_scatter; using Feature_adder::classifier; using Feature_adder::scale; EchoMap echo_map; // TODO! Feature_adder_echo (Point_set_classifier* classifier, EchoMap echo_map, Scale* scale) : Feature_adder (classifier, scale), echo_map (echo_map) { } void operator() () const { Feature_handle result = classifier->template add_feature (echo_map, *(scale->grid), scale->grid_resolution(), scale->radius_neighbors()); this->mutex_lock(); scale->features.push_back (result); this->mutex_unlock(); } }; template void generate_echo_based_features(EchoMap echo_map) { for (std::size_t i = 0; i < m_scales.size(); ++ i) launch_feature_computation (new Feature_adder_echo (this, echo_map, m_scales[i])); } void generate_echo_based_features(const CGAL::Default_property_map&) { } void get_map_scale (std::map& map_scale) { for (std::size_t i = 0; i < m_scales.size(); ++ i) for (std::size_t j = 0; j < m_scales[i]->features.size(); ++ j) map_scale[m_scales[i]->features[j]] = i; } std::size_t scale_of_feature (Feature_handle att) { for (std::size_t i = 0; i < m_scales.size(); ++ i) for (std::size_t j = 0; j < m_scales[i]->features.size(); ++ j) if (m_scales[i]->features[j] == att) return i; return (std::size_t)(-1); } std::string name_att (Feature_handle att, std::map& 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 size of the smallest scale - The features and their respective weights - The labels and the effects of the features 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 map_scale; get_map_scale (map_scale); for (std::size_t i = 0; i < this->number_of_features(); ++ i) { Feature_handle att = this->feature(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.features.feature", ptr); } for (std::size_t i = 0; i < this->number_of_labels(); ++ i) { Label_handle label = this->label(i); boost::property_tree::ptree ptr; ptr.put("id", label->name()); for (std::size_t j = 0; j < this->number_of_features(); ++ j) { Feature_handle att = this->feature(j); if (att->weight() == 0) continue; boost::property_tree::ptree ptr2; ptr2.put("id", name_att (att, map_scale)); Classification::Feature::Effect effect = label->feature_effect(att); if (effect == Classification::Feature::PENALIZING) ptr2.put("effect", "penalized"); else if (effect == Classification::Feature::NEUTRAL) ptr2.put("effect", "neutral"); else if (effect == Classification::Feature::FAVORING) ptr2.put("effect", "favored"); ptr.add_child("feature", ptr2); } tree.add_child("classification.labels.label", ptr); } // Write property tree to XML file boost::property_tree::xml_writer_settings settings(' ', 3); boost::property_tree::write_xml(output, tree, settings); } /*! \brief Loads a configuration from the stream `input`. All data structures, features and labels 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_features()`. The input file should be in the XML format written by the `save_configuration()` method. \tparam VectorMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `Geom_traits::Vector_3`. \tparam ColorMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `CGAL::Classification::RGB_Color`. \tparam EchoMap model of `ReadablePropertyMap` whose key type is the value type of the iterator of `PointRange` and value type is `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 bool load_configuration (std::istream& input, VectorMap normal_map = VectorMap(), ColorMap color_map = ColorMap(), EchoMap echo_map = EchoMap()) { typedef typename Default::Get::type Vmap; typedef typename Default::Get::type Cmap; typedef typename Default::Get::type Emap; return load_configuration_impl (input, get_parameter(normal_map), get_parameter(color_map), get_parameter(echo_map)); } /*! \brief Writes the classified point set in a colored and labeled PLY format in the stream `output`. The input points are written in a PLY format with the addition of the following PLY properties: - a property `label` that indicates which label is assigned to the point. The labels 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. */ 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 colors; std::map map_labels; output << "comment label -1 is (unclassified)" << std::endl; for (std::size_t i = 0; i < this->number_of_labels(); ++ i) { map_labels.insert (std::make_pair (this->label(i), i)); output << "comment label " << i << " is " << this->label(i)->name() << std::endl; RGB_Color c = {{ (unsigned char)(64 + rand() % 128), (unsigned char)(64 + rand() % 128), (unsigned char)(64 + rand() % 128) }}; if (this->label(i)->name() == "vegetation") c = {{ 0, 255, 27 }}; else if (this->label(i)->name() == "ground") c = {{ 245, 180, 0 }}; else if (this->label(i)->name() == "roof") c = {{ 255, 0, 170 }}; else if (this->label(i)->name() == "facade") c = {{ 100, 0, 255 }}; colors.push_back (c); } map_labels.insert (std::make_pair (Label_handle(), this->number_of_labels())); output << "end_header" << std::endl; std::size_t i = 0; for (Iterator it = m_input.begin(); it != m_input.end(); ++ it) { Label_handle t = this->label_of(i); std::size_t idx = map_labels[t]; if (idx == this->number_of_labels()) 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 const T& get_parameter (const T& t) { return t; } template Default_property_map get_parameter (const Default&) { return Default_property_map(); } template void generate_features_impl (std::size_t nb_scales, VectorMap normal_map, ColorMap color_map, EchoMap echo_map) { CGAL::Real_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)); } t.stop(); CGAL_CLASSIFICATION_CERR << "Scales computed in " << t.time() << " second(s)" << std::endl; t.reset(); t.start(); #ifdef CGAL_LINKED_WITH_TBB m_tasks = new tbb::task_group; #endif generate_point_based_features (); generate_normal_based_features (normal_map); generate_color_based_features (color_map); generate_echo_based_features (echo_map); #ifdef CGAL_LINKED_WITH_TBB m_tasks->wait(); delete m_tasks; #endif t.stop(); CGAL_CLASSIFICATION_CERR << "Features computed in " << t.time() << " second(s)" << std::endl; for (std::size_t i = 0; i < m_adders.size(); ++ i) delete m_adders[i]; } template struct Feature_adder_variant_0 : public Feature_adder { using Feature_adder::classifier; using Feature_adder::scale; Feature_adder_variant_0 (Point_set_classifier* classifier, Scale* scale) : Feature_adder (classifier, scale) { } void operator() () const { Feature_handle result = classifier->template add_feature (*(scale->eigen)); this->mutex_lock(); scale->features.push_back (result); this->mutex_unlock(); } }; template void generate_multiscale_feature_variant_0 () { for (std::size_t i = 0; i < m_scales.size(); ++ i) launch_feature_computation (new Feature_adder_variant_0 (this, m_scales[i])); } template struct Feature_adder_variant_1 : public Feature_adder { using Feature_adder::classifier; using Feature_adder::scale; PointMap point_map; // TODO! Feature_adder_variant_1 (Point_set_classifier* classifier, PointMap point_map, Scale* scale) : Feature_adder (classifier, scale), point_map (point_map) { } void operator() () const { Feature_handle result = classifier->template add_feature (point_map, *(scale->eigen)); this->mutex_lock(); scale->features.push_back (result); this->mutex_unlock(); } }; template void generate_multiscale_feature_variant_1 () { for (std::size_t i = 0; i < m_scales.size(); ++ i) launch_feature_computation (new Feature_adder_variant_1 (this, m_item_map, m_scales[i])); } template struct Feature_adder_variant_2 : public Feature_adder { using Feature_adder::classifier; using Feature_adder::scale; PointMap point_map; // TODO! Feature_adder_variant_2 (Point_set_classifier* classifier, PointMap point_map, Scale* scale) : Feature_adder (classifier, scale), point_map (point_map) { } void operator() () const { Feature_handle result = classifier->template add_feature (point_map, *(scale->grid), scale->grid_resolution(), scale->radius_neighbors()); this->mutex_lock(); scale->features.push_back (result); this->mutex_unlock(); } }; template void generate_multiscale_feature_variant_2 () { for (std::size_t i = 0; i < m_scales.size(); ++ i) launch_feature_computation (new Feature_adder_variant_2 (this, m_item_map, m_scales[i])); } template struct Feature_adder_variant_3 : public Feature_adder { using Feature_adder::classifier; using Feature_adder::scale; PointMap point_map; // TODO! Feature_adder_variant_3 (Point_set_classifier* classifier, PointMap point_map, Scale* scale) : Feature_adder (classifier, scale), point_map (point_map) { } void operator() () const { Feature_handle result = classifier->template add_feature (point_map, *(scale->grid), scale->grid_resolution(), scale->radius_dtm()); this->mutex_lock(); scale->features.push_back (result); this->mutex_unlock(); } }; template void generate_multiscale_feature_variant_3 () { for (std::size_t i = 0; i < m_scales.size(); ++ i) launch_feature_computation (new Feature_adder_variant_3 (this, m_item_map, m_scales[i])); } template bool load_configuration_impl (std::istream& input, VectorMap normal_map, ColorMap color_map, EchoMap echo_map) { typedef Classification::Feature::Echo_scatter Echo_scatter; typedef Classification::Feature::Hsv Hsv; clear(); boost::property_tree::ptree tree; boost::property_tree::read_xml(input, tree); double voxel_size = tree.get("classification.parameters.voxel_size"); m_scales.push_back (new Scale (m_input, m_item_map, m_bbox, voxel_size)); CGAL::Real_timer t; std::map att_map; BOOST_FOREACH(boost::property_tree::ptree::value_type &v, tree.get_child("classification.features")) { std::string full_id = v.second.get("id"); std::vector 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("weight"); // Generate the right feature if possible if (id == "anisotropy") this->template add_feature(*(m_scales[scale]->eigen)); else if (id == "distance_to_plane") this->template add_feature(m_item_map, *(m_scales[scale]->eigen)); else if (id == "eigentropy") this->template add_feature(*(m_scales[scale]->eigen)); else if (id == "elevation") { t.start(); this->template add_feature(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_feature(*(m_scales[scale]->eigen)); else if (id == "omnivariance") this->template add_feature(*(m_scales[scale]->eigen)); else if (id == "planarity") this->template add_feature(*(m_scales[scale]->eigen)); else if (id == "sphericity") this->template add_feature(*(m_scales[scale]->eigen)); else if (id == "sum_eigen") this->template add_feature(*(m_scales[scale]->eigen)); else if (id == "surface_variation") this->template add_feature(*(m_scales[scale]->eigen)); else if (id == "vertical_dispersion") this->template add_feature(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 >::value) this->template add_feature(*(m_scales[scale]->eigen)); else this->template add_feature(normal_map); } else if (id == "echo_scatter") { if (boost::is_convertible >::value) { CGAL_CLASSIFICATION_CERR << "Warning: echo_scatter required but no echo map given." << std::endl; continue; } this->template add_feature(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 >::value) { CGAL_CLASSIFICATION_CERR << "Warning: color feature required but no color map given." << std::endl; continue; } if (boost::starts_with(id.c_str(), "hue")) { double value = boost::lexical_cast(id.c_str() + 4); this->template add_feature(color_map, 0, value, 22.5); } else if (boost::starts_with(id.c_str(), "saturation")) { double value = boost::lexical_cast(id.c_str() + 11); this->template add_feature(color_map, 1, value, 12.5); } else if (boost::starts_with(id.c_str(), "value")) { double value = boost::lexical_cast(id.c_str() + 6); this->template add_feature(color_map, 2, value, 12.5); } } else { CGAL_CLASSIFICATION_CERR << "Warning: unknown feature \"" << id << "\"" << std::endl; continue; } Feature_handle att = this->feature (this->number_of_features() - 1); m_scales[scale]->features.push_back (att); att->set_weight(weight); att_map[full_id] = att; } BOOST_FOREACH(boost::property_tree::ptree::value_type &v, tree.get_child("classification.labels")) { std::string label_id = v.second.get("id"); Label_handle new_label = this->add_label (label_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("id"); std::map::iterator it = att_map.find(att_id); if (it == att_map.end()) continue; Feature_handle att = it->second; std::string effect = v2.second.get("effect"); if (effect == "penalized") new_label->set_feature_effect (att, Classification::Feature::PENALIZING); else if (effect == "neutral") new_label->set_feature_effect (att, Classification::Feature::NEUTRAL); else new_label->set_feature_effect (att, Classification::Feature::FAVORING); } } return true; } }; } // namespace CGAL #endif // CGAL_POINT_SET_CLASSIFIER_H