diff --git a/.github/workflows/build_doc.yml b/.github/workflows/build_doc.yml
index 5378f637349..12bbd688af9 100644
--- a/.github/workflows/build_doc.yml
+++ b/.github/workflows/build_doc.yml
@@ -42,6 +42,7 @@ jobs:
repository: ${{ github.repository }}
ref: refs/pull/${{ steps.get_pr_number.outputs.result }}/merge
token: ${{ secrets.PUSH_TO_CGAL_GITHUB_IO_TOKEN }}
+ fetch-depth: 2
- name: install dependencies
@@ -60,10 +61,9 @@ jobs:
if: steps.get_round.outputs.result != 'stop'
run: |
set -ex
- git clone https://CGAL:${{ secrets.PUSH_TO_CGAL_GITHUB_IO_TOKEN }}@github.com/CGAL/cgal.github.io.git --depth=5
mkdir -p build_doc && cd build_doc && cmake ../Documentation/doc
- - name: Upload Doc
+ - name: Build and Upload Doc
if: steps.get_round.outputs.result != 'stop'
run: |
set -ex
@@ -71,14 +71,27 @@ jobs:
ROUND=${{ steps.get_round.outputs.result }}
wget --no-verbose cgal.github.io -O tmp.html
if ! egrep -q "\/$PR_NUMBER\/$ROUND" tmp.html; then
- mkdir -p cgal.github.io/${PR_NUMBER}/$ROUND
+ #list impacted packages
+ LIST_OF_PKGS=$(git diff --name-only HEAD^1 HEAD |cut -s -d/ -f1 |sort -u | xargs -I {} ls -d {}/package_info 2>/dev/null |cut -d/ -f1 |egrep -v Installation||true)
+ if [ "$LIST_OF_PKGS" = "" ]; then
+ exit 1
+ fi
cd build_doc && make -j2 doc && make -j2 doc_with_postprocessing
- cp -r ./doc_output/* ../cgal.github.io/${PR_NUMBER}/$ROUND
- cd ../cgal.github.io
- egrep -v " ${PR_NUMBER}\." index.html > tmp.html
+ cd ..
+ git clone https://CGAL:${{ secrets.PUSH_TO_CGAL_GITHUB_IO_TOKEN }}@github.com/CGAL/cgal.github.io.git
+ mkdir -p cgal.github.io/${PR_NUMBER}/$ROUND
+ for f in $LIST_OF_PKGS
+ do
+ if [ -d ./build_doc/doc_output/$f ]; then
+ cp -r ./build_doc/doc_output/$f ./cgal.github.io/${PR_NUMBER}/$ROUND
+ fi
+ done
+ cp -r ./build_doc/doc_output/Manual ./cgal.github.io/${PR_NUMBER}/$ROUND
+ cd ./cgal.github.io
+ egrep -v " ${PR_NUMBER}\." index.html > tmp.html || true
echo "
Manual for PR ${PR_NUMBER} ($ROUND)." >> ./tmp.html
mv tmp.html index.html
- git add ${PR_NUMBER}/$ROUND && git commit -q -a -m "Add ${PR_NUMBER} $ROUND" && git push -q -u origin master
+ git add ${PR_NUMBER}/$ROUND && git commit -q --amend -m "base commit" && git push -q -f -u origin master
else
exit 1
fi
@@ -88,7 +101,7 @@ jobs:
if: steps.get_round.outputs.result != 'stop'
with:
script: |
- const address = "The documentation is built. You can find it here : https://cgal.github.io/${{ steps.get_pr_number.outputs.result }}/${{ steps.get_round.outputs.result }}/Manual/index.html"
+ const address = "The documentation is built. It will be available, after a few minutes, here : https://cgal.github.io/${{ steps.get_pr_number.outputs.result }}/${{ steps.get_round.outputs.result }}/Manual/index.html"
github.issues.createComment({
owner: "CGAL",
repo: "cgal",
diff --git a/.github/workflows/delete_doc.yml b/.github/workflows/delete_doc.yml
index c1152d37ca9..1304a7895c9 100644
--- a/.github/workflows/delete_doc.yml
+++ b/.github/workflows/delete_doc.yml
@@ -18,10 +18,15 @@ jobs:
git clone https://maxGimeno:${{ secrets.PUSH_TO_CGAL_GITHUB_IO_TOKEN }}@github.com/CGAL/cgal.github.io.git --depth=5
PR_NUMBER=$(python -c "import json; import os; y = json.load(open(os.environ['GITHUB_EVENT_PATH'])); print(y[\"number\"])")
cd cgal.github.io/
- egrep -v " ${PR_NUMBER}\." index.html > tmp.html
+ egrep -v " ${PR_NUMBER}\." index.html > tmp.html || true
if [ -n "$(diff -q ./index.html ./tmp.html)" ]; then
mv tmp.html index.html
- #git rm -r ${PR_NUMBER} && git commit -a -m "Remove ${PR_NUMBER}" && git push -u origin master
- git commit -a -m "Remove ${PR_NUMBER}" && git push -u origin master
+ fi
+ if [ -d ${PR_NUMBER} ]; then
+ git rm -r ${PR_NUMBER}
+ fi
+ #git diff exits with 1 if there is a diff
+ if !git diff --quiet; then
+ git commit -a --amend -m"base commit" && git push -f -u origin master
fi
diff --git a/.travis/build_package.sh b/.travis/build_package.sh
index 0d275ee5885..dc41aa16dd1 100755
--- a/.travis/build_package.sh
+++ b/.travis/build_package.sh
@@ -88,6 +88,7 @@ cd $ROOT
echo '#include "CGAL/remove_outliers.h"' >> main.cpp
cd build
mytime cmake -DCMAKE_INSTALL_PREFIX=../../install -DCGAL_BUILD_THREE_DOC=TRUE ..
+ exit 0
fi
if [ "$ARG" = "Installation" ]
diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_dcel_base.h b/Arrangement_on_surface_2/include/CGAL/Arr_dcel_base.h
index 16befc318ee..16a15d7ea56 100644
--- a/Arrangement_on_surface_2/include/CGAL/Arr_dcel_base.h
+++ b/Arrangement_on_surface_2/include/CGAL/Arr_dcel_base.h
@@ -91,6 +91,11 @@ public:
/*! Destructor. */
virtual ~Arr_vertex_base() {}
+ // Access/modification for pointer squatting
+ void* inc() const { return p_inc; }
+ void set_inc(void * inc) const
+ { const_cast(*this).p_inc = inc; }
+
/*! Check if the point pointer is nullptr. */
bool has_null_point() const { return (p_pt == nullptr); }
diff --git a/Arrangement_on_surface_2/include/CGAL/Arr_overlay_2.h b/Arrangement_on_surface_2/include/CGAL/Arr_overlay_2.h
index e4c739c8c23..393958f3424 100644
--- a/Arrangement_on_surface_2/include/CGAL/Arr_overlay_2.h
+++ b/Arrangement_on_surface_2/include/CGAL/Arr_overlay_2.h
@@ -40,6 +40,89 @@
namespace CGAL {
+template
+class Indexed_sweep_accessor
+{
+ const Arr1& arr1;
+ const Arr2& arr2;
+ mutable std::vector backup_inc;
+
+public:
+
+ Indexed_sweep_accessor (const Arr1& arr1, const Arr2& arr2)
+ : arr1(arr1), arr2(arr2) { }
+
+ std::size_t nb_vertices() const
+ {
+ return arr1.number_of_vertices() + arr2.number_of_vertices();
+ }
+
+ std::size_t min_end_index (const Curve& c) const
+ {
+ if (c.red_halfedge_handle() != typename Curve::HH_red())
+ return reinterpret_cast(c.red_halfedge_handle()->target()->inc());
+ // else
+ CGAL_assertion (c.blue_halfedge_handle() != typename Curve::HH_blue());
+ return reinterpret_cast(c.blue_halfedge_handle()->target()->inc());
+ }
+
+ std::size_t max_end_index (const Curve& c) const
+ {
+ if (c.red_halfedge_handle() != typename Curve::HH_red())
+ return reinterpret_cast(c.red_halfedge_handle()->source()->inc());
+ // else
+ CGAL_assertion (c.blue_halfedge_handle() != typename Curve::HH_blue());
+ return reinterpret_cast(c.blue_halfedge_handle()->source()->inc());
+ }
+
+ const Curve& curve (const Curve& c) const
+ {
+ return c;
+ }
+
+ // Initializes indices by squatting Vertex::inc();
+ void before_init() const
+ {
+ std::size_t idx = 0;
+ backup_inc.resize (nb_vertices());
+ for (typename Arr1::Vertex_const_iterator vit = arr1.vertices_begin();
+ vit != arr1.vertices_end(); ++vit, ++idx)
+ {
+ CGAL_assertion (idx < backup_inc.size());
+ backup_inc[idx] = vit->inc();
+ vit->set_inc (reinterpret_cast(idx));
+ }
+ for (typename Arr2::Vertex_const_iterator vit = arr2.vertices_begin();
+ vit != arr2.vertices_end(); ++vit, ++idx)
+ {
+ CGAL_assertion (idx < backup_inc.size());
+ backup_inc[idx] = vit->inc();
+ vit->set_inc (reinterpret_cast(idx));
+ }
+ }
+
+ // Restores state of arrangements before index squatting
+ void after_init() const
+ {
+ std::size_t idx = 0;
+ for (typename Arr1::Vertex_const_iterator vit = arr1.vertices_begin();
+ vit != arr1.vertices_end(); ++vit, ++idx)
+ {
+ CGAL_assertion (idx < backup_inc.size());
+ vit->set_inc (backup_inc[idx]);
+ }
+ for (typename Arr2::Vertex_const_iterator vit = arr2.vertices_begin();
+ vit != arr2.vertices_end(); ++vit, ++idx)
+ {
+ CGAL_assertion (idx < backup_inc.size());
+ vit->set_inc (backup_inc[idx]);
+ }
+ }
+
+private:
+
+};
+
/*! Compute the overlay of two input arrangements.
* \tparam GeometryTraitsA_2 the geometry traits of the first arrangement.
* \tparam GeometryTraitsB_2 the geometry traits of the second arrangement.
@@ -183,7 +266,14 @@ overlay(const Arrangement_on_surface_2& arr1
if (total_iso_verts == 0) {
// Clear the result arrangement and perform the sweep to construct it.
arr.clear();
- surface_sweep.sweep(xcvs_vec.begin(), xcvs_vec.end());
+ if (std::is_same::value)
+ surface_sweep.sweep (xcvs_vec.begin(), xcvs_vec.end());
+ else
+ surface_sweep.indexed_sweep (xcvs_vec,
+ Indexed_sweep_accessor
+
+ (arr1, arr2));
xcvs_vec.clear();
return;
}
@@ -215,8 +305,16 @@ overlay(const Arrangement_on_surface_2& arr1
// Clear the result arrangement and perform the sweep to construct it.
arr.clear();
- surface_sweep.sweep(xcvs_vec.begin(), xcvs_vec.end(),
- pts_vec.begin(), pts_vec.end());
+ if (std::is_same::value)
+ surface_sweep.sweep(xcvs_vec.begin(), xcvs_vec.end(),
+ pts_vec.begin(), pts_vec.end());
+ else
+ surface_sweep.indexed_sweep (xcvs_vec,
+ Indexed_sweep_accessor
+
+ (arr1, arr2),
+ pts_vec.begin(), pts_vec.end());
xcvs_vec.clear();
pts_vec.clear();
}
diff --git a/Arrangement_on_surface_2/include/CGAL/Surface_sweep_2/Arr_overlay_traits_2.h b/Arrangement_on_surface_2/include/CGAL/Surface_sweep_2/Arr_overlay_traits_2.h
index 48815d767f6..3243890c1be 100644
--- a/Arrangement_on_surface_2/include/CGAL/Surface_sweep_2/Arr_overlay_traits_2.h
+++ b/Arrangement_on_surface_2/include/CGAL/Surface_sweep_2/Arr_overlay_traits_2.h
@@ -133,6 +133,8 @@ public:
class Ex_x_monotone_curve_2 {
public:
typedef Base_x_monotone_curve_2 Base;
+ typedef Halfedge_handle_red HH_red;
+ typedef Halfedge_handle_blue HH_blue;
protected:
Base m_base_xcv; // The base curve.
diff --git a/BGL/doc/BGL/Concepts/FaceGraph.h b/BGL/doc/BGL/Concepts/FaceGraph.h
index 08501700261..40346595ac6 100644
--- a/BGL/doc/BGL/Concepts/FaceGraph.h
+++ b/BGL/doc/BGL/Concepts/FaceGraph.h
@@ -25,7 +25,7 @@ A face descriptor must be `DefaultConstructible`, `Assignable`, `EqualityCompara
\sa \link PkgBGLConcepts Graph Concepts \endlink
*/
class FaceGraph {
- /// Returns a special `boost::graph_traits::face_descriptor` object which
+ /// Returns a special `boost::graph_traits::%face_descriptor` object which
/// does not refer to any face of graph object which type is `FaceGraph`.
static boost::graph_traits::halfedge_descriptor null_face();
};
diff --git a/BGL/doc/BGL/Concepts/HalfedgeGraph.h b/BGL/doc/BGL/Concepts/HalfedgeGraph.h
index b69cbb20447..3769db82e16 100644
--- a/BGL/doc/BGL/Concepts/HalfedgeGraph.h
+++ b/BGL/doc/BGL/Concepts/HalfedgeGraph.h
@@ -44,7 +44,7 @@ A model of `HalfedgeGraph` must have the interior property `vertex_point` attach
\sa \link PkgBGLConcepts Graph Concepts \endlink
*/
class HalfedgeGraph {
- /// Returns a special `boost::graph_traits::halfedge_descriptor` object which
+ /// Returns a special `boost::graph_traits::%halfedge_descriptor` object which
/// does not refer to any halfedge of graph object which type is `HalfedgeGraph`.
static boost::graph_traits::halfedge_descriptor null_halfedge();
};
diff --git a/CGAL_ImageIO/include/CGAL/ImageIO/analyze_impl.h b/CGAL_ImageIO/include/CGAL/ImageIO/analyze_impl.h
index 1154d6b361a..1c326b6fa1f 100644
--- a/CGAL_ImageIO/include/CGAL/ImageIO/analyze_impl.h
+++ b/CGAL_ImageIO/include/CGAL/ImageIO/analyze_impl.h
@@ -33,17 +33,18 @@
/** Magic header for ANALYZE files written in big endian format */
#define ANALYZE_BE_MAGIC "\134\001\000\000"
-#define DT_NONE 0
-#define DT_UNKNOWN 0 /*Unknown data type*/
-#define DT_BINARY 1 /*Binary (1 bit per voxel)*/
-#define DT_UNSIGNED_CHAR 2 /*Unsigned character (8 bits per voxel)*/
-#define DT_SIGNED_SHORT 4 /*Signed short (16 bits per voxel)*/
-#define DT_SIGNED_INT 8 /*Signed integer (32 bits per voxel)*/
-#define DT_FLOAT 16 /*Floating point (32 bits per voxel)*/
-#define DT_COMPLEX 32 /*Complex (64 bits per voxel; 2 floating point numbers) */
-#define DT_DOUBLE 64 /*Double precision (64 bits per voxel)*/
-#define DT_RGB 128 /* */
-#define DT_ALL 255 /* */
+//use prefix CGAL_analyze_impl_ to avoid clashing and breaking dirent.h
+#define CGAL_analyze_impl_DT_NONE 0
+#define CGAL_analyze_impl_DT_UNKNOWN 0 /*Unknown data type*/
+#define CGAL_analyze_impl_DT_BINARY 1 /*Binary (1 bit per voxel)*/
+#define CGAL_analyze_impl_DT_UNSIGNED_CHAR 2 /*Unsigned character (8 bits per voxel)*/
+#define CGAL_analyze_impl_DT_SIGNED_SHORT 4 /*Signed short (16 bits per voxel)*/
+#define CGAL_analyze_impl_DT_SIGNED_INT 8 /*Signed integer (32 bits per voxel)*/
+#define CGAL_analyze_impl_DT_FLOAT 16 /*Floating point (32 bits per voxel)*/
+#define CGAL_analyze_impl_DT_COMPLEX 32 /*Complex (64 bits per voxel; 2 floating point numbers) */
+#define CGAL_analyze_impl_DT_DOUBLE 64 /*Double precision (64 bits per voxel)*/
+#define CGAL_analyze_impl_DT_RGB 128 /* */
+#define CGAL_analyze_impl_DT_ALL 255 /* */
#include
@@ -373,17 +374,17 @@ int _readAnalyzeHeader( _image* im, const char* name,
switch(analyzeHeader->dime.datatype)
{
- case DT_BINARY:
- case DT_UNSIGNED_CHAR:
- case DT_SIGNED_SHORT:
- case DT_SIGNED_INT:
- case DT_FLOAT:
- case DT_COMPLEX:
- case DT_DOUBLE:
+ case CGAL_analyze_impl_DT_BINARY:
+ case CGAL_analyze_impl_DT_UNSIGNED_CHAR:
+ case CGAL_analyze_impl_DT_SIGNED_SHORT:
+ case CGAL_analyze_impl_DT_SIGNED_INT:
+ case CGAL_analyze_impl_DT_FLOAT:
+ case CGAL_analyze_impl_DT_COMPLEX:
+ case CGAL_analyze_impl_DT_DOUBLE:
im->vdim = 1;
break ;
- case DT_RGB:
+ case CGAL_analyze_impl_DT_RGB:
im->vdim = 3;
break ;
@@ -396,17 +397,17 @@ int _readAnalyzeHeader( _image* im, const char* name,
switch(analyzeHeader->dime.datatype)
{
- case DT_BINARY:
- case DT_UNSIGNED_CHAR:
- case DT_SIGNED_SHORT:
- case DT_SIGNED_INT:
- case DT_RGB:
+ case CGAL_analyze_impl_DT_BINARY:
+ case CGAL_analyze_impl_DT_UNSIGNED_CHAR:
+ case CGAL_analyze_impl_DT_SIGNED_SHORT:
+ case CGAL_analyze_impl_DT_SIGNED_INT:
+ case CGAL_analyze_impl_DT_RGB:
im->wordKind = WK_FIXED;
break ;
- case DT_FLOAT:
- case DT_COMPLEX:
- case DT_DOUBLE:
+ case CGAL_analyze_impl_DT_FLOAT:
+ case CGAL_analyze_impl_DT_COMPLEX:
+ case CGAL_analyze_impl_DT_DOUBLE:
im->wordKind = WK_FLOAT;
break ;
@@ -419,17 +420,17 @@ int _readAnalyzeHeader( _image* im, const char* name,
switch(analyzeHeader->dime.datatype)
{
- case DT_BINARY:
- case DT_UNSIGNED_CHAR:
- case DT_RGB:
+ case CGAL_analyze_impl_DT_BINARY:
+ case CGAL_analyze_impl_DT_UNSIGNED_CHAR:
+ case CGAL_analyze_impl_DT_RGB:
im->sign = SGN_UNSIGNED;
break ;
- case DT_SIGNED_SHORT:
- case DT_SIGNED_INT:
- case DT_FLOAT:
- case DT_COMPLEX:
- case DT_DOUBLE:
+ case CGAL_analyze_impl_DT_SIGNED_SHORT:
+ case CGAL_analyze_impl_DT_SIGNED_INT:
+ case CGAL_analyze_impl_DT_FLOAT:
+ case CGAL_analyze_impl_DT_COMPLEX:
+ case CGAL_analyze_impl_DT_DOUBLE:
im->sign = SGN_SIGNED;
break ;
@@ -441,7 +442,7 @@ int _readAnalyzeHeader( _image* im, const char* name,
}
im->wdim = analyzeHeader->dime.bitpix;
- if( analyzeHeader->dime.datatype == DT_RGB )
+ if( analyzeHeader->dime.datatype == CGAL_analyze_impl_DT_RGB )
{
im->wdim /= 3 ;
}
@@ -612,10 +613,10 @@ writeAnalyzeHeader( const _image* im )
if( im->wdim == 1 ) {
if ( im->vdim == 1 ) {
- hdr.dime.datatype = DT_UNSIGNED_CHAR ;
+ hdr.dime.datatype = CGAL_analyze_impl_DT_UNSIGNED_CHAR ;
}
else if ( im->vdim == 3 ) {
- hdr.dime.datatype = DT_RGB ;
+ hdr.dime.datatype = CGAL_analyze_impl_DT_RGB ;
}
else {
fprintf( stderr, "%s: unsupported image type\n", proc );
@@ -643,7 +644,7 @@ writeAnalyzeHeader( const _image* im )
if ( imin > *buf ) imin = *buf;
}
if ( imax < 32768 ) {
- hdr.dime.datatype = DT_SIGNED_SHORT ;
+ hdr.dime.datatype = CGAL_analyze_impl_DT_SIGNED_SHORT ;
}
else {
fprintf( stderr, "%s: conversion from unsigned short to short impossible, max=%d\n", proc, imax );
@@ -676,7 +677,7 @@ writeAnalyzeHeader( const _image* im )
if ( imax < *buf ) imax = *buf;
if ( imin > *buf ) imin = *buf;
}
- hdr.dime.datatype = DT_SIGNED_SHORT ;
+ hdr.dime.datatype = CGAL_analyze_impl_DT_SIGNED_SHORT ;
}
else if( im->wdim == 4 ) {
int *buf = (int*)im->data;
@@ -686,7 +687,7 @@ writeAnalyzeHeader( const _image* im )
if ( imax < *buf ) imax = *buf;
if ( imin > *buf ) imin = *buf;
}
- hdr.dime.datatype = DT_SIGNED_INT ;
+ hdr.dime.datatype = CGAL_analyze_impl_DT_SIGNED_INT ;
}
else {
fprintf( stderr, "%s: unsupported image type\n", proc );
@@ -699,10 +700,10 @@ writeAnalyzeHeader( const _image* im )
return -1;
}
if( im->wdim == 4 ) {
- hdr.dime.datatype = DT_FLOAT ;
+ hdr.dime.datatype = CGAL_analyze_impl_DT_FLOAT ;
}
else if( im->wdim == 8 ) {
- hdr.dime.datatype = DT_DOUBLE ;
+ hdr.dime.datatype = CGAL_analyze_impl_DT_DOUBLE ;
}
else {
fprintf( stderr, "%s: unsupported image type\n", proc );
diff --git a/Classification/doc/Classification/Classification.txt b/Classification/doc/Classification/Classification.txt
index 56bf40b6a77..c1a578a8420 100644
--- a/Classification/doc/Classification/Classification.txt
+++ b/Classification/doc/Classification/Classification.txt
@@ -8,10 +8,6 @@ namespace CGAL {
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.
-\note This component requires C++11 and depends on the Boost libraries
-[Serialization](https://www.boost.org/libs/serialization) and
-[IO Streams](https://www.boost.org/libs/iostreams) (compiled with the GZIP dependency).
-
\section Classification_Organization Package Organization
%Classification of data sets is achieved as follows (see Figure \cgalFigureRef{Classification_organization_fig}):
@@ -35,7 +31,9 @@ Currently, \cgal provides data structures to handle classification of point sets
\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).
+A label represents how an item should be classified, for example: _vegetation_, _building_, _road_, etc. In \cgal, a label has a name, an index (in a label set), a standard index (for example, the index of the label in the ASPRS standard) and a color. It is simply identified by a [Label_handle](@ref CGAL::Classification::Label_handle). Note that names, standard indices and colors are not used for identification: two labels in the same set can have the same name, standard index and color but not the same handle.
+
+If labels are initialized with their names only, standard indices and colors can be deduced in some cases (see [Label_set::add()](@ref CGAL::Classification::Label_set::add)).
The following code snippet shows how to add labels to the classification object:
@@ -231,6 +229,14 @@ An [example](\ref Classification_example_ethz_random_forest) shows how to
use this classifier. For more details about the algorithm, please refer
to README provided in the [ETH Zurich's code archive](https://www.ethz.ch/content/dam/ethz/special-interest/baug/igp/photogrammetry-remote-sensing-dam/documents/sourcecode-and-datasets/Random%20Forest/rforest.zip).
+\subsubsection Classification_ETHZ_random_forest_deprecated Deprecated IO
+
+The IO functions of this classifier were changed in \cgal
+5.2. Configurations generated from previous versions are not valid
+anymore and should be converted first as shown in the following example:
+
+\cgalExample{Classification/example_deprecated_conversion.cpp}
+
\subsection Classification_OpenCV_random_forest OpenCV Random Forest
The second classifier is [OpenCV::Random_forest_classifier](@ref CGAL::Classification::OpenCV::Random_forest_classifier).
diff --git a/Classification/doc/Classification/PackageDescription.txt b/Classification/doc/Classification/PackageDescription.txt
index 3d78a28993a..16c1b565b5e 100644
--- a/Classification/doc/Classification/PackageDescription.txt
+++ b/Classification/doc/Classification/PackageDescription.txt
@@ -77,8 +77,7 @@ Data structures specialized to classify clusters.
\cgalPkgShortInfoBegin
\cgalPkgSince{4.12}
-\cgalPkgDependsOn{\ref PkgSolverInterface, \ref PkgSpatialSearchingD, [Boost Serialization](https://www.boost.org/libs/serialization) and
-[Boost IO Streams](https://www.boost.org/libs/iostreams)}
+\cgalPkgDependsOn{\ref PkgSolverInterface, \ref PkgSpatialSearchingD}
\cgalPkgBib{cgal:lm-clscm-12}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{Operations on Polyhedra,polyhedron_3.zip}
diff --git a/Classification/doc/Classification/examples.txt b/Classification/doc/Classification/examples.txt
index 90fde632cce..e456d0bea17 100644
--- a/Classification/doc/Classification/examples.txt
+++ b/Classification/doc/Classification/examples.txt
@@ -3,6 +3,7 @@
\example Classification/example_feature.cpp
\example Classification/example_generation_and_training.cpp
\example Classification/example_ethz_random_forest.cpp
+\example Classification/example_deprecated_conversion.cpp
\example Classification/example_opencv_random_forest.cpp
\example Classification/example_tensorflow_neural_network.cpp
\example Classification/example_mesh_classification.cpp
diff --git a/Classification/examples/Classification/CMakeLists.txt b/Classification/examples/Classification/CMakeLists.txt
index 55211fcf0e3..7eaf0379d84 100644
--- a/Classification/examples/Classification/CMakeLists.txt
+++ b/Classification/examples/Classification/CMakeLists.txt
@@ -69,6 +69,7 @@ create_single_source_cgal_program( "example_generation_and_training.cpp" )
create_single_source_cgal_program( "example_mesh_classification.cpp" )
create_single_source_cgal_program( "example_cluster_classification.cpp" )
create_single_source_cgal_program( "gis_tutorial_example.cpp" )
+create_single_source_cgal_program( "example_deprecated_conversion.cpp" )
if (TARGET CGAL::OpenCV_support)
create_single_source_cgal_program( "example_opencv_random_forest.cpp" )
@@ -89,7 +90,8 @@ foreach(target
example_cluster_classification
example_opencv_random_forest
example_tensorflow_neural_network
- gis_tutorial_example)
+ gis_tutorial_example
+ example_deprecated_conversion)
if(TARGET ${target})
target_link_libraries(${target} PUBLIC
CGAL::Eigen_support
diff --git a/Classification/examples/Classification/data/b9_clusters_config.bin b/Classification/examples/Classification/data/b9_clusters_config.bin
new file mode 100644
index 00000000000..e578223b840
Binary files /dev/null and b/Classification/examples/Classification/data/b9_clusters_config.bin differ
diff --git a/Classification/examples/Classification/data/b9_clusters_config.gz b/Classification/examples/Classification/data/b9_clusters_config.gz
deleted file mode 100644
index 8d1303c1ad0..00000000000
Binary files a/Classification/examples/Classification/data/b9_clusters_config.gz and /dev/null differ
diff --git a/Classification/examples/Classification/data/b9_mesh_config.bin b/Classification/examples/Classification/data/b9_mesh_config.bin
new file mode 100644
index 00000000000..f09011c4d1e
Binary files /dev/null and b/Classification/examples/Classification/data/b9_mesh_config.bin differ
diff --git a/Classification/examples/Classification/data/b9_mesh_config.gz b/Classification/examples/Classification/data/b9_mesh_config.gz
deleted file mode 100644
index 07af3e3496b..00000000000
Binary files a/Classification/examples/Classification/data/b9_mesh_config.gz and /dev/null differ
diff --git a/Classification/examples/Classification/example_classification.cpp b/Classification/examples/Classification/example_classification.cpp
index 9d820d37d92..b20a7860d69 100644
--- a/Classification/examples/Classification/example_classification.cpp
+++ b/Classification/examples/Classification/example_classification.cpp
@@ -13,11 +13,10 @@
#include
#include
#include
+#include
#include
-typedef CGAL::Parallel_if_available_tag Concurrency_tag;
-
typedef CGAL::Simple_cartesian Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
@@ -84,9 +83,7 @@ int main (int argc, char** argv)
std::cerr << "Computing features" << std::endl;
Feature_set features;
-#ifdef CGAL_LINKED_WITH_TBB
- features.begin_parallel_additions();
-#endif
+ features.begin_parallel_additions(); // No effect in sequential mode
Feature_handle distance_to_plane = features.add (pts, Pmap(), eigen);
Feature_handle dispersion = features.add (pts, Pmap(), grid,
@@ -94,9 +91,7 @@ int main (int argc, char** argv)
Feature_handle elevation = features.add (pts, Pmap(), grid,
radius_dtm);
-#ifdef CGAL_LINKED_WITH_TBB
- features.end_parallel_additions();
-#endif
+ features.end_parallel_additions(); // No effect in sequential mode
//! [Features]
///////////////////////////////////////////////////////////////////
@@ -105,9 +100,15 @@ int main (int argc, char** argv)
//! [Labels]
Label_set labels;
+
+ // Init name only
Label_handle ground = labels.add ("ground");
- Label_handle vegetation = labels.add ("vegetation");
- Label_handle roof = labels.add ("roof");
+
+ // Init name and color
+ Label_handle vegetation = labels.add ("vegetation", CGAL::Color(0,255,0));
+
+ // Init name, Color and standard index (here, ASPRS building index)
+ Label_handle roof = labels.add ("roof", CGAL::Color (255, 0, 0), 6);
//! [Labels]
///////////////////////////////////////////////////////////////////
@@ -146,7 +147,7 @@ int main (int argc, char** argv)
CGAL::Real_timer t;
t.start();
- Classification::classify (pts, labels, classifier, label_indices);
+ Classification::classify (pts, labels, classifier, label_indices);
t.stop();
std::cerr << "Raw classification performed in " << t.time() << " second(s)" << std::endl;
t.reset();
@@ -156,7 +157,7 @@ int main (int argc, char** argv)
///////////////////////////////////////////////////////////////////
//! [Smoothing]
t.start();
- Classification::classify_with_local_smoothing
+ Classification::classify_with_local_smoothing
(pts, Pmap(), labels, classifier,
neighborhood.sphere_neighbor_query(radius_neighbors),
label_indices);
@@ -169,7 +170,7 @@ int main (int argc, char** argv)
///////////////////////////////////////////////////////////////////
//! [Graph_cut]
t.start();
- Classification::classify_with_graphcut
+ Classification::classify_with_graphcut
(pts, Pmap(), labels, classifier,
neighborhood.k_neighbor_query(12),
0.2f, 4, label_indices);
@@ -180,36 +181,43 @@ int main (int argc, char** argv)
// 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;
+ std::vector red, green, blue;
+ red.reserve(pts.size());
+ green.reserve(pts.size());
+ blue.reserve(pts.size());
for (std::size_t i = 0; i < pts.size(); ++ i)
{
- f << pts[i] << " ";
-
Label_handle label = labels[std::size_t(label_indices[i])];
+ unsigned r = 0, g = 0, b = 0;
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;
+ r = 245; g = 180; b = 0;
}
+ else if (label == vegetation)
+ {
+ r = 0; g = 255; b = 27;
+ }
+ else if (label == roof)
+ {
+ r = 255; g = 0; b = 170;
+ }
+ red.push_back(r);
+ green.push_back(g);
+ blue.push_back(b);
}
+ std::ofstream f ("classification.ply");
+
+ CGAL::write_ply_points_with_properties
+ (f, CGAL::make_range (boost::counting_iterator(0),
+ boost::counting_iterator(pts.size())),
+ CGAL::make_ply_point_writer (CGAL::make_property_map(pts)),
+ std::make_pair(CGAL::make_property_map(red), CGAL::PLY_property("red")),
+ std::make_pair(CGAL::make_property_map(green), CGAL::PLY_property("green")),
+ std::make_pair(CGAL::make_property_map(blue), CGAL::PLY_property("blue")));
+
+
std::cerr << "All done" << std::endl;
return EXIT_SUCCESS;
}
diff --git a/Classification/examples/Classification/example_cluster_classification.cpp b/Classification/examples/Classification/example_cluster_classification.cpp
index 3c4b08f60e6..1573a0fc1ad 100644
--- a/Classification/examples/Classification/example_cluster_classification.cpp
+++ b/Classification/examples/Classification/example_cluster_classification.cpp
@@ -18,8 +18,6 @@
#include
#include
-typedef CGAL::Parallel_if_available_tag Concurrency_tag;
-
typedef CGAL::Simple_cartesian Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
@@ -37,6 +35,7 @@ typedef CGAL::Shape_detection::Point_set::Least_squares_plane_fit_region Region_growing;
namespace Classification = CGAL::Classification;
+namespace Feature = CGAL::Classification::Feature;
typedef Classification::Label_handle Label_handle;
typedef Classification::Feature_handle Feature_handle;
@@ -50,7 +49,7 @@ typedef Classification::Cluster Clu
int main (int argc, char** argv)
{
std::string filename = "data/b9.ply";
- std::string filename_config = "data/b9_clusters_config.gz";
+ std::string filename_config = "data/b9_clusters_config.bin";
if (argc > 1)
filename = argv[1];
@@ -67,7 +66,7 @@ int main (int argc, char** argv)
CGAL::Real_timer t;
t.start();
pts.add_normal_map();
- CGAL::jet_estimate_normals (pts, 12);
+ CGAL::jet_estimate_normals (pts, 12);
t.stop();
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
t.reset();
@@ -151,50 +150,38 @@ int main (int argc, char** argv)
Feature_set features;
-#ifdef CGAL_LINKED_WITH_TBB
- features.begin_parallel_additions();
-#endif
-
// First, compute means of features.
- for (std::size_t i = 0; i < pointwise_features.size(); ++ i)
- features.add (clusters, pointwise_features[i]);
-
-#ifdef CGAL_LINKED_WITH_TBB
- features.end_parallel_additions();
features.begin_parallel_additions();
-#endif
+ for (Feature_handle fh : pointwise_features)
+ features.add (clusters, fh);
+ features.end_parallel_additions();
// Then, compute variances of features (and remaining cluster features).
+ features.begin_parallel_additions();
for (std::size_t i = 0; i < pointwise_features.size(); ++ i)
- features.add (clusters,
- pointwise_features[i], // i^th feature
- features[i]); // mean of i^th feature
+ features.add (clusters,
+ pointwise_features[i], // i^th feature
+ features[i]); // mean of i^th feature
- features.add (clusters);
- features.add (clusters);
+ features.add (clusters);
+ features.add (clusters);
for (std::size_t i = 0; i < 3; ++ i)
- features.add (clusters, eigen, (unsigned int)(i));
+ features.add (clusters, eigen, (unsigned int)(i));
-#ifdef CGAL_LINKED_WITH_TBB
features.end_parallel_additions();
-#endif
//! [Features]
///////////////////////////////////////////////////////////////////
t.stop();
- // Add types.
- Label_set labels;
- Label_handle ground = labels.add ("ground");
- Label_handle vegetation = labels.add ("vegetation");
- Label_handle roof = labels.add ("roof");
+ Label_set labels = { "ground", "vegetation", "roof" };
std::vector label_indices(clusters.size(), -1);
std::cerr << "Using ETHZ Random Forest Classifier" << std::endl;
- Classification::ETHZ_random_forest_classifier classifier (labels, features);
+ Classification::ETHZ::Random_forest_classifier classifier (labels, features);
std::cerr << "Loading configuration" << std::endl;
std::ifstream in_config (filename_config, std::ios_base::in | std::ios_base::binary);
@@ -203,7 +190,7 @@ int main (int argc, char** argv)
std::cerr << "Classifying" << std::endl;
t.reset();
t.start();
- Classification::classify (clusters, labels, classifier, label_indices);
+ Classification::classify (clusters, labels, classifier, label_indices);
t.stop();
std::cerr << "Classification done in " << t.time() << " second(s)" << std::endl;
diff --git a/Classification/examples/Classification/example_deprecated_conversion.cpp b/Classification/examples/Classification/example_deprecated_conversion.cpp
new file mode 100644
index 00000000000..d63080d93af
--- /dev/null
+++ b/Classification/examples/Classification/example_deprecated_conversion.cpp
@@ -0,0 +1,20 @@
+#include
+
+#include
+#include
+
+int main (int argc, char** argv)
+{
+ if (argc != 3)
+ std::cerr << "Usage: " << argv[0] << " input.gz output.bin" << std::endl;
+ else
+ {
+ std::ifstream ifile (argv[1], std::ios_base::binary);
+ std::ofstream ofile (argv[2], std::ios_base::binary);
+
+ CGAL::Classification::ETHZ::Random_forest_classifier::
+ convert_deprecated_configuration_to_new_format(ifile, ofile);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/Classification/examples/Classification/example_ethz_random_forest.cpp b/Classification/examples/Classification/example_ethz_random_forest.cpp
index bfacdd4e047..b0333f58ec4 100644
--- a/Classification/examples/Classification/example_ethz_random_forest.cpp
+++ b/Classification/examples/Classification/example_ethz_random_forest.cpp
@@ -49,18 +49,13 @@ int main (int argc, char** argv)
Imap label_map;
bool lm_found = false;
- boost::tie (label_map, lm_found) = pts.property_map ("label");
+ std::tie (label_map, lm_found) = pts.property_map ("label");
if (!lm_found)
{
std::cerr << "Error: \"label\" property not found in input file." << std::endl;
return EXIT_FAILURE;
}
- std::vector ground_truth;
- ground_truth.reserve (pts.size());
- std::copy (pts.range(label_map).begin(), pts.range(label_map).end(),
- std::back_inserter (ground_truth));
-
Feature_set features;
std::cerr << "Generating features" << std::endl;
@@ -69,25 +64,23 @@ int main (int argc, char** argv)
Feature_generator generator (pts, pts.point_map(),
5); // using 5 scales
-#ifdef CGAL_LINKED_WITH_TBB
features.begin_parallel_additions();
-#endif
-
generator.generate_point_based_features (features);
-
-#ifdef CGAL_LINKED_WITH_TBB
features.end_parallel_additions();
-#endif
t.stop();
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
- // Add types
+ // Add labels
Label_set labels;
Label_handle ground = labels.add ("ground");
Label_handle vegetation = labels.add ("vegetation");
Label_handle roof = labels.add ("roof");
+ // Check if ground truth is valid for this label set
+ if (!labels.is_valid_ground_truth (pts.range(label_map), true))
+ return EXIT_FAILURE;
+
std::vector label_indices(pts.size(), -1);
std::cerr << "Using ETHZ Random Forest Classifier" << std::endl;
@@ -96,13 +89,13 @@ int main (int argc, char** argv)
std::cerr << "Training" << std::endl;
t.reset();
t.start();
- classifier.train (ground_truth);
+ classifier.train (pts.range(label_map));
t.stop();
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
t.reset();
t.start();
- Classification::classify_with_graphcut
+ Classification::classify_with_graphcut
(pts, pts.point_map(), labels, classifier,
generator.neighborhood().k_neighbor_query(12),
0.2f, 1, label_indices);
@@ -111,15 +104,15 @@ int main (int argc, char** argv)
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);
+ Classification::Evaluation evaluation (labels, pts.range(label_map), label_indices);
- for (std::size_t i = 0; i < labels.size(); ++ i)
+ for (Label_handle l : labels)
{
- 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 << " * " << l->name() << ": "
+ << evaluation.precision(l) << " ; "
+ << evaluation.recall(l) << " ; "
+ << evaluation.f1_score(l) << " ; "
+ << evaluation.intersection_over_union(l) << std::endl;
}
std::cerr << "Accuracy = " << evaluation.accuracy() << std::endl
@@ -136,21 +129,16 @@ int main (int argc, char** argv)
label_map[i] = label_indices[i]; // update label map with computed classification
Label_handle label = labels[label_indices[i]];
-
- if (label == ground)
- {
- red[i] = 245; green[i] = 180; blue[i] = 0;
- }
- else if (label == vegetation)
- {
- red[i] = 0; green[i] = 255; blue[i] = 27;
- }
- else if (label == roof)
- {
- red[i] = 255; green[i] = 0; blue[i] = 170;
- }
+ const CGAL::Color& color = label->color();
+ red[i] = color.red();
+ green[i] = color.green();
+ blue[i] = color.blue();
}
+ // Save configuration for later use
+ std::ofstream fconfig ("ethz_random_forest.bin", std::ios_base::binary);
+ classifier.save_configuration(fconfig);
+
// Write result
std::ofstream f ("classification.ply");
f.precision(18);
diff --git a/Classification/examples/Classification/example_feature.cpp b/Classification/examples/Classification/example_feature.cpp
index 4f77c18d941..68128ba08e8 100644
--- a/Classification/examples/Classification/example_feature.cpp
+++ b/Classification/examples/Classification/example_feature.cpp
@@ -115,7 +115,7 @@ int main (int argc, char** argv)
std::cerr << "Classifying" << std::endl;
std::vector label_indices(pts.size(), -1);
- Classification::classify_with_graphcut
+ Classification::classify_with_graphcut
(pts, Pmap(), labels, classifier,
neighborhood.k_neighbor_query(12),
0.5, 1, label_indices);
diff --git a/Classification/examples/Classification/example_generation_and_training.cpp b/Classification/examples/Classification/example_generation_and_training.cpp
index 2a34076ef77..cb911262b32 100644
--- a/Classification/examples/Classification/example_generation_and_training.cpp
+++ b/Classification/examples/Classification/example_generation_and_training.cpp
@@ -46,18 +46,13 @@ int main (int argc, char** argv)
Imap label_map;
bool lm_found = false;
- boost::tie (label_map, lm_found) = pts.property_map ("label");
+ std::tie (label_map, lm_found) = pts.property_map ("label");
if (!lm_found)
{
std::cerr << "Error: \"label\" property not found in input file." << std::endl;
return EXIT_FAILURE;
}
- std::vector ground_truth;
- ground_truth.reserve (pts.size());
- std::copy (pts.range(label_map).begin(), pts.range(label_map).end(),
- std::back_inserter (ground_truth));
-
std::cerr << "Generating features" << std::endl;
CGAL::Real_timer t;
t.start();
@@ -70,15 +65,9 @@ int main (int argc, char** argv)
std::size_t number_of_scales = 5;
Feature_generator generator (pts, pts.point_map(), number_of_scales);
-#ifdef CGAL_LINKED_WITH_TBB
features.begin_parallel_additions();
-#endif
-
generator.generate_point_based_features (features);
-
-#ifdef CGAL_LINKED_WITH_TBB
features.end_parallel_additions();
-#endif
//! [Generator]
///////////////////////////////////////////////////////////////////
@@ -86,25 +75,21 @@ int main (int argc, char** argv)
t.stop();
std::cerr << features.size() << " feature(s) generated 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_set labels = { "ground", "vegetation", "roof" };
Classifier classifier (labels, features);
std::cerr << "Training" << std::endl;
t.reset();
t.start();
- classifier.train (ground_truth, 800);
+ classifier.train (pts.range(label_map), 800);
t.stop();
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
t.reset();
t.start();
std::vector label_indices(pts.size(), -1);
- Classification::classify_with_graphcut
+ Classification::classify_with_graphcut
(pts, pts.point_map(), labels, classifier,
generator.neighborhood().k_neighbor_query(12),
0.2f, 10, label_indices);
@@ -112,15 +97,15 @@ int main (int argc, char** argv)
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);
+ Classification::Evaluation evaluation (labels, pts.range(label_map), label_indices);
- for (std::size_t i = 0; i < labels.size(); ++ i)
+ for (Label_handle l : labels)
{
- 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 << " * " << l->name() << ": "
+ << evaluation.precision(l) << " ; "
+ << evaluation.recall(l) << " ; "
+ << evaluation.f1_score(l) << " ; "
+ << evaluation.intersection_over_union(l) << std::endl;
}
std::cerr << "Accuracy = " << evaluation.accuracy() << std::endl
diff --git a/Classification/examples/Classification/example_mesh_classification.cpp b/Classification/examples/Classification/example_mesh_classification.cpp
index 465b3a4bfcd..1d081e869fa 100644
--- a/Classification/examples/Classification/example_mesh_classification.cpp
+++ b/Classification/examples/Classification/example_mesh_classification.cpp
@@ -32,7 +32,7 @@ typedef Classification::Mesh_feature_generator
int main (int argc, char** argv)
{
std::string filename = "data/b9_mesh.off";
- std::string filename_config = "data/b9_mesh_config.gz";
+ std::string filename_config = "data/b9_mesh_config.bin";
if (argc > 1)
filename = argv[1];
@@ -59,16 +59,10 @@ int main (int argc, char** argv)
std::size_t number_of_scales = 5;
Feature_generator generator (mesh, face_point_map, number_of_scales);
-#ifdef CGAL_LINKED_WITH_TBB
features.begin_parallel_additions();
-#endif
-
generator.generate_point_based_features (features); // Features that consider the mesh as a point set
generator.generate_face_based_features (features); // Features computed directly on mesh faces
-
-#ifdef CGAL_LINKED_WITH_TBB
features.end_parallel_additions();
-#endif
//! [Generator]
///////////////////////////////////////////////////////////////////
@@ -76,11 +70,7 @@ int main (int argc, char** argv)
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_set labels = { "ground", "vegetation", "roof" };
std::vector label_indices(mesh.number_of_faces(), -1);
@@ -94,7 +84,7 @@ int main (int argc, char** argv)
std::cerr << "Classifying with graphcut" << std::endl;
t.reset();
t.start();
- Classification::classify_with_graphcut
+ Classification::classify_with_graphcut
(mesh.faces(), Face_with_bbox_map(&mesh), labels, classifier,
generator.neighborhood().n_ring_neighbor_query(2),
0.2f, 1, label_indices);
diff --git a/Classification/examples/Classification/example_opencv_random_forest.cpp b/Classification/examples/Classification/example_opencv_random_forest.cpp
index e0cd90e51f0..2491e05d0a7 100644
--- a/Classification/examples/Classification/example_opencv_random_forest.cpp
+++ b/Classification/examples/Classification/example_opencv_random_forest.cpp
@@ -49,18 +49,13 @@ int main (int argc, char** argv)
Imap label_map;
bool lm_found = false;
- boost::tie (label_map, lm_found) = pts.property_map ("label");
+ std::tie (label_map, lm_found) = pts.property_map ("label");
if (!lm_found)
{
std::cerr << "Error: \"label\" property not found in input file." << std::endl;
return EXIT_FAILURE;
}
- std::vector ground_truth;
- ground_truth.reserve (pts.size());
- std::copy (pts.range(label_map).begin(), pts.range(label_map).end(),
- std::back_inserter (ground_truth));
-
Feature_set features;
std::cerr << "Generating features" << std::endl;
@@ -69,19 +64,14 @@ int main (int argc, char** argv)
Feature_generator generator (pts, pts.point_map(),
5); // using 5 scales
-#ifdef CGAL_LINKED_WITH_TBB
features.begin_parallel_additions();
-#endif
-
generator.generate_point_based_features (features);
-#ifdef CGAL_LINKED_WITH_TBB
features.end_parallel_additions();
-#endif
t.stop();
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
- // Add types
+ // Add labels
Label_set labels;
Label_handle ground = labels.add ("ground");
Label_handle vegetation = labels.add ("vegetation");
@@ -95,13 +85,13 @@ int main (int argc, char** argv)
std::cerr << "Training" << std::endl;
t.reset();
t.start();
- classifier.train (ground_truth);
+ classifier.train (pts.range(label_map));
t.stop();
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
t.reset();
t.start();
- Classification::classify_with_graphcut
+ Classification::classify_with_graphcut
(pts, pts.point_map(), labels, classifier,
generator.neighborhood().k_neighbor_query(12),
0.2f, 1, label_indices);
@@ -110,15 +100,15 @@ int main (int argc, char** argv)
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);
+ Classification::Evaluation evaluation (labels, pts.range(label_map), label_indices);
- for (std::size_t i = 0; i < labels.size(); ++ i)
+ for (Label_handle l : labels)
{
- 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 << " * " << l->name() << ": "
+ << evaluation.precision(l) << " ; "
+ << evaluation.recall(l) << " ; "
+ << evaluation.f1_score(l) << " ; "
+ << evaluation.intersection_over_union(l) << std::endl;
}
std::cerr << "Accuracy = " << evaluation.accuracy() << std::endl
@@ -135,19 +125,10 @@ int main (int argc, char** argv)
label_map[i] = label_indices[i]; // update label map with computed classification
Label_handle label = labels[label_indices[i]];
-
- if (label == ground)
- {
- red[i] = 245; green[i] = 180; blue[i] = 0;
- }
- else if (label == vegetation)
- {
- red[i] = 0; green[i] = 255; blue[i] = 27;
- }
- else if (label == roof)
- {
- red[i] = 255; green[i] = 0; blue[i] = 170;
- }
+ const CGAL::Color& color = label->color();
+ red[i] = color.red();
+ green[i] = color.green();
+ blue[i] = color.blue();
}
// Write result
diff --git a/Classification/examples/Classification/example_tensorflow_neural_network.cpp b/Classification/examples/Classification/example_tensorflow_neural_network.cpp
index 01fd4f1cac2..6a454360285 100644
--- a/Classification/examples/Classification/example_tensorflow_neural_network.cpp
+++ b/Classification/examples/Classification/example_tensorflow_neural_network.cpp
@@ -49,18 +49,13 @@ int main (int argc, char** argv)
Imap label_map;
bool lm_found = false;
- boost::tie (label_map, lm_found) = pts.property_map ("label");
+ std::tie (label_map, lm_found) = pts.property_map ("label");
if (!lm_found)
{
std::cerr << "Error: \"label\" property not found in input file." << std::endl;
return EXIT_FAILURE;
}
- std::vector ground_truth;
- ground_truth.reserve (pts.size());
- std::copy (pts.range(label_map).begin(), pts.range(label_map).end(),
- std::back_inserter (ground_truth));
-
Feature_set features;
std::cerr << "Generating features" << std::endl;
@@ -69,20 +64,14 @@ int main (int argc, char** argv)
Feature_generator generator (pts, pts.point_map(),
5); // using 5 scales
-#ifdef CGAL_LINKED_WITH_TBB
features.begin_parallel_additions();
-#endif
-
generator.generate_point_based_features (features);
-
-#ifdef CGAL_LINKED_WITH_TBB
features.end_parallel_additions();
-#endif
t.stop();
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
- // Add types
+ // Add labels
Label_set labels;
Label_handle ground = labels.add ("ground");
Label_handle vegetation = labels.add ("vegetation");
@@ -96,7 +85,7 @@ int main (int argc, char** argv)
std::cerr << "Training" << std::endl;
t.reset();
t.start();
- classifier.train (ground_truth,
+ classifier.train (pts.range(ground_truth),
true, // restart from scratch
100); // 100 iterations
t.stop();
@@ -113,15 +102,15 @@ int main (int argc, char** argv)
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);
+ Classification::Evaluation evaluation (labels, pts.range(ground_truth), label_indices);
- for (std::size_t i = 0; i < labels.size(); ++ i)
+ for (Label_handle l : labels)
{
- 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 << " * " << l->name() << ": "
+ << evaluation.precision(l) << " ; "
+ << evaluation.recall(l) << " ; "
+ << evaluation.f1_score(l) << " ; "
+ << evaluation.intersection_over_union(l) << std::endl;
}
std::cerr << "Accuracy = " << evaluation.accuracy() << std::endl
@@ -138,19 +127,10 @@ int main (int argc, char** argv)
label_map[i] = label_indices[i]; // update label map with computed classification
Label_handle label = labels[label_indices[i]];
-
- if (label == ground)
- {
- red[i] = 245; green[i] = 180; blue[i] = 0;
- }
- else if (label == vegetation)
- {
- red[i] = 0; green[i] = 255; blue[i] = 27;
- }
- else if (label == roof)
- {
- red[i] = 255; green[i] = 0; blue[i] = 170;
- }
+ const CGAL::Color& color = label->color();
+ red[i] = color.red();
+ green[i] = color.green();
+ blue[i] = color.blue();
}
// Write result
diff --git a/Classification/include/CGAL/Classification/Cluster.h b/Classification/include/CGAL/Classification/Cluster.h
index 4b8b002c3b5..a0d0f209602 100644
--- a/Classification/include/CGAL/Classification/Cluster.h
+++ b/Classification/include/CGAL/Classification/Cluster.h
@@ -46,7 +46,7 @@ class Cluster
{
public:
- typedef typename ItemMap::value_type Item;
+ using Item = typename boost::property_traits::value_type;
/// \cond SKIP_IN_MANUAL
struct Neighbor_query
@@ -64,9 +64,9 @@ public:
class Point_idx_to_point_unary_function
{
public:
- typedef std::size_t argument_type;
- typedef typename ItemMap::reference result_type;
- typedef boost::readable_property_map_tag category;
+ using argument_type = std::size_t;
+ using result_type = typename boost::property_traits::reference;
+ using category = boost::readable_property_map_tag;
const ItemRange* m_range;
ItemMap m_item_map;
@@ -105,9 +105,9 @@ public:
\param item_map property map to access the input items.
*/
Cluster (const ItemRange& range, ItemMap item_map)
- : neighbors (new std::vector())
+ : neighbors (std::make_shared >())
, m_range (&range), m_item_map (item_map)
- , m_inliers (new std::vector())
+ , m_inliers (std::make_shared >())
, m_training(-1), m_label(-1)
{ }
diff --git a/Classification/include/CGAL/Classification/ETHZ/Random_forest_classifier.h b/Classification/include/CGAL/Classification/ETHZ/Random_forest_classifier.h
index e2f831b5aca..6b22c41ad00 100644
--- a/Classification/include/CGAL/Classification/ETHZ/Random_forest_classifier.h
+++ b/Classification/include/CGAL/Classification/ETHZ/Random_forest_classifier.h
@@ -73,7 +73,7 @@ class Random_forest_classifier
const Label_set& m_labels;
const Feature_set& m_features;
- Forest* m_rfc;
+ std::shared_ptr m_rfc;
public:
@@ -81,16 +81,16 @@ public:
/// @{
/*!
- \brief Instantiates the classifier using the sets of `labels` and `features`.
+ \brief instantiates the classifier using the sets of `labels` and `features`.
*/
Random_forest_classifier (const Label_set& labels,
const Feature_set& features)
- : m_labels (labels), m_features (features), m_rfc (nullptr)
+ : m_labels (labels), m_features (features)
{ }
/*!
- \brief Copies the `other` classifier's configuration using another
+ \brief copies the `other` classifier's configuration using another
set of `features`.
This constructor can be used to apply a trained random forest to
@@ -105,7 +105,7 @@ public:
defined(CGAL_LINKED_WITH_BOOST_SERIALIZATION))
Random_forest_classifier (const Random_forest_classifier& other,
const Feature_set& features)
- : m_labels (other.m_labels), m_features (features), m_rfc (nullptr)
+ : m_labels (other.m_labels), m_features (features)
{
std::stringstream stream;
other.save_configuration(stream);
@@ -113,14 +113,6 @@ public:
}
#endif
- /// \cond SKIP_IN_MANUAL
- ~Random_forest_classifier ()
- {
- if (m_rfc != nullptr)
- delete m_rfc;
- }
- /// \endcond
-
/// @}
/// \name Training
@@ -138,7 +130,7 @@ public:
/// \endcond
/*!
- \brief Runs the training algorithm.
+ \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
@@ -179,6 +171,8 @@ public:
std::size_t num_trees = 25,
std::size_t max_depth = 20)
{
+ CGAL_precondition (m_labels.is_valid_ground_truth (ground_truth));
+
CGAL::internal::liblearning::RandomForest::ForestParams params;
params.n_trees = num_trees;
params.max_depth = max_depth;
@@ -186,32 +180,40 @@ public:
std::vector gt;
std::vector ft;
- std::size_t idx = 0;
- for (const auto& ig : ground_truth)
+#ifdef CGAL_CLASSIFICATION_VERBOSE
+ std::vector count (m_labels.size(), 0);
+#endif
+
+ std::size_t i = 0;
+ for (const auto& gt_value : ground_truth)
{
- int g = int(ig);
+ int g = int(gt_value);
if (g != -1)
{
for (std::size_t f = 0; f < m_features.size(); ++ f)
- ft.push_back(m_features[f]->value(idx));
+ ft.push_back(m_features[f]->value(i));
gt.push_back(g);
+#ifdef CGAL_CLASSIFICATION_VERBOSE
+ count[std::size_t(g)] ++;
+#endif
}
- ++ idx;
+ ++ i;
}
- CGAL_CLASSIFICATION_CERR << "Using " << gt.size() << " inliers" << std::endl;
+ CGAL_CLASSIFICATION_CERR << "Using " << gt.size() << " inliers:" << std::endl;
+#ifdef CGAL_CLASSIFICATION_VERBOSE
+ for (std::size_t i = 0; i < m_labels.size(); ++ i)
+ std::cerr << " * " << m_labels[i]->name() << ": " << count[i] << " inlier(s)" << std::endl;
+#endif
CGAL::internal::liblearning::DataView2D label_vector (&(gt[0]), gt.size(), 1);
CGAL::internal::liblearning::DataView2D feature_vector(&(ft[0]), gt.size(), ft.size() / gt.size());
- if (m_rfc != nullptr && reset_trees)
- {
- delete m_rfc;
- m_rfc = nullptr;
- }
+ if (m_rfc && reset_trees)
+ m_rfc.reset();
- if (m_rfc == nullptr)
- m_rfc = new Forest (params);
+ if (!m_rfc)
+ m_rfc = std::make_shared (params);
CGAL::internal::liblearning::RandomForest::AxisAlignedRandomSplitGenerator generator;
@@ -245,7 +247,7 @@ public:
/// @{
/*!
- \brief Computes, for each feature, how many nodes in the forest
+ \brief computes, for each feature, how many nodes in the forest
uses it as a split criterion.
Each tree of the random forest recursively splits the training
@@ -280,35 +282,37 @@ public:
/// @{
/*!
- \brief Saves the current configuration in the stream `output`.
+ \brief saves the current configuration in the stream `output`.
This allows to easily save and recover a specific classification
configuration.
- The output file is written in an GZIP container that is readable
- by the `load_configuration()` method.
+ The output file is written in a binary format that is readable by
+ the `load_configuration()` method.
*/
#if defined(DOXYGEN_RUNNING) || \
(defined(CGAL_LINKED_WITH_BOOST_IOSTREAMS) && \
defined(CGAL_LINKED_WITH_BOOST_SERIALIZATION))
void save_configuration (std::ostream& output) const
{
- boost::iostreams::filtering_ostream outs;
- outs.push(boost::iostreams::gzip_compressor());
- outs.push(output);
- boost::archive::text_oarchive oas(outs);
- oas << BOOST_SERIALIZATION_NVP(*m_rfc);
+ m_rfc->write(output);
}
#endif
/*!
- \brief Loads a configuration from the stream `input`.
+ \brief loads a configuration from the stream `input`.
- The input file should be a GZIP container written by the
+ The input file should be a binary file written by the
`save_configuration()` method. The feature set of the classifier
should contain the exact same features in the exact same order as
the ones present when the file was generated using
`save_configuration()`.
+
+ \warning If the file you are trying to load was saved using CGAL
+ 5.1 or earlier, you have to convert it first using
+ `convert_deprecated_configuration_to_new_format()` as the exchange
+ format for ETHZ Random Forest changed in CGAL 5.2.
+
*/
#if defined(DOXYGEN_RUNNING) || \
(defined(CGAL_LINKED_WITH_BOOST_IOSTREAMS) && \
@@ -316,9 +320,51 @@ public:
void load_configuration (std::istream& input)
{
CGAL::internal::liblearning::RandomForest::ForestParams params;
- if (m_rfc != nullptr)
- delete m_rfc;
- m_rfc = new Forest (params);
+ m_rfc = std::make_shared (params);
+
+ m_rfc->read(input);
+ }
+#endif
+
+ /// @}
+
+ /// \name Deprecated Input/Output
+ /// @{
+
+ /*!
+ \brief converts a deprecated configuration (in compressed ASCII
+ format) to a new configuration (in binary format).
+
+ The input file should be a GZIP container written by the
+ `save_configuration()` method from CGAL 5.1 and earlier. The
+ output is a valid configuration for CGAL 5.2 and later.
+
+ \note This function depends on the Boost libraries
+ [Serialization](https://www.boost.org/libs/serialization) and
+ [IO Streams](https://www.boost.org/libs/iostreams) (compiled with the GZIP dependency).
+ */
+#if defined(DOXYGEN_RUNNING) || \
+ (defined(CGAL_LINKED_WITH_BOOST_IOSTREAMS) && \
+ defined(CGAL_LINKED_WITH_BOOST_SERIALIZATION))
+ static void convert_deprecated_configuration_to_new_format (std::istream& input, std::ostream& output)
+ {
+ Label_set dummy_labels;
+ Feature_set dummy_features;
+ Random_forest_classifier classifier (dummy_labels, dummy_features);
+ classifier.load_deprecated_configuration(input);
+ classifier.save_configuration(output);
+ }
+#endif
+
+/// @}
+
+ /// \cond SKIP_IN_MANUAL
+#if defined(CGAL_LINKED_WITH_BOOST_IOSTREAMS) && \
+ defined(CGAL_LINKED_WITH_BOOST_SERIALIZATION)
+ void load_deprecated_configuration (std::istream& input)
+ {
+ CGAL::internal::liblearning::RandomForest::ForestParams params;
+ m_rfc = std::make_shared (params);
boost::iostreams::filtering_istream ins;
ins.push(boost::iostreams::gzip_decompressor());
@@ -327,8 +373,8 @@ public:
ias >> BOOST_SERIALIZATION_NVP(*m_rfc);
}
#endif
+ /// \endcond
-/// @}
};
diff --git a/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/common-libraries.hpp b/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/common-libraries.hpp
index 97e13f532ce..e2fdfd42c87 100644
--- a/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/common-libraries.hpp
+++ b/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/common-libraries.hpp
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -109,6 +110,30 @@ struct ForestParams {
ar & BOOST_SERIALIZATION_NVP(sample_reduction);
}
#endif
+
+ void write (std::ostream& os)
+ {
+ I_Binary_write_size_t_into_uinteger32 (os, n_classes);
+ I_Binary_write_size_t_into_uinteger32 (os, n_features);
+ I_Binary_write_size_t_into_uinteger32 (os, n_samples);
+ I_Binary_write_size_t_into_uinteger32 (os, n_in_bag_samples);
+ I_Binary_write_size_t_into_uinteger32 (os, max_depth);
+ I_Binary_write_size_t_into_uinteger32 (os, n_trees);
+ I_Binary_write_size_t_into_uinteger32 (os, min_samples_per_node);
+ I_Binary_write_float32 (os, sample_reduction);
+ }
+
+ void read (std::istream& is)
+ {
+ I_Binary_read_size_t_from_uinteger32 (is, n_classes);
+ I_Binary_read_size_t_from_uinteger32 (is, n_features);
+ I_Binary_read_size_t_from_uinteger32 (is, n_samples);
+ I_Binary_read_size_t_from_uinteger32 (is, n_in_bag_samples);
+ I_Binary_read_size_t_from_uinteger32 (is, max_depth);
+ I_Binary_read_size_t_from_uinteger32 (is, n_trees);
+ I_Binary_read_size_t_from_uinteger32 (is, min_samples_per_node);
+ I_Binary_read_float32 (is, sample_reduction);
+ }
};
struct QuadraticSplitter {
@@ -248,6 +273,18 @@ struct AxisAlignedSplitter {
ar & BOOST_SERIALIZATION_NVP(threshold);
}
#endif
+
+ void write (std::ostream& os)
+ {
+ os.write((char*)(&feature), sizeof(int));
+ os.write((char*)(&threshold), sizeof(FeatureType));
+ }
+
+ void read (std::istream& is)
+ {
+ is.read((char*)(&feature), sizeof(int));
+ is.read((char*)(&threshold), sizeof(FeatureType));
+ }
};
struct AxisAlignedRandomSplitGenerator {
diff --git a/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/forest.hpp b/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/forest.hpp
index 6ffe0e0b719..4611becf711 100644
--- a/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/forest.hpp
+++ b/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/forest.hpp
@@ -31,7 +31,7 @@
#endif
#include
-
+#include
#include
#ifdef CGAL_LINKED_WITH_TBB
@@ -164,10 +164,10 @@ public:
f (seed_start, sample_idxes, trees, samples, labels, params.n_in_bag_samples, split_generator);
#ifndef CGAL_LINKED_WITH_TBB
- CGAL_static_assertion_msg (!(boost::is_convertible::value),
+ CGAL_static_assertion_msg (!(std::is_convertible::value),
"Parallel_tag is enabled but TBB is unavailable.");
#else
- if (boost::is_convertible::value)
+ if (std::is_convertible::value)
{
tbb::parallel_for(tbb::blocked_range(nb_trees, nb_trees + params.n_trees), f);
}
@@ -232,6 +232,28 @@ public:
}
#endif
+ void write (std::ostream& os)
+ {
+ params.write(os);
+
+ I_Binary_write_size_t_into_uinteger32 (os, trees.size());
+ for (std::size_t i_tree = 0; i_tree < trees.size(); ++i_tree)
+ trees[i_tree].write(os);
+ }
+
+ void read (std::istream& is)
+ {
+ params.read(is);
+
+ std::size_t nb_trees;
+ I_Binary_read_size_t_from_uinteger32 (is, nb_trees);
+ for (std::size_t i = 0; i < nb_trees; ++ i)
+ {
+ trees.push_back (new TreeType(¶ms));
+ trees.back().read(is);
+ }
+ }
+
void get_feature_usage (std::vector& count) const
{
for (std::size_t i_tree = 0; i_tree < trees.size(); ++i_tree)
diff --git a/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/node.hpp b/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/node.hpp
index f510119a557..75ecc1037e6 100644
--- a/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/node.hpp
+++ b/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/node.hpp
@@ -22,6 +22,8 @@
#include "../dataview.h"
#include "common-libraries.hpp"
+#include
+
#if defined(CGAL_LINKED_WITH_BOOST_IOSTREAMS) && defined(CGAL_LINKED_WITH_BOOST_SERIALIZATION)
#include
#include
@@ -253,9 +255,46 @@ public:
}
#endif
+ void write (std::ostream& os)
+ {
+ I_Binary_write_bool (os, is_leaf);
+ I_Binary_write_size_t_into_uinteger32 (os, n_samples);
+ I_Binary_write_size_t_into_uinteger32 (os, depth);
+ splitter.write(os);
+
+ for (const float& f : node_dist)
+ I_Binary_write_float32 (os, f);
+
+ if (!is_leaf)
+ {
+ left->write(os);
+ right->write(os);
+ }
+ }
+
+ void read (std::istream& is)
+ {
+ I_Binary_read_bool (is, is_leaf);
+ I_Binary_read_size_t_from_uinteger32 (is, n_samples);
+ I_Binary_read_size_t_from_uinteger32 (is, depth);
+ splitter.read(is);
+
+ node_dist.resize(params->n_classes, 0.0f);
+ for (std::size_t i = 0; i < node_dist.size(); ++ i)
+ I_Binary_read_float32 (is, node_dist[i]);
+
+ if (!is_leaf)
+ {
+ left.reset(new Derived(depth + 1, params));
+ right.reset(new Derived(depth + 1, params));
+ left->read(is);
+ right->read(is);
+ }
+ }
+
void get_feature_usage (std::vector& count) const
{
- if (!is_leaf)
+ if (!is_leaf && splitter.feature != -1)
{
count[std::size_t(splitter.feature)] ++;
left->get_feature_usage(count);
diff --git a/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/tree.hpp b/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/tree.hpp
index c8a07f020d7..ba75c6e2f98 100644
--- a/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/tree.hpp
+++ b/Classification/include/CGAL/Classification/ETHZ/internal/random-forest/tree.hpp
@@ -129,6 +129,17 @@ public:
}
#endif
+ void write (std::ostream& os)
+ {
+ root_node->write(os);
+ }
+
+ void read (std::istream& is)
+ {
+ root_node.reset(new NodeT(0, params));
+ root_node->read(is);
+ }
+
void get_feature_usage (std::vector& count) const
{
root_node->get_feature_usage(count);
diff --git a/Classification/include/CGAL/Classification/Evaluation.h b/Classification/include/CGAL/Classification/Evaluation.h
index 071f6ab8c9f..eeff26f73d6 100644
--- a/Classification/include/CGAL/Classification/Evaluation.h
+++ b/Classification/include/CGAL/Classification/Evaluation.h
@@ -17,9 +17,10 @@
#include
#include
-#include
#include
+#include
+
#include