Merge branch 'Classification-GF-old' into Classification-GF
|
|
@ -0,0 +1,389 @@
|
|||
namespace CGAL {
|
||||
/*!
|
||||
|
||||
\mainpage User Manual
|
||||
\anchor Chapter_Classification
|
||||
\cgalAutoToc
|
||||
\author Simon Giraudot, Florent Lafarge
|
||||
|
||||
This component implements the algorithm described in \cgalCite{cgal:lm-clscm-12} (section 2), generalized to handle different types of data, multiple features and multiple labels. It classifies a data set into a user-defined set of labels, such as _ground_, _vegetation_ and _buildings_. A flexible API is provided so that users can classify any type of data which they can index and for which they can compute relevant features, compute their own local features on the input data set and define their own labels.
|
||||
|
||||
\section Classification_Organization Package Organization
|
||||
|
||||
%Classification of data sets is achieved as follows (see Figure \cgalFigureRef{Classification_organization_fig}):
|
||||
|
||||
- some analysis is performed on the input data set;
|
||||
- features are computed based on this analysis;
|
||||
- a set of labels (for example: _ground_, _building_, _vegetation_) is defined by the user;
|
||||
- a classifier is defined and trained: from the set of values taken by the features at an input item, it measures the likelihood of this item to belong to one label or another;
|
||||
- classification is computed itemwise using the classifier;
|
||||
- additional regularization can be used by smoothing either locally or globally through a _Graph Cut_ \cgalCite{Boykov2001FastApproximate} approach.
|
||||
|
||||
\cgalFigureBegin{Classification_organization_fig,organization.svg}
|
||||
Organization of the package.
|
||||
\cgalFigureEnd
|
||||
|
||||
This package is designed to be easily extended by users: more specifically, features and labels can be defined by users to handle any data they need to classify.
|
||||
|
||||
Currently, \cgal only provides data structures to handle classification of point sets.
|
||||
|
||||
\section Classification_structures Data Structures
|
||||
|
||||
\subsection Classification_analysis Analysis
|
||||
|
||||
%Classification is based on the computation of local features. These features can take advantage of shared data structures that are precomputed and stored separately.
|
||||
|
||||
\cgal provides the following structures:
|
||||
|
||||
- [Point_set_neighborhood](@ref CGAL::Classification::Point_set_neighborhood) stores spatial searching structures and provides adapted queries for points;
|
||||
- [Local_eigen_analysis](@ref CGAL::Classification::Local_eigen_analysis) precomputes covariance matrices on local neighborhoods of points and stores the associated eigenvectors and eigenvalues;
|
||||
- [Planimetric_grid](@ref CGAL::Classification::Planimetric_grid) is a 2D grid used for digital terrain modeling.
|
||||
|
||||
Most of these features depend on a scale parameter. \cgal provides a method to estimate the average spacing based on a number of neighbors (see [CGAL::compute_average_spacing()](@ref compute_average_spacing)), which usually provides satisfying results in the absence of noise. In the presence of noise, [CGAL::estimate_global_range_scale()](@ref estimate_global_range_scale) provides an estimation of the smallest scale such that the point set has the local dimension of a surface (this method is both robust to noise and outliers, see \ref Classification_sowf_result).
|
||||
|
||||
The eigen analysis can be used to estimate normals. Note however that this analysis (based on Principal Component Analysis) might not be robust to a high level of noise. \cgal also provides more robust normal estimation functions (see for example [CGAL::jet_estimate_normals()](@ref jet_estimate_normals)).
|
||||
|
||||
The following code snippet shows how to instantiate such data structures from an input PLY point set (the full example is given at the end of the manual).
|
||||
|
||||
\snippet Classification/example_classification.cpp Analysis
|
||||
|
||||
\subsection Classification_labels Label Set
|
||||
|
||||
A label represents how an item should be classified, for example: _vegetation_, _building_, _road_, etc. In \cgal, a label has a name and is simply identified by a [Label_handle](@ref CGAL::Classification::Label_handle). Note that names are not used for identification: two labels in the same set can have the same name (but not the same handle).
|
||||
|
||||
The following code snippet shows how to add labels to the classification object.
|
||||
|
||||
\snippet Classification/example_classification.cpp Labels
|
||||
|
||||
\subsection Classification_features Feature Set
|
||||
|
||||
Features are defined as scalar fields that associate each input item with a specific value. Note that in order to limit memory consumption, we use the type `float` for these scalar values (as well as for every floating point value in this package). A feature has a name and is identified by a [Feature_handle](@ref CGAL::Classification::Feature_handle).
|
||||
|
||||
\cgal provides some predefined features that are relevant for classification of point sets:
|
||||
|
||||
- [Distance_to_plane](@ref CGAL::Classification::Feature::Distance_to_plane) measures how far away a point is from a locally estimated plane;
|
||||
- [Elevation](@ref CGAL::Classification::Feature::Elevation) computes the local distance to an estimation of the ground;
|
||||
- [Vertical_dispersion](@ref CGAL::Classification::Feature::Vertical_dispersion) computes how noisy the point set is on a local Z-cylinder;
|
||||
- [Verticality](@ref CGAL::Classification::Feature::Verticality) compares the local normal vector to the vertical vector.
|
||||
|
||||
For more details about how these different features can help to identify one label or the other, please refer to their associated reference manual pages. In addition, \cgal also provides features solely based on the eigenvalues \cgalCite{cgal:mbrsh-raofw-11} of the local covariance matrix:
|
||||
|
||||
- [Anisotropy](@ref CGAL::Classification::Feature::Anisotropy)
|
||||
- [Eigentropy](@ref CGAL::Classification::Feature::Eigentropy)
|
||||
- [Linearity](@ref CGAL::Classification::Feature::Linearity)
|
||||
- [Omnivariance](@ref CGAL::Classification::Feature::Omnivariance)
|
||||
- [Planarity](@ref CGAL::Classification::Feature::Planarity)
|
||||
- [Sphericity](@ref CGAL::Classification::Feature::Sphericity)
|
||||
- [Sum_eigenvalues](@ref CGAL::Classification::Feature::Sum_eigenvalues)
|
||||
- [Surface_variation](@ref CGAL::Classification::Feature::Surface_variation)
|
||||
|
||||
Finally, if the input data set has additional properties, these can also be used as features. For example, \cgal provides the following features:
|
||||
|
||||
- [Echo_scatter](@ref CGAL::Classification::Feature::Echo_scatter) uses the number of returns (echo) provided by most LIDAR scanners if available;
|
||||
- [Hsv](@ref CGAL::Classification::Feature::Hsv) uses input color information if available.
|
||||
|
||||
In the following code snippet, a subset of these features are instantiated. Note that all the predefined features can also be automatically generated in multiple scales (see \ref Classification_feature_generator).
|
||||
|
||||
\snippet Classification/example_classification.cpp Features
|
||||
|
||||
Users may want to define their own features, especially if the input data set comes with additional properties that were not anticipated by \cgal. A user-defined feature must inherit from [Feature_base](@ref CGAL::Classification::Feature_base) and provide a method [value()](@ref CGAL::Classification::Feature_base::value) that associates a scalar value to each input item.
|
||||
|
||||
The following example shows how to define a feature that discriminates
|
||||
points that lie inside a 2D box from the others:
|
||||
|
||||
\snippet Classification/example_feature.cpp Feature
|
||||
|
||||
This feature can then be instantiated from the feature set the same way as the others:
|
||||
|
||||
\snippet Classification/example_feature.cpp Addition
|
||||
|
||||
\subsection Classification_feature_generator Point Set Feature Generator
|
||||
|
||||
In standard classification of point sets, users commonly want to use all predefined features to get the best result possible. \cgal provides a class [Point_set_feature_generator](@ref CGAL::Classification::Point_set_feature_generator) that performs the following operations:
|
||||
|
||||
- it estimates the smallest relevant scale;
|
||||
- it generates all needed analysis structures and provides access to them;
|
||||
- it generates all possible features (among all the \cgal predefined ones) based on which property maps are available (it uses colors if available, etc.).
|
||||
|
||||
Multiple scales that are sequentially larger can be used to increase the quality of the results \cgalCite{cgal:hws-fsso3-16}. If \cgal is linked with \ref thirdpartyTBB, features can be computed in parallel to increase the overall computation speed.
|
||||
|
||||
Note that using this class in order to generate features is not mandatory, as features and data structures can all be handled by hand. It is mainly provided to make the specific case of point sets simpler to handle. Users can still add their own features within their feature set.
|
||||
|
||||
The following snippet shows how to use the feature generator:
|
||||
|
||||
\snippet Classification/example_generation_and_training.cpp Generator
|
||||
|
||||
\section Classification_classifiers Classifiers
|
||||
|
||||
%Classification relies on a classifier: this classifier is an object that, from the set of values taken by the features at an input item, computes the energy that measures the likelihood of an input item to belong to one label or another. A model of the concept `CGAL::Classification::Classifier` must take the index of an input item and store the energies associated to each label in a vector. If a classifier returns the value 0 for a pair of label and input item, it means that this item belongs to this label with certainty; large values mean that this item is not likely to belong to this label.
|
||||
|
||||
\cgal provides two models for this concept, [Sum_of_weighted_features_classifier](@ref CGAL::Classification::Sum_of_weighted_features_classifier) and [Random_forest_classifier](@ref CGAL::Classification::Random_forest_classifier).
|
||||
|
||||
To perform classification based on these classifiers, please refer to \ref Classification_classification_functions.
|
||||
|
||||
\subsection Classification_sowf Sum of Weighted Features
|
||||
|
||||
This first classifier defines the following attributes:
|
||||
|
||||
- a weight applied to each feature;
|
||||
- an effect applied to each pair of feature and label.
|
||||
|
||||
For each label, the classifier computes the energy as a sum of features normalized with both their weight and the effect they have on this specific label.
|
||||
|
||||
This classifier can be set up by hand but also embeds a training algorithm.
|
||||
|
||||
\subsubsection Classification_sowf_weights_effects Weights and Effects
|
||||
|
||||
Each feature is assigned a weight that measures its strength with respect to the other features.
|
||||
|
||||
Each pair of feature and label is assigned an effect that can either be:
|
||||
|
||||
- [FAVORING](@ref CGAL::Classification::Sum_of_weighted_features_classifier::FAVORING): the label is favored by high values of the feature;
|
||||
- [NEUTRAL](@ref CGAL::Classification::Sum_of_weighted_features_classifier::NEUTRAL): the label is not affected by the feature;
|
||||
- [PENALIZING](@ref CGAL::Classification::Sum_of_weighted_features_classifier::PENALIZING): the label is favored by low values of the feature.
|
||||
|
||||
For example, _vegetation_ is expected to have a high distance to plane and have a color close to green (if colors are available); _facades_ have a low distance to plane and a low verticality; etc.
|
||||
|
||||
Let \f$x=(x_i)_{i=1..N}\f$ be a potential classification result with \f$N\f$ the number of input items and \f$x_i\f$ the class of the \f$i^{th}\f$ item (for example: _vegetation_, _ground_, etc.). Let \f$f_j(i)\f$ be the raw value of the \f$j^{th}\f$ feature at the \f$i^{th}\f$ item and \f$w_j\f$ be the weight of this feature. We define the normalized value \f$F_j(x_i) \in [0:1]\f$ of the \f$j^{th}\f$ feature at the \f$i^{th}\f$ item as follows:
|
||||
|
||||
\f{eqnarray*}{
|
||||
F_j(x_i) = & (1 - \min(\max(0,\frac{f_j(i)}{w_j}), 1)) & \mbox{if } f_j \mbox{ favors } x_i \\
|
||||
& 0.5 & \mbox{if } f_j \mbox{ is neutral for } x_i \\
|
||||
& \min(\max(0,\frac{f_j(i)}{w_j}), 1) & \mbox{if } f_j \mbox{ penalizes } x_i
|
||||
\f}
|
||||
|
||||
The itemwise energy measures the coherence of the label \f$x_i\f$ at
|
||||
the \f$i^{th}\f$ item and is defined as:
|
||||
|
||||
\f[
|
||||
E_{di}(x_i) = \sum_{j = 1..N_f} F_j(x_i)
|
||||
\f]
|
||||
|
||||
|
||||
The following code snippet shows how to define the weights and effects of features and labels:
|
||||
|
||||
\snippet Classification/example_classification.cpp Weights
|
||||
|
||||
\subsubsection Classification_sowf_training Training
|
||||
|
||||
Each feature has a specific weight and each pair of feature-label has a specific effect. This means that the number of parameters to set up can quickly explode: if 6 features are used to classify between 4 labels, 30 parameters have to be set up (6 weights + 6x4 feature-label relationships).
|
||||
|
||||
Though it is possible to set them up one by one, \cgal also provides a method [train()](@ref CGAL::Classification::Sum_of_weighted_features_classifier::train) that requires a small set of ground truth items provided by users. More specifically, users must provide, for each label they want to classify, a set of known inliers among the input data set (for example, selecting one roof, one tree and one section of the ground). The training algorithm works as follows:
|
||||
|
||||
- for each feature, a range of weights is tested: the effect each feature have on each label is estimated. For a given weight, if a feature has the same effect on each label, it is non-relevant for classification. The range of weights such that the feature is relevant is estimated;
|
||||
|
||||
- for each feature, uniformly picked weight values are tested and their effects estimated;
|
||||
|
||||
- each inlier provided by the user is classified using this set of weights and effects;
|
||||
|
||||
- the mean intersection-over-union (see @ref Classification_evaluation) is used to evaluate the quality of this set of weights and effects;
|
||||
|
||||
- the same mechanism is repeated until all features' ranges have been tested. Weights are only changed one by one, the other ones kept to the values that gave the latest best score.
|
||||
|
||||
This usually converges to a satisfying solution (see Figure \cgalFigureRef{Classification_trainer_fig}). The number of trials is user defined, set to 300 by default. Using at least 10 times the number of features is advised (for example, at least 300 iterations if 30 features are used). If the solution is not satisfying, more inliers can be selected, for example, in a region that the user identifies as misclassified with the current configuration. The training algorithm keeps, as initialization, the best weights found at the previous round and carries on trying new weights by taking new inliers into account.
|
||||
|
||||
\cgalFigureBegin{Classification_trainer_fig,classif_training.png}
|
||||
Example of evolution of the mean intersection-over-union. The purple curve is the score computed at the current iteration, green curve is the best score found so far.
|
||||
\cgalFigureEnd
|
||||
|
||||
\subsubsection Classification_sowf_result Result
|
||||
|
||||
Figure \cgalFigureRef{Classification_sowf_result_fig} shows an example of output on a defect-laden point set. The accuracy on this example is 0.97 with a mean intersection-over-union of 0.85 (see section \ref Classification_evaluation).
|
||||
|
||||
\cgalFigureBegin{Classification_sowf_result_fig,noise_outliers.png}
|
||||
Example of classification on a point set with medium noise and outliers (left: input, right: output). _Ground_ is orange, _roofs_ are pink, _vegetation_ is green. Outliers are classified with an additional label _outlier_ in black.
|
||||
\cgalFigureEnd
|
||||
|
||||
|
||||
\subsection Classification_random_forest Random Forest
|
||||
|
||||
This second classifier is [Random_forest_classifier](@ref CGAL::Classification::Random_forest_classifier).
|
||||
It uses the \ref thirdpartyOpenCV library, more specifically the
|
||||
[Random Trees](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html)
|
||||
package. This classifier uses a ground truth training set to construct several
|
||||
decision trees that are then used to assign a label to each input
|
||||
item.
|
||||
|
||||
This classifier cannot be set up by hand and requires a ground truth
|
||||
training set. The training algorithm is faster but usually requires a
|
||||
higher number of inliers than the previous classifier.
|
||||
|
||||
An [example](\ref Classification_example_random_forest) shows how to
|
||||
use this classifier. For more details about the method, please refer
|
||||
to [the official documentation](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html)
|
||||
of OpenCV.
|
||||
|
||||
|
||||
\section Classification_classification_functions Classification Functions
|
||||
|
||||
%Classification is performed by minimizing an energy over the input data set that may include regularization. \cgal provides three different methods for classification, ranging from high speed / low quality to low speed / high quality:
|
||||
|
||||
- `CGAL::Classification::classify()`
|
||||
- `CGAL::Classification::classify_with_local_smoothing()`
|
||||
- `CGAL::Classification::classify_with_graphcut()`
|
||||
|
||||
On a point set of 3 millions of points, the first method takes about 4 seconds, the second about 40 seconds and the third about 2 minutes.
|
||||
|
||||
\cgalFigureBegin{Classification_image,classif.png}
|
||||
Top-Left: input point set. Top-Right: raw output classification represented by a set of colors (_ground_ is orange, _facades_ are blue, _roofs_ are pink and _vegetation_ is green). Bottom-Left: output classification using local smoothing. Bottom-Right: output classification using graphcut.
|
||||
\cgalFigureEnd
|
||||
|
||||
|
||||
Mathematical details are provided hereafter.
|
||||
|
||||
\subsection Classification_classify Raw classification
|
||||
|
||||
- `CGAL::Classification::classify()`: this is the fastest method
|
||||
that provides acceptable but usually noisy results (see Figure
|
||||
\cgalFigureRef{Classification_image}, top-right).
|
||||
|
||||
Let \f$x=(x_i)_{i=1..N}\f$ be a potential classification result with \f$N\f$ the number of input items and \f$x_i\f$ the label of the \f$i^{th}\f$ item (for example: _vegetation_, _ground_, etc.). The classification is performed by minimizing the following energy:
|
||||
|
||||
\f[
|
||||
E(x) = \sum_{i = 1..N} E_{di}(x_i)
|
||||
\f]
|
||||
|
||||
This energy is a sum of itemwise energies provided by the classifier and involves no regularization.
|
||||
|
||||
The following snippets show how to classify points based on a label
|
||||
set and a classifier. The result is stored in `label_indices`,
|
||||
following the same order as the input set and providing for each point
|
||||
the index (in the label set) of its assigned label.
|
||||
|
||||
\snippet Classification/example_classification.cpp Classify
|
||||
|
||||
\subsection Classification_smoothing Local Regularization
|
||||
|
||||
- `CGAL::Classification::classify_with_local_smoothing()`: this
|
||||
method is a tradeoff between quality and efficiency (see Figure
|
||||
\cgalFigureRef{Classification_image}, bottom-left). The minimized
|
||||
energy is defined as follows:
|
||||
|
||||
|
||||
\f[
|
||||
E(x) = \sum_{i = 1..N} E_{si}(x_i)
|
||||
\f]
|
||||
|
||||
The energy \f$E_{si}(x_i)\f$ is defined on a small local neighborhood
|
||||
\f$Nb(i)\f$ of the \f$i^{th}\f$ item (including itself):
|
||||
|
||||
\f[
|
||||
E_{si}(x_i) = \frac{\sum_{k \in Nb(i)} E_{di}(x_k)}{\left| Nb(i) \right|}
|
||||
\f]
|
||||
|
||||
This allows to eliminate local noisy variations of assigned
|
||||
labels. Increasing the size of the neighborhood
|
||||
increases the noise reduction at the cost of higher computation times.
|
||||
|
||||
The following snippets show how to classify points using local
|
||||
smoothing by providing a model of `CGAL::Classification::NeighborQuery`.
|
||||
|
||||
\snippet Classification/example_classification.cpp Smoothing
|
||||
|
||||
\subsection Classification_graphcut Global Regularization (Graph Cut)
|
||||
|
||||
- `CGAL::Classification::classify_with_graphcut()`: this method
|
||||
offers the best quality but requires longer computation time (see
|
||||
Figure \cgalFigureRef{Classification_image}, bottom-right). The
|
||||
total energy that is minimized is the sum of the partial data term
|
||||
\f$E_{di}(x_i)\f$ and of a pairwise interaction energy defined by the
|
||||
standard Potts model \cgalCite{cgal:l-mrfmi-09} :
|
||||
|
||||
\f[
|
||||
E(x) = \sum_{i = 1..N} E_{di}(x_i) + \gamma \sum_{i \sim j} \mathbf{1}_{x_i \neq x_j}
|
||||
\f]
|
||||
|
||||
where \f$\gamma>0\f$ is the parameter of the Potts model that
|
||||
quantifies the strengh of the regularization, \f$i \sim j\f$
|
||||
represents the pairs of neighboring items and
|
||||
\f$\mathbf{1}_{\{.\}}\f$ the characteristic function.
|
||||
|
||||
A _Graph Cut_ based algorithm (Alpha Expansion) is used to quickly reach
|
||||
an approximate solution close to the global optimum of this energy.
|
||||
|
||||
This method allows to consistently segment the input data set in
|
||||
piecewise constant parts and to correct large wrongly classified
|
||||
clusters. Increasing \f$\gamma\f$ produces more regular result with a
|
||||
constant computation time.
|
||||
|
||||
To speed up computations, the input domain can be subdivided into
|
||||
smaller subsets such that several smaller graph cuts are applied
|
||||
instead of a big one. The computation of these smaller graph cuts can
|
||||
be done in parallel. Increasing the number of subsets allows for
|
||||
faster computation times but can also reduce the quality of the
|
||||
results.
|
||||
|
||||
The following snippet shows how to classify points using a graph cut
|
||||
regularization providing a model of
|
||||
`CGAL::Classification::NeighborQuery`, a strengh parameter
|
||||
\f$\gamma\f$ and a number of subdivisions.
|
||||
|
||||
\snippet Classification/example_classification.cpp Graph_cut
|
||||
|
||||
\section Classification_evaluation Evaluation
|
||||
|
||||
The class [Evaluation](@ref CGAL::Classification::Evaluation) allows
|
||||
users to evaluate the reliability of the classification with respect
|
||||
to a provided ground truth. The following measurements are available:
|
||||
|
||||
- [precision()](@ref CGAL::Classification::Evaluation::precision) computes, for one label, the ratio of true positives over the total number of detected positives;
|
||||
- [recall()](@ref CGAL::Classification::Evaluation::recall) computes, for one label, the ratio of true positives over the total number of provided inliers of this label;
|
||||
- [f1_score()](@ref CGAL::Classification::Evaluation::f1_score) is the harmonic mean of precision and recall;
|
||||
- [intersection_over_union()](@ref CGAL::Classification::Evaluation::intersection_over_union) computes the ratio of true positives over the union of the detected positives and of the provided inliers;
|
||||
- [accuracy()](@ref CGAL::Classification::Evaluation::accuracy) computes the ratio of all true positives over the total number of provided inliers;
|
||||
- [mean_f1_score()](@ref CGAL::Classification::Evaluation::mean_f1_score);
|
||||
- [mean_intersection_over_union()](@ref CGAL::Classification::Evaluation::mean_intersection_over_union).
|
||||
|
||||
All these values range from 0 (poor quality) to 1 (perfect quality).
|
||||
|
||||
\section Classification_examples Full Examples
|
||||
|
||||
\subsection Classification_example_general Classification
|
||||
|
||||
The following example:
|
||||
|
||||
- reads an input file (LIDAR point set in PLY format);
|
||||
- computes useful structures from this input;
|
||||
- computes features from the input and the precomputed structures;
|
||||
- defines 3 labels (_vegetation_, _ground_ and _roof_);
|
||||
- sets up the classification classifier [Sum_of_weighted_features_classifier](@ref CGAL::Classification::Sum_of_weighted_features_classifier);
|
||||
- classifies the point set with the 3 different methods (this is for
|
||||
the sake of the example: each method overwrites the previous result,
|
||||
users should only call one of the methods);
|
||||
- saves the result in a colored PLY format.
|
||||
|
||||
\cgalExample{Classification/example_classification.cpp}
|
||||
|
||||
\subsection Classification_example_feeature Defining a Custom Feature
|
||||
|
||||
The following example shows how to define a custom feature and how to integrate it in the \cgal classification framework.
|
||||
|
||||
\cgalExample{Classification/example_feature.cpp}
|
||||
|
||||
\subsection Classification_example_training Feature Generation and Training
|
||||
|
||||
The following example:
|
||||
|
||||
- reads a point set with a training set (embedded as a PLY feature _label_);
|
||||
- automatically generates features on 5 scales;
|
||||
- trains the classifier [Sum_of_weighted_features_classifier](@ref CGAL::Classification::Sum_of_weighted_features_classifier) using 800 trials;
|
||||
- runs the algorithm using the graphcut regularization;
|
||||
- prints some evaluation measurements;
|
||||
- saves the configuration of the classifier for further use.
|
||||
|
||||
\cgalExample{Classification/example_generation_and_training.cpp}
|
||||
|
||||
\subsection Classification_example_random_forest Random Forest
|
||||
|
||||
The following example shows how to use the classifier [Random_forest_classifier](@ref CGAL::Classification::Random_forest_classifier) using an input training set.
|
||||
|
||||
\cgalExample{Classification/example_random_forest.cpp}
|
||||
|
||||
\section Classification_history History
|
||||
|
||||
This package is based on a research code by [Florent Lafarge](https://www-sop.inria.fr/members/Florent.Lafarge/) that was generalized, extended and packaged by [Simon Giraudot](http://geometryfactory.com/who-we-are/).
|
||||
|
||||
|
||||
|
||||
*/
|
||||
} /* namespace CGAL */
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
namespace CGAL
|
||||
{
|
||||
|
||||
namespace Classification
|
||||
{
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationConcepts
|
||||
\cgalConcept
|
||||
|
||||
Concept describing a classifier used by classification functions (see
|
||||
`CGAL::Classification::classify()`, `CGAL::Classification::classify_with_local_smoothing()` and
|
||||
`CGAL::Classification::classify_with_graphcut()`).
|
||||
|
||||
\cgalHasModel `CGAL::Classification::Sum_of_weighted_features_classifier`
|
||||
\cgalHasModel `CGAL::Classification::Random_forest_classifier`
|
||||
|
||||
*/
|
||||
class Classifier
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
\brief Computes for each label indexed from 0 to `out.size()`, the
|
||||
energy of this label applied to point at `item_index`.
|
||||
*/
|
||||
void operator() (std::size_t item_index, std::vector<float>& out) const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
namespace CGAL
|
||||
{
|
||||
|
||||
namespace Classification
|
||||
{
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationConcepts
|
||||
\cgalConcept
|
||||
|
||||
Concept describing a neighbor query used for classification.
|
||||
|
||||
\cgalHasModel `CGAL::Classification::Point_set_neighborhood::K_neighbor_query`
|
||||
\cgalHasModel `CGAL::Classification::Point_set_neighborhood::Sphere_neighbor_query`
|
||||
|
||||
*/
|
||||
class NeighborQuery
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
\brief Type of the data that is classified.
|
||||
*/
|
||||
typedef unspecified_type value_type;
|
||||
|
||||
/*!
|
||||
|
||||
\brief Puts in `output` the indices of the neighbors of `query`.
|
||||
|
||||
\tparam OutputIterator An output iterator accepting `std::size_t`
|
||||
values.
|
||||
*/
|
||||
template <typename OutputIterator>
|
||||
OutputIterator operator() (const value_type& query, OutputIterator output) const;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
|
||||
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Classification"
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*!
|
||||
\defgroup PkgClassification Classification Reference
|
||||
|
||||
\defgroup PkgClassificationConcepts Concepts
|
||||
\ingroup PkgClassification
|
||||
|
||||
\defgroup PkgClassificationMain Main Functions
|
||||
\ingroup PkgClassification
|
||||
|
||||
Functions that perform classification based on a set of labels and a classifier, with or without regularization.
|
||||
|
||||
\defgroup PkgClassificationClassifiers Classifiers
|
||||
\ingroup PkgClassification
|
||||
|
||||
Classifiers are functors that, given a label set and an input item, associate this input item with an energy for each label. This energy measures the likelihood of the item to belong to this label.
|
||||
|
||||
\defgroup PkgClassificationDataStructures Data Structures
|
||||
\ingroup PkgClassification
|
||||
|
||||
Useful data structures that are used to compute features (computation of eigenvalues, for example) and to regularize classification (neighborhood).
|
||||
|
||||
\defgroup PkgClassificationLabel Label
|
||||
\ingroup PkgClassification
|
||||
|
||||
A label represents how an item should be classified, for example: _vegetation_, _building_, _road_, etc.
|
||||
|
||||
\defgroup PkgClassificationFeature Feature
|
||||
\ingroup PkgClassification
|
||||
|
||||
Features are defined as scalar fields that associates each input item with a specific value.
|
||||
|
||||
\defgroup PkgClassificationFeatures Predefined Features
|
||||
\ingroup PkgClassification
|
||||
|
||||
\cgal provides some predefined features that are relevant for classification of point sets.
|
||||
|
||||
\addtogroup PkgClassification
|
||||
|
||||
\cgalPkgDescriptionBegin{Classification, PkgClassificationSummary}
|
||||
\cgalPkgPicture{data_classif.png}
|
||||
|
||||
\cgalPkgSummaryBegin
|
||||
\cgalPkgAuthors{Simon Giraudot, Florent Lafarge}
|
||||
\cgalPkgDesc{This component implements an algorithm that classifies a data set into a user-defined set of labels (such as ground, vegetation, buildings, etc.). A flexible API is provided so that users can classify any type of data, compute their own local features on the input data set and define their own labels.}
|
||||
\cgalPkgManuals{Chapter_Classification, PkgClassification}
|
||||
\cgalPkgSummaryEnd
|
||||
|
||||
\cgalPkgShortInfoBegin
|
||||
\cgalPkgSince{4.9}
|
||||
\cgalPkgBib{cgal:lm-clscm-12}
|
||||
\cgalPkgLicense{\ref licensesGPL "GPL"}
|
||||
\cgalPkgDemo{Operations on Polyhedra,polyhedron_3.zip}
|
||||
\cgalPkgShortInfoEnd
|
||||
|
||||
\cgalPkgDescriptionEnd
|
||||
|
||||
\cgalClassifedRefPages
|
||||
|
||||
## Concepts ##
|
||||
|
||||
- `CGAL::Classification::Classifier`
|
||||
- `CGAL::Classification::NeighborQuery`
|
||||
|
||||
## Main Functions ##
|
||||
|
||||
- `CGAL::Classification::classify()`
|
||||
- `CGAL::Classification::classify_with_local_smoothing()`
|
||||
- `CGAL::Classification::classify_with_graphcut()`
|
||||
|
||||
## Classifiers ##
|
||||
|
||||
- `CGAL::Classification::Sum_of_weighted_features_classifier`
|
||||
- `CGAL::Classification::Random_forest_classifier`
|
||||
|
||||
## Data Structures ##
|
||||
|
||||
- `CGAL::Classification::Local_eigen_analysis`
|
||||
- `CGAL::Classification::Planimetric_grid<Geom_traits, PointRange, PointMap>`
|
||||
- `CGAL::Classification::Point_set_feature_generator<Geom_traits, PointRange, PointMap, ConcurrencyTag, DiagonalizeTraits>`
|
||||
- `CGAL::Classification::Point_set_neighborhood<Geom_traits, PointRange, PointMap>`
|
||||
- `CGAL::Classification::Evaluation`
|
||||
|
||||
## Label ##
|
||||
|
||||
- `CGAL::Classification::Label`
|
||||
- `CGAL::Classification::Label_handle`
|
||||
- `CGAL::Classification::Label_set`
|
||||
|
||||
## Feature ##
|
||||
|
||||
- `CGAL::Classification::Feature_base`
|
||||
- `CGAL::Classification::Feature_handle`
|
||||
- `CGAL::Classification::Feature_set`
|
||||
|
||||
## Predefined Features ##
|
||||
|
||||
- `CGAL::Classification::Feature::Anisotropy`
|
||||
- `CGAL::Classification::Feature::Distance_to_plane<PointRange, PointMap>`
|
||||
- `CGAL::Classification::Feature::Echo_scatter<Geom_traits, PointRange, PointMap, EchoMap>`
|
||||
- `CGAL::Classification::Feature::Eigentropy`
|
||||
- `CGAL::Classification::Feature::Elevation<Geom_traits, PointRange, PointMap>`
|
||||
- `CGAL::Classification::Feature::Hsv<Geom_traits, PointRange, ColorMap>`
|
||||
- `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<Geom_traits, PointRange, PointMap>`
|
||||
- `CGAL::Classification::Feature::Verticality<Geom_traits>`
|
||||
|
||||
|
||||
*/
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
Manual
|
||||
Kernel_23
|
||||
STL_Extension
|
||||
Algebraic_foundations
|
||||
Circulator
|
||||
Stream_support
|
||||
Solver_interface
|
||||
Point_set_processing_3
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
\example Classification/example_classification.cpp
|
||||
\example Classification/example_feature.cpp
|
||||
\example Classification/example_generation_and_training.cpp
|
||||
\example Classification/example_random_forest.cpp
|
||||
*/
|
||||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -0,0 +1,66 @@
|
|||
# Created by the script cgal_create_CMakeLists
|
||||
# This is the CMake script for compiling a set of CGAL applications.
|
||||
|
||||
project( Classification )
|
||||
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
# CGAL and its components
|
||||
find_package( CGAL QUIET COMPONENTS )
|
||||
|
||||
if ( NOT CGAL_FOUND )
|
||||
|
||||
message(STATUS "This project requires the CGAL library, and will not be compiled.")
|
||||
return()
|
||||
|
||||
endif()
|
||||
|
||||
# include helper file
|
||||
include( ${CGAL_USE_FILE} )
|
||||
|
||||
|
||||
# Boost and its components
|
||||
find_package( Boost REQUIRED )
|
||||
|
||||
if ( NOT Boost_FOUND )
|
||||
|
||||
message(STATUS "This project requires the Boost library, and will not be compiled.")
|
||||
|
||||
return()
|
||||
|
||||
endif()
|
||||
|
||||
find_package( TBB )
|
||||
if( TBB_FOUND )
|
||||
include(${TBB_USE_FILE})
|
||||
list(APPEND CGAL_3RD_PARTY_LIBRARIES ${TBB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
find_package(OpenCV)
|
||||
|
||||
# include for local directory
|
||||
include_directories( BEFORE include )
|
||||
|
||||
# include for local package
|
||||
include_directories( BEFORE ../../include )
|
||||
|
||||
|
||||
# Creating entries for all C++ files with "main" routine
|
||||
# ##########################################################
|
||||
|
||||
include( CGAL_CreateSingleSourceCGALProgram )
|
||||
|
||||
create_single_source_cgal_program( "example_classification.cpp" )
|
||||
create_single_source_cgal_program( "example_generation_and_training.cpp" )
|
||||
create_single_source_cgal_program( "example_feature.cpp" )
|
||||
|
||||
if( OpenCV_FOUND )
|
||||
include_directories( ${OpenCV_INCLUDE_DIRS} )
|
||||
create_single_source_cgal_program( "example_random_forest.cpp" )
|
||||
target_link_libraries( example_random_forest ${OpenCV_LIBS} )
|
||||
else()
|
||||
message(STATUS "OpenCV not found, random forest example won't be compiled.")
|
||||
endif()
|
||||
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Classification.h>
|
||||
#include <CGAL/bounding_box.h>
|
||||
|
||||
#include <CGAL/IO/read_ply_points.h>
|
||||
|
||||
#include <CGAL/Real_timer.h>
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
typedef Kernel::Point_3 Point;
|
||||
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
|
||||
typedef std::vector<Point> Point_range;
|
||||
typedef CGAL::Identity_property_map<Point> Pmap;
|
||||
|
||||
namespace Classification = CGAL::Classification;
|
||||
|
||||
typedef Classification::Sum_of_weighted_features_classifier Classifier;
|
||||
|
||||
typedef Classification::Planimetric_grid<Kernel, Point_range, Pmap> Planimetric_grid;
|
||||
typedef Classification::Point_set_neighborhood<Kernel, Point_range, Pmap> Neighborhood;
|
||||
typedef Classification::Local_eigen_analysis Local_eigen_analysis;
|
||||
|
||||
typedef Classification::Label_handle Label_handle;
|
||||
typedef Classification::Feature_handle Feature_handle;
|
||||
typedef Classification::Label_set Label_set;
|
||||
typedef Classification::Feature_set Feature_set;
|
||||
|
||||
typedef Classification::Feature::Distance_to_plane<Point_range, Pmap> Distance_to_plane;
|
||||
typedef Classification::Feature::Linearity Linearity;
|
||||
typedef Classification::Feature::Omnivariance Omnivariance;
|
||||
typedef Classification::Feature::Planarity Planarity;
|
||||
typedef Classification::Feature::Surface_variation Surface_variation;
|
||||
typedef Classification::Feature::Elevation<Kernel, Point_range, Pmap> Elevation;
|
||||
typedef Classification::Feature::Vertical_dispersion<Kernel, Point_range, Pmap> Dispersion;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Analysis]
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
std::string filename (argc > 1 ? argv[1] : "data/b9.ply");
|
||||
std::ifstream in (filename.c_str());
|
||||
std::vector<Point> pts;
|
||||
|
||||
std::cerr << "Reading input" << std::endl;
|
||||
if (!in
|
||||
|| !(CGAL::read_ply_points (in, std::back_inserter (pts))))
|
||||
{
|
||||
std::cerr << "Error: cannot read " << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
float grid_resolution = 0.34;
|
||||
float radius_neighbors = 1.7;
|
||||
float radius_dtm = 15.0;
|
||||
|
||||
std::cerr << "Computing useful structures" << std::endl;
|
||||
|
||||
Iso_cuboid_3 bbox = CGAL::bounding_box (pts.begin(), pts.end());
|
||||
|
||||
Planimetric_grid grid (pts, Pmap(), bbox, grid_resolution);
|
||||
Neighborhood neighborhood (pts, Pmap());
|
||||
Local_eigen_analysis eigen (pts, Pmap(), neighborhood.k_neighbor_query(6));
|
||||
|
||||
//! [Analysis]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Features]
|
||||
|
||||
std::cerr << "Computing features" << std::endl;
|
||||
Feature_set features;
|
||||
Feature_handle distance_to_plane = features.add<Distance_to_plane> (pts, Pmap(), eigen);
|
||||
Feature_handle linearity = features.add<Linearity> (pts, eigen);
|
||||
Feature_handle omnivariance = features.add<Omnivariance> (pts, eigen);
|
||||
Feature_handle planarity = features.add<Planarity> (pts, eigen);
|
||||
Feature_handle surface_variation = features.add<Surface_variation> (pts, eigen);
|
||||
Feature_handle dispersion = features.add<Dispersion> (pts, Pmap(), grid,
|
||||
radius_neighbors);
|
||||
Feature_handle elevation = features.add<Elevation> (pts, Pmap(), grid,
|
||||
radius_dtm);
|
||||
|
||||
//! [Features]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Labels]
|
||||
|
||||
Label_set labels;
|
||||
Label_handle ground = labels.add ("ground");
|
||||
Label_handle vegetation = labels.add ("vegetation");
|
||||
Label_handle roof = labels.add ("roof");
|
||||
|
||||
//! [Labels]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Weights]
|
||||
|
||||
std::cerr << "Setting weights" << std::endl;
|
||||
Classifier classifier (labels, features);
|
||||
classifier.set_weight (distance_to_plane, 6.75e-2);
|
||||
classifier.set_weight (linearity, 1.19);
|
||||
classifier.set_weight (omnivariance, 1.34e-1);
|
||||
classifier.set_weight (planarity, 7.32e-1);
|
||||
classifier.set_weight (surface_variation, 1.36e-1);
|
||||
classifier.set_weight (dispersion, 5.45e-1);
|
||||
classifier.set_weight (elevation, 1.47e1);
|
||||
|
||||
std::cerr << "Setting effects" << std::endl;
|
||||
classifier.set_effect (ground, distance_to_plane, Classifier::NEUTRAL);
|
||||
classifier.set_effect (ground, linearity, Classifier::PENALIZING);
|
||||
classifier.set_effect (ground, omnivariance, Classifier::NEUTRAL);
|
||||
classifier.set_effect (ground, planarity, Classifier::FAVORING);
|
||||
classifier.set_effect (ground, surface_variation, Classifier::PENALIZING);
|
||||
classifier.set_effect (ground, dispersion, Classifier::NEUTRAL);
|
||||
classifier.set_effect (ground, elevation, Classifier::PENALIZING);
|
||||
|
||||
classifier.set_effect (vegetation, distance_to_plane, Classifier::FAVORING);
|
||||
classifier.set_effect (vegetation, linearity, Classifier::NEUTRAL);
|
||||
classifier.set_effect (vegetation, omnivariance, Classifier::FAVORING);
|
||||
classifier.set_effect (vegetation, planarity, Classifier::NEUTRAL);
|
||||
classifier.set_effect (vegetation, surface_variation, Classifier::NEUTRAL);
|
||||
classifier.set_effect (vegetation, dispersion, Classifier::FAVORING);
|
||||
classifier.set_effect (vegetation, elevation, Classifier::NEUTRAL);
|
||||
|
||||
classifier.set_effect (roof, distance_to_plane, Classifier::NEUTRAL);
|
||||
classifier.set_effect (roof, linearity, Classifier::PENALIZING);
|
||||
classifier.set_effect (roof, omnivariance, Classifier::FAVORING);
|
||||
classifier.set_effect (roof, planarity, Classifier::FAVORING);
|
||||
classifier.set_effect (roof, surface_variation, Classifier::PENALIZING);
|
||||
classifier.set_effect (roof, dispersion, Classifier::NEUTRAL);
|
||||
classifier.set_effect (roof, elevation, Classifier::FAVORING);
|
||||
|
||||
//! [Weights]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Run classification
|
||||
std::cerr << "Classifying" << std::endl;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Classify]
|
||||
std::vector<std::size_t> label_indices;
|
||||
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
Classification::classify<CGAL::Parallel_tag> (pts, labels, classifier, label_indices);
|
||||
t.stop();
|
||||
std::cerr << "Raw classification performed in " << t.time() << " second(s)" << std::endl;
|
||||
t.reset();
|
||||
//! [Classify]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Smoothing]
|
||||
t.start();
|
||||
Classification::classify_with_local_smoothing<CGAL::Parallel_tag>
|
||||
(pts, Pmap(), labels, classifier,
|
||||
neighborhood.sphere_neighbor_query(radius_neighbors),
|
||||
label_indices);
|
||||
t.stop();
|
||||
std::cerr << "Classification with local smoothing performed in " << t.time() << " second(s)" << std::endl;
|
||||
t.reset();
|
||||
//! [Smoothing]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Graph_cut]
|
||||
t.start();
|
||||
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
||||
(pts, Pmap(), labels, classifier,
|
||||
neighborhood.k_neighbor_query(12),
|
||||
0.2, 4, label_indices);
|
||||
t.stop();
|
||||
std::cerr << "Classification with graphcut performed in " << t.time() << " second(s)" << std::endl;
|
||||
//! [Graph_cut]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Save the output in a colored PLY format
|
||||
|
||||
std::ofstream f ("classification.ply");
|
||||
f << "ply" << std::endl
|
||||
<< "format ascii 1.0" << std::endl
|
||||
<< "element vertex " << pts.size() << std::endl
|
||||
<< "property float x" << std::endl
|
||||
<< "property float y" << std::endl
|
||||
<< "property float z" << std::endl
|
||||
<< "property uchar red" << std::endl
|
||||
<< "property uchar green" << std::endl
|
||||
<< "property uchar blue" << std::endl
|
||||
<< "end_header" << std::endl;
|
||||
|
||||
for (std::size_t i = 0; i < pts.size(); ++ i)
|
||||
{
|
||||
f << pts[i] << " ";
|
||||
|
||||
Label_handle label = labels[label_indices[i]];
|
||||
if (label == ground)
|
||||
f << "245 180 0" << std::endl;
|
||||
else if (label == vegetation)
|
||||
f << "0 255 27" << std::endl;
|
||||
else if (label == roof)
|
||||
f << "255 0 170" << std::endl;
|
||||
else
|
||||
{
|
||||
f << "0 0 0" << std::endl;
|
||||
std::cerr << "Error: unknown classification label" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "All done" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Classification.h>
|
||||
#include <CGAL/IO/read_ply_points.h>
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
typedef Kernel::Point_3 Point;
|
||||
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
|
||||
typedef std::vector<Point> Point_range;
|
||||
typedef CGAL::Identity_property_map<Point> Pmap;
|
||||
|
||||
namespace Classification = CGAL::Classification;
|
||||
|
||||
typedef Classification::Sum_of_weighted_features_classifier Classifier;
|
||||
|
||||
typedef Classification::Point_set_neighborhood<Kernel, Point_range, Pmap> Neighborhood;
|
||||
typedef Classification::Local_eigen_analysis Local_eigen_analysis;
|
||||
|
||||
typedef Classification::Label_handle Label_handle;
|
||||
typedef Classification::Feature_handle Feature_handle;
|
||||
typedef Classification::Label_set Label_set;
|
||||
typedef Classification::Feature_set Feature_set;
|
||||
|
||||
typedef Classification::Feature::Sphericity Sphericity;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Feature]
|
||||
|
||||
// User-defined feature that identifies a specific area of the 3D
|
||||
// space. This feature takes value 1 for points that lie inside the
|
||||
// area and 0 for the others.
|
||||
class My_feature : public CGAL::Classification::Feature_base
|
||||
{
|
||||
const Point_range& range;
|
||||
double xmin, xmax, ymin, ymax;
|
||||
public:
|
||||
My_feature (const Point_range& range,
|
||||
double xmin, double xmax, double ymin, double ymax)
|
||||
: range (range), xmin(xmin), xmax(xmax), ymin(ymin), ymax(ymax)
|
||||
{
|
||||
this->set_name ("my_feature");
|
||||
}
|
||||
|
||||
float value (std::size_t pt_index)
|
||||
{
|
||||
if (xmin < range[pt_index].x() && range[pt_index].x() < xmax &&
|
||||
ymin < range[pt_index].y() && range[pt_index].y() < ymax)
|
||||
return 1.f;
|
||||
else
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//! [Feature]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
std::string filename (argc > 1 ? argv[1] : "data/b9.ply");
|
||||
std::ifstream in (filename.c_str());
|
||||
std::vector<Point> pts;
|
||||
|
||||
std::cerr << "Reading input" << std::endl;
|
||||
if (!in
|
||||
|| !(CGAL::read_ply_points (in, std::back_inserter (pts))))
|
||||
{
|
||||
std::cerr << "Error: cannot read " << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Neighborhood neighborhood (pts, Pmap());
|
||||
Local_eigen_analysis eigen (pts, Pmap(), neighborhood.k_neighbor_query(6));
|
||||
|
||||
Label_set labels;
|
||||
Label_handle a = labels.add ("label_A");
|
||||
Label_handle b = labels.add ("label_B");
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Addition]
|
||||
|
||||
std::cerr << "Computing features" << std::endl;
|
||||
Feature_set features;
|
||||
|
||||
// Feature that identifies points whose x coordinate is between -20
|
||||
// and 20 and whose y coordinate is between -15 and 15
|
||||
Feature_handle my_feature = features.add<My_feature> (pts, -20., 20., -15., 15.);
|
||||
|
||||
//! [Addition]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
Feature_handle sphericity = features.add<Sphericity> (pts, eigen);
|
||||
|
||||
Classifier classifier (labels, features);
|
||||
|
||||
std::cerr << "Setting weights" << std::endl;
|
||||
classifier.set_weight(sphericity, 0.5);
|
||||
classifier.set_weight(my_feature, 0.25);
|
||||
|
||||
std::cerr << "Setting up labels" << std::endl;
|
||||
classifier.set_effect (a, sphericity, Classifier::FAVORING);
|
||||
classifier.set_effect (a, my_feature, Classifier::FAVORING);
|
||||
classifier.set_effect (b, sphericity, Classifier::PENALIZING);
|
||||
classifier.set_effect (b, my_feature, Classifier::PENALIZING);
|
||||
|
||||
std::vector<std::size_t> label_indices;
|
||||
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
||||
(pts, Pmap(), labels, classifier,
|
||||
neighborhood.k_neighbor_query(12),
|
||||
0.5, 1, label_indices);
|
||||
|
||||
std::cerr << "All done" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
//#define CGAL_CLASSIFICATION_VERBOSE
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Classification.h>
|
||||
#include <CGAL/IO/read_ply_points.h>
|
||||
|
||||
#include <CGAL/Real_timer.h>
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
typedef Kernel::Point_3 Point;
|
||||
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
|
||||
typedef std::vector<Point> Point_range;
|
||||
typedef CGAL::Identity_property_map<Point> Pmap;
|
||||
|
||||
namespace Classification = CGAL::Classification;
|
||||
|
||||
typedef Classification::Label_handle Label_handle;
|
||||
typedef Classification::Feature_handle Feature_handle;
|
||||
typedef Classification::Label_set Label_set;
|
||||
typedef Classification::Feature_set Feature_set;
|
||||
|
||||
typedef Classification::Sum_of_weighted_features_classifier Classifier;
|
||||
|
||||
typedef Classification::Point_set_feature_generator<Kernel, Point_range, Pmap> Feature_generator;
|
||||
|
||||
/*
|
||||
This interpreter is used to read a PLY input that contains training
|
||||
attributes (with the PLY "label" property).
|
||||
*/
|
||||
class My_ply_interpreter
|
||||
{
|
||||
std::vector<Point>& points;
|
||||
std::vector<std::size_t>& labels;
|
||||
|
||||
public:
|
||||
My_ply_interpreter (std::vector<Point>& points,
|
||||
std::vector<std::size_t>& labels)
|
||||
: points (points), labels (labels)
|
||||
{ }
|
||||
|
||||
// Init and test if input file contains the right properties
|
||||
bool is_applicable (CGAL::Ply_reader& reader)
|
||||
{
|
||||
return reader.does_tag_exist<double> ("x")
|
||||
&& reader.does_tag_exist<double> ("y")
|
||||
&& reader.does_tag_exist<double> ("z")
|
||||
&& reader.does_tag_exist<int> ("label");
|
||||
}
|
||||
|
||||
// Describes how to process one line (= one point object)
|
||||
void process_line (CGAL::Ply_reader& reader)
|
||||
{
|
||||
double x = 0., y = 0., z = 0.;
|
||||
int l = 0;
|
||||
|
||||
reader.assign (x, "x");
|
||||
reader.assign (y, "y");
|
||||
reader.assign (z, "z");
|
||||
reader.assign (l, "label");
|
||||
|
||||
points.push_back (Point (x, y, z));
|
||||
labels.push_back(std::size_t(l));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
std::string filename (argc > 1 ? argv[1] : "data/b9_training.ply");
|
||||
std::ifstream in (filename.c_str());
|
||||
std::vector<Point> pts;
|
||||
std::vector<std::size_t> ground_truth;
|
||||
|
||||
std::cerr << "Reading input" << std::endl;
|
||||
My_ply_interpreter interpreter (pts, ground_truth);
|
||||
if (!in
|
||||
|| !(CGAL::read_ply_custom_points (in, interpreter, Kernel())))
|
||||
{
|
||||
std::cerr << "Error: cannot read " << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
//! [Generator]
|
||||
|
||||
Feature_set features;
|
||||
|
||||
std::cerr << "Generating features" << std::endl;
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
Feature_generator generator (features, pts, Pmap(),
|
||||
5); // using 5 scales
|
||||
t.stop();
|
||||
std::cerr << features.size() << " feature(s) generated in " << t.time() << " second(s)" << std::endl;
|
||||
|
||||
//! [Generator]
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add types
|
||||
Label_set labels;
|
||||
Label_handle ground = labels.add ("ground");
|
||||
Label_handle vegetation = labels.add ("vegetation");
|
||||
Label_handle roof = labels.add ("roof");
|
||||
Label_handle facade = labels.add ("facade");
|
||||
|
||||
Classifier classifier (labels, features);
|
||||
|
||||
std::cerr << "Training" << std::endl;
|
||||
t.reset();
|
||||
t.start();
|
||||
classifier.train<CGAL::Sequential_tag> (ground_truth, 800);
|
||||
t.stop();
|
||||
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
|
||||
|
||||
t.reset();
|
||||
t.start();
|
||||
std::vector<std::size_t> label_indices;
|
||||
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
||||
(pts, Pmap(), labels, classifier,
|
||||
generator.neighborhood().k_neighbor_query(12),
|
||||
0.2, 10, label_indices);
|
||||
t.stop();
|
||||
std::cerr << "Classification with graphcut done in " << t.time() << " second(s)" << std::endl;
|
||||
|
||||
std::cerr << "Precision, recall, F1 scores and IoU:" << std::endl;
|
||||
Classification::Evaluation evaluation (labels, ground_truth, label_indices);
|
||||
|
||||
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||
{
|
||||
std::cerr << " * " << labels[i]->name() << ": "
|
||||
<< evaluation.precision(labels[i]) << " ; "
|
||||
<< evaluation.recall(labels[i]) << " ; "
|
||||
<< evaluation.f1_score(labels[i]) << " ; "
|
||||
<< evaluation.intersection_over_union(labels[i]) << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Accuracy = " << evaluation.accuracy() << std::endl
|
||||
<< "Mean F1 score = " << evaluation.mean_f1_score() << std::endl
|
||||
<< "Mean IoU = " << evaluation.mean_intersection_over_union() << std::endl;
|
||||
|
||||
|
||||
/// Save the configuration to be able to reload it later
|
||||
std::ofstream fconfig ("config.xml");
|
||||
classifier.save_configuration (fconfig);
|
||||
fconfig.close();
|
||||
|
||||
std::cerr << "All done" << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
//#define CGAL_CLASSIFICATION_VERBOSE
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Classification.h>
|
||||
#include <CGAL/Classification/Random_forest_classifier.h>
|
||||
#include <CGAL/IO/read_ply_points.h>
|
||||
|
||||
#include <CGAL/Real_timer.h>
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
typedef Kernel::Point_3 Point;
|
||||
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
|
||||
typedef std::vector<Point> Point_range;
|
||||
typedef CGAL::Identity_property_map<Point> Pmap;
|
||||
|
||||
namespace Classification = CGAL::Classification;
|
||||
|
||||
typedef Classification::Label_handle Label_handle;
|
||||
typedef Classification::Feature_handle Feature_handle;
|
||||
typedef Classification::Label_set Label_set;
|
||||
typedef Classification::Feature_set Feature_set;
|
||||
|
||||
typedef Classification::Random_forest_classifier Classifier;
|
||||
|
||||
typedef Classification::Point_set_feature_generator<Kernel, Point_range, Pmap> Feature_generator;
|
||||
|
||||
/*
|
||||
This interpreter is used to read a PLY input that contains training
|
||||
attributes (with the PLY "label" property).
|
||||
*/
|
||||
class My_ply_interpreter
|
||||
{
|
||||
std::vector<Point>& points;
|
||||
std::vector<std::size_t>& labels;
|
||||
|
||||
public:
|
||||
My_ply_interpreter (std::vector<Point>& points,
|
||||
std::vector<std::size_t>& labels)
|
||||
: points (points), labels (labels)
|
||||
{ }
|
||||
|
||||
// Init and test if input file contains the right properties
|
||||
bool is_applicable (CGAL::Ply_reader& reader)
|
||||
{
|
||||
return reader.does_tag_exist<double> ("x")
|
||||
&& reader.does_tag_exist<double> ("y")
|
||||
&& reader.does_tag_exist<double> ("z")
|
||||
&& reader.does_tag_exist<int> ("label");
|
||||
}
|
||||
|
||||
// Describes how to process one line (= one point object)
|
||||
void process_line (CGAL::Ply_reader& reader)
|
||||
{
|
||||
double x = 0., y = 0., z = 0.;
|
||||
int l = 0;
|
||||
|
||||
reader.assign (x, "x");
|
||||
reader.assign (y, "y");
|
||||
reader.assign (z, "z");
|
||||
reader.assign (l, "label");
|
||||
|
||||
points.push_back (Point (x, y, z));
|
||||
labels.push_back(std::size_t(l));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
std::string filename (argc > 1 ? argv[1] : "data/b9_training.ply");
|
||||
std::ifstream in (filename.c_str());
|
||||
std::vector<Point> pts;
|
||||
std::vector<std::size_t> ground_truth;
|
||||
|
||||
std::cerr << "Reading input" << std::endl;
|
||||
My_ply_interpreter interpreter (pts, ground_truth);
|
||||
if (!in
|
||||
|| !(CGAL::read_ply_custom_points (in, interpreter, Kernel())))
|
||||
{
|
||||
std::cerr << "Error: cannot read " << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Feature_set features;
|
||||
|
||||
std::cerr << "Generating features" << std::endl;
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
Feature_generator generator (features, pts, Pmap(),
|
||||
5); // using 5 scales
|
||||
t.stop();
|
||||
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
|
||||
|
||||
// Add types
|
||||
Label_set labels;
|
||||
Label_handle ground = labels.add ("ground");
|
||||
Label_handle vegetation = labels.add ("vegetation");
|
||||
Label_handle roof = labels.add ("roof");
|
||||
Label_handle facade = labels.add ("facade");
|
||||
|
||||
Classifier classifier (labels, features);
|
||||
|
||||
std::cerr << "Training" << std::endl;
|
||||
t.reset();
|
||||
t.start();
|
||||
classifier.train (ground_truth);
|
||||
t.stop();
|
||||
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
|
||||
|
||||
t.reset();
|
||||
t.start();
|
||||
std::vector<std::size_t> label_indices;
|
||||
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
||||
(pts, Pmap(), labels, classifier,
|
||||
generator.neighborhood().k_neighbor_query(12),
|
||||
0.2, 10, label_indices);
|
||||
t.stop();
|
||||
std::cerr << "Classification with graphcut done in " << t.time() << " second(s)" << std::endl;
|
||||
|
||||
std::cerr << "Precision, recall, F1 scores and IoU:" << std::endl;
|
||||
Classification::Evaluation evaluation (labels, ground_truth, label_indices);
|
||||
|
||||
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||
{
|
||||
std::cerr << " * " << labels[i]->name() << ": "
|
||||
<< evaluation.precision(labels[i]) << " ; "
|
||||
<< evaluation.recall(labels[i]) << " ; "
|
||||
<< evaluation.f1_score(labels[i]) << " ; "
|
||||
<< evaluation.intersection_over_union(labels[i]) << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Accuracy = " << evaluation.accuracy() << std::endl
|
||||
<< "Mean F1 score = " << evaluation.mean_f1_score() << std::endl
|
||||
<< "Mean IoU = " << evaluation.mean_intersection_over_union() << std::endl;
|
||||
|
||||
std::ofstream f ("classification.ply");
|
||||
f.precision(18);
|
||||
f << "ply" << std::endl
|
||||
<< "format ascii 1.0" << std::endl
|
||||
<< "element vertex " << pts.size() << std::endl
|
||||
<< "property float x" << std::endl
|
||||
<< "property float y" << std::endl
|
||||
<< "property float z" << std::endl
|
||||
<< "property uchar red" << std::endl
|
||||
<< "property uchar green" << std::endl
|
||||
<< "property uchar blue" << std::endl
|
||||
<< "end_header" << std::endl;
|
||||
|
||||
for (std::size_t i = 0; i < pts.size(); ++ i)
|
||||
{
|
||||
f << pts[i] << " ";
|
||||
|
||||
Label_handle label = labels[label_indices[i]];
|
||||
if (label == ground)
|
||||
f << "245 180 0" << std::endl;
|
||||
else if (label == vegetation)
|
||||
f << "0 255 27" << std::endl;
|
||||
else if (label == roof)
|
||||
f << "255 0 170" << std::endl;
|
||||
else if (label == facade)
|
||||
f << "128 128 128" << std::endl;
|
||||
else
|
||||
{
|
||||
f << "0 0 0" << std::endl;
|
||||
std::cerr << "Error: unknown classification label" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "All done" << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// 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_CLASSIFICATION_H
|
||||
#define CGAL_CLASSIFICATION_H
|
||||
|
||||
#include <CGAL/Classification/classify.h>
|
||||
#include <CGAL/Classification/Sum_of_weighted_features_classifier.h>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
#include <CGAL/Classification/Random_forest_classifier.h>
|
||||
#endif
|
||||
|
||||
#include <CGAL/Classification/Color.h>
|
||||
#include <CGAL/Classification/Evaluation.h>
|
||||
#include <CGAL/Classification/Feature_base.h>
|
||||
#include <CGAL/Classification/Feature_set.h>
|
||||
#include <CGAL/Classification/Label.h>
|
||||
#include <CGAL/Classification/Label_set.h>
|
||||
#include <CGAL/Classification/Local_eigen_analysis.h>
|
||||
#include <CGAL/Classification/Planimetric_grid.h>
|
||||
#include <CGAL/Classification/Point_set_feature_generator.h>
|
||||
#include <CGAL/Classification/Point_set_neighborhood.h>
|
||||
|
||||
#include <CGAL/Classification/Feature/Distance_to_plane.h>
|
||||
#include <CGAL/Classification/Feature/Echo_scatter.h>
|
||||
#include <CGAL/Classification/Feature/Eigen.h>
|
||||
#include <CGAL/Classification/Feature/Elevation.h>
|
||||
#include <CGAL/Classification/Feature/Hsv.h>
|
||||
#include <CGAL/Classification/Feature/Vertical_dispersion.h>
|
||||
#include <CGAL/Classification/Feature/Verticality.h>
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_H
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
// 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_CLASSIFICATION_COLOR_H
|
||||
#define CGAL_CLASSIFICATION_COLOR_H
|
||||
|
||||
#include <CGAL/array.h>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationColor
|
||||
|
||||
%Color described in red/green/blue space. Each component is stored
|
||||
as an unsigned char ranging from 0 (no color) to 255 (full color).
|
||||
*/
|
||||
typedef CGAL::cpp11::array<unsigned char, 3> RGB_Color;
|
||||
/*!
|
||||
\ingroup PkgClassificationColor
|
||||
|
||||
%Color described in hue/saturation/value space. Each component is stored
|
||||
as a float:
|
||||
|
||||
- `hue` ranges from 0° to 360° (corresponding to the color tint)
|
||||
- `saturation` ranges from 0.0 (gray) to 100.0 (full saturation)
|
||||
- `value` ranges from 0.0 (black) to 100.0 (white)
|
||||
*/
|
||||
typedef CGAL::cpp11::array<float, 3> HSV_Color;
|
||||
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
inline HSV_Color rgb_to_hsv (const RGB_Color& c)
|
||||
{
|
||||
double r = (double)(c[0]) / 255.;
|
||||
double g = (double)(c[1]) / 255.;
|
||||
double b = (double)(c[2]) / 255.;
|
||||
double Cmax = (std::max) (r, (std::max) (g, b));
|
||||
double Cmin = (std::min) (r, (std::min) (g, b));
|
||||
double delta = Cmax - Cmin;
|
||||
double H = 0.;
|
||||
|
||||
if (delta != 0.)
|
||||
{
|
||||
if (Cmax == r)
|
||||
H = 60. * ((g - b) / delta);
|
||||
else if (Cmax == g)
|
||||
H = 60. * (((b - r) / delta) + 2.);
|
||||
else
|
||||
H = 60. * (((r - g) / delta) + 4.);
|
||||
}
|
||||
if (H < 0.) H += 360.;
|
||||
double S = (Cmax == 0. ? 0. : 100. * (delta / Cmax));
|
||||
double V = 100. * Cmax;
|
||||
HSV_Color out = {{ float(H), float(S), float(V) }};
|
||||
return out;
|
||||
}
|
||||
|
||||
inline RGB_Color hsv_to_rgb (const HSV_Color& c)
|
||||
{
|
||||
double h = c[0];
|
||||
double s = c[1];
|
||||
double v = c[2];
|
||||
|
||||
s /= 100.;
|
||||
v /= 100.;
|
||||
double C = v*s;
|
||||
int hh = (int)(h/60.);
|
||||
double X = C * (1-CGAL::abs (hh % 2 - 1));
|
||||
double r = 0, g = 0, b = 0;
|
||||
|
||||
if( hh>=0 && hh<1 )
|
||||
{
|
||||
r = C;
|
||||
g = X;
|
||||
}
|
||||
else if( hh>=1 && hh<2 )
|
||||
{
|
||||
r = X;
|
||||
g = C;
|
||||
}
|
||||
else if( hh>=2 && hh<3 )
|
||||
{
|
||||
g = C;
|
||||
b = X;
|
||||
}
|
||||
else if( hh>=3 && hh<4 )
|
||||
{
|
||||
g = X;
|
||||
b = C;
|
||||
}
|
||||
else if( hh>=4 && hh<5 )
|
||||
{
|
||||
r = X;
|
||||
b = C;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = C;
|
||||
b = X;
|
||||
}
|
||||
double m = v-C;
|
||||
r += m;
|
||||
g += m;
|
||||
b += m;
|
||||
r *= 255.0;
|
||||
g *= 255.0;
|
||||
b *= 255.0;
|
||||
|
||||
RGB_Color out = {{ (unsigned char)r, (unsigned char)g, (unsigned char)b }};
|
||||
return out;
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
} // namespace Classification
|
||||
} // namespace CGAL
|
||||
|
||||
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_COLOR_H
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
// 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_CLASSIFICATION_EVALUATION_H
|
||||
#define CGAL_CLASSIFICATION_EVALUATION_H_
|
||||
|
||||
#include <CGAL/Classification/Label.h>
|
||||
#include <CGAL/Classification/Label_set.h>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationDataStructures
|
||||
|
||||
\brief Class to compute several measurements to evaluate the quality
|
||||
of a classification output.
|
||||
*/
|
||||
class Evaluation
|
||||
{
|
||||
mutable std::map<Label_handle, std::size_t> m_map_labels;
|
||||
|
||||
std::vector<float> m_precision;
|
||||
std::vector<float> m_recall;
|
||||
std::vector<float> m_iou; // intersection over union
|
||||
float m_accuracy;
|
||||
float m_mean_iou;
|
||||
float m_mean_f1;
|
||||
|
||||
public:
|
||||
|
||||
/// \name Constructor
|
||||
/// @{
|
||||
|
||||
|
||||
/*!
|
||||
|
||||
\brief Instantiates an evaluation object and computes all
|
||||
measurements.
|
||||
|
||||
\param labels labels used.
|
||||
|
||||
\param ground_truth vector of label indices: it should contain the
|
||||
index of the corresponding label in the `Label_set` provided in the
|
||||
constructor. Input items that do not have a ground truth information
|
||||
should be given the value `std::size_t(-1)`.
|
||||
|
||||
\param result similar to `ground_truth` but contained the result of
|
||||
a classification.
|
||||
|
||||
*/
|
||||
Evaluation (const Label_set& labels,
|
||||
const std::vector<std::size_t>& ground_truth,
|
||||
const std::vector<std::size_t>& result)
|
||||
: m_precision (labels.size()),
|
||||
m_recall (labels.size()),
|
||||
m_iou (labels.size())
|
||||
{
|
||||
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||
m_map_labels[labels[i]] = i;
|
||||
|
||||
std::vector<std::size_t> true_positives (labels.size());
|
||||
std::vector<std::size_t> false_positives (labels.size());
|
||||
std::vector<std::size_t> false_negatives (labels.size());
|
||||
|
||||
std::size_t sum_true_positives = 0;
|
||||
std::size_t total = 0;
|
||||
|
||||
for (std::size_t j = 0; j < ground_truth.size(); ++ j)
|
||||
{
|
||||
std::size_t gt = ground_truth[j];
|
||||
std::size_t res = result[j];
|
||||
if (gt == std::size_t(-1) || res == std::size_t(-1))
|
||||
continue;
|
||||
++ total;
|
||||
if (gt == res)
|
||||
{
|
||||
++ true_positives[gt];
|
||||
++ sum_true_positives;
|
||||
continue;
|
||||
}
|
||||
++ false_positives[res];
|
||||
++ false_negatives[gt];
|
||||
}
|
||||
|
||||
m_mean_iou = 0.;
|
||||
m_mean_f1 = 0.;
|
||||
|
||||
for (std::size_t j = 0; j < labels.size(); ++ j)
|
||||
{
|
||||
m_precision[j] = true_positives[j] / float(true_positives[j] + false_positives[j]);
|
||||
m_recall[j] = true_positives[j] / float(true_positives[j] + false_negatives[j]);
|
||||
m_iou[j] = true_positives[j] / float(true_positives[j] + false_positives[j] + false_negatives[j]);
|
||||
|
||||
m_mean_iou += m_iou[j];
|
||||
m_mean_f1 += 2. * (m_precision[j] * m_recall[j])
|
||||
/ (m_precision[j] + m_recall[j]);
|
||||
}
|
||||
|
||||
m_mean_iou /= labels.size();
|
||||
m_mean_f1 /= labels.size();
|
||||
m_accuracy = sum_true_positives / float(total);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Label Evaluation
|
||||
/// @{
|
||||
|
||||
|
||||
/*!
|
||||
|
||||
\brief Returns the precision of the training for the given label.
|
||||
|
||||
Precision is the number of true positives divided by the sum of
|
||||
the true positives and the false positives.
|
||||
|
||||
*/
|
||||
float precision (Label_handle label) const
|
||||
{
|
||||
return m_precision[m_map_labels[label]];
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
\brief Returns the recall of the training for the given label.
|
||||
|
||||
Recall is the number of true positives divided by the sum of
|
||||
the true positives and the false negatives.
|
||||
|
||||
*/
|
||||
float recall (Label_handle label) const
|
||||
{
|
||||
return m_recall[m_map_labels[label]];
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
\brief Returns the \f$F_1\f$ score of the training for the given label.
|
||||
|
||||
\f$F_1\f$ score is the harmonic mean of `precision()` and `recall()`:
|
||||
|
||||
\f[
|
||||
F_1 = 2 \times \frac{precision \times recall}{precision + recall}
|
||||
\f]
|
||||
|
||||
*/
|
||||
float f1_score (Label_handle label) const
|
||||
{
|
||||
std::size_t label_idx = m_map_labels[label];
|
||||
return 2. * (m_precision[label_idx] * m_recall[label_idx])
|
||||
/ (m_precision[label_idx] + m_recall[label_idx]);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the intersection over union of the training for the
|
||||
given label.
|
||||
|
||||
Intersection over union is the number of true positives divided by
|
||||
the sum of the true positives, of the false positives and of the
|
||||
false negatives.
|
||||
*/
|
||||
float intersection_over_union (Label_handle label) const
|
||||
{
|
||||
return m_iou[m_map_labels[label]];
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Global Evaluation
|
||||
/// @{
|
||||
|
||||
|
||||
/*!
|
||||
\brief Returns the accuracy of the training.
|
||||
|
||||
Accuracy is the total number of true positives divided by the
|
||||
total number of provided inliers.
|
||||
*/
|
||||
float accuracy() const { return m_accuracy; }
|
||||
|
||||
/*!
|
||||
\brief Returns the mean \f$F_1\f$ score of the training over all
|
||||
labels (see `f1_score()`).
|
||||
*/
|
||||
float mean_f1_score() const { return m_mean_f1; }
|
||||
|
||||
/*!
|
||||
\brief Returns the mean intersection over union of the training
|
||||
over all labels (see `intersection_over_union()`).
|
||||
*/
|
||||
float mean_intersection_over_union() const { return m_mean_iou; }
|
||||
|
||||
/// @}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_EVALUATION_H_
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||
// 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, Florent Lafarge
|
||||
|
||||
#ifndef CGAL_CLASSIFICATION_FEATURE_DISTANCE_TO_PLANE_H
|
||||
#define CGAL_CLASSIFICATION_FEATURE_DISTANCE_TO_PLANE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
namespace Feature {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on local distance to a fitted
|
||||
plane. Characterizing a level of non-planarity can help to
|
||||
identify noisy parts of the input such as vegetation. This
|
||||
feature computes the distance of a point to a locally fitted
|
||||
plane.
|
||||
|
||||
Its default name is "distance_to_plane".
|
||||
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
`PointMap`.
|
||||
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `CGAL::Point_3`.
|
||||
*/
|
||||
template <typename PointRange, typename PointMap>
|
||||
class Distance_to_plane : public Feature_base
|
||||
{
|
||||
|
||||
typedef typename Kernel_traits<typename PointMap::value_type>::Kernel Kernel;
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
std::vector<float> distance_to_plane_feature;
|
||||
#else
|
||||
const PointRange& input;
|
||||
PointMap point_map;
|
||||
const Local_eigen_analysis& eigen;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
Distance_to_plane (const PointRange& input,
|
||||
PointMap point_map,
|
||||
const Local_eigen_analysis& eigen)
|
||||
#ifndef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
: input(input), point_map(point_map), eigen(eigen)
|
||||
#endif
|
||||
{
|
||||
this->set_name ("distance_to_plane");
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
for(std::size_t i = 0; i < input.size(); i++)
|
||||
distance_to_plane_feature.push_back
|
||||
(CGAL::sqrt (CGAL::squared_distance (get(point_map, *(input.begin()+i)), eigen.plane<Kernel>(i))));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float value (std::size_t pt_index)
|
||||
{
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
return distance_to_plane_feature[pt_index];
|
||||
#else
|
||||
return CGAL::sqrt (CGAL::squared_distance
|
||||
(get(point_map, *(input.begin()+pt_index)), eigen.plane<Kernel>(pt_index)));
|
||||
#endif
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
} // namespace Feature
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURE_DISTANCE_TO_PLANE_H
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||
// 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, Florent Lafarge
|
||||
|
||||
#ifndef CGAL_CLASSIFICATION_FEATURE_ECHO_SCATTER_H
|
||||
#define CGAL_CLASSIFICATION_FEATURE_ECHO_SCATTER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
namespace Feature {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on echo scatter. The number of returns (echo
|
||||
number) is a useful information provided by most LIDAR sensors. It
|
||||
can help to identify trees.
|
||||
|
||||
Its default name is "echo_scatter".
|
||||
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
`PointMap`.
|
||||
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `GeomTraits::Point_3`.
|
||||
\tparam EchoMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `std::size_t`.
|
||||
*/
|
||||
template <typename GeomTraits, typename PointRange, typename PointMap, typename EchoMap>
|
||||
class Echo_scatter : public Feature_base
|
||||
{
|
||||
public:
|
||||
typedef Classification::Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
private:
|
||||
typedef Classification::Image<float> Image_float;
|
||||
|
||||
std::vector<float> echo_scatter;
|
||||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param echo_map property map to access the echo values of the input points.
|
||||
\param grid precomputed `Planimetric_grid`.
|
||||
\param radius_neighbors radius of local neighborhoods.
|
||||
*/
|
||||
Echo_scatter (const PointRange& input,
|
||||
EchoMap echo_map,
|
||||
const Grid& grid,
|
||||
float radius_neighbors = 1.)
|
||||
{
|
||||
this->set_name ("echo_scatter");
|
||||
Image_float Scatter(grid.width(), grid.height());
|
||||
for (std::size_t j = 0; j < grid.height(); j++)
|
||||
for (std::size_t i = 0; i < grid.width(); i++)
|
||||
Scatter(i,j)=0;
|
||||
|
||||
std::size_t square = (std::size_t)(0.5 * radius_neighbors / grid.resolution()) + 1;
|
||||
|
||||
for (std::size_t j = 0; j < grid.height(); j++){
|
||||
for (std::size_t i = 0; i < grid.width(); i++){
|
||||
|
||||
if(grid.has_points(i,j)){
|
||||
|
||||
std::size_t squareXmin = (i < square ? 0 : i - square);
|
||||
std::size_t squareXmax = (std::min) (grid.width()-1, i + square);
|
||||
std::size_t squareYmin = (j < square ? 0 : j - square);
|
||||
std::size_t squareYmax = (std::min) (grid.height()-1, j + square);
|
||||
|
||||
std::size_t NB_echo_sup=0;
|
||||
std::size_t NB_echo_total=0;
|
||||
|
||||
for(std::size_t k = squareXmin; k <= squareXmax; k++){
|
||||
for(std::size_t l = squareYmin; l <= squareYmax; l++){
|
||||
|
||||
if(CGAL::sqrt(pow((float)k-i,2)+pow((float)l-j,2))<=(float)0.5*radius_neighbors/grid.resolution())
|
||||
{
|
||||
typename Grid::iterator end = grid.indices_end(k,l);
|
||||
std::size_t nb = 0;
|
||||
for (typename Grid::iterator it = grid.indices_begin(k,l); it != end; ++ it)
|
||||
{
|
||||
++ nb;
|
||||
if(get(echo_map, *(input.begin()+(*it))) > 1)
|
||||
NB_echo_sup++;
|
||||
}
|
||||
|
||||
NB_echo_total=NB_echo_total+nb;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Scatter(i,j)=(float)NB_echo_sup/NB_echo_total;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
for(std::size_t i = 0; i < input.size(); i++){
|
||||
std::size_t I= grid.x(i);
|
||||
std::size_t J= grid.y(i);
|
||||
echo_scatter.push_back((float)Scatter(I,J));
|
||||
}
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float value (std::size_t pt_index)
|
||||
{
|
||||
return echo_scatter[pt_index];
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
} // namespace Feature
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURE_ECHO_SCATTER_H
|
||||
|
|
@ -0,0 +1,463 @@
|
|||
// 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_CLASSIFICATION_FEATURES_EIGEN_H
|
||||
#define CGAL_CLASSIFICATION_FEATURES_EIGEN_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <CGAL/Classification/Local_eigen_analysis.h>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
namespace Feature {
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
class Eigen_feature : public Feature_base
|
||||
{
|
||||
protected:
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
std::vector<float> attrib;
|
||||
#else
|
||||
const Classification::Local_eigen_analysis& eigen;
|
||||
#endif
|
||||
|
||||
public:
|
||||
template <typename InputRange>
|
||||
Eigen_feature (const InputRange&,
|
||||
const Classification::Local_eigen_analysis& eigen)
|
||||
#ifndef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
: eigen (eigen)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
virtual void init (std::size_t size, const Classification::Local_eigen_analysis& eigen)
|
||||
{
|
||||
attrib.reserve (size);
|
||||
for (std::size_t i = 0; i < size; ++ i)
|
||||
attrib.push_back (get_value (eigen, i));
|
||||
}
|
||||
#else
|
||||
virtual void init (std::size_t, const Classification::Local_eigen_analysis&)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual float get_value (const Classification::Local_eigen_analysis& eigen, std::size_t i) = 0;
|
||||
virtual float value (std::size_t pt_index)
|
||||
{
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
return attrib[pt_index];
|
||||
#else
|
||||
return get_value(eigen, pt_index);
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on the eigenvalues of the covariance matrix of a
|
||||
local neighborhood. Linearity is defined, for the 3 eigenvalues
|
||||
\f$\lambda_1 \ge \lambda_2 \ge \lambda_3 \ge 0\f$, as:
|
||||
|
||||
\f[
|
||||
\frac{\lambda_1 - \lambda_2}{\lambda_1}
|
||||
\f]
|
||||
|
||||
Its default name is "linearity".
|
||||
*/
|
||||
class Linearity
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
: public Feature_base
|
||||
#else
|
||||
: public Eigen_feature
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
Constructs the feature.
|
||||
|
||||
\tparam Input model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator`.
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
template <typename InputRange>
|
||||
Linearity (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen) : Eigen_feature (input, eigen)
|
||||
{
|
||||
this->set_name("linearity");
|
||||
this->init(input.size(), eigen);
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float get_value (const Local_eigen_analysis& eigen, std::size_t i)
|
||||
{
|
||||
const Local_eigen_analysis::Eigenvalues& ev = eigen.eigenvalue(i);
|
||||
if (ev[2] < 1e-15)
|
||||
return 0.;
|
||||
else
|
||||
return ((ev[2] - ev[1]) / ev[2]);
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on the eigenvalues of the covariance matrix of a
|
||||
local neighborhood. Planarity is defined, for the 3 eigenvalues
|
||||
\f$\lambda_1 \ge \lambda_2 \ge \lambda_3 \ge 0\f$, as:
|
||||
|
||||
\f[
|
||||
\frac{\lambda_2 - \lambda_3}{\lambda_1}
|
||||
\f]
|
||||
|
||||
Its default name is "planarity".
|
||||
*/
|
||||
class Planarity
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
: public Feature_base
|
||||
#else
|
||||
: public Eigen_feature
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
template <typename InputRange>
|
||||
Planarity (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: Eigen_feature(input, eigen)
|
||||
{
|
||||
this->set_name("planarity");
|
||||
this->init(input.size(), eigen);
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float get_value (const Local_eigen_analysis& eigen, std::size_t i)
|
||||
{
|
||||
const Local_eigen_analysis::Eigenvalues& ev = eigen.eigenvalue(i);
|
||||
if (ev[2] < 1e-15)
|
||||
return 0.;
|
||||
else
|
||||
return ((ev[1] - ev[0]) / ev[2]);
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
};
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on the eigenvalues of the covariance matrix of a
|
||||
local neighborhood. Sphericity is defined, for the 3 eigenvalues
|
||||
\f$\lambda_1 \ge \lambda_2 \ge \lambda_3 \ge 0\f$, as:
|
||||
|
||||
\f[
|
||||
\frac{\lambda_3}{\lambda_1}
|
||||
\f]
|
||||
|
||||
Its default name is "sphericity".
|
||||
*/
|
||||
class Sphericity
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
: public Feature_base
|
||||
#else
|
||||
: public Eigen_feature
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
template <typename InputRange>
|
||||
Sphericity (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: Eigen_feature(input, eigen)
|
||||
{
|
||||
this->set_name("sphericity");
|
||||
this->init(input.size(), eigen);
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float get_value (const Local_eigen_analysis& eigen, std::size_t i)
|
||||
{
|
||||
const Local_eigen_analysis::Eigenvalues& ev = eigen.eigenvalue(i);
|
||||
if (ev[2] < 1e-15)
|
||||
return 0.;
|
||||
else
|
||||
return (ev[0] / ev[2]);
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on the eigenvalues of the covariance matrix of a
|
||||
local neighborhood. Omnivariance is defined, for the 3 eigenvalues
|
||||
\f$\lambda_1 \ge \lambda_2 \ge \lambda_3 \ge 0\f$, as:
|
||||
|
||||
\f[
|
||||
(\lambda_1 \times \lambda_2 \times \lambda_3)^{\frac{1}{3}}
|
||||
\f]
|
||||
|
||||
Its default name is "omnivariance".
|
||||
*/
|
||||
class Omnivariance
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
: public Feature_base
|
||||
#else
|
||||
: public Eigen_feature
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
template <typename InputRange>
|
||||
Omnivariance (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: Eigen_feature(input, eigen)
|
||||
{
|
||||
this->set_name("omnivariance");
|
||||
this->init(input.size(), eigen);
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float get_value (const Local_eigen_analysis& eigen, std::size_t i)
|
||||
{
|
||||
const Local_eigen_analysis::Eigenvalues& ev = eigen.eigenvalue(i);
|
||||
return (std::pow (CGAL::abs(ev[0] * ev[1] * ev[2]), 0.333333333));
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on the eigenvalues of the covariance matrix of a
|
||||
local neighborhood. Anisotropy is defined, for the 3 eigenvalues
|
||||
\f$\lambda_1 \ge \lambda_2 \ge \lambda_3 \ge 0\f$, as:
|
||||
|
||||
\f[
|
||||
\frac{\lambda_1 - \lambda_3}{\lambda_1}
|
||||
\f]
|
||||
|
||||
Its default name is "anisotropy".
|
||||
*/
|
||||
class Anisotropy
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
: public Feature_base
|
||||
#else
|
||||
: public Eigen_feature
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
template <typename InputRange>
|
||||
Anisotropy (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: Eigen_feature(input, eigen)
|
||||
{
|
||||
this->set_name("anisotropy");
|
||||
this->init(input.size(), eigen);
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float get_value (const Local_eigen_analysis& eigen, std::size_t i)
|
||||
{
|
||||
const Local_eigen_analysis::Eigenvalues& ev = eigen.eigenvalue(i);
|
||||
if (ev[2] < 1e-15)
|
||||
return 0.;
|
||||
else
|
||||
return ((ev[2] - ev[0]) / ev[2]);
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on the eigenvalues of the covariance matrix of a
|
||||
local neighborhood. Eigentropy is defined, for the 3 eigenvalues
|
||||
\f$\lambda_1 \ge \lambda_2 \ge \lambda_3 \ge 0\f$, as:
|
||||
|
||||
\f[
|
||||
- \sum_{i=1}^3 \lambda_i \times \log{\lambda_i}
|
||||
\f]
|
||||
|
||||
Its default name is "eigentropy".
|
||||
*/
|
||||
class Eigentropy
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
: public Feature_base
|
||||
#else
|
||||
: public Eigen_feature
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
template <typename InputRange>
|
||||
Eigentropy (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: Eigen_feature(input, eigen)
|
||||
{
|
||||
this->set_name("eigentropy");
|
||||
this->init(input.size(), eigen);
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float get_value (const Local_eigen_analysis& eigen, std::size_t i)
|
||||
{
|
||||
const Local_eigen_analysis::Eigenvalues& ev = eigen.eigenvalue(i);
|
||||
if (ev[0] < 1e-15
|
||||
|| ev[1] < 1e-15
|
||||
|| ev[2] < 1e-15)
|
||||
return 0.;
|
||||
else
|
||||
return (- ev[0] * std::log(ev[0])
|
||||
- ev[1] * std::log(ev[1])
|
||||
- ev[2] * std::log(ev[2]));
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on the eigenvalues of the covariance matrix of a
|
||||
local neighborhood. The sum of the eigenvalues is defined, for the
|
||||
3 eigenvalues \f$\lambda_1 \ge \lambda_2 \ge \lambda_3 \ge 0\f$,
|
||||
as:
|
||||
|
||||
\f[
|
||||
\lambda_1 + \lambda_2 + \lambda_3
|
||||
\f]
|
||||
|
||||
Its default name is "sum_eigen".
|
||||
*/
|
||||
class Sum_eigenvalues
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
: public Feature_base
|
||||
#else
|
||||
: public Eigen_feature
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
template <typename InputRange>
|
||||
Sum_eigenvalues (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: Eigen_feature(input, eigen)
|
||||
{
|
||||
this->set_name("sum_eigen");
|
||||
this->init(input.size(), eigen);
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float get_value (const Local_eigen_analysis& eigen, std::size_t i)
|
||||
{
|
||||
return eigen.sum_of_eigenvalues(i);
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on the eigenvalues of the covariance
|
||||
matrix of a local neighborhood. Surface variation is defined, for
|
||||
the 3 eigenvalues \f$\lambda_1 \ge \lambda_2 \ge \lambda_3 \ge
|
||||
0\f$, as:
|
||||
|
||||
\f[
|
||||
\frac{\lambda_3}{\lambda_1 + \lambda_2 + \lambda_3}
|
||||
\f]
|
||||
|
||||
Its default name is "surface_variation".
|
||||
*/
|
||||
class Surface_variation
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
: public Feature_base
|
||||
#else
|
||||
: public Eigen_feature
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
template <typename InputRange>
|
||||
Surface_variation (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: Eigen_feature(input, eigen)
|
||||
{
|
||||
this->set_name("surface_variation");
|
||||
this->init(input.size(), eigen);
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float get_value (const Local_eigen_analysis& eigen, std::size_t i)
|
||||
{
|
||||
const Local_eigen_analysis::Eigenvalues& ev = eigen.eigenvalue(i);
|
||||
if (ev[0] + ev[1] + ev[2] < 1e-15)
|
||||
return 0.;
|
||||
else
|
||||
return (ev[0] / (ev[0] + ev[1] + ev[2]));
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
} // namespace Feature
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURES_EIGEN_H
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||
// 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) : Florent Lafarge, Simon Giraudot
|
||||
|
||||
#ifndef CGAL_CLASSIFICATION_FEATURE_ELEVATION_H
|
||||
#define CGAL_CLASSIFICATION_FEATURE_ELEVATION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <CGAL/Classification/Feature_base.h>
|
||||
#include <CGAL/Classification/Image.h>
|
||||
#include <CGAL/Classification/Planimetric_grid.h>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
namespace Feature {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on local elevation. The local position of the
|
||||
ground can be computed for urban scenes. This feature computes
|
||||
the distance to the local estimation of the ground. It is useful
|
||||
to discriminate the ground from horizontal roofs.
|
||||
|
||||
Its default name is "elevation".
|
||||
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
`PointMap`.
|
||||
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `GeomTraits::Point_3`.
|
||||
|
||||
*/
|
||||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Elevation : public Feature_base
|
||||
{
|
||||
typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3;
|
||||
|
||||
typedef Image<float> Image_float;
|
||||
typedef Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
std::vector<float> elevation_feature;
|
||||
#else
|
||||
const PointRange& input;
|
||||
PointMap point_map;
|
||||
const Grid& grid;
|
||||
Image_float dtm;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
\param grid precomputed `Planimetric_grid`.
|
||||
\param radius_dtm radius for digital terrain modeling (should be
|
||||
larger than the width and length of the largest building).
|
||||
*/
|
||||
Elevation (const PointRange& input,
|
||||
PointMap point_map,
|
||||
const Grid& grid,
|
||||
float radius_dtm = -1.)
|
||||
#ifndef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
: input(input), point_map(point_map), grid(grid)
|
||||
#endif
|
||||
{
|
||||
this->set_name ("elevation");
|
||||
if (radius_dtm < 0.)
|
||||
radius_dtm = 100. * grid.resolution();
|
||||
|
||||
//DEM
|
||||
Image_float dem(grid.width(),grid.height());
|
||||
|
||||
for (std::size_t j = 0; j < grid.height(); ++ j)
|
||||
for (std::size_t i = 0; i < grid.width(); ++ i)
|
||||
{
|
||||
float mean = 0.;
|
||||
std::size_t nb = 0;
|
||||
typename Grid::iterator end = grid.indices_end(i,j);
|
||||
for (typename Grid::iterator it = grid.indices_begin(i,j); it != end; ++ it)
|
||||
{
|
||||
mean += get(point_map, *(input.begin()+(*it))).z();
|
||||
++ nb;
|
||||
}
|
||||
if (nb == 0)
|
||||
continue;
|
||||
mean /= nb;
|
||||
dem(i,j) = mean;
|
||||
}
|
||||
|
||||
std::size_t square = (std::size_t)(0.5 * radius_dtm / grid.resolution()) + 1;
|
||||
|
||||
Image_float dtm_x(grid.width(),grid.height());
|
||||
|
||||
for (std::size_t j = 0; j < grid.height(); ++ j)
|
||||
{
|
||||
for (std::size_t i = 0; i < grid.width(); ++ i)
|
||||
{
|
||||
std::size_t squareXmin = (i < square ? 0 : i - square);
|
||||
std::size_t squareXmax = (std::min)(grid.width() - 1, i + square);
|
||||
|
||||
std::vector<float> z;
|
||||
z.reserve(squareXmax - squareXmin +1 );
|
||||
for(std::size_t k = squareXmin; k <= squareXmax; k++)
|
||||
if (dem(k,j) != 0.)
|
||||
z.push_back (dem(k,j));
|
||||
if (z.empty())
|
||||
continue;
|
||||
std::nth_element (z.begin(), z.begin() + (z.size() / 10), z.end());
|
||||
dtm_x(i,j) = z[z.size() / 10];
|
||||
}
|
||||
}
|
||||
dem.free();
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
Image_float dtm(grid.width(),grid.height());
|
||||
#else
|
||||
dtm = Image_float(grid.width(),grid.height());
|
||||
#endif
|
||||
|
||||
for (std::size_t i = 0; i < grid.width(); ++ i)
|
||||
{
|
||||
for (std::size_t j = 0; j < grid.height(); ++ j)
|
||||
{
|
||||
std::size_t squareYmin = (j < square ? 0 : j - square);
|
||||
std::size_t squareYmax = (std::min)(grid.height() - 1, j + square);
|
||||
std::vector<float> z;
|
||||
z.reserve(squareYmax - squareYmin +1 );
|
||||
for(std::size_t l = squareYmin; l <= squareYmax; l++)
|
||||
if (dtm_x(i,l) != 0.)
|
||||
z.push_back (dtm_x(i,l));
|
||||
if (z.empty())
|
||||
continue;
|
||||
std::nth_element (z.begin(), z.begin() + (z.size() / 10), z.end());
|
||||
dtm(i,j) = z[z.size() / 10];
|
||||
}
|
||||
}
|
||||
dtm_x.free();
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
elevation_feature.reserve(input.size());
|
||||
for (std::size_t i = 0; i < input.size(); i++){
|
||||
std::size_t I = grid.x(i);
|
||||
std::size_t J = grid.y(i);
|
||||
elevation_feature.push_back ((float)(get(point_map, *(input.begin()+i)).z()-dtm(I,J)));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float value (std::size_t pt_index)
|
||||
{
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
return elevation_feature[pt_index];
|
||||
#else
|
||||
std::size_t I = grid.x(pt_index);
|
||||
std::size_t J = grid.y(pt_index);
|
||||
return ((float)(get(point_map, *(input.begin()+pt_index)).z()-dtm(I,J)));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
} // namespace Feature
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURE_ELEVATION_H
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
// 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_CLASSIFICATION_FEATURE_HSV_H
|
||||
#define CGAL_CLASSIFICATION_FEATURE_HSV_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <CGAL/Classification/Color.h>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
namespace Feature {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on HSV colorimetric information. If the input
|
||||
point cloud has colorimetric information, it can be used for
|
||||
classification purposes. This feature is based on a Gaussian
|
||||
probabilistic model on one of the three HSV channels (hue,
|
||||
saturation or value). It computes the probability of the color of
|
||||
the input point to match this specific color channel defined by a
|
||||
mean and a standard deviation.
|
||||
|
||||
The HSV channels are defined this way:
|
||||
|
||||
- Hue ranges from 0 to 360 and measures the general "tint" of the
|
||||
color (green, blue, pink, etc.)
|
||||
|
||||
- Saturation ranges from 0 to 100 and measures the "strength" of the
|
||||
color (0 is gray and 100 is the fully saturated color)
|
||||
|
||||
- Value ranges from 0 to 100 and measures the "brightness" of the
|
||||
color (0 is black and 100 is the fully bright color)
|
||||
|
||||
For example, such an feature using the channel 0 (hue) with a
|
||||
mean of 90 (which corresponds to a green hue) can help to identify
|
||||
trees.
|
||||
|
||||
\image html trees.png
|
||||
|
||||
<center><em>Left: input point set with colors. Right: HSV feature on hue with
|
||||
a mean of 90 (from low values in white to high values in dark
|
||||
red).</em></center>
|
||||
|
||||
Its default name is the channel followed by the mean value (for
|
||||
example: "hue_180", "saturation_20" or "value_98").
|
||||
|
||||
\note The user only needs to provide a map to standard (and more common)
|
||||
RGB colors, the conversion to HSV is done internally.
|
||||
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
`ColorMap`.
|
||||
\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`.
|
||||
*/
|
||||
template <typename GeomTraits, typename PointRange, typename ColorMap>
|
||||
class Hsv : public Feature_base
|
||||
{
|
||||
public:
|
||||
|
||||
/// Selected channel.
|
||||
enum Channel
|
||||
{
|
||||
HUE = 0, ///< 0
|
||||
SATURATION = 1, ///< 1
|
||||
VALUE = 2 ///< 2
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
typedef typename Classification::RGB_Color RGB_Color;
|
||||
typedef typename Classification::HSV_Color HSV_Color;
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
std::vector<float> color_feature;
|
||||
#else
|
||||
const PointRange& input;
|
||||
ColorMap color_map;
|
||||
Channel m_channel;
|
||||
float m_mean;
|
||||
float m_sd;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
|
||||
\brief Constructs a feature based on the given color channel,
|
||||
mean and standard deviation.
|
||||
|
||||
\param input point range.
|
||||
\param color_map property map to access the colors of the input points.
|
||||
\param channel chosen HSV channel.
|
||||
\param mean mean value of the specified channel.
|
||||
\param sd standard deviation of the specified channel.
|
||||
*/
|
||||
Hsv (const PointRange& input,
|
||||
ColorMap color_map,
|
||||
Channel channel,
|
||||
float mean, float sd)
|
||||
#ifndef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
: input(input), color_map(color_map), m_channel(channel), m_mean(mean), m_sd(sd)
|
||||
#endif
|
||||
{
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
for(std::size_t i = 0; i < input.size();i++)
|
||||
{
|
||||
HSV_Color c = Classification::rgb_to_hsv (get(color_map, *(input.begin()+i)));
|
||||
color_feature.push_back (std::exp (-(c[std::size_t(channel)] - mean)
|
||||
* (c[std::size_t(channel)] - mean) / (2. * sd * sd)));
|
||||
}
|
||||
#endif
|
||||
|
||||
std::ostringstream oss;
|
||||
if (channel == HUE) oss << "hue";
|
||||
else if (channel == SATURATION) oss << "saturation";
|
||||
else if (channel == VALUE) oss << "value";
|
||||
oss << "_" << mean;
|
||||
this->set_name (oss.str());
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float value (std::size_t pt_index)
|
||||
{
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
return color_feature[pt_index];
|
||||
#else
|
||||
HSV_Color c = Classification::rgb_to_hsv (get(color_map, *(input.begin()+pt_index)));
|
||||
return std::exp (-(c[std::size_t(m_channel)] - m_mean)
|
||||
* (c[std::size_t(m_channel)] - m_mean) / (2. * m_sd * m_sd));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
} // namespace Feature
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURE_HSV_H
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
// 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_CLASSIFICATION_FEATURE_VERTICAL_DISPERSION_H
|
||||
#define CGAL_CLASSIFICATION_FEATURE_VERTICAL_DISPERSION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <CGAL/number_utils.h>
|
||||
#include <CGAL/Classification/Image.h>
|
||||
#include <CGAL/Classification/Planimetric_grid.h>
|
||||
#include <boost/algorithm/minmax_element.hpp>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
namespace Feature {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on local vertical dispersion of points. Urban
|
||||
scenes can often be decomposed as a set of 2D regions with
|
||||
different heights. While these heights are usually piecewise
|
||||
constant or piecewise linear, on some specific parts of the scene
|
||||
such as vegetation, they can become extremely unstable. This
|
||||
feature quantifies the vertical dispersion of the points on a
|
||||
local Z-cylinder around the points.
|
||||
|
||||
Its default name is "vertical_dispersion".
|
||||
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
`PointMap`.
|
||||
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `GeomTraits::Point_3`.
|
||||
*/
|
||||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Vertical_dispersion : public Feature_base
|
||||
{
|
||||
typedef Classification::Image<float> Image_float;
|
||||
typedef Classification::Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
std::vector<float> vertical_dispersion;
|
||||
#else
|
||||
const Grid& grid;
|
||||
Image_float Dispersion;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
\param grid precomputed `Planimetric_grid`.
|
||||
\param radius_neighbors radius of local neighborhoods.
|
||||
*/
|
||||
Vertical_dispersion (const PointRange& input,
|
||||
PointMap point_map,
|
||||
const Grid& grid,
|
||||
float radius_neighbors = -1.)
|
||||
#ifndef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
: grid (grid)
|
||||
#endif
|
||||
{
|
||||
this->set_name ("vertical_dispersion");
|
||||
if (radius_neighbors < 0.)
|
||||
radius_neighbors = 5. * grid.resolution();
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
Image_float Dispersion(grid.width(), grid.height());
|
||||
#else
|
||||
Dispersion = Image_float(grid.width(), grid.height());
|
||||
#endif
|
||||
|
||||
for (std::size_t j = 0; j < grid.height(); j++)
|
||||
for (std::size_t i = 0; i < grid.width(); i++)
|
||||
Dispersion(i,j)=0;
|
||||
|
||||
std::size_t square = (std::size_t)(0.5 * radius_neighbors / grid.resolution()) + 1;
|
||||
typename GeomTraits::Vector_3 verti (0., 0., 1.);
|
||||
|
||||
std::vector<float> hori;
|
||||
|
||||
for (std::size_t j = 0; j < grid.height(); j++){
|
||||
for (std::size_t i = 0; i < grid.width(); i++){
|
||||
|
||||
if(!(grid.has_points(i,j)))
|
||||
continue;
|
||||
hori.clear();
|
||||
|
||||
std::size_t squareXmin = (i < square ? 0 : i - square);
|
||||
std::size_t squareXmax = (std::min) (grid.width()-1, i + square);
|
||||
std::size_t squareYmin = (j < square ? 0 : j - square);
|
||||
std::size_t squareYmax = (std::min) (grid.height()-1, j + square);
|
||||
|
||||
float bound = (float)0.5*radius_neighbors/grid.resolution();
|
||||
bound = CGAL::square(bound);
|
||||
for(std::size_t k = squareXmin; k <= squareXmax; k++)
|
||||
for(std::size_t l = squareYmin; l <= squareYmax; l++)
|
||||
{
|
||||
if(CGAL::square((float)(k-i))+ CGAL::square((float)(l-j))
|
||||
<= bound)
|
||||
{
|
||||
for (typename Grid::iterator it = grid.indices_begin(k,l); it != grid.indices_end(k,l); ++ it)
|
||||
hori.push_back (get(point_map, *(input.begin()+(*it))).z());
|
||||
}
|
||||
}
|
||||
|
||||
if (hori.empty())
|
||||
continue;
|
||||
|
||||
std::vector<float>::iterator min_it, max_it;
|
||||
boost::tie(min_it, max_it)
|
||||
= boost::minmax_element (hori.begin(), hori.end());
|
||||
|
||||
std::vector<bool> occupy (1 + (std::size_t)((*max_it - *min_it) / grid.resolution()), false);
|
||||
|
||||
for (std::size_t k = 0; k < hori.size(); ++ k)
|
||||
{
|
||||
std::size_t index = (std::size_t)((hori[k] - *min_it) / grid.resolution());
|
||||
occupy[index] = true;
|
||||
}
|
||||
|
||||
std::size_t nb_occ = 0;
|
||||
for (std::size_t k = 0; k < occupy.size(); ++ k)
|
||||
if (occupy[k])
|
||||
++ nb_occ;
|
||||
|
||||
Dispersion(i,j)= 1.f - (nb_occ / (float)(occupy.size()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
for (std::size_t i = 0; i < input.size(); i++)
|
||||
{
|
||||
std::size_t I= grid.x(i);
|
||||
std::size_t J= grid.y(i);
|
||||
vertical_dispersion.push_back((float)Dispersion(I,J));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float value (std::size_t pt_index)
|
||||
{
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
return vertical_dispersion[pt_index];
|
||||
#else
|
||||
std::size_t I = grid.x(pt_index);
|
||||
std::size_t J = grid.y(pt_index);
|
||||
return ((float)Dispersion(I,J));
|
||||
#endif
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURE_VERTICAL_DISPERSION_H
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
// 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_CLASSIFICATION_FEATURE_VERTICALITY_H
|
||||
#define CGAL_CLASSIFICATION_FEATURE_VERTICALITY_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <CGAL/Classification/Local_eigen_analysis.h>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
namespace Feature {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeatures
|
||||
|
||||
%Feature based on local verticality. The orientation of the
|
||||
local tangent plane of the considered point can be useful to
|
||||
discriminate facades from the ground. This feature can use
|
||||
normal vectors if available or eigen analysis that estimates
|
||||
tangent planes from local neighborhoods.
|
||||
|
||||
Its default name is "verticality".
|
||||
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
*/
|
||||
template <typename GeomTraits>
|
||||
class Verticality : public Feature_base
|
||||
{
|
||||
const typename GeomTraits::Vector_3 vertical;
|
||||
std::vector<float> verticality_feature;
|
||||
const Local_eigen_analysis* eigen;
|
||||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature using local eigen analysis.
|
||||
|
||||
\tparam InputRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator`.
|
||||
\param input point range.
|
||||
\param eigen class with precomputed eigenvectors and eigenvalues.
|
||||
*/
|
||||
#if defined(CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES) || defined(DOXYGEN_RUNNING)
|
||||
template <typename InputRange>
|
||||
Verticality (const InputRange& input,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: vertical (0., 0., 1.), eigen(NULL)
|
||||
{
|
||||
this->set_name ("verticality");
|
||||
|
||||
for (std::size_t i = 0; i < input.size(); i++)
|
||||
{
|
||||
typename GeomTraits::Vector_3 normal = eigen.normal_vector(i);
|
||||
normal = normal / CGAL::sqrt (normal * normal);
|
||||
verticality_feature.push_back (1. - CGAL::abs(normal * vertical));
|
||||
}
|
||||
}
|
||||
#else
|
||||
template <typename InputRange>
|
||||
Verticality (const InputRange&,
|
||||
const Local_eigen_analysis& eigen)
|
||||
: vertical (0., 0., 1.), eigen (&eigen)
|
||||
{
|
||||
this->set_name ("verticality");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\brief Constructs the feature using provided normals of points.
|
||||
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
`VectorMap`.
|
||||
\tparam VectorMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `GeomTraits::Vector_3`.
|
||||
\param input point range.
|
||||
\param normal_map property map to access the normal vectors of the input points.
|
||||
*/
|
||||
template <typename PointRange, typename VectorMap>
|
||||
Verticality (const PointRange& input,
|
||||
VectorMap normal_map)
|
||||
: vertical (0., 0., 1.), eigen(NULL)
|
||||
{
|
||||
this->set_name ("verticality");
|
||||
for (std::size_t i = 0; i < input.size(); i++)
|
||||
{
|
||||
typename GeomTraits::Vector_3 normal = get(normal_map, *(input.begin()+i));
|
||||
normal = normal / CGAL::sqrt (normal * normal);
|
||||
verticality_feature.push_back (1. - std::fabs(normal * vertical));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual float value (std::size_t pt_index)
|
||||
{
|
||||
#ifndef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
if (eigen != NULL)
|
||||
{
|
||||
typename GeomTraits::Vector_3 normal = eigen->normal_vector<GeomTraits>(pt_index);
|
||||
normal = normal / CGAL::sqrt (normal * normal);
|
||||
return (1. - CGAL::abs(normal * vertical));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return verticality_feature[pt_index];
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
} // namespace Feature
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURE_VERTICALITY_H
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// 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_CLASSIFICATION_FEATURE_BASE_H
|
||||
#define CGAL_CLASSIFICATION_FEATURE_BASE_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeature
|
||||
|
||||
\brief Abstract class describing a classification feature that
|
||||
associates a scalar value to each item of the classification input.
|
||||
*/
|
||||
|
||||
|
||||
class Feature_base
|
||||
{
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
Feature_base() : m_name ("abstract_feature") { }
|
||||
virtual ~Feature_base() { }
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns the name of the feature (initialized to
|
||||
`abstract_feature` for `Feature_base`).
|
||||
*/
|
||||
const std::string& name() const { return m_name; }
|
||||
|
||||
/*!
|
||||
\brief Changes the name of the feature.
|
||||
*/
|
||||
void set_name (const std::string& name) { m_name = name; }
|
||||
|
||||
/*!
|
||||
\brief Returns the value taken by the feature for at the item for
|
||||
the item at position `index`. This method must be implemented by
|
||||
inherited classes.
|
||||
*/
|
||||
virtual float value (std::size_t index) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
/*!
|
||||
\ingroup PkgClassificationFeature
|
||||
|
||||
\brief %Handle to a `Feature_base`.
|
||||
|
||||
\cgalModels Handle
|
||||
*/
|
||||
class Feature_handle { };
|
||||
#else
|
||||
typedef boost::shared_ptr<Feature_base> Feature_handle;
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURE_BASE_H
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
// 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_CLASSIFICATION_FEATURE_SET_H
|
||||
#define CGAL_CLASSIFICATION_FEATURE_SET_H
|
||||
|
||||
#include <CGAL/Classification/Feature_base.h>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
#include <tbb/mutex.h>
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeature
|
||||
|
||||
\brief Set of features (see `Feature_base`) used as input by
|
||||
classification algorithms. This class handles both the instantiation,
|
||||
the addition and the deletion of features.
|
||||
|
||||
*/
|
||||
class Feature_set
|
||||
{
|
||||
typedef std::vector<Feature_handle> Base;
|
||||
Base m_features;
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
tbb::mutex m_mutex;
|
||||
void mutex_lock() { m_mutex.lock(); }
|
||||
void mutex_unlock() { m_mutex.unlock(); }
|
||||
#else // CGAL_LINKED_WITH_TBB
|
||||
void mutex_lock() { }
|
||||
void mutex_unlock() { }
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
public:
|
||||
|
||||
Feature_set() { }
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual ~Feature_set() { }
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Instantiates a new feature and adds it to the set.
|
||||
|
||||
\tparam Feature type of the feature, inherited from
|
||||
`Feature_base`.
|
||||
|
||||
\tparam T types of the parameters of the feature's constructor.
|
||||
|
||||
\param t parameters of the feature's constructor.
|
||||
|
||||
\return a handle to the newly added feature.
|
||||
*/
|
||||
#if (!defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES) && !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE)) || DOXYGEN_RUNNING
|
||||
template <typename Feature, typename ... T>
|
||||
Feature_handle add (T&& ... t)
|
||||
{
|
||||
Feature_handle fh (new Feature(std::forward<T>(t)...));
|
||||
mutex_lock();
|
||||
m_features.push_back (fh);
|
||||
mutex_unlock();
|
||||
return fh;
|
||||
}
|
||||
#else
|
||||
template <typename Feature, typename T1>
|
||||
Feature_handle add (T1& t1)
|
||||
{
|
||||
Feature_handle fh (new Feature(t1));
|
||||
mutex_lock();
|
||||
m_features.push_back (fh);
|
||||
mutex_unlock();
|
||||
return fh;
|
||||
}
|
||||
template <typename Feature, typename T1, typename T2>
|
||||
Feature_handle add (T1& t1, T2& t2)
|
||||
{
|
||||
Feature_handle fh (new Feature(t1, t2));
|
||||
mutex_lock();
|
||||
m_features.push_back (fh);
|
||||
mutex_unlock();
|
||||
return fh;
|
||||
}
|
||||
template <typename Feature, typename T1, typename T2, typename T3>
|
||||
Feature_handle add (T1& t1, T2& t2, T3& t3)
|
||||
{
|
||||
Feature_handle fh (new Feature(t1, t2, t3));
|
||||
mutex_lock();
|
||||
m_features.push_back (fh);
|
||||
mutex_unlock();
|
||||
return fh;
|
||||
}
|
||||
template <typename Feature, typename T1, typename T2, typename T3, typename T4>
|
||||
Feature_handle add (T1& t1, T2& t2, T3& t3, T4& t4)
|
||||
{
|
||||
Feature_handle fh (new Feature(t1, t2, t3, t4));
|
||||
mutex_lock();
|
||||
m_features.push_back (fh);
|
||||
mutex_unlock();
|
||||
return fh;
|
||||
}
|
||||
template <typename Feature, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
Feature_handle add (T1& t1, T2& t2, T3& t3, T4& t4, T5& t5)
|
||||
{
|
||||
Feature_handle fh (new Feature(t1, t2, t3, t4, t5));
|
||||
mutex_lock();
|
||||
m_features.push_back (fh);
|
||||
mutex_unlock();
|
||||
return fh;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\brief Removes a feature.
|
||||
|
||||
\param feature the handle to feature type that must be removed.
|
||||
|
||||
\return `true` if the feature was correctly removed, `false` if
|
||||
its handle was not found.
|
||||
*/
|
||||
bool remove (Feature_handle feature)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
||||
if (m_features[i] == feature)
|
||||
{
|
||||
m_features.erase (m_features.begin() + i);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns how many features are defined.
|
||||
*/
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_features.size();
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief Returns the \f$i^{th}\f$ feature.
|
||||
*/
|
||||
Feature_handle operator[](std::size_t i) const
|
||||
{
|
||||
return m_features[i];
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Removes all features.
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_features.clear();
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
void free_memory(std::size_t i)
|
||||
{
|
||||
m_features[i] = Feature_handle();
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_FEATURE_SET_H
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
// 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_CLASSIFICATION_IMAGE_H
|
||||
#define CGAL_CLASSIFICATION_IMAGE_H
|
||||
|
||||
namespace CGAL {
|
||||
namespace Classification {
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
|
||||
template <typename Type>
|
||||
class Image
|
||||
{
|
||||
std::size_t m_width;
|
||||
std::size_t m_height;
|
||||
Type* m_raw;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Image () : m_width(0), m_height(0), m_raw (NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Image (std::size_t width, std::size_t height)
|
||||
: m_width (width),
|
||||
m_height (height)
|
||||
{
|
||||
if (m_width * m_height > 0)
|
||||
m_raw = new Type[width * height]();
|
||||
else
|
||||
m_raw = NULL;
|
||||
}
|
||||
|
||||
~Image ()
|
||||
{
|
||||
free();
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
if (m_raw != NULL)
|
||||
delete[] m_raw;
|
||||
m_raw = NULL;
|
||||
}
|
||||
|
||||
Image (const Image& other)
|
||||
: m_width (other.width()),
|
||||
m_height (other.height())
|
||||
|
||||
{
|
||||
if (m_width * m_height > 0)
|
||||
{
|
||||
m_raw = new Type[m_width * m_height];
|
||||
std::copy (other.m_raw, other.m_raw + (m_width * m_height), this->m_raw);
|
||||
}
|
||||
else
|
||||
m_raw = NULL;
|
||||
}
|
||||
Image& operator= (const Image& other)
|
||||
{
|
||||
if (m_raw != NULL)
|
||||
delete[] m_raw;
|
||||
|
||||
m_raw = NULL;
|
||||
m_width = other.width();
|
||||
m_height = other.height();
|
||||
if (m_width * m_height > 0)
|
||||
{
|
||||
m_raw = new Type[m_width * m_height];
|
||||
std::copy (other.m_raw, other.m_raw + (m_width * m_height), this->m_raw);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::size_t width() const { return m_width; }
|
||||
std::size_t height() const { return m_height; }
|
||||
|
||||
Type& operator() (const std::size_t& x, const std::size_t& y)
|
||||
{
|
||||
// return m_raw[y * m_width + x];
|
||||
return m_raw[x * m_height + y];
|
||||
}
|
||||
const Type& operator() (const std::size_t& x, const std::size_t& y) const
|
||||
{
|
||||
// return m_raw[y * m_width + x];
|
||||
return m_raw[x * m_height + y];
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/// \endcond
|
||||
|
||||
} // namespace Classification
|
||||
} // namespace CGAL
|
||||
|
||||
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_IMAGE_H
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// 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_CLASSIFICATION_LABEL_H
|
||||
#define CGAL_CLASSIFICATION_LABEL_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationLabel
|
||||
|
||||
\brief %Classification label (for example: vegetation, ground, etc.)
|
||||
defined as a set of relationships with classification features.
|
||||
|
||||
*/
|
||||
class Label
|
||||
{
|
||||
private:
|
||||
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
\param name name of the classification label (e.g. vegetation).
|
||||
*/
|
||||
Label (std::string name) : m_name (name) { }
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
};
|
||||
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
/*!
|
||||
\ingroup PkgClassificationLabel
|
||||
|
||||
\brief %Handle to a classification `Label`.
|
||||
|
||||
\cgalModels Handle
|
||||
*/
|
||||
class Label_handle { };
|
||||
#else
|
||||
typedef boost::shared_ptr<Label> Label_handle;
|
||||
#endif
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_LABEL_H
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// 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_CLASSIFICATION_LABEL_SET_H
|
||||
#define CGAL_CLASSIFICATION_LABEL_SET_H
|
||||
|
||||
#include <CGAL/Classification/Label.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationLabel
|
||||
|
||||
\brief Set of `Label` used as input by classification
|
||||
algorithms.
|
||||
|
||||
*/
|
||||
class Label_set
|
||||
{
|
||||
typedef std::vector<Label_handle> Base;
|
||||
Base m_labels;
|
||||
|
||||
public:
|
||||
|
||||
Label_set() { }
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual ~Label_set() { }
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Adds a label.
|
||||
|
||||
\note Names are not used for identification: two labels in the
|
||||
same set can have the same name (but not the same handle).
|
||||
|
||||
\param name name of the label.
|
||||
|
||||
\return a handle to the newly added label.
|
||||
*/
|
||||
Label_handle add (const char* name)
|
||||
{
|
||||
Label_handle out (new Classification::Label (name));
|
||||
m_labels.push_back (out);
|
||||
return out;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Removes a label.
|
||||
|
||||
\param label the handle to the label that must be removed.
|
||||
|
||||
\return `true` if the label was correctly removed,
|
||||
`false` if its handle was not found.
|
||||
*/
|
||||
bool remove (Label_handle label)
|
||||
{
|
||||
std::size_t idx = (std::size_t)(-1);
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
if (m_labels[i] == label)
|
||||
{
|
||||
m_labels.erase (m_labels.begin() + i);
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
if (idx == (std::size_t)(-1))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns how many labels are defined.
|
||||
*/
|
||||
std::size_t size () const
|
||||
{
|
||||
return m_labels.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the \f$i^{th}\f$ label.
|
||||
*/
|
||||
Label_handle operator[] (std::size_t i) const
|
||||
{
|
||||
return m_labels[i];
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief Removes all labels.
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_labels.clear();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_LABEL_SET_H
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
// 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_CLASSIFICATION_LOCAL_EIGEN_ANALYSIS_H
|
||||
#define CGAL_CLASSIFICATION_LOCAL_EIGEN_ANALYSIS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <CGAL/Search_traits_3.h>
|
||||
#include <CGAL/Fuzzy_sphere.h>
|
||||
#include <CGAL/Orthogonal_k_neighbor_search.h>
|
||||
#include <CGAL/Default_diagonalize_traits.h>
|
||||
#include <CGAL/centroid.h>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/blocked_range.h>
|
||||
#include <tbb/scalable_allocator.h>
|
||||
#include <tbb/mutex.h>
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationDataStructures
|
||||
|
||||
\brief Class that precomputes and stores the eigenvectors and
|
||||
eigenvalues of the covariance matrices of all points of a point
|
||||
set using a local neighborhood.
|
||||
|
||||
This class can be used to compute eigen features (see
|
||||
\ref PkgClassificationFeatures) and to estimate local normal vectors
|
||||
and tangent planes.
|
||||
|
||||
*/
|
||||
class Local_eigen_analysis
|
||||
{
|
||||
public:
|
||||
typedef CGAL::cpp11::array<float, 3> Eigenvalues; ///< Eigenvalues (sorted in ascending order)
|
||||
|
||||
private:
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
template <typename PointRange, typename PointMap, typename NeighborQuery, typename DiagonalizeTraits>
|
||||
class Compute_eigen_values
|
||||
{
|
||||
Local_eigen_analysis& m_eigen;
|
||||
const PointRange& m_input;
|
||||
PointMap m_point_map;
|
||||
const NeighborQuery& m_neighbor_query;
|
||||
float& m_mean_range;
|
||||
tbb::mutex& m_mutex;
|
||||
|
||||
public:
|
||||
|
||||
Compute_eigen_values (Local_eigen_analysis& eigen,
|
||||
const PointRange& input,
|
||||
PointMap point_map,
|
||||
const NeighborQuery& neighbor_query,
|
||||
float& mean_range,
|
||||
tbb::mutex& mutex)
|
||||
: m_eigen (eigen), m_input (input), m_point_map (point_map),
|
||||
m_neighbor_query (neighbor_query), m_mean_range (mean_range), m_mutex (mutex)
|
||||
{ }
|
||||
|
||||
void operator()(const tbb::blocked_range<std::size_t>& r) const
|
||||
{
|
||||
std::vector<std::size_t> neighbors;
|
||||
for (std::size_t i = r.begin(); i != r.end(); ++ i)
|
||||
{
|
||||
neighbors.clear();
|
||||
m_neighbor_query (get(m_point_map, *(m_input.begin()+i)), std::back_inserter (neighbors));
|
||||
|
||||
std::vector<typename PointMap::value_type> neighbor_points;
|
||||
neighbor_points.reserve(neighbors.size());
|
||||
for (std::size_t j = 0; j < neighbors.size(); ++ j)
|
||||
neighbor_points.push_back (get(m_point_map, *(m_input.begin()+neighbors[j])));
|
||||
|
||||
m_mutex.lock();
|
||||
m_mean_range += CGAL::sqrt (CGAL::squared_distance (get(m_point_map, *(m_input.begin() + i)),
|
||||
get(m_point_map, *(m_input.begin() + neighbors.back()))));
|
||||
m_mutex.unlock();
|
||||
|
||||
m_eigen.compute<typename PointMap::value_type,
|
||||
DiagonalizeTraits> (i, get(m_point_map, *(m_input.begin()+i)), neighbor_points);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef CGAL::cpp11::array<float, 3> float3;
|
||||
std::vector<float3> m_eigenvalues;
|
||||
std::vector<float> m_sum_eigenvalues;
|
||||
std::vector<float3> m_centroids;
|
||||
std::vector<float3> m_smallest_eigenvectors;
|
||||
#ifdef CGAL_CLASSIFICATION_EIGEN_FULL_STORAGE
|
||||
std::vector<float3> m_middle_eigenvectors;
|
||||
std::vector<float3> m_largest_eigenvectors;
|
||||
#endif
|
||||
float m_mean_range;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
Local_eigen_analysis () { }
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Computes the local eigen analysis of an input range based
|
||||
on a local neighborhood.
|
||||
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type is
|
||||
`RandomAccessIterator` and its value type is the key type of
|
||||
`PointMap`.
|
||||
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `CGAL::Point_3`.
|
||||
\tparam NeighborQuery model of `NeighborQuery`
|
||||
\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. It can be omitted: if Eigen 3 (or greater)
|
||||
is available and `CGAL_EIGEN3_ENABLED` is defined then an overload
|
||||
using `Eigen_diagonalize_traits` is provided. Otherwise, the
|
||||
internal implementation `Diagonalize_traits` is used.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
\param neighbor_query object used to access neighborhoods of points.
|
||||
*/
|
||||
template <typename PointRange,
|
||||
typename PointMap,
|
||||
typename NeighborQuery,
|
||||
#if defined(DOXYGEN_RUNNING)
|
||||
typename ConcurrencyTag,
|
||||
#elif defined(CGAL_LINKED_WITH_TBB)
|
||||
typename ConcurrencyTag = CGAL::Parallel_tag,
|
||||
#else
|
||||
typename ConcurrencyTag = CGAL::Sequential_tag,
|
||||
#endif
|
||||
#if defined(DOXYGEN_RUNNING)
|
||||
typename DiagonalizeTraits>
|
||||
#else
|
||||
typename DiagonalizeTraits = CGAL::Default_diagonalize_traits<float, 3> >
|
||||
#endif
|
||||
Local_eigen_analysis (const PointRange& input,
|
||||
PointMap point_map,
|
||||
const NeighborQuery& neighbor_query,
|
||||
const ConcurrencyTag& = ConcurrencyTag(),
|
||||
const DiagonalizeTraits& = DiagonalizeTraits())
|
||||
{
|
||||
m_eigenvalues.resize (input.size());
|
||||
m_sum_eigenvalues.resize (input.size());
|
||||
m_centroids.resize (input.size());
|
||||
m_smallest_eigenvectors.resize (input.size());
|
||||
#ifdef CGAL_CLASSIFICATION_EIGEN_FULL_STORAGE
|
||||
m_middle_eigenvectors.resize (input.size());
|
||||
m_largest_eigenvectors.resize (input.size());
|
||||
#endif
|
||||
|
||||
m_mean_range = 0.;
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
tbb::mutex mutex;
|
||||
Compute_eigen_values<PointRange, PointMap, NeighborQuery, DiagonalizeTraits>
|
||||
f(*this, input, point_map, neighbor_query, m_mean_range, mutex);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, input.size ()), f);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (std::size_t i = 0; i < input.size(); i++)
|
||||
{
|
||||
std::vector<std::size_t> neighbors;
|
||||
neighbor_query (get(point_map, *(input.begin()+i)), std::back_inserter (neighbors));
|
||||
|
||||
std::vector<typename PointMap::value_type> neighbor_points;
|
||||
for (std::size_t j = 0; j < neighbors.size(); ++ j)
|
||||
neighbor_points.push_back (get(point_map, *(input.begin()+neighbors[j])));
|
||||
|
||||
m_mean_range += CGAL::sqrt (CGAL::squared_distance (get(point_map, *(input.begin() + i)),
|
||||
get(point_map, *(input.begin() + neighbors.back()))));
|
||||
|
||||
compute<typename PointMap::value_type, DiagonalizeTraits>
|
||||
(i, get(point_map, *(input.begin()+i)), neighbor_points);
|
||||
}
|
||||
}
|
||||
m_mean_range /= input.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the estimated unoriented normal vector of the point at position `index`.
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
*/
|
||||
template <typename GeomTraits>
|
||||
typename GeomTraits::Vector_3 normal_vector (std::size_t index) const
|
||||
{
|
||||
return typename GeomTraits::Vector_3(double(m_smallest_eigenvectors[index][0]),
|
||||
double(m_smallest_eigenvectors[index][1]),
|
||||
double(m_smallest_eigenvectors[index][2]));
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the estimated local tangent plane of the point at position `index`.
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
*/
|
||||
template <typename GeomTraits>
|
||||
typename GeomTraits::Plane_3 plane (std::size_t index) const
|
||||
{
|
||||
return typename GeomTraits::Plane_3
|
||||
(typename GeomTraits::Point_3 (double(m_centroids[index][0]),
|
||||
double(m_centroids[index][1]),
|
||||
double(m_centroids[index][2])),
|
||||
typename GeomTraits::Vector_3 (double(m_smallest_eigenvectors[index][0]),
|
||||
double(m_smallest_eigenvectors[index][1]),
|
||||
double(m_smallest_eigenvectors[index][2])));
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the normalized eigenvalues of the point at position `index`.
|
||||
*/
|
||||
const Eigenvalues& eigenvalue (std::size_t index) const { return m_eigenvalues[index]; }
|
||||
|
||||
/*!
|
||||
\brief Returns the sum of eigenvalues of the point at position `index`.
|
||||
*/
|
||||
float sum_of_eigenvalues (std::size_t index) const { return m_sum_eigenvalues[index]; }
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
float mean_range() const { return m_mean_range; }
|
||||
/// \endcond
|
||||
|
||||
private:
|
||||
|
||||
template <typename Point, typename DiagonalizeTraits>
|
||||
void compute (std::size_t index, const Point& query, std::vector<Point>& neighbor_points)
|
||||
{
|
||||
typedef typename Kernel_traits<Point>::Kernel::Vector_3 Vector;
|
||||
|
||||
if (neighbor_points.size() == 0)
|
||||
{
|
||||
Eigenvalues v = {{ 0.f, 0.f, 0.f }};
|
||||
m_eigenvalues[index] = v;
|
||||
m_centroids[index] = {{ float(query.x()), float(query.y()), float(query.z()) }};
|
||||
m_smallest_eigenvectors[index] = {{ 0.f, 0.f, 1.f }};
|
||||
#ifdef CGAL_CLASSIFICATION_EIGEN_FULL_STORAGE
|
||||
m_middle_eigenvectors[index] = {{ 0.f, 1.f, 0.f }};
|
||||
m_largest_eigenvectors[index] = {{ 1.f, 0.f, 0.f }};
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
Point centroid = CGAL::centroid (neighbor_points.begin(), neighbor_points.end());
|
||||
m_centroids[index] = {{ float(centroid.x()), float(centroid.y()), float(centroid.z()) }};
|
||||
|
||||
CGAL::cpp11::array<float, 6> covariance = {{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }};
|
||||
|
||||
for (std::size_t i = 0; i < neighbor_points.size(); ++ i)
|
||||
{
|
||||
Vector d = neighbor_points[i] - centroid;
|
||||
covariance[0] += d.x () * d.x ();
|
||||
covariance[1] += d.x () * d.y ();
|
||||
covariance[2] += d.x () * d.z ();
|
||||
covariance[3] += d.y () * d.y ();
|
||||
covariance[4] += d.y () * d.z ();
|
||||
covariance[5] += d.z () * d.z ();
|
||||
}
|
||||
|
||||
CGAL::cpp11::array<float, 3> evalues = {{ 0.f, 0.f, 0.f }};
|
||||
CGAL::cpp11::array<float, 9> evectors = {{ 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 0.f }};
|
||||
|
||||
DiagonalizeTraits::diagonalize_selfadjoint_covariance_matrix
|
||||
(covariance, evalues, evectors);
|
||||
|
||||
// Normalize
|
||||
float sum = evalues[0] + evalues[1] + evalues[2];
|
||||
if (sum > 0.f)
|
||||
for (std::size_t i = 0; i < 3; ++ i)
|
||||
evalues[i] = evalues[i] / sum;
|
||||
m_sum_eigenvalues[index] = float(sum);
|
||||
m_eigenvalues[index] = {{ float(evalues[0]), float(evalues[1]), float(evalues[2]) }};
|
||||
m_smallest_eigenvectors[index] = {{ float(evectors[0]), float(evectors[1]), float(evectors[2]) }};
|
||||
#ifdef CGAL_CLASSIFICATION_EIGEN_FULL_STORAGE
|
||||
m_middle_eigenvectors[index] = {{ float(evectors[3]), float(evectors[4]), float(evectors[5]) }};
|
||||
m_largest_eigenvectors[index] = {{ float(evectors[6]), float(evectors[7]), float(evectors[8]) }};
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_LOCAL_EIGEN_ANALYSIS_H
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||
// 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, Florent Lafarge
|
||||
|
||||
#ifndef CGAL_CLASSIFICATION_PLANIMETRIC_GRID_H
|
||||
#define CGAL_CLASSIFICATION_PLANIMETRIC_GRID_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <CGAL/Classification/Image.h>
|
||||
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationDataStructures
|
||||
|
||||
\brief Class that precomputes a 2D planimetric grid.
|
||||
|
||||
The grid is composed of squared cells with a user-defined size,
|
||||
each cell containing the list of indices of the points whose
|
||||
projection along the Z-axis lies within this cell. The mapping
|
||||
from each point to the cell it lies in is also stored.
|
||||
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type is
|
||||
`RandomAccessIterator` and its value type is the key type of
|
||||
`PointMap`.
|
||||
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `GeomTraits::Point_3`.
|
||||
*/
|
||||
|
||||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Planimetric_grid
|
||||
{
|
||||
public:
|
||||
typedef typename GeomTraits::Point_3 Point_3;
|
||||
typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3;
|
||||
|
||||
private:
|
||||
typedef Image<std::vector<std::size_t> > Image_indices;
|
||||
typedef Image<bool> Image_bool;
|
||||
|
||||
Image_indices m_grid;
|
||||
float m_resolution;
|
||||
std::vector<std::size_t> m_x;
|
||||
std::vector<std::size_t> m_y;
|
||||
Planimetric_grid* m_lower_scale;
|
||||
std::size_t m_current_scale;
|
||||
|
||||
std::size_t m_width;
|
||||
std::size_t m_height;
|
||||
std::vector<bool> m_has_points;
|
||||
|
||||
public:
|
||||
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
typedef unspecified_type iterator; ///< A forward iterator with value type `std::size_t`.
|
||||
#else
|
||||
class iterator
|
||||
: public boost::iterator_facade<iterator, std::size_t, std::forward_iterator_tag>
|
||||
{
|
||||
public:
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
iterator(const Planimetric_grid* lowest_scale,
|
||||
std::size_t scale,
|
||||
std::size_t large_x,
|
||||
std::size_t large_y,
|
||||
bool end = false)
|
||||
: m_lowest_scale (lowest_scale)
|
||||
, m_idx(0)
|
||||
{
|
||||
std::size_t size = 1;
|
||||
|
||||
while (scale != 0)
|
||||
{
|
||||
size *= 2;
|
||||
-- scale;
|
||||
}
|
||||
|
||||
std::size_t xmin = large_x * size;
|
||||
m_xmax = (large_x + 1) * size;
|
||||
m_ymin = large_y * size;
|
||||
m_ymax = (large_y + 1) * size;
|
||||
|
||||
m_pos_x = xmin;
|
||||
m_pos_y = m_ymin;
|
||||
|
||||
bool found_one = false;
|
||||
for (std::size_t x = xmin; x < m_xmax; ++ x)
|
||||
{
|
||||
for (std::size_t y = m_ymin; y < m_ymax; ++ y)
|
||||
if (m_lowest_scale->has_points(x,y))
|
||||
{
|
||||
m_pos_x = x;
|
||||
m_pos_y = y;
|
||||
found_one = true;
|
||||
break;
|
||||
}
|
||||
if(found_one)
|
||||
break;
|
||||
}
|
||||
|
||||
if (end && found_one)
|
||||
{
|
||||
m_pos_x = m_xmax;
|
||||
m_pos_y = m_ymax;
|
||||
}
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
++ m_idx;
|
||||
if (m_idx == m_lowest_scale->m_grid(m_pos_x, m_pos_y).size())
|
||||
{
|
||||
m_idx = 0;
|
||||
do
|
||||
{
|
||||
++ m_pos_y;
|
||||
if (m_pos_y == m_ymax)
|
||||
{
|
||||
m_pos_y = m_ymin;
|
||||
|
||||
++ m_pos_x;
|
||||
if (m_pos_x == m_xmax) // end() reached
|
||||
{
|
||||
m_pos_y = m_ymax; // put y to max so that this == end()
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
while (!(m_lowest_scale->has_points(m_pos_x, m_pos_y)));
|
||||
}
|
||||
}
|
||||
|
||||
bool equal (const iterator& other) const
|
||||
{
|
||||
return (m_pos_x == other.m_pos_x &&
|
||||
m_pos_y == other.m_pos_y &&
|
||||
m_idx == other.m_idx);
|
||||
}
|
||||
|
||||
std::size_t& dereference() const
|
||||
{
|
||||
return const_cast<std::size_t&>(m_lowest_scale->m_grid(m_pos_x, m_pos_y)[m_idx]);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const Planimetric_grid* m_lowest_scale;
|
||||
std::size_t m_xmin, m_xmax, m_ymin, m_ymax;
|
||||
|
||||
std::size_t m_size;
|
||||
std::size_t m_idx;
|
||||
std::size_t m_pos_x;
|
||||
std::size_t m_pos_y;
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
Planimetric_grid () { }
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Constructs a planimetric grid based on the input range.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
\param bbox bounding box of the input range.
|
||||
\param grid_resolution resolution of the planimetric grid.
|
||||
*/
|
||||
Planimetric_grid (const PointRange& input,
|
||||
PointMap point_map,
|
||||
const Iso_cuboid_3& bbox,
|
||||
float grid_resolution)
|
||||
: m_resolution (grid_resolution), m_lower_scale(NULL), m_current_scale(0)
|
||||
{
|
||||
m_width = (std::size_t)((bbox.xmax() - bbox.xmin()) / grid_resolution) + 1;
|
||||
m_height = (std::size_t)((bbox.ymax() - bbox.ymin()) / grid_resolution) + 1;
|
||||
|
||||
m_grid = Image_indices (m_width, m_height);
|
||||
|
||||
for (std::size_t i = 0; i < input.size(); ++ i)
|
||||
{
|
||||
const Point_3& p = get(point_map, *(input.begin()+i));
|
||||
m_x.push_back ((std::size_t)((p.x() - bbox.xmin()) / grid_resolution));
|
||||
m_y.push_back ((std::size_t)((p.y() - bbox.ymin()) / grid_resolution));
|
||||
m_grid(m_x.back(), m_y.back()).push_back (i);
|
||||
}
|
||||
// std::cerr << "Grid size = " << width << " " << height << std::endl;
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
Planimetric_grid (Planimetric_grid* lower_scale)
|
||||
: m_resolution (lower_scale->resolution() * 2), m_lower_scale (lower_scale)
|
||||
{
|
||||
m_current_scale = lower_scale->m_current_scale + 1;
|
||||
|
||||
m_width = (m_lower_scale->width() + 1) / 2;
|
||||
m_height = (m_lower_scale->height() + 1) / 2;
|
||||
|
||||
m_has_points.reserve(m_width * m_height);
|
||||
for (std::size_t x = 0; x < m_width; ++ x)
|
||||
for (std::size_t y = 0; y < m_height; ++ y)
|
||||
{
|
||||
m_has_points[x * m_height + y]
|
||||
= (m_lower_scale->has_points(x*2, y*2)
|
||||
|| m_lower_scale->has_points(x*2, y*2 + 1)
|
||||
|| m_lower_scale->has_points(x*2 + 1, y*2 + 1)
|
||||
|| m_lower_scale->has_points(x*2 + 1, y*2));
|
||||
}
|
||||
// std::cerr << "Grid size = " << width() << " " << height() << std::endl;
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
|
||||
/*!
|
||||
\brief Returns the resolution of the grid.
|
||||
*/
|
||||
float resolution() const
|
||||
{
|
||||
return m_resolution;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the number of cells along the X-axis.
|
||||
*/
|
||||
std::size_t width() const
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
/*!
|
||||
\brief Returns the number of cells along the Y-axis.
|
||||
*/
|
||||
std::size_t height() const
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
const Planimetric_grid* lowest_scale() const
|
||||
{
|
||||
if (m_current_scale == 0)
|
||||
return this;
|
||||
else
|
||||
return m_lower_scale->lowest_scale();
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns the begin iterator on the indices of the points
|
||||
lying in the cell at position `(x,y)`.
|
||||
*/
|
||||
iterator indices_begin(std::size_t x, std::size_t y) const
|
||||
{
|
||||
return iterator (lowest_scale(), m_current_scale, x, y);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the past-the-end iterator on the indices of the points
|
||||
lying in the cell at position `(x,y)`.
|
||||
*/
|
||||
iterator indices_end(std::size_t x, std::size_t y) const
|
||||
{
|
||||
return iterator (lowest_scale(), m_current_scale, x, y, true);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns `false` if the cell at position `(x,y)` is empty, `true` otherwise.
|
||||
*/
|
||||
bool has_points(std::size_t x, std::size_t y) const
|
||||
{
|
||||
if (m_current_scale == 0)
|
||||
{
|
||||
if (x >= m_grid.width() || y >= m_grid.height())
|
||||
return false;
|
||||
return (!(m_grid(x,y).empty()));
|
||||
}
|
||||
else
|
||||
return m_has_points[x * m_height + y];
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the `x` grid coordinate of the point at position `index`.
|
||||
*/
|
||||
std::size_t x(std::size_t index) const
|
||||
{
|
||||
if (m_lower_scale == NULL)
|
||||
return m_x[index];
|
||||
else
|
||||
return m_lower_scale->x(index) / 2;
|
||||
}
|
||||
/*!
|
||||
\brief Returns the `y` grid coordinate of the point at position `index`.
|
||||
*/
|
||||
std::size_t y(std::size_t index) const
|
||||
{
|
||||
if (m_lower_scale == NULL)
|
||||
return m_y[index];
|
||||
else
|
||||
return m_lower_scale->y(index) / 2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_PLANIMETRIC_GRID_H
|
||||
|
|
@ -0,0 +1,752 @@
|
|||
// 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_CLASSIFICATION_POINT_SET_FEATURE_GENERATOR_H
|
||||
#define CGAL_CLASSIFICATION_POINT_SET_FEATURE_GENERATOR_H
|
||||
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/Classification/Point_set_neighborhood.h>
|
||||
#include <CGAL/Classification/Planimetric_grid.h>
|
||||
#include <CGAL/Classification/Local_eigen_analysis.h>
|
||||
#include <CGAL/Classification/Feature_base.h>
|
||||
#include <CGAL/Classification/Feature/Hsv.h>
|
||||
#include <CGAL/Classification/Feature/Distance_to_plane.h>
|
||||
#include <CGAL/Classification/Feature/Echo_scatter.h>
|
||||
#include <CGAL/Classification/Feature/Elevation.h>
|
||||
#include <CGAL/Classification/Feature/Vertical_dispersion.h>
|
||||
#include <CGAL/Classification/Feature/Verticality.h>
|
||||
#include <CGAL/Classification/Feature/Eigen.h>
|
||||
#include <CGAL/Classification/Label.h>
|
||||
|
||||
#include <CGAL/bounding_box.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/Real_timer.h>
|
||||
#include <CGAL/demangle.h>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
#include <tbb/task_group.h>
|
||||
#include <tbb/mutex.h>
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationDataStructures
|
||||
|
||||
\brief Generates a set of generic features for point set
|
||||
classification.
|
||||
|
||||
This class takes care of computing all necessary data structures and
|
||||
of generating a set of generic features at multiple scales to
|
||||
increase the reliability of the classification.
|
||||
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type is
|
||||
`RandomAccessIterator` and its value type is the key type of
|
||||
`PointMap`.
|
||||
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `GeomTraits::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. It can be omitted: if Eigen 3 (or greater)
|
||||
is available and `CGAL_EIGEN3_ENABLED` is defined then an overload
|
||||
using `Eigen_diagonalize_traits` is provided. Otherwise, the
|
||||
internal implementation `Diagonalize_traits` is used.
|
||||
|
||||
*/
|
||||
template <typename GeomTraits,
|
||||
typename PointRange,
|
||||
typename PointMap,
|
||||
#if defined(DOXYGEN_RUNNING)
|
||||
typename ConcurrencyTag,
|
||||
#elif defined(CGAL_LINKED_WITH_TBB)
|
||||
typename ConcurrencyTag = CGAL::Parallel_tag,
|
||||
#else
|
||||
typename ConcurrencyTag = CGAL::Sequential_tag,
|
||||
#endif
|
||||
#if defined(DOXYGEN_RUNNING)
|
||||
typename DiagonalizeTraits>
|
||||
#else
|
||||
typename DiagonalizeTraits = CGAL::Default_diagonalize_traits<float,3> >
|
||||
#endif
|
||||
class Point_set_feature_generator
|
||||
{
|
||||
|
||||
public:
|
||||
typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3;
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
typedef typename PointRange::const_iterator Iterator;
|
||||
typedef typename PointMap::value_type Point;
|
||||
/// \endcond
|
||||
|
||||
typedef Classification::Planimetric_grid
|
||||
<GeomTraits, PointRange, PointMap> Planimetric_grid;
|
||||
typedef Classification::Point_set_neighborhood
|
||||
<GeomTraits, PointRange, PointMap> 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 Classification::Feature::Anisotropy Anisotropy;
|
||||
typedef Classification::Feature::Distance_to_plane
|
||||
<PointRange, PointMap> Distance_to_plane;
|
||||
typedef Classification::Feature::Eigentropy Eigentropy;
|
||||
typedef Classification::Feature::Elevation
|
||||
<GeomTraits, PointRange, PointMap> 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
|
||||
<GeomTraits, PointRange, PointMap> Dispersion;
|
||||
typedef Classification::Feature::Verticality
|
||||
<GeomTraits> Verticality;
|
||||
|
||||
typedef typename Classification::RGB_Color RGB_Color;
|
||||
/// \endcond
|
||||
|
||||
private:
|
||||
|
||||
struct Scale
|
||||
{
|
||||
Neighborhood* neighborhood;
|
||||
Planimetric_grid* grid;
|
||||
Local_eigen_analysis* eigen;
|
||||
float voxel_size;
|
||||
|
||||
Scale (const PointRange& input, PointMap point_map,
|
||||
const Iso_cuboid_3& bbox, float voxel_size,
|
||||
Planimetric_grid* lower_grid = NULL)
|
||||
: 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), ConcurrencyTag(), DiagonalizeTraits());
|
||||
|
||||
float 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();
|
||||
|
||||
if (lower_grid == NULL)
|
||||
grid = new Planimetric_grid (input, point_map, bbox, this->voxel_size);
|
||||
else
|
||||
grid = new Planimetric_grid(lower_grid);
|
||||
t.stop();
|
||||
CGAL_CLASSIFICATION_CERR << "Planimetric grid computed in " << t.time() << " second(s)" << std::endl;
|
||||
t.reset();
|
||||
}
|
||||
~Scale()
|
||||
{
|
||||
if (neighborhood != NULL)
|
||||
delete neighborhood;
|
||||
if (grid != NULL)
|
||||
delete grid;
|
||||
delete eigen;
|
||||
}
|
||||
|
||||
void reduce_memory_footprint(bool delete_neighborhood)
|
||||
{
|
||||
delete grid;
|
||||
grid = NULL;
|
||||
if (delete_neighborhood)
|
||||
{
|
||||
delete neighborhood;
|
||||
neighborhood = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
float grid_resolution() const { return voxel_size; }
|
||||
float radius_neighbors() const { return voxel_size * 5; }
|
||||
float radius_dtm() const { return voxel_size * 100; }
|
||||
|
||||
};
|
||||
|
||||
Iso_cuboid_3 m_bbox;
|
||||
std::vector<Scale*> m_scales;
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
tbb::task_group* m_tasks;
|
||||
#endif
|
||||
|
||||
struct Feature_adder
|
||||
{
|
||||
mutable Point_set_feature_generator* generator;
|
||||
std::size_t scale;
|
||||
|
||||
Feature_adder (Point_set_feature_generator* generator, std::size_t scale)
|
||||
: generator (generator), scale (scale) { }
|
||||
|
||||
virtual ~Feature_adder() { }
|
||||
|
||||
virtual void operator()() const = 0;
|
||||
};
|
||||
friend Feature_adder;
|
||||
|
||||
const PointRange& m_input;
|
||||
PointMap m_point_map;
|
||||
std::vector<Feature_adder*> m_adders;
|
||||
Feature_set* m_features;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/*!
|
||||
\brief Generates all possible features from an input range.
|
||||
|
||||
The size of the smallest scale is automatically estimated using a
|
||||
method equivalent to `CGAL::compute_average_spacing()` using 6
|
||||
neighbors. 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`
|
||||
- The version of `CGAL::Classification::Feature::Verticality` based on eigenvalues
|
||||
|
||||
If normal vectors are provided (if `VectorMap` is different from
|
||||
`CGAL::Default`), the following feature is generated at each
|
||||
scale:
|
||||
|
||||
- The version of `CGAL::Classification::Feature::Verticality` 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
|
||||
`GeomTraits::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 features the feature set where the features are instantiated.
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
\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>
|
||||
Point_set_feature_generator(Feature_set& features,
|
||||
const PointRange& input,
|
||||
PointMap point_map,
|
||||
std::size_t nb_scales,
|
||||
VectorMap normal_map = VectorMap(),
|
||||
ColorMap color_map = ColorMap(),
|
||||
EchoMap echo_map = EchoMap())
|
||||
: m_input (input), m_point_map (point_map), m_features (&features)
|
||||
{
|
||||
m_bbox = CGAL::bounding_box
|
||||
(boost::make_transform_iterator (m_input.begin(), CGAL::Property_map_to_unary_function<PointMap>(m_point_map)),
|
||||
boost::make_transform_iterator (m_input.end(), CGAL::Property_map_to_unary_function<PointMap>(m_point_map)));
|
||||
|
||||
typedef typename Default::Get<VectorMap, typename GeomTraits::Vector_3 >::type
|
||||
Vmap;
|
||||
typedef typename Default::Get<ColorMap, RGB_Color >::type
|
||||
Cmap;
|
||||
typedef typename Default::Get<EchoMap, std::size_t >::type
|
||||
Emap;
|
||||
|
||||
generate_features_impl (nb_scales,
|
||||
get_parameter<Vmap>(normal_map),
|
||||
get_parameter<Cmap>(color_map),
|
||||
get_parameter<Emap>(echo_map));
|
||||
}
|
||||
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual ~Point_set_feature_generator()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void reduce_memory_footprint()
|
||||
{
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
{
|
||||
m_scales[i]->reduce_memory_footprint(i > 0);
|
||||
}
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
|
||||
/*!
|
||||
\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`.
|
||||
*/
|
||||
const Neighborhood& neighborhood(std::size_t scale = 0) const { return (*m_scales[scale]->neighborhood); }
|
||||
/*!
|
||||
\brief Returns the planimetric grid structure at scale `scale`.
|
||||
*/
|
||||
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`.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
float 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`).
|
||||
*/
|
||||
float 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.
|
||||
*/
|
||||
float radius_dtm(std::size_t scale = 0) const { return m_scales[scale]->radius_dtm(); }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
delete m_scales[i];
|
||||
m_scales.clear();
|
||||
}
|
||||
|
||||
void generate_point_based_features ()
|
||||
{
|
||||
|
||||
generate_multiscale_feature_variant_0<Anisotropy> ();
|
||||
generate_multiscale_feature_variant_0<Eigentropy> ();
|
||||
generate_multiscale_feature_variant_0<Linearity> ();
|
||||
generate_multiscale_feature_variant_0<Omnivariance> ();
|
||||
generate_multiscale_feature_variant_0<Planarity> ();
|
||||
generate_multiscale_feature_variant_0<Sphericity> ();
|
||||
generate_multiscale_feature_variant_0<Sum_eigen> ();
|
||||
generate_multiscale_feature_variant_0<Surface_variation> ();
|
||||
|
||||
generate_multiscale_feature_variant_1<Distance_to_plane> ();
|
||||
generate_multiscale_feature_variant_2<Dispersion> ();
|
||||
generate_multiscale_feature_variant_3<Elevation> ();
|
||||
}
|
||||
|
||||
template <typename FeatureAdder>
|
||||
void launch_feature_computation (FeatureAdder* adder)
|
||||
{
|
||||
m_adders.push_back (adder);
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
m_tasks->run (*adder);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
(*adder)();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename VectorMap>
|
||||
struct Feature_adder_verticality : public Feature_adder
|
||||
{
|
||||
using Feature_adder::generator;
|
||||
using Feature_adder::scale;
|
||||
VectorMap normal_map;
|
||||
|
||||
// TODO!
|
||||
Feature_adder_verticality (Point_set_feature_generator* generator, VectorMap normal_map, std::size_t scale)
|
||||
: Feature_adder (generator, scale), normal_map (normal_map) { }
|
||||
|
||||
void operator() () const
|
||||
{
|
||||
Feature_handle fh = generator->m_features->template add<Verticality> (generator->m_input, normal_map);
|
||||
std::ostringstream oss;
|
||||
oss << fh->name() << "_" << scale;
|
||||
fh->set_name (oss.str());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename VectorMap>
|
||||
void generate_normal_based_features(VectorMap normal_map)
|
||||
{
|
||||
launch_feature_computation (new Feature_adder_verticality<VectorMap> (this, normal_map, 0));
|
||||
}
|
||||
|
||||
void generate_normal_based_features(const CGAL::Default_property_map<Iterator, typename GeomTraits::Vector_3>&)
|
||||
{
|
||||
generate_multiscale_feature_variant_0<Verticality> ();
|
||||
}
|
||||
|
||||
template <typename ColorMap>
|
||||
struct Feature_adder_color : public Feature_adder
|
||||
{
|
||||
typedef Classification::Feature::Hsv<GeomTraits, PointRange, ColorMap> Hsv;
|
||||
|
||||
using Feature_adder::generator;
|
||||
using Feature_adder::scale;
|
||||
ColorMap color_map;
|
||||
std::size_t channel;
|
||||
float mean;
|
||||
float sd;
|
||||
|
||||
// TODO!
|
||||
Feature_adder_color (Point_set_feature_generator* generator, ColorMap color_map, std::size_t scale,
|
||||
std::size_t channel, float mean, float sd)
|
||||
: Feature_adder (generator, scale), color_map (color_map),
|
||||
channel (channel), mean (mean), sd (sd) { }
|
||||
|
||||
void operator() () const
|
||||
{
|
||||
Feature_handle fh = generator->m_features->template add<Hsv> (generator->m_input, color_map,
|
||||
(typename Hsv::Channel)(channel), mean, sd);
|
||||
std::ostringstream oss;
|
||||
oss << fh->name() << "_" << scale;
|
||||
fh->set_name (oss.str());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ColorMap>
|
||||
void generate_color_based_features(ColorMap color_map)
|
||||
{
|
||||
for (std::size_t i = 0; i <= 8; ++ i)
|
||||
launch_feature_computation (new Feature_adder_color<ColorMap> (this, color_map, 0,
|
||||
0, 45 * i, 22.5));
|
||||
|
||||
for (std::size_t i = 0; i <= 4; ++ i)
|
||||
launch_feature_computation (new Feature_adder_color<ColorMap> (this, color_map, 0,
|
||||
1, 25 * i, 12.5));
|
||||
|
||||
for (std::size_t i = 0; i <= 4; ++ i)
|
||||
launch_feature_computation (new Feature_adder_color<ColorMap> (this, color_map, 0,
|
||||
2, 25 * i, 12.5));
|
||||
}
|
||||
|
||||
void generate_color_based_features(const CGAL::Default_property_map<Iterator, RGB_Color>&)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template <typename EchoMap>
|
||||
struct Feature_adder_echo : public Feature_adder
|
||||
{
|
||||
typedef Classification::Feature::Echo_scatter<GeomTraits, PointRange, PointMap, EchoMap> Echo_scatter;
|
||||
|
||||
using Feature_adder::generator;
|
||||
using Feature_adder::scale;
|
||||
EchoMap echo_map;
|
||||
|
||||
// TODO!
|
||||
Feature_adder_echo (Point_set_feature_generator* generator, EchoMap echo_map, std::size_t scale)
|
||||
: Feature_adder (generator, scale), echo_map (echo_map) { }
|
||||
|
||||
void operator() () const
|
||||
{
|
||||
Feature_handle fh = generator->m_features->template add<Echo_scatter> (generator->m_input,
|
||||
echo_map,
|
||||
generator->grid(scale),
|
||||
generator->radius_neighbors(scale));
|
||||
std::ostringstream oss;
|
||||
oss << fh->name() << "_" << scale;
|
||||
fh->set_name (oss.str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename EchoMap>
|
||||
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<EchoMap> (this, echo_map, i));
|
||||
}
|
||||
|
||||
void generate_echo_based_features(const CGAL::Default_property_map<Iterator, std::size_t>&)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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_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);
|
||||
|
||||
float voxel_size = -1;
|
||||
// voxel_size = 0.05; // WARNING: do not keep (-1 is the right value)
|
||||
|
||||
|
||||
m_scales.push_back (new Scale (m_input, m_point_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_point_map, m_bbox, voxel_size, m_scales[i-1]->grid));
|
||||
}
|
||||
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 <typename Feature_type>
|
||||
struct Feature_adder_variant_0 : public Feature_adder
|
||||
{
|
||||
using Feature_adder::generator;
|
||||
using Feature_adder::scale;
|
||||
|
||||
Feature_adder_variant_0 (Point_set_feature_generator* generator, std::size_t scale)
|
||||
: Feature_adder (generator, scale) { }
|
||||
|
||||
void operator() () const
|
||||
{
|
||||
Feature_handle fh = generator->m_features->template add<Feature_type> (generator->m_input, generator->eigen(scale));
|
||||
std::ostringstream oss;
|
||||
oss << fh->name() << "_" << scale;
|
||||
fh->set_name (oss.str());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Feature_type>
|
||||
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<Feature_type> (this, i));
|
||||
}
|
||||
|
||||
template <typename Feature_type>
|
||||
struct Feature_adder_variant_1 : public Feature_adder
|
||||
{
|
||||
using Feature_adder::generator;
|
||||
using Feature_adder::scale;
|
||||
PointMap point_map;
|
||||
|
||||
// TODO!
|
||||
Feature_adder_variant_1 (Point_set_feature_generator* generator, PointMap point_map, std::size_t scale)
|
||||
: Feature_adder (generator, scale), point_map (point_map) { }
|
||||
|
||||
void operator() () const
|
||||
{
|
||||
Feature_handle fh = generator->m_features->template add<Feature_type> (generator->m_input, point_map,
|
||||
generator->eigen(scale));
|
||||
std::ostringstream oss;
|
||||
oss << fh->name() << "_" << scale;
|
||||
fh->set_name (oss.str());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Feature_type>
|
||||
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<Feature_type> (this, m_point_map, i));
|
||||
}
|
||||
|
||||
template <typename Feature_type>
|
||||
struct Feature_adder_variant_2 : public Feature_adder
|
||||
{
|
||||
using Feature_adder::generator;
|
||||
using Feature_adder::scale;
|
||||
PointMap point_map;
|
||||
|
||||
// TODO!
|
||||
Feature_adder_variant_2 (Point_set_feature_generator* generator, PointMap point_map, std::size_t scale)
|
||||
: Feature_adder (generator, scale), point_map (point_map) { }
|
||||
|
||||
void operator() () const
|
||||
{
|
||||
Feature_handle fh = generator->m_features->template add<Feature_type>
|
||||
(generator->m_input, point_map,
|
||||
generator->grid(scale),
|
||||
generator->radius_neighbors(scale));
|
||||
std::ostringstream oss;
|
||||
oss << fh->name() << "_" << scale;
|
||||
fh->set_name (oss.str());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Feature_type>
|
||||
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<Feature_type> (this, m_point_map, i));
|
||||
}
|
||||
|
||||
template <typename Feature_type>
|
||||
struct Feature_adder_variant_3 : public Feature_adder
|
||||
{
|
||||
using Feature_adder::generator;
|
||||
using Feature_adder::scale;
|
||||
PointMap point_map;
|
||||
|
||||
// TODO!
|
||||
Feature_adder_variant_3 (Point_set_feature_generator* generator, PointMap point_map, std::size_t scale)
|
||||
: Feature_adder (generator, scale), point_map (point_map) { }
|
||||
|
||||
void operator() () const
|
||||
{
|
||||
Feature_handle fh = generator->m_features->template add<Feature_type> (generator->m_input,
|
||||
point_map,
|
||||
generator->grid(scale),
|
||||
generator->radius_dtm(scale));
|
||||
std::ostringstream oss;
|
||||
oss << fh->name() << "_" << scale;
|
||||
fh->set_name (oss.str());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Feature_type>
|
||||
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<Feature_type> (this, m_point_map, i));
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_POINT_SET_FEATURE_GENERATOR_H
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
// 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_CLASSIFICATION_POINT_SET_NEIGHBORHOOD_H
|
||||
#define CGAL_CLASSIFICATION_POINT_SET_NEIGHBORHOOD_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/iterator/counting_iterator.hpp>
|
||||
|
||||
#include <CGAL/Search_traits_3.h>
|
||||
#include <CGAL/Fuzzy_sphere.h>
|
||||
#include <CGAL/Orthogonal_k_neighbor_search.h>
|
||||
#include <CGAL/Default_diagonalize_traits.h>
|
||||
#include <CGAL/centroid.h>
|
||||
#include <CGAL/grid_simplify_point_set.h>
|
||||
|
||||
#include <CGAL/Classification/Image.h>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationDataStructures
|
||||
|
||||
\brief Class that precomputes spatial searching structures for an
|
||||
input point set and gives access to the local neighborhood of a
|
||||
point as a set of indices.
|
||||
|
||||
It allows the user to generate models of `NeighborQuery` based on
|
||||
a fixed range neighborhood or on a fixed K number of neighbors. In
|
||||
addition, the spatial searching structures can be computed on a
|
||||
simplified version of the point set to allow for neighbor queries
|
||||
at a higher scale.
|
||||
|
||||
\tparam GeomTraits is a model of \cgal Kernel.
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type is
|
||||
`RandomAccessIterator` and its value type is the key type of
|
||||
`PointMap`.
|
||||
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `PointRange` and value type
|
||||
is `GeomTraits::Point_3`.
|
||||
*/
|
||||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Point_set_neighborhood
|
||||
{
|
||||
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Point_3 Point;
|
||||
|
||||
class My_point_property_map{
|
||||
const PointRange* input;
|
||||
PointMap point_map;
|
||||
|
||||
public:
|
||||
typedef Point value_type;
|
||||
typedef const value_type& reference;
|
||||
typedef std::size_t key_type;
|
||||
typedef boost::lvalue_property_map_tag category;
|
||||
My_point_property_map () { }
|
||||
My_point_property_map (const PointRange *input, PointMap point_map)
|
||||
: input (input), point_map (point_map) { }
|
||||
reference operator[] (key_type k) const { return get(point_map, *(input->begin()+k)); }
|
||||
friend inline reference get (const My_point_property_map& ppmap, key_type i)
|
||||
{ return ppmap[i]; }
|
||||
};
|
||||
|
||||
typedef Search_traits_3<GeomTraits> SearchTraits_3;
|
||||
typedef Search_traits_adapter <std::size_t, My_point_property_map, SearchTraits_3> Search_traits;
|
||||
typedef Sliding_midpoint<Search_traits> Splitter;
|
||||
typedef Distance_adapter<std::size_t, My_point_property_map, Euclidean_distance<SearchTraits_3> > Distance;
|
||||
typedef Kd_tree<Search_traits, Splitter, Tag_true> Tree;
|
||||
typedef Fuzzy_sphere<Search_traits> Sphere;
|
||||
typedef Orthogonal_k_neighbor_search<Search_traits, Distance, Splitter, Tree> Knn;
|
||||
|
||||
|
||||
Tree* m_tree;
|
||||
Distance m_distance;
|
||||
|
||||
std::vector<std::vector<std::size_t> > m_precomputed_neighbors;
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
Functor that computes the neighborhood of an input point with a
|
||||
fixed number of neighbors.
|
||||
|
||||
\cgalModels CGAL::Classification::NeighborQuery
|
||||
*/
|
||||
class K_neighbor_query
|
||||
{
|
||||
public:
|
||||
typedef typename Point_set_neighborhood::Point value_type; ///<
|
||||
private:
|
||||
const Point_set_neighborhood& neighborhood;
|
||||
unsigned int k;
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs a K neighbor query object.
|
||||
\param neighborhood point set neighborhood object.
|
||||
\param k number of neighbors per query.
|
||||
*/
|
||||
K_neighbor_query (const Point_set_neighborhood& neighborhood, unsigned int k)
|
||||
: neighborhood (neighborhood), k(k) { }
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
template <typename OutputIterator>
|
||||
OutputIterator operator() (const value_type& query, OutputIterator output) const
|
||||
{
|
||||
neighborhood.k_neighbors (query, k, output);
|
||||
return output;
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
/*!
|
||||
Functor that computes the neighborhood of an input point defined
|
||||
as the points lying in a sphere of fixed radius centered at the
|
||||
input point.
|
||||
|
||||
\cgalModels CGAL::Classification::NeighborQuery
|
||||
*/
|
||||
class Sphere_neighbor_query
|
||||
{
|
||||
public:
|
||||
typedef typename Point_set_neighborhood::Point value_type; ///<
|
||||
private:
|
||||
const Point_set_neighborhood& neighborhood;
|
||||
float radius;
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs a range neighbor query object.
|
||||
\param neighborhood point set neighborhood object.
|
||||
\param radius radius of the neighbor query sphere.
|
||||
*/
|
||||
Sphere_neighbor_query (const Point_set_neighborhood& neighborhood, float radius)
|
||||
: neighborhood (neighborhood), radius(radius) { }
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
template <typename OutputIterator>
|
||||
OutputIterator operator() (const value_type& query, OutputIterator output) const
|
||||
{
|
||||
neighborhood.sphere_neighbors (query, radius, output);
|
||||
return output;
|
||||
}
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
friend class K_neighbor_query;
|
||||
friend class Sphere_neighbor_query;
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
Point_set_neighborhood () : m_tree (NULL) { }
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Constructs a neighborhood object based on the input range.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
*/
|
||||
Point_set_neighborhood (const PointRange& input,
|
||||
PointMap point_map)
|
||||
: m_tree (NULL)
|
||||
{
|
||||
My_point_property_map pmap (&input, point_map);
|
||||
m_tree = new Tree (boost::counting_iterator<std::size_t> (0),
|
||||
boost::counting_iterator<std::size_t> (input.size()),
|
||||
Splitter(),
|
||||
Search_traits (pmap));
|
||||
m_distance = Distance (pmap);
|
||||
m_tree->build();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Constructs a simplified neighborhood object based on the input range.
|
||||
|
||||
This method first computes a simplified version of the input point
|
||||
set by voxelization: a 3D grid is defined and for each subset
|
||||
present in one cell, only the point closest to the centroid of
|
||||
this subset is used.
|
||||
|
||||
\param input input range.
|
||||
\param point_map property map to access the input points.
|
||||
\param voxel_size size of the cells of the 3D grid used for simplification.
|
||||
*/
|
||||
Point_set_neighborhood (const PointRange& input,
|
||||
PointMap point_map,
|
||||
float voxel_size)
|
||||
: m_tree (NULL)
|
||||
{
|
||||
// First, simplify
|
||||
std::vector<std::size_t> indices (input.size());
|
||||
for (std::size_t i = 0; i < indices.size(); ++ i)
|
||||
indices[i] = i;
|
||||
My_point_property_map pmap (&input, point_map);
|
||||
|
||||
voxelize_point_set(indices, pmap, voxel_size);
|
||||
|
||||
m_tree = new Tree (indices.begin(), indices.end(),
|
||||
Splitter(),
|
||||
Search_traits (pmap));
|
||||
m_distance = Distance (pmap);
|
||||
m_tree->build();
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
~Point_set_neighborhood ()
|
||||
{
|
||||
if (m_tree != NULL)
|
||||
delete m_tree;
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns a neighbor query object with fixed number of neighbors `k`.
|
||||
*/
|
||||
K_neighbor_query k_neighbor_query (const unsigned int k) const
|
||||
{
|
||||
return K_neighbor_query (*this, k);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns a neighbor query object with fixed radius `radius`.
|
||||
*/
|
||||
Sphere_neighbor_query sphere_neighbor_query (const float radius) const
|
||||
{
|
||||
return Sphere_neighbor_query (*this, radius);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template <typename OutputIterator>
|
||||
void sphere_neighbors (const Point& query, const FT radius_neighbors, OutputIterator output) const
|
||||
{
|
||||
CGAL_assertion (m_tree != NULL);
|
||||
Sphere fs (query, radius_neighbors, 0, m_tree->traits());
|
||||
m_tree->search (output, fs);
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
void k_neighbors (const Point& query, const unsigned int k, OutputIterator output) const
|
||||
{
|
||||
CGAL_assertion (m_tree != NULL);
|
||||
Knn search (*m_tree, query, k, 0, true, m_distance);
|
||||
for (typename Knn::iterator it = search.begin(); it != search.end(); ++ it)
|
||||
*(output ++) = it->first;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
void voxelize_point_set (std::vector<std::size_t>& indices, Map point_map,
|
||||
float voxel_size)
|
||||
{
|
||||
std::map<Point, std::vector<std::size_t> > grid;
|
||||
|
||||
for (std::size_t i = 0; i < indices.size(); ++ i)
|
||||
{
|
||||
const Point& p = get(point_map, indices[i]);
|
||||
Point ref (std::floor(p.x() / voxel_size),
|
||||
std::floor(p.y() / voxel_size),
|
||||
std::floor(p.z() / voxel_size));
|
||||
typename std::map<Point, std::vector<std::size_t> >::iterator it;
|
||||
boost::tie (it, boost::tuples::ignore)
|
||||
= grid.insert (std::make_pair (ref, std::vector<std::size_t>()));
|
||||
it->second.push_back (indices[i]);
|
||||
}
|
||||
indices.clear();
|
||||
for (typename std::map<Point, std::vector<std::size_t> >::iterator
|
||||
it = grid.begin(); it != grid.end(); ++ it)
|
||||
{
|
||||
const std::vector<std::size_t>& pts = it->second;
|
||||
Point centroid = CGAL::centroid (boost::make_transform_iterator
|
||||
(pts.begin(),
|
||||
CGAL::Property_map_to_unary_function<Map>(point_map)),
|
||||
boost::make_transform_iterator
|
||||
(pts.end(),
|
||||
CGAL::Property_map_to_unary_function<Map>(point_map)));
|
||||
std::size_t chosen = 0;
|
||||
float min_dist = (std::numeric_limits<float>::max)();
|
||||
for (std::size_t i = 0; i < pts.size(); ++ i)
|
||||
{
|
||||
float dist = CGAL::squared_distance(get(point_map, pts[i]), centroid);
|
||||
if (dist < min_dist)
|
||||
{
|
||||
min_dist = dist;
|
||||
chosen = pts[i];
|
||||
}
|
||||
}
|
||||
indices.push_back (chosen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_POINT_SET_POINT_SET_NEIGHBORHOOD_H
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
#ifndef CGAL_CLASSIFICATION_RANDOM_FOREST_CLASSIFIER_H
|
||||
#define CGAL_CLASSIFICATION_RANDOM_FOREST_CLASSIFIER_H
|
||||
|
||||
#include <CGAL/Classification/Feature_set.h>
|
||||
#include <CGAL/Classification/Label_set.h>
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationClassifiers
|
||||
|
||||
\brief %Classifier based on a random forest algorithm.
|
||||
|
||||
\note This class requires the \ref thirdpartyOpenCV library.
|
||||
|
||||
\cgalModels `CGAL::Classification::Classifier`
|
||||
*/
|
||||
class Random_forest_classifier
|
||||
{
|
||||
const Label_set& m_labels;
|
||||
const Feature_set& m_features;
|
||||
int m_max_depth;
|
||||
int m_min_sample_count;
|
||||
int m_max_categories;
|
||||
int m_max_number_of_trees_in_the_forest;
|
||||
float m_forest_accuracy;
|
||||
|
||||
#if (CV_MAJOR_VERSION < 3)
|
||||
CvRTrees* rtree;
|
||||
#else
|
||||
cv::Ptr<cv::ml::RTrees> rtree;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
\brief Instantiate the classifier using the sets of `labels` and `features`.
|
||||
|
||||
Parameters documentation is copy-pasted from [the official documentation of OpenCV](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html). For more details on this method, please refer to it.
|
||||
|
||||
\param labels label set used.
|
||||
\param features feature set used.
|
||||
\param max_depth the depth of the tree. A low value will likely underfit and conversely a high value will likely overfit. The optimal value can be obtained using cross validation or other suitable methods.
|
||||
\param min_sample_count minimum samples required at a leaf node for it to be split. A reasonable value is a small percentage of the total data e.g. 1%.
|
||||
\param max_categories Cluster possible values of a categorical variable into \f$ K \leq max\_categories \f$ clusters to find a suboptimal split. If a discrete variable, on which the training procedure tries to make a split, takes more than max_categories values, the precise best subset estimation may take a very long time because the algorithm is exponential. Instead, many decision trees engines (including ML) try to find sub-optimal split in this case by clustering all the samples into max_categories clusters that is some categories are merged together. The clustering is applied only in \f$ n>2-class \f$ classification problems for categorical variables with \f$ N > max\_categories \f$ possible values. In case of regression and 2-class classification the optimal split can be found efficiently without employing clustering, thus the parameter is not used in these cases.
|
||||
\param max_number_of_trees_in_the_forest The maximum number of trees in the forest (surprise, surprise). Typically the more trees you have the better the accuracy. However, the improvement in accuracy generally diminishes and asymptotes pass a certain number of trees. Also to keep in mind, the number of tree increases the prediction time linearly.
|
||||
\param forest_accuracy Sufficient accuracy (OOB error).
|
||||
*/
|
||||
Random_forest_classifier (const Label_set& labels,
|
||||
const Feature_set& features,
|
||||
int max_depth = 20,
|
||||
int min_sample_count = 5,
|
||||
int max_categories = 15,
|
||||
int max_number_of_trees_in_the_forest = 100,
|
||||
float forest_accuracy = 0.01f)
|
||||
: m_labels (labels), m_features (features),
|
||||
m_max_depth (max_depth), m_min_sample_count (min_sample_count),
|
||||
m_max_categories (max_categories),
|
||||
m_max_number_of_trees_in_the_forest (max_number_of_trees_in_the_forest),
|
||||
m_forest_accuracy (forest_accuracy)
|
||||
#if (CV_MAJOR_VERSION < 3)
|
||||
, rtree (NULL)
|
||||
#endif
|
||||
{ }
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
~Random_forest_classifier ()
|
||||
{
|
||||
#if (CV_MAJOR_VERSION < 3)
|
||||
if (rtree != NULL)
|
||||
delete rtree;
|
||||
#endif
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Runs the training algorithm.
|
||||
|
||||
From the set of provided ground truth, this algorithm estimates
|
||||
sets up the random trees that produce the most accurate result
|
||||
with respect to this ground truth.
|
||||
|
||||
\pre At least one ground truth item should be assigned to each
|
||||
label.
|
||||
|
||||
\param ground_truth vector of label indices. It should contain for
|
||||
each input item, in the same order as the input set, the index of
|
||||
the corresponding label in the `Label_set` provided in the
|
||||
constructor. Input items that do not have a ground truth
|
||||
information should be given the value `std::size_t(-1)`.
|
||||
*/
|
||||
void train (const std::vector<std::size_t>& ground_truth)
|
||||
{
|
||||
#if (CV_MAJOR_VERSION < 3)
|
||||
if (rtree != NULL)
|
||||
delete rtree;
|
||||
#endif
|
||||
|
||||
std::size_t nb_samples = 0;
|
||||
for (std::size_t i = 0; i < ground_truth.size(); ++ i)
|
||||
if (ground_truth[i] != std::size_t(-1))
|
||||
++ nb_samples;
|
||||
|
||||
|
||||
cv::Mat training_features (nb_samples, m_features.size(), CV_32FC1);
|
||||
cv::Mat training_labels (nb_samples, 1, CV_32FC1);
|
||||
|
||||
for (std::size_t i = 0, index = 0; i < ground_truth.size(); ++ i)
|
||||
if (ground_truth[i] != std::size_t(-1))
|
||||
{
|
||||
for (std::size_t f = 0; f < m_features.size(); ++ f)
|
||||
training_features.at<float>(index, f) = m_features[f]->value(i);
|
||||
training_labels.at<float>(index, 0) = ground_truth[i];
|
||||
++ index;
|
||||
}
|
||||
|
||||
#if (CV_MAJOR_VERSION < 3)
|
||||
float* priors = new float[m_labels.size()];
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
priors[i] = 1.;
|
||||
|
||||
CvRTParams params (m_max_depth, m_min_sample_count,
|
||||
0, false, m_max_categories, priors, false, 0,
|
||||
m_max_number_of_trees_in_the_forest,
|
||||
m_forest_accuracy,
|
||||
CV_TERMCRIT_ITER | CV_TERMCRIT_EPS
|
||||
);
|
||||
|
||||
cv::Mat var_type (m_features.size() + 1, 1, CV_8U);
|
||||
var_type.setTo (cv::Scalar(CV_VAR_NUMERICAL));
|
||||
|
||||
rtree = new CvRTrees;
|
||||
rtree->train (training_features, CV_ROW_SAMPLE, training_labels,
|
||||
cv::Mat(), cv::Mat(), var_type, cv::Mat(), params);
|
||||
|
||||
delete[] priors;
|
||||
#else
|
||||
rtree = cv::ml::RTrees::create();
|
||||
rtree->setMaxDepth (m_max_depth);
|
||||
rtree->setMinSampleCount (m_min_sample_count);
|
||||
rtree->setMaxCategories (m_max_categories);
|
||||
rtree->setCalculateVarImportance (false);
|
||||
rtree->setRegressionAccuracy (m_forest_accuracy);
|
||||
rtree->setUseSurrogates(false);
|
||||
rtree->setPriors(cv::Mat());
|
||||
rtree->setCalculateVarImportance(false);
|
||||
|
||||
cv::TermCriteria criteria (cv::TermCriteria::EPS + cv::TermCriteria::COUNT, m_max_number_of_trees_in_the_forest, 0.01f);
|
||||
rtree->setTermCriteria (criteria);
|
||||
|
||||
cv::Ptr<cv::ml::TrainData> tdata = cv::ml::TrainData::create
|
||||
(training_features, cv::ml::ROW_SAMPLE, training_labels);
|
||||
|
||||
rtree->train (tdata);
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void set_max_depth (int max_depth) { m_max_depth = max_depth; }
|
||||
void set_min_sample_count (int min_sample_count) { m_min_sample_count = min_sample_count; }
|
||||
void set_max_categories (int max_categories) { m_max_categories = max_categories; }
|
||||
void set_max_number_of_trees_in_the_forest (int max_number_of_trees_in_the_forest)
|
||||
{ m_max_number_of_trees_in_the_forest = max_number_of_trees_in_the_forest; }
|
||||
void set_forest_accuracy (float forest_accuracy) { m_forest_accuracy = forest_accuracy; }
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
void operator() (std::size_t item_index, std::vector<float>& out) const
|
||||
{
|
||||
out.resize (m_labels.size(), 0.);
|
||||
|
||||
cv::Mat feature (1, m_features.size(), CV_32FC1);
|
||||
for (std::size_t f = 0; f < m_features.size(); ++ f)
|
||||
feature.at<float>(0, f) = m_features[f]->value(item_index);
|
||||
|
||||
//compute the result of each tree
|
||||
#if (CV_MAJOR_VERSION < 3)
|
||||
std::size_t nb_trees = std::size_t(rtree->get_tree_count());
|
||||
for (std::size_t i = 0; i < nb_trees; i++)
|
||||
{
|
||||
std::size_t l = rtree->get_tree(int(i))->predict(feature, cv::Mat())->value;
|
||||
out[l] += 1.;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < out.size(); ++ i)
|
||||
out[i] = - std::log (out[i] / nb_trees);
|
||||
#else
|
||||
|
||||
std::vector<float> result (1, 0);
|
||||
|
||||
rtree->predict (feature, result);
|
||||
|
||||
for (std::size_t i = 0; i < out.size(); ++ i)
|
||||
if (i == std::size_t(result[0]))
|
||||
out[i] = 0.;
|
||||
else
|
||||
out[i] = 1.;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void save_configuration (const char* filename)
|
||||
{
|
||||
rtree->save(filename);
|
||||
}
|
||||
|
||||
void load_configuration (const char* filename)
|
||||
{
|
||||
#if (CV_MAJOR_VERSION < 3)
|
||||
if (rtree != NULL)
|
||||
delete rtree;
|
||||
rtree = new CvRTrees;
|
||||
rtree->load(filename);
|
||||
#else
|
||||
rtree = cv::ml::StatModel::load<cv::ml::RTrees> (filename);
|
||||
#endif
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_RANDOM_FOREST_CLASSIFIER_H
|
||||
|
|
@ -0,0 +1,970 @@
|
|||
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||
// 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, Florent Lafarge
|
||||
|
||||
#ifndef CLASSIFICATION_SUM_OF_WEIGHTED_FEATURES_CLASSIFIER_H
|
||||
#define CLASSIFICATION_SUM_OF_WEIGHTED_FEATURES_CLASSIFIER_H
|
||||
|
||||
#include <CGAL/Classification/Feature_set.h>
|
||||
#include <CGAL/Classification/Label_set.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>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/blocked_range.h>
|
||||
#include <tbb/scalable_allocator.h>
|
||||
#include <tbb/mutex.h>
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
//#define CGAL_CLASSIFICATION_VERBOSE
|
||||
#if defined(CGAL_CLASSIFICATION_VERBOSE)
|
||||
#define CGAL_CLASSIFICATION_CERR std::cerr
|
||||
#else
|
||||
#define CGAL_CLASSIFICATION_CERR std::ostream(0)
|
||||
#endif
|
||||
|
||||
//#define CGAL_CLASSTRAINING_VERBOSE
|
||||
#if defined(CGAL_CLASSTRAINING_VERBOSE)
|
||||
#define CGAL_CLASSTRAINING_CERR std::cerr
|
||||
#else
|
||||
#define CGAL_CLASSTRAINING_CERR std::ostream(0)
|
||||
#endif
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationClassifiers
|
||||
|
||||
\brief %Classifier based on the sum of weighted features with
|
||||
user-defined effects on labels.
|
||||
|
||||
\cgalModels `CGAL::Classification::Classifier`
|
||||
*/
|
||||
class Sum_of_weighted_features_classifier
|
||||
{
|
||||
public:
|
||||
|
||||
enum Effect /// Defines the effect of a feature on a type.
|
||||
{
|
||||
FAVORING = 0, ///< High values of the feature favor this type
|
||||
NEUTRAL = 1, ///< The feature has no effect on this type
|
||||
PENALIZING = 2 ///< Low values of the feature favor this type
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
class Compute_iou
|
||||
{
|
||||
std::vector<std::size_t>& m_training_set;
|
||||
const Sum_of_weighted_features_classifier& m_classifier;
|
||||
std::size_t m_label;
|
||||
std::vector<std::size_t>& m_true_positives;
|
||||
std::vector<std::size_t>& m_false_positives;
|
||||
std::vector<std::size_t>& m_false_negatives;
|
||||
std::vector<tbb::mutex>& m_tp_mutex;
|
||||
std::vector<tbb::mutex>& m_fp_mutex;
|
||||
std::vector<tbb::mutex>& m_fn_mutex;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Compute_iou (std::vector<std::size_t>& training_set,
|
||||
const Sum_of_weighted_features_classifier& classifier,
|
||||
std::size_t label,
|
||||
std::vector<std::size_t>& true_positives,
|
||||
std::vector<std::size_t>& false_positives,
|
||||
std::vector<std::size_t>& false_negatives,
|
||||
std::vector<tbb::mutex>& tp_mutex,
|
||||
std::vector<tbb::mutex>& fp_mutex,
|
||||
std::vector<tbb::mutex>& fn_mutex)
|
||||
: m_training_set (training_set)
|
||||
, m_classifier (classifier)
|
||||
, m_label (label)
|
||||
, m_true_positives (true_positives)
|
||||
, m_false_positives (false_positives)
|
||||
, m_false_negatives (false_negatives)
|
||||
, m_tp_mutex (tp_mutex)
|
||||
, m_fp_mutex (fp_mutex)
|
||||
, m_fn_mutex (fn_mutex)
|
||||
{ }
|
||||
|
||||
void operator()(const tbb::blocked_range<std::size_t>& r) const
|
||||
{
|
||||
for (std::size_t k = r.begin(); k != r.end(); ++ k)
|
||||
{
|
||||
std::size_t res = 0;
|
||||
|
||||
std::vector<float> v;
|
||||
m_classifier (m_training_set[k], v);
|
||||
|
||||
float min = std::numeric_limits<float>::max();
|
||||
for(std::size_t l = 0; l < v.size(); ++ l)
|
||||
if (v[l] < min)
|
||||
{
|
||||
min = v[l];
|
||||
res = l;
|
||||
}
|
||||
|
||||
if (m_label == res)
|
||||
{
|
||||
m_tp_mutex[m_label].lock();
|
||||
++ m_true_positives[m_label];
|
||||
m_tp_mutex[m_label].unlock();
|
||||
continue;
|
||||
}
|
||||
m_fp_mutex[res].lock();
|
||||
++ m_false_positives[res];
|
||||
m_fp_mutex[res].unlock();
|
||||
|
||||
m_fn_mutex[m_label].lock();
|
||||
++ m_false_negatives[m_label];
|
||||
m_fn_mutex[m_label].unlock();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
|
||||
const Label_set& m_labels;
|
||||
const Feature_set& m_features;
|
||||
std::vector<float> m_weights;
|
||||
std::vector<std::vector<Effect> > m_effect_table;
|
||||
mutable std::map<Label_handle, std::size_t> m_map_labels;
|
||||
mutable std::map<Feature_handle, std::size_t> m_map_features;
|
||||
|
||||
public:
|
||||
|
||||
/// \name Constructor
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
|
||||
\brief Instantiate the classifier using the sets of `labels` and `features`.
|
||||
|
||||
\note If the label set of the feature set are modified after
|
||||
instantiating this object (addition of removal of a label and/or of
|
||||
a feature), another classifier object should be instantiated as the
|
||||
internal data structures of this one are invalidated.
|
||||
*/
|
||||
Sum_of_weighted_features_classifier (const Label_set& labels,
|
||||
const Feature_set& features)
|
||||
: m_labels (labels), m_features (features),
|
||||
m_weights (features.size(), 1.),
|
||||
m_effect_table (labels.size(), std::vector<Effect>
|
||||
(features.size(),
|
||||
NEUTRAL))
|
||||
{
|
||||
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||
m_map_labels[labels[i]] = i;
|
||||
for (std::size_t i = 0; i < features.size(); ++ i)
|
||||
m_map_features[features[i]] = i;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Weights and Effects
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Sets the weight of `feature` (`weight` must be positive).
|
||||
*/
|
||||
void set_weight (Feature_handle feature, float weight)
|
||||
{
|
||||
m_weights[m_map_features[feature]] = weight;
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
void set_weight (std::size_t feature, float weight)
|
||||
{
|
||||
m_weights[feature] = weight;
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns the weight of `feature`.
|
||||
*/
|
||||
float weight (Feature_handle feature) const
|
||||
{
|
||||
return m_weights[m_map_features[feature]];
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
float weight (std::size_t feature) const
|
||||
{
|
||||
return m_weights[feature];
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Sets the `effect` of `feature` on `label`.
|
||||
*/
|
||||
void set_effect (Label_handle label, Feature_handle feature,
|
||||
Effect effect)
|
||||
{
|
||||
m_effect_table[m_map_labels[label]][m_map_features[feature]] = effect;
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
void set_effect (std::size_t label, std::size_t feature,
|
||||
Effect effect)
|
||||
{
|
||||
m_effect_table[label][feature] = effect;
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns the `effect` of `feature` on `label`.
|
||||
*/
|
||||
Effect effect (Label_handle label, Feature_handle feature) const
|
||||
{
|
||||
return m_effect_table[m_map_labels[label]][m_map_features[feature]];
|
||||
}
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
Effect effect (std::size_t label, std::size_t feature) const
|
||||
{
|
||||
return m_effect_table[label][feature];
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/// @}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
void operator() (std::size_t item_index,
|
||||
std::vector<float>& out) const
|
||||
{
|
||||
out.resize (m_labels.size());
|
||||
for (std::size_t l = 0; l < m_labels.size(); ++ l)
|
||||
{
|
||||
out[l] = 0.;
|
||||
for (std::size_t f = 0; f < m_features.size(); ++ f)
|
||||
if (weight(f) != 0.)
|
||||
out[l] += value (l, f, item_index);
|
||||
}
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/// \name Training
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Runs the training algorithm.
|
||||
|
||||
From the set of provided ground truth, this algorithm estimates
|
||||
the sets of weights and effects that produce the most accurate
|
||||
result with respect to this ground truth. Old weights and effects
|
||||
are discarded.
|
||||
|
||||
\pre At least one ground truth item should be assigned to each
|
||||
label.
|
||||
|
||||
\param ground_truth vector of label indices. It should contain for
|
||||
each input item, in the same order as the input set, the index of
|
||||
the corresponding label in the `Label_set` provided in the
|
||||
constructor. Input items that do not have a ground truth
|
||||
information should be given the value `std::size_t(-1)`.
|
||||
|
||||
\param nb_tests number of tests to perform. Higher values may
|
||||
provide the user with better results at the cost of a higher
|
||||
computation time. Using a value of at least 10 times the number of
|
||||
features is advised.
|
||||
|
||||
\return mean intersection-over-union over each label between the
|
||||
provided ground truth and the best classification found by the
|
||||
training set.
|
||||
*/
|
||||
template <typename ConcurrencyTag>
|
||||
float train (const std::vector<std::size_t>& ground_truth,
|
||||
unsigned int nb_tests = 300)
|
||||
{
|
||||
std::vector<std::vector<std::size_t> > training_sets (m_labels.size());
|
||||
std::size_t nb_tot = 0;
|
||||
for (std::size_t i = 0; i < ground_truth.size(); ++ i)
|
||||
if (ground_truth[i] != std::size_t(-1))
|
||||
{
|
||||
training_sets[ground_truth[i]].push_back (i);
|
||||
++ nb_tot;
|
||||
}
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << "Training using " << nb_tot << " inliers" << std::endl;
|
||||
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
if (training_sets.size() <= i || training_sets[i].empty())
|
||||
std::cerr << "WARNING: \"" << m_labels[i]->name() << "\" doesn't have a training set." << std::endl;
|
||||
|
||||
std::vector<float> best_weights (m_features.size(), 1.);
|
||||
|
||||
struct Feature_training
|
||||
{
|
||||
std::size_t i;
|
||||
float wmin;
|
||||
float wmax;
|
||||
float factor;
|
||||
|
||||
bool operator<(const Feature_training& other) const
|
||||
{
|
||||
return (wmin / wmax) < (other.wmin / other.wmax);
|
||||
}
|
||||
};
|
||||
std::vector<Feature_training> feature_train;
|
||||
std::size_t nb_trials = 100;
|
||||
float wmin = 1e-5f, wmax = 1e5f;
|
||||
float factor = std::pow (wmax/wmin, 1. / (float)nb_trials);
|
||||
|
||||
for (std::size_t j = 0; j < m_features.size(); ++ j)
|
||||
{
|
||||
Feature_handle feature = m_features[j];
|
||||
best_weights[j] = weight(j);
|
||||
|
||||
std::size_t nb_useful = 0;
|
||||
float min = (std::numeric_limits<float>::max)();
|
||||
float max = -(std::numeric_limits<float>::max)();
|
||||
|
||||
set_weight(j, wmin);
|
||||
for (std::size_t i = 0; i < 100; ++ i)
|
||||
{
|
||||
estimate_feature_effect(j, training_sets);
|
||||
if (feature_useful(j))
|
||||
{
|
||||
CGAL_CLASSTRAINING_CERR << "#";
|
||||
nb_useful ++;
|
||||
min = (std::min) (min, weight(j));
|
||||
max = (std::max) (max, weight(j));
|
||||
}
|
||||
else
|
||||
CGAL_CLASSTRAINING_CERR << "-";
|
||||
set_weight(j, factor * weight(j));
|
||||
}
|
||||
CGAL_CLASSTRAINING_CERR << std::endl;
|
||||
CGAL_CLASSTRAINING_CERR << feature->name() << " useful in "
|
||||
<< nb_useful << "% of the cases, in interval [ "
|
||||
<< min << " ; " << max << " ]" << std::endl;
|
||||
if (nb_useful < 2)
|
||||
{
|
||||
set_weight(j, 0.);
|
||||
best_weights[j] = weight(j);
|
||||
continue;
|
||||
}
|
||||
|
||||
feature_train.push_back (Feature_training());
|
||||
feature_train.back().i = j;
|
||||
feature_train.back().wmin = min / factor;
|
||||
feature_train.back().wmax = max * factor;
|
||||
|
||||
if (best_weights[j] == 1.)
|
||||
{
|
||||
set_weight(j, 0.5 * (feature_train.back().wmin + feature_train.back().wmax));
|
||||
best_weights[j] = weight(j);
|
||||
}
|
||||
else
|
||||
set_weight(j, best_weights[j]);
|
||||
estimate_feature_effect(j, training_sets);
|
||||
}
|
||||
|
||||
std::size_t nb_trials_per_feature = 1 + (std::size_t)(nb_tests / (float)(feature_train.size()));
|
||||
CGAL_CLASSIFICATION_CERR << "Trials = " << nb_tests << ", features = " << feature_train.size()
|
||||
<< ", trials per feature = " << nb_trials_per_feature << std::endl;
|
||||
for (std::size_t i = 0; i < feature_train.size(); ++ i)
|
||||
feature_train[i].factor
|
||||
= std::pow (feature_train[i].wmax / feature_train[i].wmin,
|
||||
1. / (float)nb_trials_per_feature);
|
||||
|
||||
|
||||
float best_score = 0.;
|
||||
best_score = compute_mean_iou<ConcurrencyTag>(training_sets);
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << "TRAINING GLOBALLY: Best score evolution: " << std::endl;
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << 100. * best_score << "% (found at initialization)" << std::endl;
|
||||
|
||||
std::sort (feature_train.begin(), feature_train.end());
|
||||
for (std::size_t i = 0; i < feature_train.size(); ++ i)
|
||||
{
|
||||
const Feature_training& tr = feature_train[i];
|
||||
std::size_t current_feature_changed = tr.i;
|
||||
Feature_handle current_feature = m_features[current_feature_changed];
|
||||
|
||||
std::size_t nb_used = 0;
|
||||
for (std::size_t j = 0; j < m_features.size(); ++ j)
|
||||
{
|
||||
if (j == current_feature_changed)
|
||||
continue;
|
||||
|
||||
set_weight(j, best_weights[j]);
|
||||
estimate_feature_effect(j, training_sets);
|
||||
if (feature_useful(j))
|
||||
nb_used ++;
|
||||
else
|
||||
set_weight(j, 0.);
|
||||
}
|
||||
|
||||
set_weight(current_feature_changed, tr.wmin);
|
||||
for (std::size_t j = 0; j < nb_trials_per_feature; ++ j)
|
||||
{
|
||||
estimate_feature_effect(current_feature_changed, training_sets);
|
||||
|
||||
float worst_score = 0.;
|
||||
worst_score = compute_mean_iou<ConcurrencyTag>(training_sets);
|
||||
if (worst_score > best_score)
|
||||
{
|
||||
best_score = worst_score;
|
||||
CGAL_CLASSIFICATION_CERR << 100. * best_score << "% (found at iteration "
|
||||
<< (i * nb_trials_per_feature) + j << "/" << nb_tests << ", "
|
||||
<< nb_used + (feature_useful(current_feature_changed) ? 1 : 0)
|
||||
<< "/" << m_features.size() << " feature(s) used)" << std::endl;
|
||||
for (std::size_t k = 0; k < m_features.size(); ++ k)
|
||||
best_weights[k] = weight(k);
|
||||
}
|
||||
set_weight(current_feature_changed, weight(current_feature_changed) * tr.factor);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < best_weights.size(); ++ i)
|
||||
set_weight(i, best_weights[i]);
|
||||
|
||||
estimate_features_effects(training_sets);
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << std::endl << "Best score found is at least " << 100. * best_score
|
||||
<< "% of correct classification" << std::endl;
|
||||
|
||||
std::size_t nb_removed = 0;
|
||||
for (std::size_t i = 0; i < best_weights.size(); ++ i)
|
||||
{
|
||||
Feature_handle feature = m_features[i];
|
||||
CGAL_CLASSTRAINING_CERR << "FEATURE " << feature->name() << ": " << best_weights[i] << std::endl;
|
||||
set_weight(i, best_weights[i]);
|
||||
|
||||
Effect side = effect(0, i);
|
||||
bool to_remove = true;
|
||||
for (std::size_t j = 0; j < m_labels.size(); ++ j)
|
||||
{
|
||||
Label_handle clabel = m_labels[j];
|
||||
if (effect(j,i) == FAVORING)
|
||||
CGAL_CLASSTRAINING_CERR << " * Favored for ";
|
||||
else if (effect(j,i) == PENALIZING)
|
||||
CGAL_CLASSTRAINING_CERR << " * Penalized for ";
|
||||
else
|
||||
CGAL_CLASSTRAINING_CERR << " * Neutral for ";
|
||||
if (effect(j,i) != side)
|
||||
to_remove = false;
|
||||
CGAL_CLASSTRAINING_CERR << clabel->name() << std::endl;
|
||||
}
|
||||
if (to_remove)
|
||||
{
|
||||
CGAL_CLASSTRAINING_CERR << " -> Useless! Should be removed" << std::endl;
|
||||
++ nb_removed;
|
||||
}
|
||||
}
|
||||
CGAL_CLASSIFICATION_CERR << nb_removed
|
||||
<< " feature(s) out of " << m_features.size() << " are useless" << std::endl;
|
||||
|
||||
return best_score;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
template <typename ConcurrencyTag>
|
||||
float train_random (const std::vector<std::size_t>& ground_truth,
|
||||
unsigned int nb_tests = 300)
|
||||
{
|
||||
std::vector<std::vector<std::size_t> > training_sets (m_labels.size());
|
||||
std::size_t nb_tot = 0;
|
||||
for (std::size_t i = 0; i < ground_truth.size(); ++ i)
|
||||
if (ground_truth[i] != std::size_t(-1))
|
||||
{
|
||||
training_sets[ground_truth[i]].push_back (i);
|
||||
++ nb_tot;
|
||||
}
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << "Training using " << nb_tot << " inliers" << std::endl;
|
||||
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
if (training_sets.size() <= i || training_sets[i].empty())
|
||||
std::cerr << "WARNING: \"" << m_labels[i]->name() << "\" doesn't have a training set." << std::endl;
|
||||
|
||||
std::vector<float> best_weights (m_features.size(), 1.);
|
||||
|
||||
struct Feature_training
|
||||
{
|
||||
std::size_t i;
|
||||
float wmin;
|
||||
float wmax;
|
||||
float factor;
|
||||
|
||||
bool operator<(const Feature_training& other) const
|
||||
{
|
||||
return (wmin / wmax) < (other.wmin / other.wmax);
|
||||
}
|
||||
};
|
||||
std::vector<Feature_training> feature_train;
|
||||
std::size_t nb_trials = 100;
|
||||
float wmin = 1e-5, wmax = 1e5;
|
||||
float factor = std::pow (wmax/wmin, 1. / (float)nb_trials);
|
||||
|
||||
for (std::size_t j = 0; j < m_features.size(); ++ j)
|
||||
{
|
||||
Feature_handle feature = m_features[j];
|
||||
best_weights[j] = weight(j);
|
||||
|
||||
std::size_t nb_useful = 0;
|
||||
float min = (std::numeric_limits<float>::max)();
|
||||
float max = -(std::numeric_limits<float>::max)();
|
||||
|
||||
set_weight(j, wmin);
|
||||
for (std::size_t i = 0; i < 100; ++ i)
|
||||
{
|
||||
estimate_feature_effect(j, training_sets);
|
||||
if (feature_useful(j))
|
||||
{
|
||||
CGAL_CLASSTRAINING_CERR << "#";
|
||||
nb_useful ++;
|
||||
min = (std::min) (min, weight(j));
|
||||
max = (std::max) (max, weight(j));
|
||||
}
|
||||
else
|
||||
CGAL_CLASSTRAINING_CERR << "-";
|
||||
set_weight(j, factor * weight(j));
|
||||
}
|
||||
CGAL_CLASSTRAINING_CERR << std::endl;
|
||||
CGAL_CLASSTRAINING_CERR << feature->name() << " useful in "
|
||||
<< nb_useful << "% of the cases, in interval [ "
|
||||
<< min << " ; " << max << " ]" << std::endl;
|
||||
if (nb_useful < 2)
|
||||
{
|
||||
set_weight(j, 0.);
|
||||
best_weights[j] = weight(j);
|
||||
continue;
|
||||
}
|
||||
|
||||
feature_train.push_back (Feature_training());
|
||||
feature_train.back().i = j;
|
||||
feature_train.back().wmin = min / factor;
|
||||
feature_train.back().wmax = max * factor;
|
||||
|
||||
if (best_weights[j] == 1.)
|
||||
{
|
||||
set_weight(j, 0.5 * (feature_train.back().wmin + feature_train.back().wmax));
|
||||
best_weights[j] = weight(j);
|
||||
}
|
||||
else
|
||||
set_weight(j, best_weights[j]);
|
||||
estimate_feature_effect(j, training_sets);
|
||||
}
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << "Trials = " << nb_tests << ", features = " << feature_train.size() << std::endl;
|
||||
|
||||
|
||||
float best_score = compute_mean_iou<ConcurrencyTag>(training_sets);
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << "TRAINING GLOBALLY: Best score evolution: " << std::endl;
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << 100. * best_score << "% (found at initialization)" << std::endl;
|
||||
|
||||
for (std::size_t i = 0; i < std::size_t(nb_tests); ++ i)
|
||||
{
|
||||
std::size_t nb_used = 0;
|
||||
std::size_t j = rand() % feature_train.size();
|
||||
set_weight (feature_train[j].i,
|
||||
feature_train[j].wmin + ((feature_train[j].wmax - feature_train[j].wmin)
|
||||
* (rand() / float(RAND_MAX))));
|
||||
estimate_feature_effect(feature_train[j].i, training_sets);
|
||||
|
||||
float worst_score = compute_mean_iou<ConcurrencyTag>(training_sets);
|
||||
|
||||
if (worst_score > best_score)
|
||||
{
|
||||
best_score = worst_score;
|
||||
CGAL_CLASSIFICATION_CERR << 100. * best_score << "% (found at iteration "
|
||||
<< i << "/" << nb_tests << ", "
|
||||
<< nb_used
|
||||
<< "/" << m_features.size() << " feature(s) used)" << std::endl;
|
||||
for (std::size_t k = 0; k < m_features.size(); ++ k)
|
||||
best_weights[k] = weight(k);
|
||||
}
|
||||
set_weight (feature_train[j].i,
|
||||
best_weights[feature_train[j].i]);
|
||||
estimate_feature_effect(feature_train[j].i, training_sets);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < best_weights.size(); ++ i)
|
||||
set_weight(i, best_weights[i]);
|
||||
|
||||
estimate_features_effects(training_sets);
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << std::endl << "Best score found is at least " << 100. * best_score
|
||||
<< "% of correct classification" << std::endl;
|
||||
|
||||
std::size_t nb_removed = 0;
|
||||
for (std::size_t i = 0; i < best_weights.size(); ++ i)
|
||||
{
|
||||
Feature_handle feature = m_features[i];
|
||||
CGAL_CLASSTRAINING_CERR << "FEATURE " << feature->name() << ": " << best_weights[i] << std::endl;
|
||||
set_weight(i, best_weights[i]);
|
||||
|
||||
Effect side = effect(0, i);
|
||||
bool to_remove = true;
|
||||
for (std::size_t j = 0; j < m_labels.size(); ++ j)
|
||||
{
|
||||
Label_handle clabel = m_labels[j];
|
||||
if (effect(j,i) == FAVORING)
|
||||
CGAL_CLASSTRAINING_CERR << " * Favored for ";
|
||||
else if (effect(j,i) == PENALIZING)
|
||||
CGAL_CLASSTRAINING_CERR << " * Penalized for ";
|
||||
else
|
||||
CGAL_CLASSTRAINING_CERR << " * Neutral for ";
|
||||
if (effect(j,i) != side)
|
||||
to_remove = false;
|
||||
CGAL_CLASSTRAINING_CERR << clabel->name() << std::endl;
|
||||
}
|
||||
if (to_remove)
|
||||
{
|
||||
CGAL_CLASSTRAINING_CERR << " -> Useless! Should be removed" << std::endl;
|
||||
++ nb_removed;
|
||||
}
|
||||
}
|
||||
CGAL_CLASSIFICATION_CERR << nb_removed
|
||||
<< " feature(s) out of " << m_features.size() << " are useless" << std::endl;
|
||||
|
||||
return best_score;
|
||||
}
|
||||
/// \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 weight of each feature
|
||||
- The effects of each feature on each label
|
||||
|
||||
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;
|
||||
|
||||
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
||||
{
|
||||
if (weight(m_features[i]) == 0)
|
||||
continue;
|
||||
boost::property_tree::ptree ptr;
|
||||
|
||||
ptr.put("name", m_features[i]->name());
|
||||
ptr.put("weight", weight(m_features[i]));
|
||||
tree.add_child("classification.features.feature", ptr);
|
||||
}
|
||||
|
||||
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
{
|
||||
boost::property_tree::ptree ptr;
|
||||
ptr.put("name", m_labels[i]->name());
|
||||
for (std::size_t j = 0; j < m_features.size(); ++ j)
|
||||
{
|
||||
if (weight(j) == 0)
|
||||
continue;
|
||||
boost::property_tree::ptree ptr2;
|
||||
ptr2.put("name", m_features[j]->name());
|
||||
Effect e = effect(i, j);
|
||||
if (e == PENALIZING)
|
||||
ptr2.put("effect", "penalized");
|
||||
else if (e == NEUTRAL)
|
||||
ptr2.put("effect", "neutral");
|
||||
else if (e == 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<std::string> settings(' ', 3);
|
||||
boost::property_tree::write_xml(output, tree, settings);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Loads a configuration from the stream `input`. A
|
||||
configuration is a set of weights and effects.
|
||||
|
||||
The input file should be in the XML format written by the
|
||||
`save_configuration()` method. Labels and features are described
|
||||
in the XML file by their name and the corresponding `Label` and
|
||||
`Feature_base` object should therefore be given the same names as
|
||||
the ones they had when saving the configuration.
|
||||
|
||||
\note If a feature (or label) found in the input file is not found
|
||||
in the `Feature_set` (`Label_set`) provided by the user in the
|
||||
constructor, and if `verbose` is set up to `true`, a warning is
|
||||
displayed.
|
||||
|
||||
\note If a feature (or label) provided by the user in the
|
||||
constructor is not described in the input file, the corresponding
|
||||
weights and effects are kept to their default values (1 for the
|
||||
weight and `NEUTRAL` for the effect).
|
||||
|
||||
\param input input stream.
|
||||
\param verbose displays warning if set to `true`. The method is
|
||||
silent otherwise.
|
||||
|
||||
\return `true` if all weights and effects found in the
|
||||
configuration file were applicable to the feature set and label
|
||||
set of this classifier, `false` otherwise.
|
||||
*/
|
||||
bool load_configuration (std::istream& input, bool verbose = false)
|
||||
{
|
||||
bool out = true;
|
||||
std::map<std::string, std::size_t> map_n2l;
|
||||
std::map<std::string, std::size_t> map_n2f;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
map_n2l.insert (std::make_pair (m_labels[i]->name(), i));
|
||||
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
||||
map_n2f.insert (std::make_pair (m_features[i]->name(), i));
|
||||
|
||||
boost::property_tree::ptree tree;
|
||||
boost::property_tree::read_xml(input, tree);
|
||||
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, tree.get_child("classification.features"))
|
||||
{
|
||||
std::string name = v.second.get<std::string>("name");
|
||||
std::map<std::string, std::size_t>::iterator
|
||||
found = map_n2f.find (name);
|
||||
if (found != map_n2f.end())
|
||||
m_weights[found->second] = v.second.get<float>("weight");
|
||||
else
|
||||
{
|
||||
if (verbose)
|
||||
std::cerr << "Warning: feature \"" << name << "\" in configuration file not found" << std::endl;
|
||||
out = false;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, tree.get_child("classification.labels"))
|
||||
{
|
||||
std::string label_name = v.second.get<std::string>("name");
|
||||
std::map<std::string, std::size_t>::iterator
|
||||
found = map_n2l.find (label_name);
|
||||
std::size_t l = 0;
|
||||
if (found != map_n2l.end())
|
||||
l = found->second;
|
||||
else
|
||||
{
|
||||
if (verbose)
|
||||
std::cerr << "Warning: label \"" << label_name << "\" in configuration file not found" << std::endl;
|
||||
out = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type &v2, v.second)
|
||||
{
|
||||
if (v2.first == "name")
|
||||
continue;
|
||||
|
||||
std::string feature_name = v2.second.get<std::string>("name");
|
||||
|
||||
std::map<std::string, std::size_t>::iterator
|
||||
found2 = map_n2f.find (feature_name);
|
||||
std::size_t f = 0;
|
||||
if (found2 != map_n2f.end())
|
||||
f = found2->second;
|
||||
else if (verbose)
|
||||
{
|
||||
if (verbose)
|
||||
std::cerr << "Warning: feature \"" << feature_name << "\" in configuration file not found" << std::endl;
|
||||
out = false;
|
||||
continue;
|
||||
}
|
||||
std::string e = v2.second.get<std::string>("effect");
|
||||
if (e == "penalized")
|
||||
set_effect (l, f, PENALIZING);
|
||||
else if (e == "neutral")
|
||||
set_effect (l, f, NEUTRAL);
|
||||
else
|
||||
set_effect (l, f, FAVORING);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
|
||||
float value (std::size_t label, std::size_t feature, std::size_t index) const
|
||||
{
|
||||
if (m_effect_table[label][feature] == FAVORING)
|
||||
return favored (feature, index);
|
||||
else if (m_effect_table[label][feature] == PENALIZING)
|
||||
return penalized (feature, index);
|
||||
else
|
||||
return ignored (feature, index);
|
||||
}
|
||||
|
||||
float normalized (std::size_t feature, std::size_t index) const
|
||||
{
|
||||
return (std::max) (0.f, (std::min) (1.f, m_features[feature]->value(index) / m_weights[feature]));
|
||||
}
|
||||
float favored (std::size_t feature, std::size_t index) const
|
||||
{
|
||||
return (1. - normalized (feature, index));
|
||||
}
|
||||
float penalized (std::size_t feature, std::size_t index) const
|
||||
{
|
||||
return normalized (feature, index);
|
||||
}
|
||||
float ignored (std::size_t, std::size_t) const
|
||||
{
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
void estimate_features_effects(std::vector<std::vector<std::size_t> >& training_sets)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
||||
estimate_feature_effect (i, training_sets);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void estimate_feature_effect (std::size_t feature,
|
||||
std::vector<std::vector<std::size_t> >& training_sets)
|
||||
{
|
||||
std::vector<float> mean (m_labels.size(), 0.);
|
||||
|
||||
for (std::size_t j = 0; j < m_labels.size(); ++ j)
|
||||
{
|
||||
for (std::size_t k = 0; k < training_sets[j].size(); ++ k)
|
||||
{
|
||||
float val = normalized(feature, training_sets[j][k]);
|
||||
mean[j] += val;
|
||||
}
|
||||
mean[j] /= training_sets[j].size();
|
||||
}
|
||||
|
||||
std::vector<float> sd (m_labels.size(), 0.);
|
||||
|
||||
for (std::size_t j = 0; j < m_labels.size(); ++ j)
|
||||
{
|
||||
Label_handle clabel = m_labels[j];
|
||||
|
||||
for (std::size_t k = 0; k < training_sets[j].size(); ++ k)
|
||||
{
|
||||
float val = normalized(feature, training_sets[j][k]);
|
||||
sd[j] += (val - mean[j]) * (val - mean[j]);
|
||||
}
|
||||
sd[j] = std::sqrt (sd[j] / training_sets[j].size());
|
||||
if (mean[j] - sd[j] > (2./3.))
|
||||
set_effect (j, feature, FAVORING);
|
||||
else if (mean[j] + sd[j] < (1./3.))
|
||||
set_effect (j, feature, PENALIZING);
|
||||
else
|
||||
set_effect (j, feature, NEUTRAL);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ConcurrencyTag>
|
||||
float compute_mean_iou (std::vector<std::vector<std::size_t> >& training_sets)
|
||||
{
|
||||
std::vector<std::size_t> true_positives (m_labels.size());
|
||||
std::vector<std::size_t> false_positives (m_labels.size());
|
||||
std::vector<std::size_t> false_negatives (m_labels.size());
|
||||
|
||||
for (std::size_t j = 0; j < training_sets.size(); ++ j)
|
||||
{
|
||||
std::size_t gt = j;
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
std::vector<tbb::mutex> tp_mutex (m_labels.size());
|
||||
std::vector<tbb::mutex> fp_mutex (m_labels.size());
|
||||
std::vector<tbb::mutex> fn_mutex (m_labels.size());
|
||||
Compute_iou f(training_sets[j], *this, j,
|
||||
true_positives, false_positives, false_negatives,
|
||||
tp_mutex, fp_mutex, fn_mutex);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, training_sets[j].size ()), f);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
for (std::size_t k = 0; k < training_sets[j].size(); ++ k)
|
||||
{
|
||||
std::size_t res = 0;
|
||||
|
||||
std::vector<float> v;
|
||||
(*this) (training_sets[j][k], v);
|
||||
|
||||
float min = std::numeric_limits<float>::max();
|
||||
for(std::size_t l = 0; l < m_labels.size(); ++ l)
|
||||
if (v[l] < min)
|
||||
{
|
||||
min = v[l];
|
||||
res = l;
|
||||
}
|
||||
|
||||
if (gt == res)
|
||||
{
|
||||
++ true_positives[gt];
|
||||
continue;
|
||||
}
|
||||
++ false_positives[res];
|
||||
++ false_negatives[gt];
|
||||
}
|
||||
}
|
||||
|
||||
float out = 0.;
|
||||
|
||||
for (std::size_t j = 0; j < m_labels.size(); ++ j)
|
||||
{
|
||||
float iou = true_positives[j] / float(true_positives[j] + false_positives[j] + false_negatives[j]);
|
||||
out += iou;
|
||||
}
|
||||
|
||||
return out / m_labels.size();
|
||||
}
|
||||
|
||||
|
||||
bool feature_useful (std::size_t feature)
|
||||
{
|
||||
Effect side = effect(0, feature);
|
||||
for (std::size_t k = 1; k < m_labels.size(); ++ k)
|
||||
if (effect(k, feature) != side)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // CLASSIFICATION_SUM_OF_WEIGHTED_FEATURES_CLASSIFIER_H
|
||||
|
|
@ -0,0 +1,539 @@
|
|||
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||
// 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, Florent Lafarge
|
||||
|
||||
#ifndef CGAL_CLASSIFICATION_CLASSIFY_H
|
||||
#define CGAL_CLASSIFICATION_CLASSIFY_H
|
||||
|
||||
#include <CGAL/internal/Surface_mesh_segmentation/Alpha_expansion_graph_cut.h>
|
||||
|
||||
#include <CGAL/Classification/Label_set.h>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/blocked_range.h>
|
||||
#include <tbb/scalable_allocator.h>
|
||||
#include <tbb/mutex.h>
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
namespace internal {
|
||||
|
||||
template <typename Classifier>
|
||||
class Classify_functor
|
||||
{
|
||||
const Label_set& m_labels;
|
||||
const Classifier& m_classifier;
|
||||
std::vector<std::size_t>& m_out;
|
||||
|
||||
public:
|
||||
|
||||
Classify_functor (const Label_set& labels,
|
||||
const Classifier& classifier,
|
||||
std::vector<std::size_t>& out)
|
||||
: m_labels (labels), m_classifier (classifier), m_out (out)
|
||||
{ }
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
void operator()(const tbb::blocked_range<std::size_t>& r) const
|
||||
{
|
||||
for (std::size_t s = r.begin(); s != r.end(); ++ s)
|
||||
apply(s);
|
||||
}
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
inline void apply (std::size_t s) const
|
||||
{
|
||||
std::size_t nb_class_best=0;
|
||||
std::vector<float> values;
|
||||
m_classifier (s, values);
|
||||
|
||||
float val_class_best = (std::numeric_limits<float>::max)();
|
||||
for(std::size_t k = 0; k < m_labels.size(); ++ k)
|
||||
{
|
||||
if(val_class_best > values[k])
|
||||
{
|
||||
val_class_best = values[k];
|
||||
nb_class_best = k;
|
||||
}
|
||||
}
|
||||
|
||||
m_out[s] = nb_class_best;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Classifier>
|
||||
class Classify_functor_local_smoothing_preprocessing
|
||||
{
|
||||
const Label_set& m_labels;
|
||||
const Classifier& m_classifier;
|
||||
std::vector<std::vector<float> >& m_values;
|
||||
|
||||
public:
|
||||
|
||||
Classify_functor_local_smoothing_preprocessing
|
||||
(const Label_set& labels,
|
||||
const Classifier& classifier,
|
||||
std::vector<std::vector<float> >& values)
|
||||
: m_labels (labels), m_classifier (classifier), m_values (values)
|
||||
{ }
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
void operator()(const tbb::blocked_range<std::size_t>& r) const
|
||||
{
|
||||
for (std::size_t s = r.begin(); s != r.end(); ++ s)
|
||||
apply (s);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void apply (std::size_t s) const
|
||||
{
|
||||
std::vector<float> values;
|
||||
m_classifier(s, values);
|
||||
for(std::size_t k = 0; k < m_labels.size(); ++ k)
|
||||
m_values[k][s] = values[k];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ItemRange, typename ItemMap, typename NeighborQuery>
|
||||
class Classify_functor_local_smoothing
|
||||
{
|
||||
const ItemRange& m_input;
|
||||
const ItemMap m_item_map;
|
||||
const Label_set& m_labels;
|
||||
const std::vector<std::vector<float> >& m_values;
|
||||
const NeighborQuery& m_neighbor_query;
|
||||
std::vector<std::size_t>& m_out;
|
||||
|
||||
public:
|
||||
|
||||
Classify_functor_local_smoothing (const ItemRange& input,
|
||||
ItemMap item_map,
|
||||
const Label_set& labels,
|
||||
const std::vector<std::vector<float> >& values,
|
||||
const NeighborQuery& neighbor_query,
|
||||
std::vector<std::size_t>& out)
|
||||
: m_input (input), m_item_map (item_map), m_labels (labels),
|
||||
m_values(values),
|
||||
m_neighbor_query (neighbor_query),
|
||||
m_out (out)
|
||||
{ }
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
void operator()(const tbb::blocked_range<std::size_t>& r) const
|
||||
{
|
||||
for (std::size_t s = r.begin(); s != r.end(); ++ s)
|
||||
apply (s);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void apply (std::size_t s) const
|
||||
{
|
||||
std::vector<std::size_t> neighbors;
|
||||
m_neighbor_query (get (m_item_map, *(m_input.begin()+s)), std::back_inserter (neighbors));
|
||||
|
||||
std::vector<float> mean (m_values.size(), 0.);
|
||||
for (std::size_t n = 0; n < neighbors.size(); ++ n)
|
||||
for (std::size_t j = 0; j < m_values.size(); ++ j)
|
||||
mean[j] += m_values[j][neighbors[n]];
|
||||
|
||||
std::size_t nb_class_best=0;
|
||||
float val_class_best = (std::numeric_limits<float>::max)();
|
||||
for(std::size_t k = 0; k < mean.size(); ++ k)
|
||||
{
|
||||
mean[k] /= neighbors.size();
|
||||
if(val_class_best > mean[k])
|
||||
{
|
||||
val_class_best = mean[k];
|
||||
nb_class_best = k;
|
||||
}
|
||||
}
|
||||
|
||||
m_out[s] = nb_class_best;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
template <typename ItemRange, typename ItemMap,
|
||||
typename Classifier, typename NeighborQuery>
|
||||
class Classify_functor_graphcut
|
||||
{
|
||||
const ItemRange& m_input;
|
||||
ItemMap m_item_map;
|
||||
const Label_set& m_labels;
|
||||
const Classifier& m_classifier;
|
||||
const NeighborQuery& m_neighbor_query;
|
||||
float m_strength;
|
||||
const std::vector<std::vector<std::size_t> >& m_indices;
|
||||
const std::vector<std::pair<std::size_t, std::size_t> >& m_input_to_indices;
|
||||
std::vector<std::size_t>& m_out;
|
||||
|
||||
#ifdef CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
|
||||
typedef CGAL::internal::Alpha_expansion_graph_cut_boost Alpha_expansion;
|
||||
#else
|
||||
typedef CGAL::internal::Alpha_expansion_graph_cut_boykov_kolmogorov Alpha_expansion;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
Classify_functor_graphcut (const ItemRange& input,
|
||||
ItemMap item_map,
|
||||
const Label_set& labels,
|
||||
const Classifier& classifier,
|
||||
const NeighborQuery& neighbor_query,
|
||||
float strength,
|
||||
const std::vector<std::vector<std::size_t> >& indices,
|
||||
const std::vector<std::pair<std::size_t, std::size_t> >& input_to_indices,
|
||||
std::vector<std::size_t>& out)
|
||||
: m_input (input), m_item_map (item_map), m_labels (labels),
|
||||
m_classifier (classifier), m_neighbor_query (neighbor_query),
|
||||
m_strength (strength), m_indices (indices), m_input_to_indices (input_to_indices), m_out (out)
|
||||
{ }
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
void operator()(const tbb::blocked_range<std::size_t>& r) const
|
||||
{
|
||||
for (std::size_t s = r.begin(); s != r.end(); ++ s)
|
||||
apply(s);
|
||||
}
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
|
||||
inline void apply (std::size_t sub) const
|
||||
{
|
||||
if (m_indices[sub].empty())
|
||||
return;
|
||||
|
||||
std::vector<std::pair<std::size_t, std::size_t> > edges;
|
||||
std::vector<double> edge_weights;
|
||||
std::vector<std::vector<double> > probability_matrix
|
||||
(m_labels.size(), std::vector<double>(m_indices[sub].size(), 0.));
|
||||
std::vector<std::size_t> assigned_label (m_indices[sub].size());
|
||||
|
||||
for (std::size_t j = 0; j < m_indices[sub].size(); ++ j)
|
||||
{
|
||||
std::size_t s = m_indices[sub][j];
|
||||
|
||||
std::vector<std::size_t> neighbors;
|
||||
|
||||
m_neighbor_query (get(m_item_map, *(m_input.begin()+s)), std::back_inserter (neighbors));
|
||||
|
||||
for (std::size_t i = 0; i < neighbors.size(); ++ i)
|
||||
if (sub == m_input_to_indices[neighbors[i]].first
|
||||
&& j != m_input_to_indices[neighbors[i]].second)
|
||||
{
|
||||
edges.push_back (std::make_pair (j, m_input_to_indices[neighbors[i]].second));
|
||||
edge_weights.push_back (m_strength);
|
||||
}
|
||||
|
||||
std::vector<float> values;
|
||||
m_classifier(s, values);
|
||||
std::size_t nb_class_best = 0;
|
||||
float val_class_best = (std::numeric_limits<float>::max)();
|
||||
for(std::size_t k = 0; k < m_labels.size(); ++ k)
|
||||
{
|
||||
float value = values[k];
|
||||
probability_matrix[k][j] = value;
|
||||
|
||||
if(val_class_best > value)
|
||||
{
|
||||
val_class_best = value;
|
||||
nb_class_best = k;
|
||||
}
|
||||
}
|
||||
assigned_label[j] = nb_class_best;
|
||||
}
|
||||
|
||||
Alpha_expansion graphcut;
|
||||
graphcut(edges, edge_weights, probability_matrix, assigned_label);
|
||||
|
||||
for (std::size_t i = 0; i < assigned_label.size(); ++ i)
|
||||
m_out[m_indices[sub][i]] = assigned_label[i];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// \endcond
|
||||
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationMain
|
||||
|
||||
\brief Runs the classification algorithm without any regularization.
|
||||
|
||||
There is no relationship between items, the classification energy
|
||||
is only minimized itemwise. This method is quick but produces
|
||||
suboptimal results.
|
||||
|
||||
\tparam ConcurrencyTag enables sequential versus parallel
|
||||
algorithm. Possible values are `Parallel_tag` or `Sequential_tag`.
|
||||
|
||||
\tparam ItemRange model of `ConstRange`. Its iterator type is
|
||||
`RandomAccessIterator`. Its value type depends on the data that is
|
||||
classified (for example, `CGAL::Point_3` or `CGAL::Triangle_3`).
|
||||
|
||||
\tparam Classifier model of `Classifier`.
|
||||
|
||||
\param input input range.
|
||||
\param labels set of input labels.
|
||||
\param classifier input classifier.
|
||||
\param output where to store the result. It is stored as a sequence,
|
||||
ordered like the input range, containing for each point the index
|
||||
(in the `Label_set`) of the assigned label.
|
||||
|
||||
*/
|
||||
template <typename ConcurrencyTag,
|
||||
typename ItemRange,
|
||||
typename Classifier>
|
||||
void classify (const ItemRange& input,
|
||||
const Label_set& labels,
|
||||
const Classifier& classifier,
|
||||
std::vector<std::size_t>& output)
|
||||
{
|
||||
output.resize(input.size());
|
||||
|
||||
internal::Classify_functor<Classifier>
|
||||
f (labels, classifier, output);
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, input.size ()), f);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (std::size_t i = 0; i < input.size(); ++ i)
|
||||
f.apply(i);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationMain
|
||||
|
||||
\brief Runs the classification algorithm with a local smoothing.
|
||||
|
||||
The computed classification energy is smoothed on a user defined
|
||||
local neighborhood of items. This method is a compromise between
|
||||
efficiency and better quality results.
|
||||
|
||||
\tparam ConcurrencyTag enables sequential versus parallel
|
||||
algorithm. Possible values are `Parallel_tag` or `Sequential_tag`.
|
||||
\tparam ItemRange model of `ConstRange`. Its iterator type is
|
||||
`RandomAccessIterator`.
|
||||
\tparam ItemMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `ItemRange` and value type
|
||||
is the type of item to classify (for example, `CGAL::Point_3`).
|
||||
\tparam NeighborQuery model of `NeighborQuery`.
|
||||
\tparam Classifier model of `Classifier`.
|
||||
|
||||
\param input input range.
|
||||
\param item_map property map to access the input items.
|
||||
\param labels set of input labels.
|
||||
\param classifier input classifier.
|
||||
\param neighbor_query used to access neighborhoods of items.
|
||||
\param output where to store the result. It is stored as a sequence,
|
||||
ordered like the input range, containing for each point the index
|
||||
(in the `Label_set`) of the assigned label.
|
||||
|
||||
*/
|
||||
template <typename ConcurrencyTag,
|
||||
typename ItemRange,
|
||||
typename ItemMap,
|
||||
typename NeighborQuery,
|
||||
typename Classifier>
|
||||
void classify_with_local_smoothing (const ItemRange& input,
|
||||
const ItemMap item_map,
|
||||
const Label_set& labels,
|
||||
const Classifier& classifier,
|
||||
const NeighborQuery& neighbor_query,
|
||||
std::vector<std::size_t>& output)
|
||||
{
|
||||
output.resize(input.size());
|
||||
|
||||
std::vector<std::vector<float> > values
|
||||
(labels.size(), std::vector<float> (input.size(), -1.));
|
||||
internal::Classify_functor_local_smoothing_preprocessing<Classifier>
|
||||
f1 (labels, classifier, values);
|
||||
internal::Classify_functor_local_smoothing<ItemRange, ItemMap, NeighborQuery>
|
||||
f2 (input, item_map, labels, values, neighbor_query, output);
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, input.size ()), f1);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, input.size ()), f2);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (std::size_t i = 0; i < input.size(); ++ i)
|
||||
f1.apply(i);
|
||||
for (std::size_t i = 0; i < input.size(); ++ i)
|
||||
f2.apply(i);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationMain
|
||||
|
||||
\brief Runs the classification algorithm with a global
|
||||
regularization based on a graph cut.
|
||||
|
||||
The computed classification energy is globally regularized through
|
||||
an alpha-expansion algorithm. This method is slow but provides
|
||||
the user with good quality results.
|
||||
|
||||
To speed up computation, the input domain can be subdivided into
|
||||
smaller subsets such that several smaller graph cuts are applied
|
||||
instead of a big one. The computation of these smaller graph cuts can
|
||||
be done in parallel. Increasing the number of subsets allows for
|
||||
faster computation times but can also reduce the quality of the
|
||||
results.
|
||||
|
||||
\tparam ConcurrencyTag enables sequential versus parallel
|
||||
algorithm. Possible values are `Parallel_tag` or `Sequential_tag`.
|
||||
\tparam ItemRange model of `ConstRange`. Its iterator type is
|
||||
`RandomAccessIterator`.
|
||||
\tparam ItemMap model of `ReadablePropertyMap` whose key
|
||||
type is the value type of the iterator of `ItemRange` and value type
|
||||
is the type of item to classify (for example, `CGAL::Point_3`).
|
||||
\tparam NeighborQuery model of `NeighborQuery`.
|
||||
\tparam Classifier model of `Classifier`.
|
||||
|
||||
\param input input range.
|
||||
\param item_map property map to access the input items.
|
||||
\param labels set of input labels.
|
||||
\param classifier input classifier.
|
||||
\param neighbor_query used to access neighborhoods of items.
|
||||
\param strength strength of the regularization with respect to the
|
||||
classification energy. Higher values produce more regularized
|
||||
output but may result in a loss of details.
|
||||
\param min_number_of_subdivisions minimum number of subdivisions
|
||||
(for parallel processing to be efficient, this should be at least
|
||||
the number of cores of the processor).
|
||||
\param output where to store the result. It is stored as a sequence,
|
||||
ordered like the input range, containing for each point the index
|
||||
(in the `Label_set`) of the assigned label.
|
||||
|
||||
*/
|
||||
template <typename ConcurrencyTag,
|
||||
typename ItemRange,
|
||||
typename ItemMap,
|
||||
typename NeighborQuery,
|
||||
typename Classifier>
|
||||
void classify_with_graphcut (const ItemRange& input,
|
||||
const ItemMap item_map,
|
||||
const Label_set& labels,
|
||||
const Classifier& classifier,
|
||||
const NeighborQuery& neighbor_query,
|
||||
const float strength,
|
||||
const std::size_t min_number_of_subdivisions,
|
||||
std::vector<std::size_t>& output)
|
||||
{
|
||||
CGAL::Bbox_3 bbox = CGAL::bbox_3
|
||||
(boost::make_transform_iterator (input.begin(), CGAL::Property_map_to_unary_function<ItemMap>(item_map)),
|
||||
boost::make_transform_iterator (input.end(), CGAL::Property_map_to_unary_function<ItemMap>(item_map)));
|
||||
|
||||
float Dx = bbox.xmax() - bbox.xmin();
|
||||
float Dy = bbox.ymax() - bbox.ymin();
|
||||
float A = Dx * Dy;
|
||||
float a = A / min_number_of_subdivisions;
|
||||
float l = std::sqrt(a);
|
||||
std::size_t nb_x = std::size_t(Dx / l) + 1;
|
||||
std::size_t nb_y = std::size_t((A / nb_x) / a) + 1;
|
||||
std::size_t nb = nb_x * nb_y;
|
||||
|
||||
std::vector<CGAL::Bbox_3> bboxes;
|
||||
bboxes.reserve(nb);
|
||||
for (std::size_t x = 0; x < nb_x; ++ x)
|
||||
for (std::size_t y = 0; y < nb_y; ++ y)
|
||||
{
|
||||
bboxes.push_back
|
||||
(CGAL::Bbox_3 (bbox.xmin() + Dx * (x / float(nb_x)),
|
||||
bbox.ymin() + Dy * (y / float(nb_y)),
|
||||
bbox.zmin(),
|
||||
bbox.xmin() + Dx * ((x+1) / float(nb_x)),
|
||||
bbox.ymin() + Dy * ((y+1) / float(nb_y)),
|
||||
bbox.zmax()));
|
||||
}
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_VERBOSE
|
||||
std::cerr << "Number of divisions = " << nb_x * nb_y << std::endl;
|
||||
std::cerr << " -> Size of division: " << Dx / nb_x << " " << Dy / nb_y << std::endl;
|
||||
#endif
|
||||
|
||||
std::vector<std::vector<std::size_t> > indices (nb);
|
||||
std::vector<std::pair<std::size_t, std::size_t> > input_to_indices(input.size());
|
||||
|
||||
for (std::size_t s = 0; s < input.size(); ++ s)
|
||||
{
|
||||
CGAL::Bbox_3 b = get(item_map, *(input.begin() + s)).bbox();
|
||||
|
||||
for (std::size_t i = 0; i < bboxes.size(); ++ i)
|
||||
if (CGAL::do_overlap (b, bboxes[i]))
|
||||
{
|
||||
input_to_indices[s] = std::make_pair (i, indices[i].size());
|
||||
indices[i].push_back (s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output.resize (input.size());
|
||||
|
||||
internal::Classify_functor_graphcut<ItemRange, ItemMap, Classifier, NeighborQuery>
|
||||
f (input, item_map, labels, classifier, neighbor_query, strength, indices, input_to_indices, output);
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, indices.size ()), f);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for (std::size_t sub = 0; sub < indices.size(); ++ sub)
|
||||
f.apply (sub);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // CGAL_CLASSIFICATION_CLASSIFY_H
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
# Created by the script cgal_create_CMakeLists
|
||||
# This is the CMake script for compiling a set of CGAL applications.
|
||||
|
||||
project( Classification )
|
||||
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
# CGAL and its components
|
||||
find_package( CGAL QUIET COMPONENTS )
|
||||
|
||||
if ( NOT CGAL_FOUND )
|
||||
|
||||
message(STATUS "This project requires the CGAL library, and will not be compiled.")
|
||||
return()
|
||||
|
||||
endif()
|
||||
|
||||
# include helper file
|
||||
include( ${CGAL_USE_FILE} )
|
||||
|
||||
|
||||
# Boost and its components
|
||||
find_package( Boost REQUIRED )
|
||||
|
||||
# Use Eigen
|
||||
find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater)
|
||||
if (EIGEN3_FOUND)
|
||||
include( ${EIGEN3_USE_FILE} )
|
||||
endif()
|
||||
|
||||
if ( NOT Boost_FOUND )
|
||||
|
||||
message(STATUS "This project requires the Boost library, and will not be compiled.")
|
||||
|
||||
return()
|
||||
|
||||
endif()
|
||||
|
||||
# include for local directory
|
||||
include_directories( BEFORE include )
|
||||
|
||||
# include for local package
|
||||
include_directories( BEFORE ../../include )
|
||||
|
||||
|
||||
# Creating entries for all C++ files with "main" routine
|
||||
# ##########################################################
|
||||
|
||||
include( CGAL_CreateSingleSourceCGALProgram )
|
||||
|
||||
#add_definitions("-DCGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE")
|
||||
|
||||
|
||||
create_single_source_cgal_program( "test_classifier.cpp" )
|
||||
create_single_source_cgal_program( "test_point_set_classifier.cpp" )
|
||||
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Random.h>
|
||||
#include <CGAL/tuple.h>
|
||||
#include <CGAL/Classifier.h>
|
||||
#include <CGAL/Classification/Local_eigen_analysis.h>
|
||||
#include <CGAL/Classification/Point_set_neighborhood.h>
|
||||
#include <CGAL/Classification/Planimetric_grid.h>
|
||||
#include <CGAL/Classification/Attribute_base.h>
|
||||
#include <CGAL/Classification/Attribute/Distance_to_plane.h>
|
||||
#include <CGAL/Classification/Attribute/Echo_scatter.h>
|
||||
#include <CGAL/Classification/Attribute/Eigen.h>
|
||||
#include <CGAL/Classification/Attribute/Elevation.h>
|
||||
#include <CGAL/Classification/Attribute/Hsv.h>
|
||||
#include <CGAL/Classification/Attribute/Vertical_dispersion.h>
|
||||
#include <CGAL/Classification/Attribute/Verticality.h>
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
typedef Kernel::Point_3 Point;
|
||||
typedef Kernel::Vector_3 Vector;
|
||||
typedef CGAL::cpp11::array<unsigned char, 3> Color;
|
||||
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
|
||||
|
||||
typedef boost::tuple<Point, Vector, Color, std::size_t> Point_with_info;
|
||||
typedef std::vector<Point_with_info> Point_range;
|
||||
|
||||
typedef CGAL::Nth_of_tuple_property_map<0, Point_with_info> Pmap;
|
||||
typedef CGAL::Nth_of_tuple_property_map<1, Point_with_info> Nmap;
|
||||
typedef CGAL::Nth_of_tuple_property_map<2, Point_with_info> Cmap;
|
||||
typedef CGAL::Nth_of_tuple_property_map<3, Point_with_info> Emap;
|
||||
|
||||
typedef CGAL::Classifier<Point_range, Pmap> Classifier;
|
||||
|
||||
typedef CGAL::Classification::Planimetric_grid<Kernel, Point_range, Pmap> Planimetric_grid;
|
||||
typedef CGAL::Classification::Point_set_neighborhood<Kernel, Point_range, Pmap> Neighborhood;
|
||||
typedef CGAL::Classification::Local_eigen_analysis<Kernel, Point_range, Pmap> Local_eigen_analysis;
|
||||
|
||||
typedef CGAL::Classification::Type_handle Type_handle;
|
||||
typedef CGAL::Classification::Attribute_handle Attribute_handle;
|
||||
|
||||
typedef CGAL::Classification::Attribute::Anisotropy
|
||||
<Kernel, Point_range, Pmap> Anisotropy;
|
||||
typedef CGAL::Classification::Attribute::Distance_to_plane
|
||||
<Kernel, Point_range, Pmap> Distance_to_plane;
|
||||
typedef CGAL::Classification::Attribute::Eigentropy
|
||||
<Kernel, Point_range, Pmap> Eigentropy;
|
||||
typedef CGAL::Classification::Attribute::Elevation
|
||||
<Kernel, Point_range, Pmap> Elevation;
|
||||
typedef CGAL::Classification::Attribute::Linearity
|
||||
<Kernel, Point_range, Pmap> Linearity;
|
||||
typedef CGAL::Classification::Attribute::Omnivariance
|
||||
<Kernel, Point_range, Pmap> Omnivariance;
|
||||
typedef CGAL::Classification::Attribute::Planarity
|
||||
<Kernel, Point_range, Pmap> Planarity;
|
||||
typedef CGAL::Classification::Attribute::Sphericity
|
||||
<Kernel, Point_range, Pmap> Sphericity;
|
||||
typedef CGAL::Classification::Attribute::Sum_eigenvalues
|
||||
<Kernel, Point_range, Pmap> Sum_eigen;
|
||||
typedef CGAL::Classification::Attribute::Surface_variation
|
||||
<Kernel, Point_range, Pmap> Surface_variation;
|
||||
typedef CGAL::Classification::Attribute::Vertical_dispersion
|
||||
<Kernel, Point_range, Pmap> Dispersion;
|
||||
typedef CGAL::Classification::Attribute::Verticality
|
||||
<Kernel, Point_range, Pmap> Verticality;
|
||||
typedef CGAL::Classification::RGB_Color RGB_Color;
|
||||
typedef CGAL::Classification::Attribute::Hsv
|
||||
<Kernel, Point_range, Cmap> Hsv;
|
||||
typedef CGAL::Classification::Attribute::Hsv
|
||||
<Kernel, Point_range, Cmap> Hsv;
|
||||
typedef CGAL::Classification::Attribute::Echo_scatter
|
||||
<Kernel, Point_range, Pmap, Emap> Echo_scatter;
|
||||
|
||||
typedef CGAL::Classification::Attribute::Effect Attribute_effect;
|
||||
|
||||
int main (int, char**)
|
||||
{
|
||||
Point_range range;
|
||||
range.reserve(10000);
|
||||
for (std::size_t i = 0; i < 10000; ++ i)
|
||||
range.push_back (boost::make_tuple
|
||||
(Point (CGAL::get_default_random().get_double(),
|
||||
CGAL::get_default_random().get_double(),
|
||||
CGAL::get_default_random().get_double()),
|
||||
Vector (CGAL::get_default_random().get_double(),
|
||||
CGAL::get_default_random().get_double(),
|
||||
CGAL::get_default_random().get_double()),
|
||||
CGAL::make_array ((unsigned char)(CGAL::get_default_random().get_int(0, 255)),
|
||||
(unsigned char)(CGAL::get_default_random().get_int(0, 255)),
|
||||
(unsigned char)(CGAL::get_default_random().get_int(0, 255))),
|
||||
std::size_t(CGAL::get_default_random().get_int(0, 10))));
|
||||
|
||||
double grid_resolution = 0.34;
|
||||
double radius_neighbors = 1.7;
|
||||
double radius_dtm = 15.0;
|
||||
|
||||
Classifier classifier (range, Pmap());
|
||||
Iso_cuboid_3 bbox = CGAL::bounding_box (boost::make_transform_iterator
|
||||
(range.begin(),
|
||||
CGAL::Property_map_to_unary_function<Pmap>()),
|
||||
boost::make_transform_iterator
|
||||
(range.end(),
|
||||
CGAL::Property_map_to_unary_function<Pmap>()));
|
||||
|
||||
Planimetric_grid grid (range, Pmap(), bbox, grid_resolution);
|
||||
Neighborhood neighborhood (range, Pmap());
|
||||
Local_eigen_analysis eigen (range, Pmap(), neighborhood.k_neighbor_query(6));
|
||||
|
||||
std::vector<Attribute_handle> att;
|
||||
|
||||
att.push_back (classifier.add_attribute<Anisotropy> (eigen));
|
||||
att.push_back (classifier.add_attribute<Dispersion> (Pmap(), grid,
|
||||
grid_resolution,
|
||||
radius_neighbors));
|
||||
att.push_back (classifier.add_attribute<Distance_to_plane> (Pmap(), eigen));
|
||||
att.push_back (classifier.add_attribute<Echo_scatter> (Emap(), grid, grid_resolution, radius_neighbors));
|
||||
att.push_back (classifier.add_attribute<Eigentropy> (eigen));
|
||||
att.push_back (classifier.add_attribute<Elevation> (Pmap(), grid,
|
||||
grid_resolution,
|
||||
radius_dtm));
|
||||
att.push_back (classifier.add_attribute<Hsv> (Cmap(), 0, 180, 90));
|
||||
att.push_back (classifier.add_attribute<Hsv> (Cmap(), 1, 50, 25));
|
||||
att.push_back (classifier.add_attribute<Hsv> (Cmap(), 2, 50, 25));
|
||||
att.push_back (classifier.add_attribute<Linearity> (eigen));
|
||||
att.push_back (classifier.add_attribute<Omnivariance> (eigen));
|
||||
att.push_back (classifier.add_attribute<Planarity> (eigen));
|
||||
att.push_back (classifier.add_attribute<Sphericity> (eigen));
|
||||
att.push_back (classifier.add_attribute<Sum_eigen> (eigen));
|
||||
att.push_back (classifier.add_attribute<Surface_variation> (eigen));
|
||||
att.push_back (classifier.add_attribute<Verticality> (eigen));
|
||||
att.push_back (classifier.add_attribute<Verticality> (Nmap()));
|
||||
att.push_back (classifier.add_attribute<Omnivariance> (eigen));
|
||||
assert (classifier.remove_attribute (att.back()));
|
||||
att.pop_back();
|
||||
assert (att.size() == classifier.number_of_attributes());
|
||||
|
||||
std::vector<Type_handle> types;
|
||||
types.push_back (classifier.add_classification_type ("type1"));
|
||||
types.push_back (classifier.add_classification_type ("type2"));
|
||||
types.push_back (classifier.add_classification_type ("type3"));
|
||||
types.push_back (classifier.add_classification_type ("type4"));
|
||||
types.push_back (classifier.add_classification_type ("type5"));
|
||||
types.push_back (classifier.add_classification_type ("type6"));
|
||||
assert (classifier.remove_classification_type (types.back()));
|
||||
types.pop_back();
|
||||
assert (types.size() == classifier.number_of_classification_types());
|
||||
|
||||
for (std::size_t i = 0; i < types.size(); ++ i)
|
||||
for (std::size_t j = 0; j < att.size(); ++ j)
|
||||
{
|
||||
att[j]->set_weight (CGAL::get_default_random().get_double());
|
||||
types[i]->set_attribute_effect
|
||||
(att[j], (Attribute_effect)(CGAL::get_default_random().get_int(0,3)));
|
||||
}
|
||||
|
||||
classifier.run();
|
||||
classifier.run_with_local_smoothing(neighborhood.k_neighbor_query(6));
|
||||
classifier.run_with_graphcut(neighborhood.k_neighbor_query(12), 0.5);
|
||||
|
||||
std::vector<Type_handle> output;
|
||||
output.reserve (10000);
|
||||
for (std::size_t i = 0; i < range.size(); ++ i)
|
||||
{
|
||||
output.push_back (classifier.classification_type_of(i));
|
||||
assert (0 <= classifier.confidence_of(i));
|
||||
}
|
||||
|
||||
|
||||
classifier.clear_classification_types();
|
||||
classifier.clear_attributes();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Random.h>
|
||||
#include <CGAL/tuple.h>
|
||||
#include <CGAL/Point_set_classifier.h>
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||
typedef Kernel::Point_3 Point;
|
||||
typedef Kernel::Vector_3 Vector;
|
||||
typedef CGAL::cpp11::array<unsigned char, 3> Color;
|
||||
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
|
||||
|
||||
typedef boost::tuple<Point, Vector, Color, std::size_t> Point_with_info;
|
||||
typedef std::vector<Point_with_info> Point_range;
|
||||
|
||||
typedef CGAL::Nth_of_tuple_property_map<0, Point_with_info> Pmap;
|
||||
typedef CGAL::Nth_of_tuple_property_map<1, Point_with_info> Nmap;
|
||||
typedef CGAL::Nth_of_tuple_property_map<2, Point_with_info> Cmap;
|
||||
typedef CGAL::Nth_of_tuple_property_map<3, Point_with_info> Emap;
|
||||
|
||||
typedef CGAL::Point_set_classifier<Kernel, Point_range, Pmap> PSC;
|
||||
|
||||
typedef CGAL::Classification::Type_handle Type_handle;
|
||||
typedef CGAL::Classification::Attribute_handle Attribute_handle;
|
||||
|
||||
typedef CGAL::Classification::Attribute::Effect Attribute_effect;
|
||||
|
||||
int main (int, char**)
|
||||
{
|
||||
Point_range range;
|
||||
range.reserve(10000);
|
||||
for (std::size_t i = 0; i < 10000; ++ i)
|
||||
range.push_back (boost::make_tuple
|
||||
(Point (CGAL::get_default_random().get_double(),
|
||||
CGAL::get_default_random().get_double(),
|
||||
CGAL::get_default_random().get_double()),
|
||||
Vector (CGAL::get_default_random().get_double(),
|
||||
CGAL::get_default_random().get_double(),
|
||||
CGAL::get_default_random().get_double()),
|
||||
CGAL::make_array ((unsigned char)(CGAL::get_default_random().get_int(0, 255)),
|
||||
(unsigned char)(CGAL::get_default_random().get_int(0, 255)),
|
||||
(unsigned char)(CGAL::get_default_random().get_int(0, 255))),
|
||||
std::size_t(CGAL::get_default_random().get_int(0, 10))));
|
||||
|
||||
PSC classifier (range, Pmap());
|
||||
classifier.generate_attributes (5, Nmap(), Cmap(), Emap());
|
||||
assert (classifier.number_of_scales() == 5);
|
||||
|
||||
std::vector<Type_handle> types;
|
||||
types.push_back (classifier.add_classification_type ("type1"));
|
||||
types.push_back (classifier.add_classification_type ("type2"));
|
||||
types.push_back (classifier.add_classification_type ("type3"));
|
||||
types.push_back (classifier.add_classification_type ("type4"));
|
||||
types.push_back (classifier.add_classification_type ("type5"));
|
||||
|
||||
for (std::size_t i = 0; i < 100; ++ i)
|
||||
classifier.set_inlier (types[CGAL::get_default_random().get_int(0, types.size())], i);
|
||||
|
||||
classifier.train(500);
|
||||
|
||||
|
||||
std::ofstream fconfig ("config.xml");
|
||||
classifier.save_configuration (fconfig);
|
||||
fconfig.close();
|
||||
|
||||
std::ofstream fclassif ("classif.ply");
|
||||
classifier.write_classification_to_ply(fclassif);
|
||||
fclassif.close();
|
||||
|
||||
classifier.clear();
|
||||
|
||||
std::ifstream fconfig2 ("config.xml");
|
||||
assert (classifier.load_configuration (fconfig2, Nmap(), Cmap(), Emap()));
|
||||
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -573,6 +573,16 @@ is usually distributed along with LAStools: for simplicity, \cgal
|
|||
provides <a href="https://github.com/CGAL/LAStools">a fork with a
|
||||
CMake based install procedure</a>.
|
||||
|
||||
\subsection thirdpartyOpenCV OpenCV
|
||||
|
||||
\sc{OpenCV} (Open Computer Vision) is a library designed for computer
|
||||
vision, computer graphics and machine learning.
|
||||
|
||||
In \cgal, \sc{OpenCV} is used by the \ref PkgClassification package.
|
||||
|
||||
The \sc{OpenCV} web site is <A HREF="http://opencv.org/">`http://opencv.org/`</A>.
|
||||
|
||||
|
||||
\section secbuilding Building CGAL
|
||||
|
||||
The results of a successful configuration are build files that control the build step.
|
||||
|
|
|
|||
|
|
@ -97,6 +97,6 @@ Barycentric_coordinates_2
|
|||
Surface_mesh
|
||||
Surface_mesh_shortest_path
|
||||
Polygon_mesh_processing
|
||||
|
||||
Classification
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ h1 {
|
|||
\package_listing{Point_set_processing_3}
|
||||
\package_listing{Point_set_shape_detection_3}
|
||||
\package_listing{Stream_lines_2}
|
||||
\package_listing{Classification}
|
||||
|
||||
|
||||
\section PartSearchStructures Spatial Searching and Sorting
|
||||
|
|
|
|||
|
|
@ -1009,6 +1009,15 @@ Teillaud"
|
|||
, pages = "209--225"
|
||||
}
|
||||
|
||||
@article{cgal:hws-fsso3-16,
|
||||
title={Fast semantic segmentation of 3D point clouds with strongly varying density},
|
||||
author={Hackel, Timo and Wegner, Jan D and Schindler, Konrad},
|
||||
journal={ISPRS Annals of the Photogrammetry, Remote Sensing and Spatial Information Sciences, Prague, Czech Republic},
|
||||
volume={3},
|
||||
pages={177--184},
|
||||
year={2016}
|
||||
}
|
||||
|
||||
@book{ cgal:hw-vrml2h-96
|
||||
,author = {Jed Hartman and Josie Wernecke}
|
||||
,title = {The {VRML} 2.0 Handbook: Building Moving Worlds on the
|
||||
|
|
@ -1070,6 +1079,15 @@ Teillaud"
|
|||
,pages = "307--320"
|
||||
}
|
||||
|
||||
@book{ cgal:l-mrfmi-09,
|
||||
author = {Li, Stan Z.},
|
||||
title = {Markov Random %Field Modeling in Image Analysis},
|
||||
year = {2009},
|
||||
isbn = {9781848002784},
|
||||
edition = {3rd},
|
||||
publisher = {Springer Publishing Company, Incorporated}
|
||||
}
|
||||
|
||||
@inproceedings { cgal:l-nmdgp-05,
|
||||
AUTHOR = {Bruno Levy},
|
||||
TITLE = {Numerical Methods for Digital Geometry Processing},
|
||||
|
|
@ -1100,6 +1118,15 @@ Teillaud"
|
|||
address = {Girona, Spain}
|
||||
}
|
||||
|
||||
@article{cgal:lm-clscm-12,
|
||||
author = {Lafarge, Florent and Mallet, Clement},
|
||||
title = {{Creating large-scale city models from 3D-point clouds: a robust approach with hybrid representation}},
|
||||
journal = {International Journal of Computer Vision},
|
||||
volume = {99},
|
||||
number = {1},
|
||||
pages = {69-85},
|
||||
year = {2012},
|
||||
}
|
||||
|
||||
@inproceedings{ cgal:lt-fmeps-98,
|
||||
author = "Peter Lindstrom and Greg Turk",
|
||||
|
|
@ -1336,6 +1363,20 @@ ABSTRACT = {We present the first complete, exact and efficient C++ implementatio
|
|||
update = "09.11 penarand"
|
||||
}
|
||||
|
||||
@article{cgal:mbrsh-raofw-11,
|
||||
title = "Relevance assessment of full-waveform lidar data for urban area classification ",
|
||||
journal = "\{ISPRS\} Journal of Photogrammetry and Remote Sensing ",
|
||||
volume = "66",
|
||||
number = "6, Supplement",
|
||||
pages = "S71 - S84",
|
||||
year = "2011",
|
||||
note = "Advances in \{LIDAR\} Data Processing and Applications ",
|
||||
issn = "0924-2716",
|
||||
doi = "http://dx.doi.org/10.1016/j.isprsjprs.2011.09.008",
|
||||
url = "http://www.sciencedirect.com/science/article/pii/S0924271611001055",
|
||||
author = "Clément Mallet and Frédéric Bretar and Michel Roux and Uwe Soergel and Christian Heipke"
|
||||
}
|
||||
|
||||
@article{cgal:ml-cfsg-00
|
||||
, author = "G. Medioni and M. Lee and C. Tang"
|
||||
, title = "A Computational Framework for Segmentation and Grouping"
|
||||
|
|
|
|||
|
|
@ -1399,7 +1399,7 @@ EXTRA_SEARCH_MAPPINGS =
|
|||
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
|
||||
# generate Latex output.
|
||||
|
||||
GENERATE_LATEX = NO
|
||||
GENERATE_LATEX = YES
|
||||
|
||||
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
|
||||
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
|
||||
|
|
|
|||
|
|
@ -956,6 +956,13 @@ and <code>src/</code> directories).
|
|||
<code>CGAL::write_ply_points()</code>
|
||||
and <code>CGAL::write_ply_points_and_normals()</code>.</li>
|
||||
</ul>
|
||||
<h3>Point Set Shape Detection</h3>
|
||||
<ul>
|
||||
<li>New post-processing
|
||||
algorithm: <code>CGAL::regularize_planes()</code>. This allows the user
|
||||
to favor parallelism, orthogonality, coplanarity and/or axial
|
||||
symmetry between detected planes.</li>
|
||||
</ul>
|
||||
<h3>Surface Mesh Parameterization</h3>
|
||||
<ul>
|
||||
<li><code>LSCM_parameterizer_3</code> now uses by default Eigen
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include <CGAL/value_type_traits.h>
|
||||
#include <CGAL/point_set_processing_assertions.h>
|
||||
#include <CGAL/Kernel_traits.h>
|
||||
#include <CGAL/IO/io.h>
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
|
@ -150,7 +151,17 @@ namespace internal {
|
|||
stream >> s;
|
||||
c = static_cast<unsigned char>(s);
|
||||
}
|
||||
|
||||
void read_ascii (std::istream& stream, float& t) const
|
||||
{
|
||||
stream >> iformat(t);
|
||||
}
|
||||
|
||||
void read_ascii (std::istream& stream, double& t) const
|
||||
{
|
||||
stream >> iformat(t);
|
||||
}
|
||||
|
||||
// Default template when Type is not a char type
|
||||
template <typename Type>
|
||||
void read_ascii (std::istream& stream, Type& t) const
|
||||
|
|
@ -158,6 +169,7 @@ namespace internal {
|
|||
stream >> t;
|
||||
}
|
||||
|
||||
|
||||
template <typename Type>
|
||||
Type read (std::istream& stream) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -314,8 +314,6 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
|
|||
|
||||
target_link_libraries( demo_framework gl_splat)
|
||||
|
||||
|
||||
|
||||
foreach( lib
|
||||
demo_framework
|
||||
scene_basic_objects
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
include( polyhedron_demo_macros )
|
||||
|
||||
if(EIGEN3_FOUND)
|
||||
|
||||
qt5_wrap_ui( classificationUI_FILES Classification_widget.ui)
|
||||
polyhedron_demo_plugin(classification_plugin Classification_plugin Point_set_item_classification ${classificationUI_FILES})
|
||||
find_package(OpenCV)
|
||||
if(OpenCV_FOUND)
|
||||
target_link_libraries(classification_plugin scene_points_with_normal_item scene_polylines_item scene_polygon_soup_item scene_color_ramp ${OpenCV_LIBS})
|
||||
target_compile_definitions(classification_plugin PUBLIC "-DCGAL_LINKED_WITH_OPENCV")
|
||||
else()
|
||||
target_link_libraries(classification_plugin scene_points_with_normal_item scene_polylines_item scene_polygon_soup_item scene_color_ramp)
|
||||
message(STATUS "NOTICE: OpenCV was not found. Random forest predicate for classification will not be available.")
|
||||
endif()
|
||||
|
||||
else(EIGEN3_FOUND)
|
||||
message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Classification plugin will not be available.")
|
||||
endif()
|
||||
|
||||
|
|
@ -0,0 +1,871 @@
|
|||
#include <QtCore/qglobal.h>
|
||||
#include <QFileDialog>
|
||||
#include <QColorDialog>
|
||||
#include <fstream>
|
||||
#include "opengl_tools.h"
|
||||
|
||||
#include "Messages_interface.h"
|
||||
#include "Scene_points_with_normal_item.h"
|
||||
#include "Point_set_item_classification.h"
|
||||
#include "Scene_polylines_item.h"
|
||||
#include "Scene_polygon_soup_item.h"
|
||||
|
||||
#include <CGAL/Three/Scene_interface.h>
|
||||
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
|
||||
|
||||
#include <CGAL/Random.h>
|
||||
|
||||
#include "ui_Classification_widget.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMainWindow>
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QInputDialog>
|
||||
#include <QSlider>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
#include <CGAL/boost/graph/split_graph_into_polylines.h>
|
||||
|
||||
using namespace CGAL::Three;
|
||||
|
||||
class Polyhedron_demo_classification_plugin :
|
||||
public QObject,
|
||||
public Polyhedron_demo_plugin_helper
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
|
||||
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
|
||||
|
||||
struct ClassRow
|
||||
{
|
||||
QLabel* label;
|
||||
|
||||
QPushButton* color_button;
|
||||
QPushButton* train;
|
||||
QPushButton* remove;
|
||||
QColor color;
|
||||
|
||||
QLabel* label2;
|
||||
QComboBox* effect;
|
||||
|
||||
ClassRow (QWidget* parent, const char* name, const QColor& color)
|
||||
: color (color)
|
||||
{
|
||||
label = new QLabel (name, parent);
|
||||
color_button = new QPushButton ("", parent);
|
||||
|
||||
QString s("background: #"
|
||||
+ QString(color.red() < 16? "0" : "") + QString::number(color.red(),16)
|
||||
+ QString(color.green() < 16? "0" : "") + QString::number(color.green(),16)
|
||||
+ QString(color.blue() < 16? "0" : "") + QString::number(color.blue(),16) + ";");
|
||||
color_button->setStyleSheet(s);
|
||||
|
||||
train = new QPushButton ("Add selection");
|
||||
remove = new QPushButton ("Remove");
|
||||
label2 = new QLabel (name, parent);
|
||||
effect = new QComboBox;
|
||||
effect->addItem("Penalized");
|
||||
effect->addItem("Neutral");
|
||||
effect->addItem("Favored");
|
||||
|
||||
}
|
||||
~ClassRow ()
|
||||
{
|
||||
}
|
||||
void change_color (const QColor& color)
|
||||
{
|
||||
this->color = color;
|
||||
QString s("background: #"
|
||||
+ QString(color.red() < 16? "0" : "") + QString::number(color.red(),16)
|
||||
+ QString(color.green() < 16? "0" : "") + QString::number(color.green(),16)
|
||||
+ QString(color.blue() < 16? "0" : "") + QString::number(color.blue(),16) + ";");
|
||||
color_button->setStyleSheet(s);
|
||||
color_button->update();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
bool applicable(QAction*) const {
|
||||
return
|
||||
qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()));
|
||||
}
|
||||
void print_message(QString message) { messages->information(message); }
|
||||
QList<QAction*> actions() const { return QList<QAction*>() << actionClassification; }
|
||||
|
||||
using Polyhedron_demo_plugin_helper::init;
|
||||
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface* m) {
|
||||
mw = mainWindow;
|
||||
scene = scene_interface;
|
||||
messages = m;
|
||||
actionClassification = new QAction(tr("Classification"), mw);
|
||||
connect(actionClassification, SIGNAL(triggered()), this, SLOT(classification_action()));
|
||||
|
||||
dock_widget = new QDockWidget("Classification", mw);
|
||||
dock_widget->setVisible(false);
|
||||
|
||||
|
||||
ui_widget.setupUi(dock_widget);
|
||||
addDockWidget(dock_widget);
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_OPENCV
|
||||
ui_widget.classifier->removeItem(1);
|
||||
#endif
|
||||
|
||||
color_att = QColor (75, 75, 77);
|
||||
|
||||
connect(ui_widget.compute_features, SIGNAL(clicked()), this,
|
||||
SLOT(on_compute_features_button_clicked()));
|
||||
connect(ui_widget.display, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(on_display_button_clicked(int)));
|
||||
connect(ui_widget.run, SIGNAL(clicked()), this,
|
||||
SLOT(on_run_button_clicked()));
|
||||
connect(ui_widget.save_config, SIGNAL(clicked()), this,
|
||||
SLOT(on_save_config_button_clicked()));
|
||||
connect(ui_widget.load_config, SIGNAL(clicked()), this,
|
||||
SLOT(on_load_config_button_clicked()));
|
||||
connect(ui_widget.run_smoothed, SIGNAL(clicked()), this,
|
||||
SLOT(on_run_smoothed_button_clicked()));
|
||||
connect(ui_widget.run_graphcut, SIGNAL(clicked()), this,
|
||||
SLOT(on_run_graphcut_button_clicked()));
|
||||
connect(ui_widget.smoothingDoubleSpinBox, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(on_smoothing_value_changed(double)));
|
||||
connect(ui_widget.subdivisionsSpinBox, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(on_subdivisions_value_changed(int)));
|
||||
connect(ui_widget.generate_items, SIGNAL(clicked()), this,
|
||||
SLOT(on_generate_items_button_clicked()));
|
||||
|
||||
connect(ui_widget.numberOfScalesSpinBox, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(on_update_nb_scales()));
|
||||
connect(ui_widget.number_of_trials, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(on_update_number_of_trials()));
|
||||
|
||||
connect(ui_widget.add_new_label, SIGNAL(clicked()), this,
|
||||
SLOT(on_add_new_label_clicked()));
|
||||
connect(ui_widget.reset_training_sets, SIGNAL(clicked()), this,
|
||||
SLOT(on_reset_training_sets_clicked()));
|
||||
connect(ui_widget.validate_selection, SIGNAL(clicked()), this,
|
||||
SLOT(on_validate_selection_clicked()));
|
||||
connect(ui_widget.train, SIGNAL(clicked()), this,
|
||||
SLOT(on_train_clicked()));
|
||||
|
||||
connect(ui_widget.selected_feature, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(on_selected_feature_changed(int)));
|
||||
connect(ui_widget.feature_weight, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(on_feature_weight_changed(int)));
|
||||
|
||||
QObject* scene_obj = dynamic_cast<QObject*>(scene_interface);
|
||||
if(scene_obj)
|
||||
{
|
||||
connect(scene_obj, SIGNAL(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)), this,
|
||||
SLOT(item_about_to_be_destroyed(CGAL::Three::Scene_item*)));
|
||||
|
||||
connect(scene_obj, SIGNAL(itemIndexSelected(int)), this,
|
||||
SLOT(update_plugin(int)));
|
||||
}
|
||||
}
|
||||
virtual void closure()
|
||||
{
|
||||
dock_widget->hide();
|
||||
for (Item_map::iterator it = item_map.begin(); it != item_map.end(); ++ it)
|
||||
{
|
||||
Item_classification_base* classif = it->second;
|
||||
classif->erase_item();
|
||||
delete classif;
|
||||
}
|
||||
item_map.clear();
|
||||
}
|
||||
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
void item_about_to_be_destroyed(CGAL::Three::Scene_item* scene_item) {
|
||||
Item_map::iterator it = item_map.find(scene_item);
|
||||
if (it != item_map.end())
|
||||
{
|
||||
Item_classification_base* classif = it->second;
|
||||
item_map.erase(it); // first erase from map, because scene->erase will cause a call to this function
|
||||
classif->erase_item();
|
||||
delete classif;
|
||||
}
|
||||
}
|
||||
|
||||
void classification_action()
|
||||
{
|
||||
dock_widget->show();
|
||||
dock_widget->raise();
|
||||
Scene_points_with_normal_item* points_item = getSelectedItem<Scene_points_with_normal_item>();
|
||||
create_from_item(points_item);
|
||||
}
|
||||
|
||||
void item_changed (Scene_item* item)
|
||||
{
|
||||
scene->itemChanged(item);
|
||||
item->invalidateOpenGLBuffers();
|
||||
}
|
||||
|
||||
void update_plugin(int)
|
||||
{
|
||||
if (dock_widget->isVisible())
|
||||
update_plugin_from_item(get_classification());
|
||||
}
|
||||
|
||||
void disable_everything ()
|
||||
{
|
||||
ui_widget.load_config->setEnabled(false);
|
||||
ui_widget.save_config->setEnabled(false);
|
||||
ui_widget.compute_features->setEnabled(false);
|
||||
ui_widget.numberOfScalesSpinBox->setEnabled(false);
|
||||
ui_widget.display->setEnabled(false);
|
||||
ui_widget.classifier->setEnabled(false);
|
||||
ui_widget.tabWidget->setEnabled(false);
|
||||
ui_widget.run->setEnabled(false);
|
||||
ui_widget.run_smoothed->setEnabled(false);
|
||||
ui_widget.frame->setEnabled(false);
|
||||
ui_widget.generate_items->setEnabled(false);
|
||||
}
|
||||
|
||||
void enable_computation()
|
||||
{
|
||||
ui_widget.compute_features->setEnabled(true);
|
||||
ui_widget.numberOfScalesSpinBox->setEnabled(true);
|
||||
ui_widget.display->setEnabled(true);
|
||||
ui_widget.classifier->setEnabled(true);
|
||||
}
|
||||
|
||||
void enable_classif()
|
||||
{
|
||||
ui_widget.load_config->setEnabled(true);
|
||||
ui_widget.save_config->setEnabled(true);
|
||||
ui_widget.tabWidget->setEnabled(true);
|
||||
ui_widget.run->setEnabled(true);
|
||||
ui_widget.run_smoothed->setEnabled(true);
|
||||
ui_widget.frame->setEnabled(true);
|
||||
ui_widget.generate_items->setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
void update_plugin_from_item(Item_classification_base* classif)
|
||||
{
|
||||
if (classif == NULL) // Deactivate plugin
|
||||
{
|
||||
disable_everything();
|
||||
ui_widget.tabWidget->setCurrentIndex(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
disable_everything();
|
||||
enable_computation();
|
||||
|
||||
ui_widget.numberOfScalesSpinBox->setValue((int)(classif->nb_scales()));
|
||||
ui_widget.number_of_trials->setValue((int)(classif->number_of_trials()));
|
||||
ui_widget.smoothingDoubleSpinBox->setValue((int)(classif->smoothing()));
|
||||
|
||||
// Clear class labels
|
||||
for (std::size_t i = 0; i < class_rows.size(); ++ i)
|
||||
{
|
||||
|
||||
ui_widget.gridLayout_3->removeWidget (class_rows[i].label);
|
||||
delete class_rows[i].label;
|
||||
ui_widget.gridLayout_3->removeWidget (class_rows[i].color_button);
|
||||
delete class_rows[i].color_button;
|
||||
ui_widget.gridLayout_3->removeWidget (class_rows[i].train);
|
||||
delete class_rows[i].train;
|
||||
ui_widget.gridLayout_3->removeWidget (class_rows[i].remove);
|
||||
delete class_rows[i].remove;
|
||||
ui_widget.gridLayout->removeWidget (class_rows[i].label2);
|
||||
delete class_rows[i].label2;
|
||||
ui_widget.gridLayout->removeWidget (class_rows[i].effect);
|
||||
delete class_rows[i].effect;
|
||||
}
|
||||
class_rows.clear();
|
||||
|
||||
// Add labels
|
||||
for (std::size_t i = 0; i < classif->number_of_labels(); ++ i)
|
||||
add_new_label (ClassRow (dock_widget, classif->label(i)->name().c_str(),
|
||||
classif->label_color(i)));
|
||||
|
||||
// Enabled classif if features computed
|
||||
if (!(classif->features_computed()))
|
||||
ui_widget.tabWidget->setCurrentIndex(0);
|
||||
else
|
||||
enable_classif();
|
||||
|
||||
int index = ui_widget.display->currentIndex();
|
||||
ui_widget.display->clear();
|
||||
ui_widget.display->addItem("Real colors");
|
||||
ui_widget.display->addItem("Classification");
|
||||
ui_widget.display->addItem("Training sets");
|
||||
ui_widget.selected_feature->clear();
|
||||
classif->fill_display_combo_box(ui_widget.display, ui_widget.selected_feature);
|
||||
if (index >= ui_widget.display->count())
|
||||
ui_widget.display->setCurrentIndex(1);
|
||||
else
|
||||
ui_widget.display->setCurrentIndex(index);
|
||||
ui_widget.selected_feature->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
void on_update_nb_scales()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
return;
|
||||
classif->nb_scales() = ui_widget.numberOfScalesSpinBox->value();
|
||||
}
|
||||
void on_update_number_of_trials()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
return;
|
||||
classif->number_of_trials() = ui_widget.number_of_trials->value();
|
||||
}
|
||||
|
||||
|
||||
Item_classification_base* get_classification(Scene_item* item = NULL)
|
||||
{
|
||||
if (!item)
|
||||
item = scene->item(scene->mainSelectionIndex());
|
||||
|
||||
if (!item)
|
||||
return NULL;
|
||||
|
||||
Item_map::iterator it = item_map.find(item);
|
||||
|
||||
if (it != item_map.end())
|
||||
return it->second;
|
||||
else if (Scene_points_with_normal_item* points_item
|
||||
= qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex())))
|
||||
return create_from_item(points_item);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Item_classification_base* create_from_item(Scene_points_with_normal_item* points_item)
|
||||
{
|
||||
if (item_map.find(points_item) != item_map.end())
|
||||
return item_map[points_item];
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
Item_classification_base* classif
|
||||
= new Point_set_item_classification (points_item);
|
||||
item_map.insert (std::make_pair (points_item, classif));
|
||||
QApplication::restoreOverrideCursor();
|
||||
update_plugin_from_item(classif);
|
||||
return classif;
|
||||
}
|
||||
|
||||
void run (Item_classification_base* classif, int method)
|
||||
{
|
||||
classif->run (method, ui_widget.classifier->currentIndex());
|
||||
}
|
||||
|
||||
void on_compute_features_button_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
classif->compute_features ();
|
||||
|
||||
update_plugin_from_item(classif);
|
||||
QApplication::restoreOverrideCursor();
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
void on_save_config_button_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
QString filename = QFileDialog::getSaveFileName(mw,
|
||||
tr("Save classification configuration"),
|
||||
QString("config.xml"),
|
||||
"Config file (*.xml);;");
|
||||
if (filename == QString())
|
||||
return;
|
||||
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
classif->save_config (filename.toStdString().c_str(),
|
||||
ui_widget.classifier->currentIndex());
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
}
|
||||
|
||||
void on_load_config_button_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
QString filename = QFileDialog::getOpenFileName(mw,
|
||||
tr("Open classification configuration"),
|
||||
".",
|
||||
"Config file (*.xml);;All Files (*)");
|
||||
|
||||
if (filename == QString())
|
||||
return;
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
classif->load_config (filename.toStdString().c_str(),
|
||||
ui_widget.classifier->currentIndex());
|
||||
update_plugin_from_item(classif);
|
||||
run (classif, 0);
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
|
||||
void on_display_button_clicked(int index)
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
return;
|
||||
|
||||
classif->change_color (index);
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
void on_run_button_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
run (classif, 0);
|
||||
QApplication::restoreOverrideCursor();
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
void on_run_smoothed_button_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
QTime time;
|
||||
time.start();
|
||||
run (classif, 1);
|
||||
std::cerr << "Smoothed classification computed in " << time.elapsed() / 1000 << " second(s)" << std::endl;
|
||||
QApplication::restoreOverrideCursor();
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
void on_run_graphcut_button_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
QTime time;
|
||||
time.start();
|
||||
run (classif, 2);
|
||||
std::cerr << "Graphcut classification computed in " << time.elapsed() / 1000 << " second(s)" << std::endl;
|
||||
QApplication::restoreOverrideCursor();
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
void on_smoothing_value_changed(double v)
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
return;
|
||||
classif->smoothing() = v;
|
||||
}
|
||||
|
||||
void on_subdivisions_value_changed(int v)
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
return;
|
||||
classif->subdivisions() = v;
|
||||
}
|
||||
|
||||
void on_generate_items_button_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
std::vector<Scene_item*> new_items;
|
||||
classif->generate_one_item_per_label
|
||||
(new_items, classif->item()->name().toStdString().c_str());
|
||||
|
||||
for (std::size_t i = 0; i < new_items.size(); ++ i)
|
||||
{
|
||||
Scene_points_with_normal_item* points_item
|
||||
= qobject_cast<Scene_points_with_normal_item*>(new_items[i]);
|
||||
if (!points_item)
|
||||
continue;
|
||||
|
||||
if (points_item->point_set()->empty())
|
||||
delete points_item;
|
||||
else
|
||||
scene->addItem (points_item);
|
||||
}
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
void on_add_new_label_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
QString name =
|
||||
QInputDialog::getText((QWidget*)mw,
|
||||
tr("Add new label"), // dialog title
|
||||
tr("Name:"), // field label
|
||||
QLineEdit::Normal,
|
||||
tr("label%1").arg(class_rows.size() + 1),
|
||||
&ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
add_new_label (ClassRow (dock_widget, name.toStdString().c_str(),
|
||||
QColor (192 + rand() % 60,
|
||||
192 + rand() % 60,
|
||||
192 + rand() % 60)));
|
||||
classif->add_new_label (class_rows.back().label->text().toStdString().c_str(),
|
||||
class_rows.back().color);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void on_reset_training_sets_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
classif->reset_training_sets();
|
||||
}
|
||||
|
||||
void on_validate_selection_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
classif->validate_selection();
|
||||
}
|
||||
|
||||
void on_train_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> classes;
|
||||
std::vector<QColor> colors;
|
||||
|
||||
for (std::size_t i = 0; i < class_rows.size(); ++ i)
|
||||
{
|
||||
classes.push_back (class_rows[i].label->text().toStdString());
|
||||
colors.push_back (class_rows[i].color);
|
||||
}
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
classif->train(ui_widget.classifier->currentIndex());
|
||||
QApplication::restoreOverrideCursor();
|
||||
update_plugin_from_item(classif);
|
||||
}
|
||||
|
||||
void add_new_label (const ClassRow& class_row)
|
||||
{
|
||||
class_rows.push_back (class_row);
|
||||
int position = static_cast<int>(class_rows.size());
|
||||
|
||||
ui_widget.gridLayout_3->addWidget (class_rows.back().label, position, 0);
|
||||
ui_widget.gridLayout_3->addWidget (class_rows.back().color_button, position, 1);
|
||||
ui_widget.gridLayout_3->addWidget (class_rows.back().train, position, 3);
|
||||
ui_widget.gridLayout_3->addWidget (class_rows.back().remove, position, 5);
|
||||
|
||||
connect(class_rows.back().remove, SIGNAL(clicked()), this,
|
||||
SLOT(on_remove_class_clicked()));
|
||||
connect(class_rows.back().color_button, SIGNAL(clicked()), this,
|
||||
SLOT(on_color_changed_clicked()));
|
||||
connect(class_rows.back().train, SIGNAL(clicked()), this,
|
||||
SLOT(on_add_selection_to_training_set_clicked()));
|
||||
|
||||
ui_widget.gridLayout->addWidget (class_rows.back().label2, position, 0);
|
||||
ui_widget.gridLayout->addWidget (class_rows.back().effect, position, 2);
|
||||
|
||||
connect(class_rows.back().effect, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(on_effect_changed(int)));
|
||||
|
||||
}
|
||||
|
||||
void on_remove_class_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
int index = ui_widget.gridLayout_3->indexOf(qobject_cast<QWidget*>(QObject::sender()));
|
||||
|
||||
int row_index, column_index, row_span, column_span;
|
||||
ui_widget.gridLayout_3->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
||||
--row_index;
|
||||
|
||||
classif->remove_label (class_rows[row_index].label->text().toStdString().c_str());
|
||||
|
||||
ui_widget.gridLayout_3->removeWidget (class_rows[row_index].label);
|
||||
delete class_rows[row_index].label;
|
||||
ui_widget.gridLayout_3->removeWidget (class_rows[row_index].color_button);
|
||||
delete class_rows[row_index].color_button;
|
||||
ui_widget.gridLayout_3->removeWidget (class_rows[row_index].train);
|
||||
delete class_rows[row_index].train;
|
||||
ui_widget.gridLayout_3->removeWidget (class_rows[row_index].remove);
|
||||
delete class_rows[row_index].remove;
|
||||
|
||||
ui_widget.gridLayout->removeWidget (class_rows[row_index].label2);
|
||||
delete class_rows[row_index].label2;
|
||||
ui_widget.gridLayout->removeWidget (class_rows[row_index].effect);
|
||||
delete class_rows[row_index].effect;
|
||||
|
||||
if (class_rows.size() > 1)
|
||||
for (std::size_t i = row_index + 1; i < class_rows.size(); ++ i)
|
||||
{
|
||||
ui_widget.gridLayout_3->addWidget (class_rows[i].label, (int)i, 0);
|
||||
ui_widget.gridLayout_3->addWidget (class_rows[i].color_button, (int)i, 1);
|
||||
ui_widget.gridLayout_3->addWidget (class_rows[i].train, (int)i, 3);
|
||||
ui_widget.gridLayout_3->addWidget (class_rows[i].remove, (int)i, 5);
|
||||
ui_widget.gridLayout->addWidget (class_rows[i].label2, (int)i, 0);
|
||||
ui_widget.gridLayout->addWidget (class_rows[i].effect, (int)i, 2);
|
||||
}
|
||||
|
||||
class_rows.erase (class_rows.begin() + row_index);
|
||||
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
void on_color_changed_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
QPushButton* color_button = qobject_cast<QPushButton*>(QObject::sender());
|
||||
int index = ui_widget.gridLayout_3->indexOf(color_button);
|
||||
int row_index, column_index, row_span, column_span;
|
||||
ui_widget.gridLayout_3->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
||||
-- row_index;
|
||||
|
||||
QColor color = class_rows[row_index].color;
|
||||
color = QColorDialog::getColor(color, (QWidget*)mw, "Change of color of label");
|
||||
class_rows[row_index].change_color (color);
|
||||
classif->change_label_color (class_rows[row_index].label->text().toStdString().c_str(),
|
||||
color);
|
||||
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
void on_add_selection_to_training_set_clicked()
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
int index = ui_widget.gridLayout_3->indexOf(qobject_cast<QWidget*>(QObject::sender()));
|
||||
int row_index, column_index, row_span, column_span;
|
||||
ui_widget.gridLayout_3->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
||||
--row_index;
|
||||
|
||||
classif->add_selection_to_training_set
|
||||
(class_rows[row_index].label->text().toStdString().c_str());
|
||||
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
||||
void on_selected_feature_changed(int v)
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (classif->number_of_features() <= (std::size_t)v)
|
||||
return;
|
||||
|
||||
Item_classification_base::Feature_handle
|
||||
att = classif->feature(v);
|
||||
|
||||
if (att == Item_classification_base::Feature_handle())
|
||||
return;
|
||||
|
||||
ui_widget.feature_weight->setValue ((int)(1001. * 2. * std::atan(classif->weight(att)) / CGAL_PI));
|
||||
|
||||
for (std::size_t i = 0; i < classif->number_of_labels(); ++ i)
|
||||
{
|
||||
CGAL::Classification::Sum_of_weighted_features_classifier::Effect
|
||||
eff = classif->effect (classif->label(i), att);
|
||||
if (eff == CGAL::Classification::Sum_of_weighted_features_classifier::PENALIZING)
|
||||
class_rows[i].effect->setCurrentIndex(0);
|
||||
else if (eff == CGAL::Classification::Sum_of_weighted_features_classifier::NEUTRAL)
|
||||
class_rows[i].effect->setCurrentIndex(1);
|
||||
else
|
||||
class_rows[i].effect->setCurrentIndex(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void on_feature_weight_changed(int v)
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
Item_classification_base::Feature_handle
|
||||
att = classif->feature(ui_widget.selected_feature->currentIndex());
|
||||
|
||||
if (att == Item_classification_base::Feature_handle())
|
||||
return;
|
||||
|
||||
classif->set_weight(att, std::tan ((CGAL_PI/2.) * v / 1001.));
|
||||
|
||||
for (std::size_t i = 0; i < class_rows.size(); ++ i)
|
||||
class_rows[i].effect->setEnabled(classif->weight(att) != 0.);
|
||||
}
|
||||
|
||||
void on_effect_changed (int v)
|
||||
{
|
||||
Item_classification_base* classif
|
||||
= get_classification();
|
||||
if(!classif)
|
||||
{
|
||||
print_message("Error: there is no point set classification item!");
|
||||
return;
|
||||
}
|
||||
Item_classification_base::Feature_handle
|
||||
att = classif->feature(ui_widget.selected_feature->currentIndex());
|
||||
|
||||
if (att == Item_classification_base::Feature_handle())
|
||||
return;
|
||||
|
||||
QComboBox* combo = qobject_cast<QComboBox*>(QObject::sender());
|
||||
for (std::size_t i = 0;i < class_rows.size(); ++ i)
|
||||
if (class_rows[i].effect == combo)
|
||||
{
|
||||
// std::cerr << att->id() << " is ";
|
||||
if (v == 0)
|
||||
{
|
||||
classif->set_effect(classif->label(i),
|
||||
att, CGAL::Classification::Sum_of_weighted_features_classifier::PENALIZING);
|
||||
}
|
||||
else if (v == 1)
|
||||
{
|
||||
classif->set_effect(classif->label(i),
|
||||
att, CGAL::Classification::Sum_of_weighted_features_classifier::NEUTRAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
classif->set_effect(classif->label(i),
|
||||
att, CGAL::Classification::Sum_of_weighted_features_classifier::FAVORING);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Messages_interface* messages;
|
||||
QAction* actionClassification;
|
||||
|
||||
QDockWidget* dock_widget;
|
||||
|
||||
std::vector<ClassRow> class_rows;
|
||||
|
||||
Ui::Classification ui_widget;
|
||||
|
||||
QColor color_att;
|
||||
|
||||
typedef std::map<Scene_item*, Item_classification_base*> Item_map;
|
||||
Item_map item_map;
|
||||
|
||||
}; // end Polyhedron_demo_classification_plugin
|
||||
|
||||
#include "Classification_plugin.moc"
|
||||
|
|
@ -0,0 +1,634 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Classification</class>
|
||||
<widget class="QDockWidget" name="Classification">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>560</width>
|
||||
<height>376</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Classification</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QPushButton" name="compute_features">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Compute features</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="numberOfScalesSpinBox">
|
||||
<property name="suffix">
|
||||
<string> scale(s)</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>using </string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>5</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="save_config">
|
||||
<property name="text">
|
||||
<string>Save config</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="load_config">
|
||||
<property name="text">
|
||||
<string>Load config</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Classifier:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="classifier">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Sum of Weighted Features</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Random Forest</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="display">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Real colors</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Classification</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Training sets</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::NoContextMenu</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_classif">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Classification</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Training set</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Label</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QPushButton" name="add_new_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add new</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QPushButton" name="train">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Train</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="reset_training_sets">
|
||||
<property name="text">
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="validate_selection">
|
||||
<property name="text">
|
||||
<string>Validate selection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="number_of_trials">
|
||||
<property name="suffix">
|
||||
<string> trials</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>300</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_advanced">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Advanced</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Features:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="selected_feature">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="feature_weight">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBothSides</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Label</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Effect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QPushButton" name="run">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="run_smoothed">
|
||||
<property name="text">
|
||||
<string>Smoothed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||
<item>
|
||||
<widget class="QPushButton" name="run_graphcut">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Graphcut</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Sub =</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="subdivisionsSpinBox">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Weight =</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="smoothingDoubleSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Smoothing strenght</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>10.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.500000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QPushButton" name="generate_items">
|
||||
<property name="text">
|
||||
<string>Generate one item per label</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
#ifndef ITEM_CLASSIFICATION_BASE_H
|
||||
#define ITEM_CLASSIFICATION_BASE_H
|
||||
|
||||
#include <CGAL/Three/Scene_item.h>
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
#include <CGAL/Classification/Feature_set.h>
|
||||
#include <CGAL/Classification/Label_set.h>
|
||||
#include <CGAL/Classification/Sum_of_weighted_features_classifier.h>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
#include <CGAL/Classification/Random_forest_classifier.h>
|
||||
#endif
|
||||
|
||||
class Item_classification_base
|
||||
{
|
||||
public:
|
||||
typedef CGAL::Classification::Label_handle Label_handle;
|
||||
typedef CGAL::Classification::Feature_handle Feature_handle;
|
||||
typedef CGAL::Classification::Label_set Label_set;
|
||||
typedef CGAL::Classification::Feature_set Feature_set;
|
||||
typedef CGAL::Classification::Sum_of_weighted_features_classifier Sum_of_weighted_features;
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
typedef CGAL::Classification::Random_forest_classifier Random_forest;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
Item_classification_base() { }
|
||||
virtual ~Item_classification_base() { }
|
||||
|
||||
virtual CGAL::Three::Scene_item* item() = 0;
|
||||
virtual void erase_item() = 0;
|
||||
|
||||
virtual void compute_features () = 0;
|
||||
|
||||
virtual void add_selection_to_training_set (const char* name) = 0;
|
||||
virtual void reset_training_sets() = 0;
|
||||
|
||||
virtual void validate_selection () = 0;
|
||||
virtual void train(int classifier) = 0;
|
||||
virtual bool run (int method, int classifier) = 0;
|
||||
|
||||
virtual void change_color (int index) = 0;
|
||||
virtual void generate_one_item_per_label(std::vector<CGAL::Three::Scene_item*>& items,
|
||||
const char* name) const = 0;
|
||||
|
||||
virtual bool write_output(std::ostream& out) = 0;
|
||||
|
||||
bool features_computed() const { return (m_features.size() != 0); }
|
||||
std::size_t number_of_features() const { return m_features.size(); }
|
||||
Feature_handle feature(std::size_t i) { return m_features[i]; }
|
||||
float weight (Feature_handle f) const { return m_sowf->weight(f); }
|
||||
void set_weight (Feature_handle f, float w) const { m_sowf->set_weight(f,w); }
|
||||
Sum_of_weighted_features::Effect effect (Label_handle l, Feature_handle f) const { return m_sowf->effect(l,f); }
|
||||
void set_effect (Label_handle l, Feature_handle f, Sum_of_weighted_features::Effect e)
|
||||
{ m_sowf->set_effect (l, f, e); }
|
||||
|
||||
void add_new_label (const char* name, const QColor& color)
|
||||
{
|
||||
m_labels.add(name);
|
||||
m_label_colors.push_back (color);
|
||||
delete m_sowf;
|
||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
delete m_random_forest;
|
||||
m_random_forest = new Random_forest (m_labels, m_features);
|
||||
#endif
|
||||
}
|
||||
void remove_label (const char* name)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
if (m_labels[i]->name() == name)
|
||||
{
|
||||
m_labels.remove(m_labels[i]);
|
||||
m_label_colors.erase (m_label_colors.begin() + i);
|
||||
break;
|
||||
}
|
||||
delete m_sowf;
|
||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
delete m_random_forest;
|
||||
m_random_forest = new Random_forest (m_labels, m_features);
|
||||
#endif
|
||||
}
|
||||
std::size_t number_of_labels() const { return m_labels.size(); }
|
||||
Label_handle label(std::size_t i) { return m_labels[i]; }
|
||||
|
||||
void fill_display_combo_box (QComboBox* cb, QComboBox* cb1) const
|
||||
{
|
||||
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Feature " << m_features[i]->name();
|
||||
cb->addItem (oss.str().c_str());
|
||||
cb1->addItem (oss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void save_config(const char* filename, int classifier)
|
||||
{
|
||||
if (m_features.size() == 0)
|
||||
{
|
||||
std::cerr << "Error: features not computed" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (classifier == 0)
|
||||
{
|
||||
std::ofstream f (filename);
|
||||
m_sowf->save_configuration (f);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
m_random_forest->save_configuration (filename);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void load_config(const char* filename, int classifier)
|
||||
{
|
||||
if (m_features.size() == 0)
|
||||
{
|
||||
std::cerr << "Error: features not computed" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (classifier == 0)
|
||||
{
|
||||
std::ifstream f (filename);
|
||||
m_sowf->load_configuration (f, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
m_random_forest->load_configuration (filename);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t& nb_scales() { return m_nb_scales; }
|
||||
std::size_t& number_of_trials() { return m_nb_trials; }
|
||||
float& smoothing() { return m_smoothing; }
|
||||
std::size_t& subdivisions() { return m_subdivisions; }
|
||||
const QColor& label_color(std::size_t i) const { return m_label_colors[i]; }
|
||||
std::size_t get_label (const char* name)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
if (m_labels[i]->name() == name)
|
||||
return i;
|
||||
return std::size_t(-1);
|
||||
}
|
||||
void change_label_color (const char* name, const QColor& color)
|
||||
{
|
||||
m_label_colors[get_label(name)] = color;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
Label_set m_labels;
|
||||
Feature_set m_features;
|
||||
std::vector<QColor> m_label_colors;
|
||||
Sum_of_weighted_features* m_sowf;
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
Random_forest* m_random_forest;
|
||||
#endif
|
||||
|
||||
std::size_t m_nb_scales;
|
||||
std::size_t m_nb_trials;
|
||||
float m_smoothing;
|
||||
std::size_t m_subdivisions;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // ITEM_CLASSIFICATION_BASE_H
|
||||
|
|
@ -0,0 +1,414 @@
|
|||
#include "Point_set_item_classification.h"
|
||||
#include "Color_ramp.h"
|
||||
|
||||
#include <CGAL/Timer.h>
|
||||
#include <CGAL/Memory_sizer.h>
|
||||
|
||||
#include <CGAL/Three/Viewer_interface.h>
|
||||
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <algorithm>
|
||||
#include <boost/array.hpp>
|
||||
|
||||
Point_set_item_classification::Point_set_item_classification(Scene_points_with_normal_item* points)
|
||||
: m_points (points),
|
||||
m_generator (NULL)
|
||||
{
|
||||
m_nb_scales = 5;
|
||||
m_index_color = 1;
|
||||
m_nb_trials = 300;
|
||||
m_smoothing = 0.5;
|
||||
m_subdivisions = 16;
|
||||
|
||||
reset_indices();
|
||||
|
||||
backup_existing_colors_and_add_new();
|
||||
m_training = m_points->point_set()->add_property_map<std::size_t>("training", std::size_t(-1)).first;
|
||||
m_classif = m_points->point_set()->add_property_map<std::size_t>("label", std::size_t(-1)).first;
|
||||
|
||||
Point_set::Property_map<int> ps_labels;
|
||||
bool found;
|
||||
boost::tie (ps_labels, found) = m_points->point_set()->property_map<int>("label");
|
||||
Point_set::Property_map<signed char> ps_labels_c;
|
||||
bool found_c;
|
||||
boost::tie (ps_labels_c, found_c) = m_points->point_set()->property_map<signed char>("label");
|
||||
if (found || found_c)
|
||||
{
|
||||
int max = 0;
|
||||
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
int l;
|
||||
if (found) l = ps_labels[*it];
|
||||
else l = int(ps_labels_c[*it] - 1);
|
||||
|
||||
m_classif[*it] = (std::size_t)l;
|
||||
m_training[*it] = (std::size_t)l;
|
||||
if (l > max)
|
||||
{
|
||||
max = l;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < max; ++ i)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "label_" << i;
|
||||
m_labels.add(oss.str().c_str());
|
||||
CGAL::Classification::HSV_Color hsv;
|
||||
hsv[0] = 360. * (i / double(max));
|
||||
hsv[1] = 76.;
|
||||
hsv[2] = 85.;
|
||||
Color rgb = CGAL::Classification::hsv_to_rgb(hsv);
|
||||
m_label_colors.push_back (QColor(rgb[0], rgb[1], rgb[2]));
|
||||
}
|
||||
|
||||
if (found)
|
||||
m_points->point_set()->remove_property_map(ps_labels);
|
||||
else
|
||||
m_points->point_set()->remove_property_map(ps_labels_c);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_labels.add("ground");
|
||||
m_labels.add("vegetation");
|
||||
m_labels.add("roof");
|
||||
m_labels.add("facade");
|
||||
|
||||
m_label_colors.push_back (QColor(245, 180, 0));
|
||||
m_label_colors.push_back (QColor(0, 255, 27));
|
||||
m_label_colors.push_back (QColor(255, 0, 170));
|
||||
m_label_colors.push_back (QColor(100, 0, 255));
|
||||
}
|
||||
|
||||
|
||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
m_random_forest = new Random_forest (m_labels, m_features);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Point_set_item_classification::~Point_set_item_classification()
|
||||
{
|
||||
if (m_sowf != NULL)
|
||||
delete m_sowf;
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
if (m_random_forest != NULL)
|
||||
delete m_random_forest;
|
||||
#endif
|
||||
if (m_generator != NULL)
|
||||
delete m_generator;
|
||||
if (m_points != NULL)
|
||||
{
|
||||
reset_colors();
|
||||
m_points->point_set()->remove_property_map(m_training);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Point_set_item_classification::backup_existing_colors_and_add_new()
|
||||
{
|
||||
if (m_points->point_set()->has_colors())
|
||||
{
|
||||
m_color = m_points->point_set()->add_property_map<Color>("real_color").first;
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
m_color[*it] = {{ (unsigned char)(255 * m_points->point_set()->red(*it)),
|
||||
(unsigned char)(255 * m_points->point_set()->green(*it)),
|
||||
(unsigned char)(255 * m_points->point_set()->blue(*it)) }};
|
||||
|
||||
m_points->point_set()->remove_colors();
|
||||
}
|
||||
|
||||
m_red = m_points->point_set()->add_property_map<unsigned char>("red").first;
|
||||
m_green = m_points->point_set()->add_property_map<unsigned char>("green").first;
|
||||
m_blue = m_points->point_set()->add_property_map<unsigned char>("blue").first;
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
m_red[*it] = 0;
|
||||
m_green[*it] = 0;
|
||||
m_blue[*it] = 0;
|
||||
}
|
||||
m_points->point_set()->check_colors();
|
||||
}
|
||||
|
||||
void Point_set_item_classification::reset_colors()
|
||||
{
|
||||
if (m_color == Point_set::Property_map<Color>())
|
||||
{
|
||||
m_points->point_set()->remove_property_map(m_red);
|
||||
m_points->point_set()->remove_property_map(m_green);
|
||||
m_points->point_set()->remove_property_map(m_blue);
|
||||
m_points->point_set()->check_colors();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
m_red[*it] = m_color[*it][0];
|
||||
m_green[*it] = m_color[*it][1];
|
||||
m_blue[*it] = m_color[*it][2];
|
||||
}
|
||||
m_points->point_set()->remove_property_map(m_color);
|
||||
}
|
||||
}
|
||||
|
||||
// Write point set to .PLY file
|
||||
bool Point_set_item_classification::write_output(std::ostream& stream)
|
||||
{
|
||||
if (m_features.size() == 0)
|
||||
return false;
|
||||
|
||||
reset_indices();
|
||||
|
||||
stream.precision (std::numeric_limits<double>::digits10 + 2);
|
||||
|
||||
// std::vector<Color> colors;
|
||||
// for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
// {
|
||||
// Color c = {{ (unsigned char)(m_labels[i].second.red()),
|
||||
// (unsigned char)(m_labels[i].second.green()),
|
||||
// (unsigned char)(m_labels[i].second.blue()) }};
|
||||
// colors.push_back (c);
|
||||
// }
|
||||
|
||||
// m_psc->write_classification_to_ply (stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Point_set_item_classification::change_color (int index)
|
||||
{
|
||||
m_index_color = index;
|
||||
|
||||
int index_color = real_index_color();
|
||||
|
||||
// Colors
|
||||
static Color_ramp ramp;
|
||||
ramp.build_red();
|
||||
reset_indices();
|
||||
if (index_color == -1) // item color
|
||||
{
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
m_red[*it] = 0;
|
||||
m_green[*it] = 0;
|
||||
m_blue[*it] = 0;
|
||||
}
|
||||
}
|
||||
else if (index_color == 0) // real colors
|
||||
{
|
||||
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
m_red[*it] = m_color[*it][0];
|
||||
m_green[*it] = m_color[*it][1];
|
||||
m_blue[*it] = m_color[*it][2];
|
||||
}
|
||||
}
|
||||
else if (index_color == 1) // classif
|
||||
{
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
QColor color (0, 0, 0);
|
||||
std::size_t c = m_classif[*it];
|
||||
|
||||
if (c != std::size_t(-1))
|
||||
color = m_label_colors[c];
|
||||
|
||||
m_red[*it] = color.red();
|
||||
m_green[*it] = color.green();
|
||||
m_blue[*it] = color.blue();
|
||||
}
|
||||
}
|
||||
else if (index_color == 2) // training
|
||||
{
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
QColor color (0, 0, 0);
|
||||
std::size_t c = m_training[*it];
|
||||
std::size_t c2 = m_classif[*it];
|
||||
|
||||
if (c != std::size_t(-1))
|
||||
color = m_label_colors[c];
|
||||
|
||||
float div = 1;
|
||||
if (c != c2)
|
||||
div = 2;
|
||||
|
||||
m_red[*it] = (color.red() / div);
|
||||
m_green[*it] = (color.green() / div);
|
||||
m_blue[*it] = (color.blue() / div);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Feature_handle feature = m_features[index_color - 3];
|
||||
|
||||
float max = 0.;
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
if (feature->value(*it) > max)
|
||||
max = feature->value(*it);
|
||||
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
float v = std::max (0.f, feature->value(*it) / max);
|
||||
m_red[*it] = (unsigned char)(ramp.r(v) * 255);
|
||||
m_green[*it] = (unsigned char)(ramp.g(v) * 255);
|
||||
m_blue[*it] = (unsigned char)(ramp.b(v) * 255);
|
||||
}
|
||||
}
|
||||
|
||||
for (Point_set::const_iterator it = m_points->point_set()->first_selected();
|
||||
it != m_points->point_set()->end(); ++ it)
|
||||
{
|
||||
m_red[*it] = 255;
|
||||
m_green[*it] = 0;
|
||||
m_blue[*it] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int Point_set_item_classification::real_index_color() const
|
||||
{
|
||||
int out = m_index_color;
|
||||
|
||||
if (out == 0 && m_color == Point_set::Property_map<Color>())
|
||||
out = -1;
|
||||
return out;
|
||||
}
|
||||
|
||||
void Point_set_item_classification::reset_indices ()
|
||||
{
|
||||
Point_set::Property_map<Point_set::Index> indices
|
||||
= m_points->point_set()->property_map<Point_set::Index>("index").first;
|
||||
|
||||
m_points->point_set()->unselect_all();
|
||||
Point_set::Index idx;
|
||||
++ idx;
|
||||
for (std::size_t i = 0; i < m_points->point_set()->size(); ++ i)
|
||||
*(indices.begin() + i) = idx ++;
|
||||
}
|
||||
|
||||
void Point_set_item_classification::compute_features ()
|
||||
{
|
||||
CGAL_assertion (!(m_points->point_set()->empty()));
|
||||
|
||||
if (m_generator != NULL)
|
||||
delete m_generator;
|
||||
|
||||
reset_indices();
|
||||
|
||||
std::cerr << "Computing features with " << m_nb_scales << " scale(s)" << std::endl;
|
||||
m_features.clear();
|
||||
|
||||
bool normals = m_points->point_set()->has_normal_map();
|
||||
bool colors = (m_color != Point_set::Property_map<Color>());
|
||||
Point_set::Property_map<boost::uint8_t> echo_map;
|
||||
bool echo;
|
||||
boost::tie (echo_map, echo) = m_points->point_set()->template property_map<boost::uint8_t>("echo");
|
||||
|
||||
if (!normals && !colors && !echo)
|
||||
m_generator = new Generator (m_features, *(m_points->point_set()), m_points->point_set()->point_map(), m_nb_scales);
|
||||
else if (!normals && !colors && echo)
|
||||
m_generator = new Generator (m_features, *(m_points->point_set()), m_points->point_set()->point_map(), m_nb_scales,
|
||||
CGAL::Default(), CGAL::Default(), echo_map);
|
||||
else if (!normals && colors && !echo)
|
||||
m_generator = new Generator (m_features, *(m_points->point_set()), m_points->point_set()->point_map(), m_nb_scales,
|
||||
CGAL::Default(), m_color);
|
||||
else if (!normals && colors && echo)
|
||||
m_generator = new Generator (m_features, *(m_points->point_set()), m_points->point_set()->point_map(), m_nb_scales,
|
||||
CGAL::Default(), m_color, echo_map);
|
||||
else if (normals && !colors && !echo)
|
||||
m_generator = new Generator (m_features, *(m_points->point_set()), m_points->point_set()->point_map(), m_nb_scales,
|
||||
m_points->point_set()->normal_map());
|
||||
else if (normals && !colors && echo)
|
||||
m_generator = new Generator (m_features, *(m_points->point_set()), m_points->point_set()->point_map(), m_nb_scales,
|
||||
m_points->point_set()->normal_map(), CGAL::Default(), echo_map);
|
||||
else if (normals && colors && !echo)
|
||||
m_generator = new Generator (m_features, *(m_points->point_set()), m_points->point_set()->point_map(), m_nb_scales,
|
||||
m_points->point_set()->normal_map(), m_color);
|
||||
else
|
||||
m_generator = new Generator (m_features, *(m_points->point_set()), m_points->point_set()->point_map(), m_nb_scales,
|
||||
m_points->point_set()->normal_map(), m_color, echo_map);
|
||||
|
||||
delete m_sowf;
|
||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
delete m_random_forest;
|
||||
m_random_forest = new Random_forest (m_labels, m_features);
|
||||
#endif
|
||||
std::cerr << "Features = " << m_features.size() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Point_set_item_classification::train(int classifier)
|
||||
{
|
||||
if (m_features.size() == 0)
|
||||
{
|
||||
std::cerr << "Error: features not computed" << std::endl;
|
||||
return;
|
||||
}
|
||||
reset_indices();
|
||||
|
||||
std::vector<std::size_t> indices (m_points->point_set()->size(), std::size_t(-1));
|
||||
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
indices[*it] = m_training[*it];
|
||||
|
||||
if (classifier == 0)
|
||||
{
|
||||
m_sowf->train<Concurrency_tag>(indices, m_nb_trials);
|
||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||
m_labels, *m_sowf,
|
||||
indices);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
m_random_forest->train (indices);
|
||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||
m_labels, *m_random_forest,
|
||||
indices);
|
||||
#endif
|
||||
}
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
m_classif[*it] = indices[*it];
|
||||
|
||||
if (m_index_color == 1 || m_index_color == 2)
|
||||
change_color (m_index_color);
|
||||
}
|
||||
|
||||
bool Point_set_item_classification::run (int method, int classifier)
|
||||
{
|
||||
if (m_features.size() == 0)
|
||||
{
|
||||
std::cerr << "Error: features not computed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
reset_indices();
|
||||
|
||||
if (classifier == 0)
|
||||
run (method, *m_sowf);
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
else
|
||||
run (method, *m_random_forest);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
#ifndef POINT_SET_ITEM_CLASSIFICATION_H
|
||||
#define POINT_SET_ITEM_CLASSIFICATION_H
|
||||
|
||||
//#define CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
|
||||
#define CGAL_CLASSIFICATION_VERBOSE
|
||||
|
||||
#include <CGAL/Three/Scene_item.h>
|
||||
|
||||
#include "Scene_points_with_normal_item.h"
|
||||
#include "Item_classification_base.h"
|
||||
#include "Polyhedron_type_fwd.h"
|
||||
#include "Kernel_type.h"
|
||||
#include "Point_set_3.h"
|
||||
|
||||
#include <CGAL/Classification.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
typedef CGAL::Parallel_tag Concurrency_tag;
|
||||
#else
|
||||
typedef CGAL::Sequential_tag Concurrency_tag;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// This class represents a point set in the OpenGL scene
|
||||
class Point_set_item_classification : public Item_classification_base
|
||||
{
|
||||
public:
|
||||
typedef Kernel::Point_3 Point_3;
|
||||
typedef Kernel::Vector_3 Vector_3;
|
||||
typedef CGAL::Classification::RGB_Color Color;
|
||||
|
||||
typedef Point_set::Point_map Point_map;
|
||||
typedef Point_set::Vector_map Vector_map;
|
||||
|
||||
typedef CGAL::Classification::Point_set_feature_generator<Kernel, Point_set, Point_map> Generator;
|
||||
|
||||
public:
|
||||
|
||||
Point_set_item_classification(Scene_points_with_normal_item* points);
|
||||
~Point_set_item_classification();
|
||||
|
||||
CGAL::Three::Scene_item* item() { return m_points; }
|
||||
void erase_item() { m_points = NULL; }
|
||||
|
||||
void compute_features ();
|
||||
|
||||
void add_selection_to_training_set (const char* name)
|
||||
{
|
||||
std::size_t label = get_label (name);
|
||||
|
||||
for (Point_set::const_iterator it = m_points->point_set()->first_selected();
|
||||
it != m_points->point_set()->end(); ++ it)
|
||||
{
|
||||
m_training[*it] = label;
|
||||
m_classif[*it] = label;
|
||||
}
|
||||
|
||||
m_points->resetSelection();
|
||||
if (m_index_color == 1 || m_index_color == 2)
|
||||
change_color (m_index_color);
|
||||
}
|
||||
void reset_training_sets()
|
||||
{
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->end(); ++ it)
|
||||
m_training[*it] = std::size_t(-1);
|
||||
}
|
||||
void validate_selection ()
|
||||
{
|
||||
for (Point_set::const_iterator it = m_points->point_set()->first_selected();
|
||||
it != m_points->point_set()->end(); ++ it)
|
||||
m_training[*it] = m_classif[*it];
|
||||
|
||||
m_points->resetSelection();
|
||||
if (m_index_color == 1 || m_index_color == 2)
|
||||
change_color (m_index_color);
|
||||
}
|
||||
void train(int classifier);
|
||||
bool run (int method, int classifier);
|
||||
|
||||
void change_color (int index);
|
||||
void generate_one_item_per_label(std::vector<CGAL::Three::Scene_item*>& items,
|
||||
const char* name) const
|
||||
{
|
||||
std::vector<Scene_points_with_normal_item*> points_item
|
||||
(m_labels.size(), NULL);
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
{
|
||||
points_item[i] = new Scene_points_with_normal_item;
|
||||
points_item[i]->setName (QString("%1 (%2)").arg(name).arg(m_labels[i]->name().c_str()));
|
||||
points_item[i]->setColor (m_label_colors[i]);
|
||||
items.push_back (points_item[i]);
|
||||
}
|
||||
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->end(); ++ it)
|
||||
{
|
||||
std::size_t c = m_classif[*it];
|
||||
if (c != std::size_t(-1))
|
||||
points_item[c]->point_set()->insert (m_points->point_set()->point(*it));
|
||||
}
|
||||
}
|
||||
|
||||
bool write_output(std::ostream& out);
|
||||
|
||||
int real_index_color() const;
|
||||
void reset_indices();
|
||||
void backup_existing_colors_and_add_new();
|
||||
void reset_colors();
|
||||
|
||||
private:
|
||||
|
||||
template <typename Classifier>
|
||||
bool run (int method, const Classifier& classifier)
|
||||
{
|
||||
std::vector<std::size_t> indices;
|
||||
|
||||
if (method == 0)
|
||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||
m_labels, classifier,
|
||||
indices);
|
||||
else if (method == 1)
|
||||
CGAL::Classification::classify_with_local_smoothing<Concurrency_tag>
|
||||
(*(m_points->point_set()), m_points->point_set()->point_map(), m_labels, classifier,
|
||||
m_generator->neighborhood().sphere_neighbor_query(m_generator->radius_neighbors()),
|
||||
indices);
|
||||
else if (method == 2)
|
||||
CGAL::Classification::classify_with_graphcut<Concurrency_tag>
|
||||
(*(m_points->point_set()), m_points->point_set()->point_map(),
|
||||
m_labels, classifier,
|
||||
m_generator->neighborhood().k_neighbor_query(12),
|
||||
m_smoothing, m_subdivisions, indices);
|
||||
|
||||
std::vector<std::size_t> ground_truth(m_points->point_set()->size(), std::size_t(-1));
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->first_selected(); ++ it)
|
||||
{
|
||||
m_classif[*it] = indices[*it];
|
||||
ground_truth[*it] = m_training[*it];
|
||||
}
|
||||
|
||||
if (m_index_color == 1 || m_index_color == 2)
|
||||
change_color (m_index_color);
|
||||
|
||||
std::cerr << "Precision, recall, F1 scores and IoU:" << std::endl;
|
||||
|
||||
CGAL::Classification::Evaluation eval (m_labels, ground_truth, indices);
|
||||
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
{
|
||||
std::cerr << " * " << m_labels[i]->name() << ": "
|
||||
<< eval.precision(m_labels[i]) << " ; "
|
||||
<< eval.recall(m_labels[i]) << " ; "
|
||||
<< eval.f1_score(m_labels[i]) << " ; "
|
||||
<< eval.intersection_over_union(m_labels[i]) << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Accuracy = " << eval.accuracy() << std::endl
|
||||
<< "Mean F1 score = " << eval.mean_f1_score() << std::endl
|
||||
<< "Mean IoU = " << eval.mean_intersection_over_union() << std::endl;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Scene_points_with_normal_item* m_points;
|
||||
|
||||
Point_set::Property_map<unsigned char> m_red;
|
||||
Point_set::Property_map<unsigned char> m_green;
|
||||
Point_set::Property_map<unsigned char> m_blue;
|
||||
Point_set::Property_map<Color> m_color;
|
||||
Point_set::Property_map<std::size_t> m_training;
|
||||
Point_set::Property_map<std::size_t> m_classif;
|
||||
|
||||
Generator* m_generator;
|
||||
|
||||
int m_index_color;
|
||||
|
||||
}; // end class Point_set_item_classification
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // POINT_SET_ITEM_CLASSIFICATION_H
|
||||
|
|
@ -252,6 +252,22 @@ public:
|
|||
return (m_blue != Byte_map());
|
||||
}
|
||||
|
||||
void remove_colors()
|
||||
{
|
||||
if (m_blue != Byte_map())
|
||||
{
|
||||
this->template remove_property_map<unsigned char>(m_red);
|
||||
this->template remove_property_map<unsigned char>(m_green);
|
||||
this->template remove_property_map<unsigned char>(m_blue);
|
||||
}
|
||||
if (m_fblue != Double_map())
|
||||
{
|
||||
this->template remove_property_map<double>(m_fred);
|
||||
this->template remove_property_map<double>(m_fgreen);
|
||||
this->template remove_property_map<double>(m_fblue);
|
||||
}
|
||||
}
|
||||
|
||||
double red (const Index& index) const
|
||||
{ return (m_red == Byte_map()) ? m_fred[index] : double(m_red[index]) / 255.; }
|
||||
double green (const Index& index) const
|
||||
|
|
|
|||
|
|
@ -435,6 +435,28 @@ make_property_map(const std::vector<T>& v)
|
|||
return make_property_map(&v[0]);
|
||||
}
|
||||
|
||||
/// \ingroup PkgProperty_map
|
||||
/// Property map that only returns the default value type
|
||||
/// \cgalModels `ReadablePropertyMap`
|
||||
template<class InputIterator, class ValueType>
|
||||
struct Default_property_map{
|
||||
const ValueType default_value;
|
||||
|
||||
typedef typename InputIterator::value_type key_type;
|
||||
typedef boost::readable_property_map_tag category;
|
||||
|
||||
Default_property_map(const ValueType& default_value = ValueType()) : default_value (default_value) { }
|
||||
|
||||
/// Free function to use a get the value from an iterator using Input_iterator_property_map.
|
||||
inline friend ValueType
|
||||
get (const Default_property_map&, const key_type&){ return ValueType(); }
|
||||
};
|
||||
|
||||
|
||||
/// \endcond
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
|
||||
|
||||
#endif // CGAL_POINT_SET_PROPERTY_MAP_H
|
||||
|
|
|
|||
|
|
@ -183,6 +183,59 @@ public:
|
|||
return is;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class Input_rep<float> {
|
||||
float& t;
|
||||
public:
|
||||
//! initialize with a reference to \a t.
|
||||
Input_rep( float& tt) : t(tt) {}
|
||||
|
||||
std::istream& operator()( std::istream& is) const
|
||||
{
|
||||
typedef std::istream istream;
|
||||
typedef istream::char_type char_type;
|
||||
typedef istream::int_type int_type;
|
||||
typedef istream::traits_type traits_type;
|
||||
|
||||
std::string buffer;
|
||||
buffer.reserve(32);
|
||||
|
||||
char_type c;
|
||||
do {
|
||||
const int_type i = is.get();
|
||||
if(i == traits_type::eof()) {
|
||||
return is;
|
||||
}
|
||||
c = static_cast<char_type>(i);
|
||||
}while (std::isspace(c));
|
||||
if(c == '-'){
|
||||
buffer += '-';
|
||||
} else if(c != '+'){
|
||||
is.unget();
|
||||
}
|
||||
do {
|
||||
const int_type i = is.get();
|
||||
if(i == traits_type::eof()) {
|
||||
is.clear(is.rdstate() & ~std::ios_base::failbit);
|
||||
break;
|
||||
}
|
||||
c = static_cast<char_type>(i);
|
||||
if(std::isdigit(c) || (c =='.') || (c =='E') || (c =='e') || (c =='+') || (c =='-')){
|
||||
buffer += c;
|
||||
}else{
|
||||
is.unget();
|
||||
break;
|
||||
}
|
||||
}while(true);
|
||||
|
||||
if(sscanf(buffer.c_str(), "%f", &t) != 1) {
|
||||
// if a 'buffer' does not contain a double, set the fail bit.
|
||||
is.setstate(std::ios_base::failbit);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/*! \relates Input_rep
|
||||
|
|
|
|||
|
|
@ -1751,4 +1751,6 @@ inline Graph::termtype Graph::what_segment(node_id i)
|
|||
return SINK;
|
||||
}
|
||||
|
||||
#undef last_node
|
||||
|
||||
#endif
|
||||
|
|
|
|||