mirror of https://github.com/CGAL/cgal
Merge remote-tracking branch 'cgal/master' into Surface_sweep_2-Small_unordered_set-GF
This commit is contained in:
commit
1beff9f1d0
|
|
@ -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 "<li><a href=https://cgal.github.io/${PR_NUMBER}/$ROUND/Manual/index.html>Manual for PR ${PR_NUMBER} ($ROUND).</a></li>" >> ./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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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" ]
|
||||
|
|
|
|||
|
|
@ -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<Arr_vertex_base&>(*this).p_inc = inc; }
|
||||
|
||||
/*! Check if the point pointer is nullptr. */
|
||||
bool has_null_point() const { return (p_pt == nullptr); }
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,89 @@
|
|||
|
||||
namespace CGAL {
|
||||
|
||||
template <typename Arr1, typename Arr2, typename Curve>
|
||||
class Indexed_sweep_accessor
|
||||
{
|
||||
const Arr1& arr1;
|
||||
const Arr2& arr2;
|
||||
mutable std::vector<void*> 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<std::size_t>(c.red_halfedge_handle()->target()->inc());
|
||||
// else
|
||||
CGAL_assertion (c.blue_halfedge_handle() != typename Curve::HH_blue());
|
||||
return reinterpret_cast<std::size_t>(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<std::size_t>(c.red_halfedge_handle()->source()->inc());
|
||||
// else
|
||||
CGAL_assertion (c.blue_halfedge_handle() != typename Curve::HH_blue());
|
||||
return reinterpret_cast<std::size_t>(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<void*>(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<void*>(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<GeometryTraitsA_2, TopologyTraitsA>& 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<typename Agt2::Bottom_side_category,
|
||||
Arr_contracted_side_tag>::value)
|
||||
surface_sweep.sweep (xcvs_vec.begin(), xcvs_vec.end());
|
||||
else
|
||||
surface_sweep.indexed_sweep (xcvs_vec,
|
||||
Indexed_sweep_accessor
|
||||
<Arr_a, Arr_b, Ovl_x_monotone_curve_2>
|
||||
(arr1, arr2));
|
||||
xcvs_vec.clear();
|
||||
return;
|
||||
}
|
||||
|
|
@ -215,8 +305,16 @@ overlay(const Arrangement_on_surface_2<GeometryTraitsA_2, TopologyTraitsA>& 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<typename Agt2::Bottom_side_category,
|
||||
Arr_contracted_side_tag>::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
|
||||
<Arr_a, Arr_b, Ovl_x_monotone_curve_2>
|
||||
(arr1, arr2),
|
||||
pts_vec.begin(), pts_vec.end());
|
||||
xcvs_vec.clear();
|
||||
pts_vec.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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<HalfedgeGraph>::face_descriptor` object which
|
||||
/// Returns a special `boost::graph_traits<HalfedgeGraph>::%face_descriptor` object which
|
||||
/// does not refer to any face of graph object which type is `FaceGraph`.
|
||||
static boost::graph_traits<HalfedgeGraph>::halfedge_descriptor null_face();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<HalfedgeGraph>::halfedge_descriptor` object which
|
||||
/// Returns a special `boost::graph_traits<HalfedgeGraph>::%halfedge_descriptor` object which
|
||||
/// does not refer to any halfedge of graph object which type is `HalfedgeGraph`.
|
||||
static boost::graph_traits<HalfedgeGraph>::halfedge_descriptor null_halfedge();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 <cstring>
|
||||
|
||||
|
|
@ -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 );
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -13,11 +13,10 @@
|
|||
#include <CGAL/bounding_box.h>
|
||||
#include <CGAL/tags.h>
|
||||
#include <CGAL/IO/read_ply_points.h>
|
||||
#include <CGAL/IO/write_ply_points.h>
|
||||
|
||||
#include <CGAL/Real_timer.h>
|
||||
|
||||
typedef CGAL::Parallel_if_available_tag Concurrency_tag;
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> 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<Distance_to_plane> (pts, Pmap(), eigen);
|
||||
Feature_handle dispersion = features.add<Dispersion> (pts, Pmap(), grid,
|
||||
|
|
@ -94,9 +91,7 @@ int main (int argc, char** argv)
|
|||
Feature_handle elevation = features.add<Elevation> (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<Concurrency_tag> (pts, labels, classifier, label_indices);
|
||||
Classification::classify<CGAL::Parallel_if_available_tag> (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<Concurrency_tag>
|
||||
Classification::classify_with_local_smoothing<CGAL::Parallel_if_available_tag>
|
||||
(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<Concurrency_tag>
|
||||
Classification::classify_with_graphcut<CGAL::Parallel_if_available_tag>
|
||||
(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<unsigned char> 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<std::size_t>(0),
|
||||
boost::counting_iterator<std::size_t>(pts.size())),
|
||||
CGAL::make_ply_point_writer (CGAL::make_property_map(pts)),
|
||||
std::make_pair(CGAL::make_property_map(red), CGAL::PLY_property<unsigned char>("red")),
|
||||
std::make_pair(CGAL::make_property_map(green), CGAL::PLY_property<unsigned char>("green")),
|
||||
std::make_pair(CGAL::make_property_map(blue), CGAL::PLY_property<unsigned char>("blue")));
|
||||
|
||||
|
||||
std::cerr << "All done" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
#include <CGAL/Shape_detection/Region_growing.h>
|
||||
#include <CGAL/Real_timer.h>
|
||||
|
||||
typedef CGAL::Parallel_if_available_tag Concurrency_tag;
|
||||
|
||||
typedef CGAL::Simple_cartesian<double> 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<Kernel,
|
|||
typedef CGAL::Shape_detection::Region_growing<Point_set, Neighbor_query, Region_type> 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<Point_set, Pmap> 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<Concurrency_tag> (pts, 12);
|
||||
CGAL::jet_estimate_normals<CGAL::Parallel_if_available_tag> (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<Classification::Feature::Cluster_mean_of_feature> (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<Feature::Cluster_mean_of_feature> (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<Classification::Feature::Cluster_variance_of_feature> (clusters,
|
||||
pointwise_features[i], // i^th feature
|
||||
features[i]); // mean of i^th feature
|
||||
features.add<Feature::Cluster_variance_of_feature> (clusters,
|
||||
pointwise_features[i], // i^th feature
|
||||
features[i]); // mean of i^th feature
|
||||
|
||||
features.add<Classification::Feature::Cluster_size> (clusters);
|
||||
features.add<Classification::Feature::Cluster_vertical_extent> (clusters);
|
||||
features.add<Feature::Cluster_size> (clusters);
|
||||
features.add<Feature::Cluster_vertical_extent> (clusters);
|
||||
|
||||
for (std::size_t i = 0; i < 3; ++ i)
|
||||
features.add<Classification::Feature::Eigenvalue> (clusters, eigen, (unsigned int)(i));
|
||||
features.add<Feature::Eigenvalue> (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<int> 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<Concurrency_tag> (clusters, labels, classifier, label_indices);
|
||||
Classification::classify<CGAL::Parallel_if_available_tag> (clusters, labels, classifier, label_indices);
|
||||
t.stop();
|
||||
|
||||
std::cerr << "Classification done in " << t.time() << " second(s)" << std::endl;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
#include <CGAL/Classification/ETHZ/Random_forest_classifier.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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<int> ("label");
|
||||
std::tie (label_map, lm_found) = pts.property_map<int> ("label");
|
||||
if (!lm_found)
|
||||
{
|
||||
std::cerr << "Error: \"label\" property not found in input file." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::vector<int> 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<int> 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<CGAL::Sequential_tag>
|
||||
Classification::classify_with_graphcut<CGAL::Parallel_if_available_tag>
|
||||
(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);
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ int main (int argc, char** argv)
|
|||
|
||||
std::cerr << "Classifying" << std::endl;
|
||||
std::vector<int> label_indices(pts.size(), -1);
|
||||
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
||||
Classification::classify_with_graphcut<CGAL::Parallel_if_available_tag>
|
||||
(pts, Pmap(), labels, classifier,
|
||||
neighborhood.k_neighbor_query(12),
|
||||
0.5, 1, label_indices);
|
||||
|
|
|
|||
|
|
@ -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<int> ("label");
|
||||
std::tie (label_map, lm_found) = pts.property_map<int> ("label");
|
||||
if (!lm_found)
|
||||
{
|
||||
std::cerr << "Error: \"label\" property not found in input file." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::vector<int> 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<CGAL::Sequential_tag> (ground_truth, 800);
|
||||
classifier.train<CGAL::Parallel_if_available_tag> (pts.range(label_map), 800);
|
||||
t.stop();
|
||||
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
|
||||
|
||||
t.reset();
|
||||
t.start();
|
||||
std::vector<int> label_indices(pts.size(), -1);
|
||||
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
||||
Classification::classify_with_graphcut<CGAL::Parallel_if_available_tag>
|
||||
(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
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ typedef Classification::Mesh_feature_generator<Kernel, Mesh, Face_point_map>
|
|||
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<int> 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<CGAL::Sequential_tag>
|
||||
Classification::classify_with_graphcut<CGAL::Parallel_if_available_tag>
|
||||
(mesh.faces(), Face_with_bbox_map(&mesh), labels, classifier,
|
||||
generator.neighborhood().n_ring_neighbor_query(2),
|
||||
0.2f, 1, label_indices);
|
||||
|
|
|
|||
|
|
@ -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<int> ("label");
|
||||
std::tie (label_map, lm_found) = pts.property_map<int> ("label");
|
||||
if (!lm_found)
|
||||
{
|
||||
std::cerr << "Error: \"label\" property not found in input file." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::vector<int> 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<CGAL::Sequential_tag>
|
||||
Classification::classify_with_graphcut<CGAL::Parallel_if_available_tag>
|
||||
(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
|
||||
|
|
|
|||
|
|
@ -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<int> ("label");
|
||||
std::tie (label_map, lm_found) = pts.property_map<int> ("label");
|
||||
if (!lm_found)
|
||||
{
|
||||
std::cerr << "Error: \"label\" property not found in input file." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::vector<int> 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
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class Cluster
|
|||
{
|
||||
public:
|
||||
|
||||
typedef typename ItemMap::value_type Item;
|
||||
using Item = typename boost::property_traits<ItemMap>::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<ItemMap>::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<std::size_t>())
|
||||
: neighbors (std::make_shared<std::vector<std::size_t> >())
|
||||
, m_range (&range), m_item_map (item_map)
|
||||
, m_inliers (new std::vector<std::size_t>())
|
||||
, m_inliers (std::make_shared<std::vector<std::size_t> >())
|
||||
, m_training(-1), m_label(-1)
|
||||
{ }
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class Random_forest_classifier
|
|||
|
||||
const Label_set& m_labels;
|
||||
const Feature_set& m_features;
|
||||
Forest* m_rfc;
|
||||
std::shared_ptr<Forest> 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<int> gt;
|
||||
std::vector<float> ft;
|
||||
|
||||
std::size_t idx = 0;
|
||||
for (const auto& ig : ground_truth)
|
||||
#ifdef CGAL_CLASSIFICATION_VERBOSE
|
||||
std::vector<std::size_t> 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<int> label_vector (&(gt[0]), gt.size(), 1);
|
||||
CGAL::internal::liblearning::DataView2D<float> 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<Forest> (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<Forest> (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<Forest> (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
|
||||
|
||||
/// @}
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <numeric>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <CGAL/IO/binary_file_io.h>
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/ptr_container/ptr_vector.hpp>
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#endif
|
||||
|
||||
#include <CGAL/algorithm.h>
|
||||
|
||||
#include <CGAL/IO/binary_file_io.h>
|
||||
#include <CGAL/tags.h>
|
||||
|
||||
#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<ConcurrencyTag, Parallel_tag>::value),
|
||||
CGAL_static_assertion_msg (!(std::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
if (std::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(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<std::size_t>& count) const
|
||||
{
|
||||
for (std::size_t i_tree = 0; i_tree < trees.size(); ++i_tree)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
#include "../dataview.h"
|
||||
#include "common-libraries.hpp"
|
||||
|
||||
#include <CGAL/IO/binary_file_io.h>
|
||||
|
||||
#if defined(CGAL_LINKED_WITH_BOOST_IOSTREAMS) && defined(CGAL_LINKED_WITH_BOOST_SERIALIZATION)
|
||||
#include <boost/serialization/scoped_ptr.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
|
|
@ -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<std::size_t>& count) const
|
||||
{
|
||||
if (!is_leaf)
|
||||
if (!is_leaf && splitter.feature != -1)
|
||||
{
|
||||
count[std::size_t(splitter.feature)] ++;
|
||||
left->get_feature_usage(count);
|
||||
|
|
|
|||
|
|
@ -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<std::size_t>& count) const
|
||||
{
|
||||
root_node->get_feature_usage(count);
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@
|
|||
#include <CGAL/Classification/Label.h>
|
||||
#include <CGAL/Classification/Label_set.h>
|
||||
|
||||
#include <boost/iterator/zip_iterator.hpp>
|
||||
#include <CGAL/Iterator_range.h>
|
||||
|
||||
#include <boost/iterator/zip_iterator.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <cmath> // for std::isnan
|
||||
|
||||
|
|
@ -27,6 +28,7 @@ namespace CGAL {
|
|||
|
||||
namespace Classification {
|
||||
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationDataStructures
|
||||
|
||||
|
|
@ -35,98 +37,103 @@ namespace Classification {
|
|||
*/
|
||||
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;
|
||||
const Label_set& m_labels;
|
||||
std::vector<std::vector<std::size_t> > m_confusion; // confusion matrix
|
||||
|
||||
public:
|
||||
|
||||
/// \name Constructor
|
||||
/// \name Constructors
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief instantiates an empty evaluation object.
|
||||
|
||||
/*!
|
||||
\param labels labels used.
|
||||
*/
|
||||
Evaluation (const Label_set& labels)
|
||||
: m_labels (labels)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
\brief Instantiates an evaluation object and computes all
|
||||
measurements.
|
||||
/*!
|
||||
|
||||
\param labels labels used.
|
||||
\brief instantiates an evaluation object and computes all
|
||||
measurements.
|
||||
|
||||
\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 `-1`.
|
||||
\param labels labels used.
|
||||
|
||||
\param result similar to `ground_truth` but contained the result of
|
||||
a classification.
|
||||
\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 `-1`.
|
||||
|
||||
*/
|
||||
\param result similar to `ground_truth` but contained the result of
|
||||
a classification.
|
||||
|
||||
*/
|
||||
template <typename GroundTruthIndexRange, typename ResultIndexRange>
|
||||
Evaluation (const Label_set& labels,
|
||||
const GroundTruthIndexRange& ground_truth,
|
||||
const ResultIndexRange& result)
|
||||
: m_precision (labels.size()),
|
||||
m_recall (labels.size()),
|
||||
m_iou (labels.size())
|
||||
: m_labels (labels)
|
||||
{
|
||||
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||
m_map_labels[labels[i]] = i;
|
||||
init();
|
||||
append(ground_truth, result);
|
||||
}
|
||||
|
||||
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());
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
void init()
|
||||
{
|
||||
m_confusion.resize (m_labels.size());
|
||||
for (std::size_t i = 0; i < m_confusion.size(); ++ i)
|
||||
m_confusion[i].resize (m_labels.size(), 0);
|
||||
}
|
||||
|
||||
std::size_t sum_true_positives = 0;
|
||||
std::size_t total = 0;
|
||||
bool label_has_ground_truth (std::size_t label_idx) const
|
||||
{
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
if (m_confusion[i][label_idx] != 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
for (const auto& zip : CGAL::make_range (boost::make_zip_iterator
|
||||
(boost::make_tuple(ground_truth.begin(), result.begin())),
|
||||
boost::make_zip_iterator
|
||||
(boost::make_tuple(ground_truth.end(), result.end()))))
|
||||
/// @}
|
||||
|
||||
/// \name Modification
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief appends more items to the evaluation object.
|
||||
|
||||
\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 `-1`.
|
||||
|
||||
\param result similar to `ground_truth` but contained the result of
|
||||
a classification.
|
||||
|
||||
*/
|
||||
template <typename GroundTruthIndexRange, typename ResultIndexRange>
|
||||
void append (const GroundTruthIndexRange& ground_truth,
|
||||
const ResultIndexRange& result)
|
||||
{
|
||||
CGAL_precondition (m_labels.is_valid_ground_truth (ground_truth));
|
||||
CGAL_precondition (m_labels.is_valid_ground_truth (result));
|
||||
|
||||
for (const auto& p : CGAL::make_range
|
||||
(boost::make_zip_iterator(boost::make_tuple(ground_truth.begin(), result.begin())),
|
||||
boost::make_zip_iterator(boost::make_tuple(ground_truth.end(), result.end()))))
|
||||
{
|
||||
int gt = static_cast<int>(boost::get<0>(zip));
|
||||
int res = static_cast<int>(boost::get<1>(zip));
|
||||
int gt = static_cast<int>(boost::get<0>(p));
|
||||
int res = static_cast<int>(boost::get<1>(p));
|
||||
if (gt == -1 || res == -1)
|
||||
continue;
|
||||
++ total;
|
||||
if (gt == res)
|
||||
{
|
||||
++ true_positives[gt];
|
||||
++ sum_true_positives;
|
||||
continue;
|
||||
}
|
||||
++ false_positives[res];
|
||||
++ false_negatives[gt];
|
||||
|
||||
++ m_confusion[std::size_t(res)][std::size_t(gt)];
|
||||
}
|
||||
|
||||
m_mean_iou = 0.;
|
||||
m_mean_f1 = 0.;
|
||||
|
||||
std::size_t correct_labels = 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]);
|
||||
|
||||
if (std::isnan(m_iou[j]))
|
||||
continue;
|
||||
|
||||
++ correct_labels;
|
||||
m_mean_iou += m_iou[j];
|
||||
m_mean_f1 += 2.f * (m_precision[j] * m_recall[j])
|
||||
/ (m_precision[j] + m_recall[j]);
|
||||
}
|
||||
|
||||
m_mean_iou /= correct_labels;
|
||||
m_mean_f1 /= correct_labels;
|
||||
m_accuracy = sum_true_positives / float(total);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
|
@ -134,10 +141,20 @@ public:
|
|||
/// \name Label Evaluation
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief returns the number of items whose ground truth is
|
||||
`ground_truth` and which were classified as `result`.
|
||||
*/
|
||||
std::size_t confusion (Label_handle ground_truth, Label_handle result)
|
||||
{
|
||||
std::size_t idx_gt = ground_truth->index();
|
||||
std::size_t idx_r = result->index();
|
||||
return m_confusion[idx_gt][idx_r];
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
\brief Returns the precision of the training for the given label.
|
||||
\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.
|
||||
|
|
@ -145,12 +162,23 @@ public:
|
|||
*/
|
||||
float precision (Label_handle label) const
|
||||
{
|
||||
return m_precision[m_map_labels[label]];
|
||||
std::size_t idx = label->index();
|
||||
if (!label_has_ground_truth(idx))
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
|
||||
std::size_t total = 0;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
total += m_confusion[idx][i];
|
||||
|
||||
if (total == 0)
|
||||
return 0.f;
|
||||
|
||||
return m_confusion[idx][idx] / float(total);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
\brief Returns the recall of the training for the given 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.
|
||||
|
|
@ -158,12 +186,19 @@ public:
|
|||
*/
|
||||
float recall (Label_handle label) const
|
||||
{
|
||||
return m_recall[m_map_labels[label]];
|
||||
std::size_t idx = label->index();
|
||||
if (!label_has_ground_truth(idx))
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
|
||||
std::size_t total = 0;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
total += m_confusion[i][idx];
|
||||
return m_confusion[idx][idx] / float(total);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
\brief Returns the \f$F_1\f$ score of the training for the given 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()`:
|
||||
|
||||
|
|
@ -174,13 +209,17 @@ public:
|
|||
*/
|
||||
float f1_score (Label_handle label) const
|
||||
{
|
||||
std::size_t label_idx = m_map_labels[label];
|
||||
return 2.f * (m_precision[label_idx] * m_recall[label_idx])
|
||||
/ (m_precision[label_idx] + m_recall[label_idx]);
|
||||
float p = precision(label);
|
||||
float r = recall(label);
|
||||
|
||||
if (p == 0.f && r == 0.f)
|
||||
return 0.f;
|
||||
|
||||
return 2.f * p * r / (p + r);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the intersection over union of the training for the
|
||||
\brief returns the intersection over union of the training for the
|
||||
given label.
|
||||
|
||||
Intersection over union is the number of true positives divided by
|
||||
|
|
@ -189,7 +228,17 @@ public:
|
|||
*/
|
||||
float intersection_over_union (Label_handle label) const
|
||||
{
|
||||
return m_iou[m_map_labels[label]];
|
||||
std::size_t idx = label->index();
|
||||
|
||||
std::size_t total = 0;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
{
|
||||
total += m_confusion[i][idx];
|
||||
if (i != idx)
|
||||
total += m_confusion[idx][i];
|
||||
}
|
||||
|
||||
return m_confusion[idx][idx] / float(total);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
|
@ -197,26 +246,226 @@ public:
|
|||
/// \name Global Evaluation
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief returns the number of misclassified items.
|
||||
*/
|
||||
std::size_t number_of_misclassified_items() const
|
||||
{
|
||||
std::size_t total = 0;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
for (std::size_t j = 0; j < m_labels.size(); ++ j)
|
||||
if (i != j)
|
||||
total += m_confusion[i][j];
|
||||
return total;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the accuracy of the training.
|
||||
\brief returns the total number of items used for evaluation.
|
||||
*/
|
||||
std::size_t number_of_items() const
|
||||
{
|
||||
std::size_t total = 0;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
for (std::size_t j = 0; j < m_labels.size(); ++ j)
|
||||
total += m_confusion[i][j];
|
||||
return total;
|
||||
}
|
||||
|
||||
/*!
|
||||
\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; }
|
||||
float accuracy() const
|
||||
{
|
||||
std::size_t true_positives = 0;
|
||||
std::size_t total = 0;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
{
|
||||
true_positives += m_confusion[i][i];
|
||||
for (std::size_t j = 0; j < m_labels.size(); ++ j)
|
||||
total += m_confusion[i][j];
|
||||
}
|
||||
return true_positives / float(total);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the mean \f$F_1\f$ score of the training over all
|
||||
\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; }
|
||||
float mean_f1_score() const
|
||||
{
|
||||
float mean = 0;
|
||||
std::size_t nb = 0;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
if (label_has_ground_truth(i))
|
||||
{
|
||||
mean += f1_score(m_labels[i]);
|
||||
++ nb;
|
||||
}
|
||||
return mean / nb;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the mean intersection over union of the training
|
||||
\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; }
|
||||
float mean_intersection_over_union() const
|
||||
{
|
||||
float mean = 0;
|
||||
std::size_t nb = 0;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
{
|
||||
float iou = intersection_over_union(m_labels[i]);
|
||||
if (!std::isnan(iou))
|
||||
{
|
||||
mean += iou;
|
||||
++ nb;
|
||||
}
|
||||
}
|
||||
return mean / nb;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Output Formatting Functions
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief outputs the evaluation in a simple ASCII format to the stream `os`.
|
||||
*/
|
||||
friend std::ostream& operator<< (std::ostream& os, const Evaluation& evaluation)
|
||||
{
|
||||
os << "Evaluation of classification:" << std::endl;
|
||||
os << " * Global results:" << std::endl;
|
||||
os << " - " << evaluation.number_of_misclassified_items()
|
||||
<< " misclassified item(s) out of " << evaluation.number_of_items() << std::endl
|
||||
<< " - Accuracy = " << evaluation.accuracy() << std::endl
|
||||
<< " - Mean F1 score = " << evaluation.mean_f1_score() << std::endl
|
||||
<< " - Mean IoU = " << evaluation.mean_intersection_over_union() << std::endl;
|
||||
os << " * Detailed results:" << std::endl;
|
||||
for (std::size_t i = 0; i < evaluation.m_labels.size(); ++ i)
|
||||
{
|
||||
os << " - \"" << evaluation.m_labels[i]->name() << "\": ";
|
||||
if (evaluation.label_has_ground_truth(i))
|
||||
os << "Precision = " << evaluation.precision(evaluation.m_labels[i]) << " ; "
|
||||
<< "Recall = " << evaluation.recall(evaluation.m_labels[i]) << " ; "
|
||||
<< "F1 score = " << evaluation.f1_score(evaluation.m_labels[i]) << " ; "
|
||||
<< "IoU = " << evaluation.intersection_over_union(evaluation.m_labels[i]) << std::endl;
|
||||
else
|
||||
os << "(no ground truth)" << std::endl;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief outputs the evaluation as an HTML page to the stream `os`.
|
||||
*/
|
||||
static std::ostream& output_to_html (std::ostream& os, const Evaluation& evaluation)
|
||||
{
|
||||
os << "<!DOCTYPE html>" << std::endl
|
||||
<< "<html>" << std::endl
|
||||
<< "<head>" << std::endl
|
||||
<< "<style type=\"text/css\">" << std::endl
|
||||
<< " body{margin:40px auto; max-width:900px; line-height:1.5; color:#333}" << std::endl
|
||||
<< " h1,h2{line-height:1.2}" << std::endl
|
||||
<< " table{width:100%}" << std::endl
|
||||
<< " table,th,td{border: 1px solid black; border-collapse: collapse; }" << std::endl
|
||||
<< " th,td{padding: 5px;}" << std::endl
|
||||
<< "</style>" << std::endl
|
||||
<< "<title>Evaluation of CGAL Classification results</title>" << std::endl
|
||||
<< "</head>" << std::endl
|
||||
<< "<body>" << std::endl
|
||||
<< "<h1>Evaluation of CGAL Classification results</h1>" << std::endl;
|
||||
|
||||
os << "<h2>Global Results</h2>" << std::endl
|
||||
<< "<ul>" << std::endl
|
||||
<< " <li>" << evaluation.number_of_misclassified_items()
|
||||
<< " misclassified item(s) out of " << evaluation.number_of_items() << "</li>" << std::endl
|
||||
<< " <li>Accuracy = " << evaluation.accuracy() << "</li>" << std::endl
|
||||
<< " <li>Mean F1 score = " << evaluation.mean_f1_score() << "</li>" << std::endl
|
||||
<< " <li>Mean IoU = " << evaluation.mean_intersection_over_union() << "</li>" << std::endl
|
||||
<< "</ul>" << std::endl;
|
||||
|
||||
const Label_set& labels = evaluation.m_labels;
|
||||
|
||||
os << "<h2>Detailed Results</h2>" << std::endl
|
||||
<< "<table>" << std::endl
|
||||
<< " <tr>" << std::endl
|
||||
<< " <th><strong>Label</strong></th>" << std::endl
|
||||
<< " <th><strong>Precision</strong></th>" << std::endl
|
||||
<< " <th><strong>Recall</strong></th>" << std::endl
|
||||
<< " <th><strong>F1 score</strong></th>" << std::endl
|
||||
<< " <th><strong>IoU</strong></th>" << std::endl
|
||||
<< " </tr>" << std::endl;
|
||||
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||
if (evaluation.label_has_ground_truth(i))
|
||||
os << " <tr>" << std::endl
|
||||
<< " <td>" << labels[i]->name() << "</td>" << std::endl
|
||||
<< " <td>" << evaluation.precision(labels[i]) << "</td>" << std::endl
|
||||
<< " <td>" << evaluation.recall(labels[i]) << "</td>" << std::endl
|
||||
<< " <td>" << evaluation.f1_score(labels[i]) << "</td>" << std::endl
|
||||
<< " <td>" << evaluation.intersection_over_union(labels[i]) << "</td>" << std::endl
|
||||
<< " </tr>" << std::endl;
|
||||
else
|
||||
os << " <tr>" << std::endl
|
||||
<< " <td>" << labels[i]->name() << "</td>" << std::endl
|
||||
<< " <td><em>(no ground truth)</em></td>" << std::endl
|
||||
<< " <td></td>" << std::endl
|
||||
<< " <td></td>" << std::endl
|
||||
<< " <td></td>" << std::endl
|
||||
<< " </tr>" << std::endl;
|
||||
|
||||
os << "</table>" << std::endl;
|
||||
|
||||
os << "<h2>Confusion Matrix</h2>" << std::endl
|
||||
<< "<table>" << std::endl
|
||||
<< " <tr>" << std::endl
|
||||
<< " <th></th>" << std::endl;
|
||||
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||
os << " <th><strong>" << labels[i]->name() << "</strong></th>" << std::endl;
|
||||
os << " <th><strong>PREDICTIONS</strong></th>" << std::endl;
|
||||
os << " </tr>" << std::endl;
|
||||
|
||||
std::vector<std::size_t> sums (labels.size(), 0);
|
||||
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||
{
|
||||
os << " <tr>" << std::endl
|
||||
<< " <td><strong>" << labels[i]->name() << "</strong></td>" << std::endl;
|
||||
std::size_t sum = 0;
|
||||
for (std::size_t j = 0; j < labels.size(); ++ j)
|
||||
{
|
||||
if (i == j)
|
||||
os << " <td><strong>" << evaluation.m_confusion[i][j] << "</strong></td>" << std::endl;
|
||||
else
|
||||
os << " <td>" << evaluation.m_confusion[i][j] << "</td>" << std::endl;
|
||||
sum += evaluation.m_confusion[i][j];
|
||||
sums[j] += evaluation.m_confusion[i][j];
|
||||
}
|
||||
os << " <td><strong>" << sum << "</strong></td>" << std::endl;
|
||||
os << " </tr>" << std::endl;
|
||||
}
|
||||
|
||||
os << " <tr>" << std::endl
|
||||
<< " <td><strong>GROUND TRUTH</strong></td>" << std::endl;
|
||||
std::size_t total = 0;
|
||||
for (std::size_t j = 0; j < labels.size(); ++ j)
|
||||
{
|
||||
os << " <td><strong>" << sums[j] << "</strong></td>" << std::endl;
|
||||
total += sums[j];
|
||||
}
|
||||
os << " <td><strong>" << total << "</strong></td>" << std::endl
|
||||
<< " </tr>" << std::endl
|
||||
<< "</table>" << std::endl
|
||||
<< "<p><em>This page was generated by the <a \
|
||||
href=\"https://doc.cgal.org/latest/Classification/index.html\">CGAL \
|
||||
Classification package</a>.</em></p>" << std::endl
|
||||
<< "</body>" << std::endl
|
||||
<< "</html>" << std::endl;
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class Cluster_mean_of_feature : public CGAL::Classification::Feature_base
|
|||
public:
|
||||
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\tparam ClusterRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class Cluster_size : public CGAL::Classification::Feature_base
|
|||
public:
|
||||
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\tparam ClusterRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class Cluster_variance_of_feature : public CGAL::Classification::Feature_base
|
|||
public:
|
||||
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\tparam ClusterRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class Cluster_vertical_extent : public CGAL::Classification::Feature_base
|
|||
public:
|
||||
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\tparam ClusterRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator` and its value type is the key type of
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ private:
|
|||
public:
|
||||
|
||||
/*!
|
||||
\brief Constructs a feature based on the given color channel.
|
||||
\brief constructs a feature based on the given color channel.
|
||||
|
||||
\param input point range.
|
||||
\param color_map property map to access the colors of the input points.
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@ namespace Feature {
|
|||
template <typename PointRange, typename PointMap>
|
||||
class Distance_to_plane : public Feature_base
|
||||
{
|
||||
|
||||
typedef typename CGAL::Kernel_traits<typename PointMap::value_type>::Kernel Kernel;
|
||||
using Kernel = typename CGAL::Kernel_traits<typename PointMap::value_type>::Kernel;
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_PRECOMPUTE_FEATURES
|
||||
std::vector<float> distance_to_plane_feature;
|
||||
|
|
@ -59,7 +58,7 @@ class Distance_to_plane : public Feature_base
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ template <typename GeomTraits, typename PointRange, typename PointMap, typename
|
|||
class Echo_scatter : public Feature_base
|
||||
{
|
||||
public:
|
||||
typedef Classification::Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
using Grid = Classification::Planimetric_grid<GeomTraits, PointRange, PointMap>;
|
||||
private:
|
||||
typedef Classification::Image<compressed_float> Image_cfloat;
|
||||
using Image_cfloat = Classification::Image<compressed_float>;
|
||||
|
||||
const Grid& grid;
|
||||
Image_cfloat Scatter;
|
||||
|
|
@ -62,7 +62,7 @@ private:
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param echo_map property map to access the echo values of the input points.
|
||||
|
|
|
|||
|
|
@ -50,11 +50,9 @@ namespace Feature {
|
|||
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 Image<compressed_float> Image_cfloat;
|
||||
typedef Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
using Image_float = Image<float>;
|
||||
using Image_cfloat = Image<compressed_float>;
|
||||
using Grid = Planimetric_grid<GeomTraits, PointRange, PointMap>;
|
||||
|
||||
const PointRange& input;
|
||||
PointMap point_map;
|
||||
|
|
@ -66,7 +64,7 @@ class Elevation : public Feature_base
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
|
|
|
|||
|
|
@ -49,10 +49,8 @@ namespace Feature {
|
|||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Height_above : public Feature_base
|
||||
{
|
||||
typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3;
|
||||
|
||||
typedef Image<float> Image_float;
|
||||
typedef Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
using Image_float = Image<float>;
|
||||
using Grid = Planimetric_grid<GeomTraits, PointRange, PointMap>;
|
||||
|
||||
const PointRange& input;
|
||||
PointMap point_map;
|
||||
|
|
@ -62,7 +60,7 @@ class Height_above : public Feature_base
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
|
|
|
|||
|
|
@ -49,8 +49,6 @@ namespace Feature {
|
|||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Height_below : public Feature_base
|
||||
{
|
||||
typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3;
|
||||
|
||||
typedef Image<float> Image_float;
|
||||
typedef Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
|
||||
|
|
@ -62,7 +60,7 @@ class Height_below : public Feature_base
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class Simple_feature : public Feature_base
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature using an input range and a property map.
|
||||
\brief constructs the feature using an input range and a property map.
|
||||
|
||||
\param input point range.
|
||||
\param property_map property map to access scalar field.
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ namespace Feature {
|
|||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Vertical_dispersion : public Feature_base
|
||||
{
|
||||
typedef Classification::Image<compressed_float> Image_cfloat;
|
||||
typedef Classification::Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
using Image_cfloat = Classification::Image<compressed_float>;
|
||||
using Grid = Classification::Planimetric_grid<GeomTraits, PointRange, PointMap>;
|
||||
|
||||
const Grid& grid;
|
||||
Image_cfloat Dispersion;
|
||||
|
|
@ -65,7 +65,7 @@ class Vertical_dispersion : public Feature_base
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
|
|
|
|||
|
|
@ -49,10 +49,8 @@ namespace Feature {
|
|||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Vertical_range : public Feature_base
|
||||
{
|
||||
typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3;
|
||||
|
||||
typedef Image<float> Image_float;
|
||||
typedef Planimetric_grid<GeomTraits, PointRange, PointMap> Grid;
|
||||
using Image_float = Image<float>;
|
||||
using Grid = Planimetric_grid<GeomTraits, PointRange, PointMap>;
|
||||
|
||||
const PointRange& input;
|
||||
PointMap point_map;
|
||||
|
|
@ -62,7 +60,7 @@ class Vertical_range : public Feature_base
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature.
|
||||
\brief constructs the feature.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class Verticality : public Feature_base
|
|||
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs the feature using local eigen analysis.
|
||||
\brief constructs the feature using local eigen analysis.
|
||||
|
||||
\tparam InputRange model of `ConstRange`. Its iterator type
|
||||
is `RandomAccessIterator`.
|
||||
|
|
@ -64,7 +64,7 @@ public:
|
|||
|
||||
|
||||
/*!
|
||||
\brief Constructs the feature using provided normals of points.
|
||||
\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
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
#include <CGAL/license/Classification.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
|
|
@ -42,18 +42,18 @@ public:
|
|||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns the name of the feature (initialized to
|
||||
\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.
|
||||
\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
|
||||
\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.
|
||||
*/
|
||||
|
|
@ -72,7 +72,6 @@ public:
|
|||
*/
|
||||
class Feature_handle { };
|
||||
#else
|
||||
//typedef boost::shared_ptr<Feature_base> Feature_handle;
|
||||
|
||||
class Feature_set;
|
||||
|
||||
|
|
@ -80,19 +79,23 @@ class Feature_handle
|
|||
{
|
||||
friend Feature_set;
|
||||
|
||||
boost::shared_ptr<boost::shared_ptr<Feature_base> > m_base;
|
||||
using Feature_base_ptr = std::unique_ptr<Feature_base>;
|
||||
std::shared_ptr<Feature_base_ptr> m_base;
|
||||
|
||||
template <typename Feature>
|
||||
Feature_handle (Feature* f) : m_base (new boost::shared_ptr<Feature_base>(f)) { }
|
||||
|
||||
template <typename Feature>
|
||||
void attach (Feature* f) const
|
||||
template <typename Feature_ptr>
|
||||
Feature_handle (Feature_ptr f)
|
||||
: m_base (std::make_shared<Feature_base_ptr>(std::move(f)))
|
||||
{
|
||||
*m_base = boost::shared_ptr<Feature_base>(f);
|
||||
}
|
||||
|
||||
template <typename Feature_ptr>
|
||||
void attach (Feature_ptr f)
|
||||
{
|
||||
*m_base = std::move(f);
|
||||
}
|
||||
public:
|
||||
|
||||
Feature_handle() : m_base (new boost::shared_ptr<Feature_base>()) { }
|
||||
Feature_handle() : m_base (std::make_shared<Feature_base_ptr>()) { }
|
||||
|
||||
Feature_base& operator*() { return **m_base; }
|
||||
|
||||
|
|
@ -107,6 +110,17 @@ public:
|
|||
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationFeature
|
||||
|
||||
\brief casts a feature handle to a specialized feature pointer.
|
||||
*/
|
||||
template <typename FeatureType>
|
||||
FeatureType* feature_cast (Feature_handle fh)
|
||||
{
|
||||
return dynamic_cast<FeatureType*>(&*(fh));
|
||||
}
|
||||
|
||||
|
||||
} // namespace Classification
|
||||
|
||||
|
|
|
|||
|
|
@ -16,15 +16,14 @@
|
|||
|
||||
#include <CGAL/Classification/Feature_base.h>
|
||||
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
#include <mutex>
|
||||
#include <tbb/task_group.h>
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
|
|
@ -40,7 +39,7 @@ the addition and the deletion of features.
|
|||
*/
|
||||
class Feature_set
|
||||
{
|
||||
typedef std::vector<Feature_handle> Base;
|
||||
using Base = std::vector<Feature_handle>;
|
||||
Base m_features;
|
||||
|
||||
struct Compare_name
|
||||
|
|
@ -54,42 +53,35 @@ class Feature_set
|
|||
};
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
tbb::task_group* m_tasks;
|
||||
std::unique_ptr<tbb::task_group> m_tasks;
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
public:
|
||||
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
using const_iterator = unspecified_type; ///< A random access constant iterator with value type `Feature_handle`.
|
||||
using iterator = unspecified_type; ///< A random access iterator with value type `Feature_handle`.
|
||||
#else
|
||||
using const_iterator = std::vector<Feature_handle>::const_iterator;
|
||||
using iterator = std::vector<Feature_handle>::iterator;
|
||||
#endif
|
||||
|
||||
/// \name Constructor
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Creates an empty feature set.
|
||||
\brief creates an empty feature set.
|
||||
*/
|
||||
Feature_set()
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
: m_tasks(nullptr)
|
||||
#endif
|
||||
{ }
|
||||
|
||||
/// @}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual ~Feature_set()
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
if (m_tasks != nullptr)
|
||||
delete m_tasks;
|
||||
for (std::size_t i = 0; i < m_adders.size(); ++ i)
|
||||
delete m_adders[i];
|
||||
#endif
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/// \name Modifications
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Instantiates a new feature and adds it to the set.
|
||||
\brief instantiates a new feature and adds it to the set.
|
||||
|
||||
If several calls of `add()` are surrounded by
|
||||
`begin_parallel_additions()` and `end_parallel_additions()`, they
|
||||
|
|
@ -113,20 +105,21 @@ public:
|
|||
Feature_handle add (T&& ... t)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
if (m_tasks != nullptr)
|
||||
if (m_tasks)
|
||||
{
|
||||
m_features.push_back (Feature_handle());
|
||||
|
||||
Parallel_feature_adder<Feature, T...>* adder
|
||||
= new Parallel_feature_adder<Feature, T...>(m_features.back(), std::forward<T>(t)...);
|
||||
|
||||
m_adders.push_back (adder);
|
||||
Parallel_feature_adder_ptr<Feature, T...> adder
|
||||
= std::make_unique<Parallel_feature_adder<Feature, T...> >
|
||||
(m_features.back(), std::forward<T>(t)...);
|
||||
m_tasks->run (*adder);
|
||||
|
||||
m_adders.emplace_back (std::move (adder));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m_features.push_back (Feature_handle (new Feature(std::forward<T>(t)...)));
|
||||
m_features.push_back (Feature_handle (std::make_unique<Feature>(std::forward<T>(t)...)));
|
||||
}
|
||||
return m_features.back();
|
||||
}
|
||||
|
|
@ -136,20 +129,21 @@ public:
|
|||
Feature_handle add_with_scale_id (std::size_t i, T&& ... t)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
if (m_tasks != nullptr)
|
||||
if (m_tasks)
|
||||
{
|
||||
m_features.push_back (Feature_handle());
|
||||
|
||||
Parallel_feature_adder<Feature, T...>* adder
|
||||
= new Parallel_feature_adder<Feature, T...>(i, m_features.back(), std::forward<T>(t)...);
|
||||
|
||||
m_adders.push_back (adder);
|
||||
Parallel_feature_adder_ptr<Feature, T...> adder
|
||||
= std::make_unique<Parallel_feature_adder<Feature, T...> >
|
||||
(i, m_features.back(), std::forward<T>(t)...);
|
||||
m_tasks->run (*adder);
|
||||
|
||||
m_adders.emplace_back (std::move (adder));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m_features.push_back (Feature_handle (new Feature(std::forward<T>(t)...)));
|
||||
m_features.push_back (Feature_handle (std::make_unique<Feature>(std::forward<T>(t)...)));
|
||||
m_features.back()->set_name (m_features.back()->name() + "_" + std::to_string(i));
|
||||
}
|
||||
return m_features.back();
|
||||
|
|
@ -158,7 +152,7 @@ public:
|
|||
|
||||
|
||||
/*!
|
||||
\brief Removes a feature.
|
||||
\brief removes a feature.
|
||||
|
||||
\param feature the handle to feature type that must be removed.
|
||||
|
||||
|
|
@ -177,7 +171,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Removes all features.
|
||||
\brief removes all features.
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
|
|
@ -190,16 +184,15 @@ public:
|
|||
/// @{
|
||||
|
||||
|
||||
#if defined(CGAL_LINKED_WITH_TBB) || defined(DOXYGEN_RUNNING)
|
||||
|
||||
/*!
|
||||
\brief Initializes structures to compute features in parallel.
|
||||
\brief initializes structures to compute features in parallel.
|
||||
|
||||
If the user wants to add features in parallel, this function
|
||||
should be called before making several calls of `add()`. After the
|
||||
calls of `add()`, `end_parallel_additions()` should be called.
|
||||
|
||||
\note This function requires \ref thirdpartyTBB.
|
||||
\note If \ref thirdpartyTBB is not available, this function does
|
||||
nothing.
|
||||
|
||||
\warning As arguments of `add()` are passed by reference and that new
|
||||
threads are started if `begin_parallel_additions()` is used, it is
|
||||
|
|
@ -212,33 +205,33 @@ public:
|
|||
*/
|
||||
void begin_parallel_additions()
|
||||
{
|
||||
m_tasks = new tbb::task_group;
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
m_tasks = std::make_unique<tbb::task_group>();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
\brief Waits for the end of parallel feature computation and
|
||||
\brief waits for the end of parallel feature computation and
|
||||
clears dedicated data structures afterwards.
|
||||
|
||||
If the user wants to add features in parallel, this function
|
||||
should be called after `begin_parallel_additions()` and several
|
||||
calls of `add()`.
|
||||
|
||||
\note This function requires \ref thirdpartyTBB.
|
||||
\note If \ref thirdpartyTBB is not available, this function does
|
||||
nothing.
|
||||
|
||||
\sa `begin_parallel_additions()`
|
||||
*/
|
||||
void end_parallel_additions()
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
m_tasks->wait();
|
||||
delete m_tasks;
|
||||
m_tasks = nullptr;
|
||||
|
||||
for (std::size_t i = 0; i < m_adders.size(); ++ i)
|
||||
delete m_adders[i];
|
||||
m_tasks.release();
|
||||
m_adders.clear();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
|
@ -246,8 +239,13 @@ public:
|
|||
/// \name Access
|
||||
/// @{
|
||||
|
||||
const_iterator begin() const { return m_features.begin(); }
|
||||
iterator begin() { return m_features.begin(); }
|
||||
const_iterator end() const { return m_features.end(); }
|
||||
iterator end() { return m_features.end(); }
|
||||
|
||||
/*!
|
||||
\brief Returns how many features are defined.
|
||||
\brief returns how many features are defined.
|
||||
*/
|
||||
std::size_t size() const
|
||||
{
|
||||
|
|
@ -256,7 +254,7 @@ public:
|
|||
|
||||
|
||||
/*!
|
||||
\brief Returns the \f$i^{th}\f$ feature.
|
||||
\brief returns the \f$i^{th}\f$ feature.
|
||||
*/
|
||||
Feature_handle operator[](std::size_t i) const
|
||||
{
|
||||
|
|
@ -293,18 +291,18 @@ private:
|
|||
{
|
||||
std::size_t scale;
|
||||
mutable Feature_handle fh;
|
||||
boost::shared_ptr<std::tuple<T...> > args;
|
||||
std::shared_ptr<std::tuple<T...> > args;
|
||||
|
||||
Parallel_feature_adder (Feature_handle fh, T&& ... t)
|
||||
: scale (std::size_t(-1)), fh (fh)
|
||||
{
|
||||
args = boost::make_shared<std::tuple<T...> >(std::forward<T>(t)...);
|
||||
args = std::make_shared<std::tuple<T...> >(std::forward<T>(t)...);
|
||||
}
|
||||
|
||||
Parallel_feature_adder (std::size_t scale, Feature_handle fh, T&& ... t)
|
||||
: scale(scale), fh (fh)
|
||||
{
|
||||
args = boost::make_shared<std::tuple<T...> >(std::forward<T>(t)...);
|
||||
args = std::make_shared<std::tuple<T...> >(std::forward<T>(t)...);
|
||||
}
|
||||
|
||||
template<int ...>
|
||||
|
|
@ -325,7 +323,7 @@ private:
|
|||
template <typename Tuple, int ... S>
|
||||
void add_feature (Tuple& t, seq<S...>) const
|
||||
{
|
||||
fh.attach (new Feature (std::forward<T>(std::get<S>(t))...));
|
||||
fh.attach (std::make_unique<Feature> (std::forward<T>(std::get<S>(t))...));
|
||||
if (scale != std::size_t(-1))
|
||||
fh->set_name (fh->name() + "_" + std::to_string(scale));
|
||||
}
|
||||
|
|
@ -337,7 +335,11 @@ private:
|
|||
|
||||
};
|
||||
|
||||
std::vector<Abstract_parallel_feature_adder*> m_adders;
|
||||
using Abstract_parallel_feature_adder_ptr = std::unique_ptr<Abstract_parallel_feature_adder>;
|
||||
template <typename Feature, typename ... T>
|
||||
using Parallel_feature_adder_ptr = std::unique_ptr<Parallel_feature_adder<Feature, T...> >;
|
||||
|
||||
std::vector<Abstract_parallel_feature_adder_ptr> m_adders;
|
||||
|
||||
/// \endcond
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
#include <CGAL/license/Classification.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#define CGAL_CLASSIFICATION_IMAGE_SIZE_LIMIT 100000000
|
||||
|
||||
|
|
@ -27,15 +27,15 @@ namespace Classification {
|
|||
template <typename Type>
|
||||
class Image
|
||||
{
|
||||
typedef std::vector<Type> Vector;
|
||||
typedef std::map<std::size_t, Type> Map;
|
||||
using Vector = std::vector<Type>;
|
||||
using Map = std::map<std::size_t, Type>;
|
||||
|
||||
std::size_t m_width;
|
||||
std::size_t m_height;
|
||||
std::size_t m_depth;
|
||||
|
||||
boost::shared_ptr<Vector> m_raw;
|
||||
boost::shared_ptr<Map> m_sparse;
|
||||
std::shared_ptr<Vector> m_raw;
|
||||
std::shared_ptr<Map> m_sparse;
|
||||
Type m_default;
|
||||
|
||||
// Forbid using copy constructor
|
||||
|
|
@ -45,7 +45,7 @@ class Image
|
|||
|
||||
public:
|
||||
|
||||
Image () : m_width(0), m_height(0), m_depth(0), m_raw (nullptr)
|
||||
Image () : m_width(0), m_height(0), m_depth(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -57,9 +57,9 @@ public:
|
|||
if (m_width * m_height * m_depth > 0)
|
||||
{
|
||||
if (m_width * m_height * m_depth < CGAL_CLASSIFICATION_IMAGE_SIZE_LIMIT)
|
||||
m_raw = boost::shared_ptr<Vector> (new Vector(m_width * m_height * m_depth));
|
||||
m_raw = std::make_shared<Vector> (m_width * m_height * m_depth);
|
||||
else
|
||||
m_sparse = boost::shared_ptr<Map> (new Map());
|
||||
m_sparse = std::make_shared<Map> ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,8 +69,8 @@ public:
|
|||
|
||||
void free()
|
||||
{
|
||||
m_raw = boost::shared_ptr<Vector>();
|
||||
m_sparse = boost::shared_ptr<Map>();
|
||||
m_raw.reset();
|
||||
m_sparse.reset();
|
||||
}
|
||||
|
||||
Image& operator= (const Image& other)
|
||||
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
Type& operator() (const std::size_t& x, const std::size_t& y, const std::size_t& z = 0)
|
||||
{
|
||||
if (m_raw == boost::shared_ptr<Vector>()) // sparse case
|
||||
if (!m_raw) // sparse case
|
||||
{
|
||||
typename Map::iterator inserted = m_sparse->insert
|
||||
(std::make_pair (coord(x,y,z), Type())).first;
|
||||
|
|
@ -105,7 +105,7 @@ public:
|
|||
}
|
||||
const Type& operator() (const std::size_t& x, const std::size_t& y, const std::size_t& z = 0) const
|
||||
{
|
||||
if (m_raw == boost::shared_ptr<Vector>()) // sparse case
|
||||
if (!m_raw) // sparse case
|
||||
{
|
||||
typename Map::iterator found = m_sparse->find (coord(x,y,z));
|
||||
if (found != m_sparse->end())
|
||||
|
|
|
|||
|
|
@ -13,38 +13,89 @@
|
|||
#define CGAL_CLASSIFICATION_LABEL_H
|
||||
|
||||
#include <CGAL/license/Classification.h>
|
||||
#include <CGAL/IO/Color.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Classification {
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
class Label_set;
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationLabel
|
||||
|
||||
\brief %Classification label (for example: vegetation, ground, etc.)
|
||||
defined as a set of relationships with classification features.
|
||||
\brief %Classification label (for example: vegetation, ground, etc.).
|
||||
|
||||
\note Labels should always be constructed from a `CGAL::Classification::Label_set` object.
|
||||
*/
|
||||
class Label
|
||||
{
|
||||
private:
|
||||
|
||||
std::string m_name;
|
||||
std::size_t m_index;
|
||||
std::size_t m_standard_index;
|
||||
CGAL::Color m_color;
|
||||
|
||||
friend Label_set;
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
\param name name of the classification label (e.g. vegetation).
|
||||
*/
|
||||
Label (std::string name) : m_name (name) { }
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
// Undocumented: Labels should be created by the set
|
||||
Label (std::string name, std::size_t index, std::size_t standard_index,
|
||||
const CGAL::Color& color)
|
||||
: m_name (name), m_index (index), m_standard_index (standard_index)
|
||||
, m_color (color)
|
||||
{ }
|
||||
/// \endcond
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
returns the name of the classification label (\a e.g. vegetation).
|
||||
*/
|
||||
const std::string& name() const { return m_name; }
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
/*!
|
||||
returns the index of the classification label in the label set.
|
||||
|
||||
\note This index cannot be changed by the user and is handled directly by the label set.
|
||||
*/
|
||||
std::size_t index() const { return m_index; }
|
||||
|
||||
/*!
|
||||
returns the standard index of the classification label (\a e.g. index in the ASPRS standard).
|
||||
|
||||
\note This index is purely user-oriented and is not used by the classification algorithms.
|
||||
*/
|
||||
std::size_t standard_index() const { return m_standard_index; }
|
||||
|
||||
/*!
|
||||
returns the color used to represent the label.
|
||||
|
||||
\note The color is purely user-oriented and is not used by the
|
||||
classification algorithms. It is not to be confused with a color
|
||||
attribute embedded in a data set which _can_ be used (see
|
||||
`Color_channel`).
|
||||
*/
|
||||
const CGAL::Color& color() const { return m_color; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Modification
|
||||
/// @{
|
||||
|
||||
void set_name (const std::string& name) { m_name = name; }
|
||||
/// \endcond
|
||||
void set_standard_index(std::size_t idx) { m_standard_index = idx; }
|
||||
void set_color (const Color& color) { m_color = color; }
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
|
|
@ -57,7 +108,7 @@ public:
|
|||
*/
|
||||
class Label_handle { };
|
||||
#else
|
||||
typedef boost::shared_ptr<Label> Label_handle;
|
||||
typedef std::shared_ptr<Label> Label_handle;
|
||||
#endif
|
||||
|
||||
} // namespace Classification
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <CGAL/Classification/Label.h>
|
||||
|
||||
#include <CGAL/Random.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
|
|
@ -31,31 +33,100 @@ algorithms.
|
|||
*/
|
||||
class Label_set
|
||||
{
|
||||
typedef std::vector<Label_handle> Base;
|
||||
using Base = std::vector<Label_handle>;
|
||||
|
||||
CGAL::Random m_random;
|
||||
Base m_labels;
|
||||
|
||||
public:
|
||||
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
using const_iterator = unspecified_type; ///< A random access constant iterator with value type `Label_handle`.
|
||||
using iterator = unspecified_type; ///< A random access iterator with value type `Label_handle`.
|
||||
#else
|
||||
using const_iterator = std::vector<Label_handle>::const_iterator;
|
||||
using iterator = std::vector<Label_handle>::iterator;
|
||||
#endif
|
||||
|
||||
/// \name Constructors
|
||||
/// @{
|
||||
|
||||
Label_set() { }
|
||||
|
||||
/*!
|
||||
\brief Initializes the set with the provided `labels` names.
|
||||
\brief constructs a label set from a set of label names.
|
||||
*/
|
||||
Label_set (const std::initializer_list<const char*>& labels)
|
||||
Label_set(std::initializer_list<const char*> labels)
|
||||
{
|
||||
for (const char* l : labels)
|
||||
add(l);
|
||||
add (l);
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual ~Label_set() { }
|
||||
/// \endcond
|
||||
/// @}
|
||||
|
||||
/// \name Modifications
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Adds a label.
|
||||
\brief adds a label.
|
||||
|
||||
\note Names, standard indices and colors are not used for
|
||||
identification: two labels in the same set can have the same name,
|
||||
standard index or color, but not the same handle. Each call to
|
||||
`add()` generates a new distinct label.
|
||||
|
||||
\param name name of the label.
|
||||
|
||||
\param color used to represent the label.
|
||||
|
||||
\param standard_index standard index of the classification label
|
||||
(i.e. index in the ASPRS standard).
|
||||
|
||||
\return a handle to the newly added label.
|
||||
*/
|
||||
Label_handle add (const char* name,
|
||||
CGAL::Color color,
|
||||
std::size_t standard_index = -1)
|
||||
{
|
||||
Label_handle out = std::make_shared<Classification::Label>
|
||||
(name, m_labels.size(), standard_index, color);
|
||||
m_labels.push_back (out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief adds a label with default standard index and color.
|
||||
|
||||
This functions tries to map label names to standard ASPRS labels
|
||||
and automatically picks the `standard_index` and `color` of the
|
||||
label:
|
||||
|
||||
- `"unassigned"` is given standard index 2 and color `(0, 0, 0)`
|
||||
- `"ground"` is given standard index 2 and color `(186, 189, 182)`
|
||||
- `"low_vegetation"` is given standard index 3 and color `(78, 154, 6)`
|
||||
- `"medium_vegetation"` is given standard index 4 and color `(138, 226, 52)`
|
||||
- `"high_vegetation"` is given standard index 5 and color `(204, 255, 201)`
|
||||
- `"building"` is given standard index 6 and color `(245, 121, 0)`
|
||||
- `"noise"` is given standard index 7 and color `(128, 0, 0)`
|
||||
- `"reserved"` is given standard index 8 and color `(233, 185, 110)`
|
||||
- `"water"` is given standard index 9 and color `(114, 159, 207)`
|
||||
- `"rail"` is given standard index 10 and color `(136, 46, 25)`
|
||||
- `"road_surface"` is given standard index 11 and color `(56, 56, 56)`
|
||||
- `"reserved_2"` is given standard index 12 and color `(193, 138, 51)`
|
||||
- `"wire_guard"` is given standard index 13 and color `(37, 61, 136)`
|
||||
- `"wire_conductor"` is given standard index 14 and color `(173, 127, 168)`
|
||||
- `"transmission_tower"` is given standard index 15 and color `(136, 138, 133)`
|
||||
- `"wire_connect"` is given standard index 16 and color `(145, 64, 236)`
|
||||
- `"bridge_deck"` is given standard index 17 and color `(213, 93, 93)`
|
||||
- `"high_noise"` is given standard index 18 and color `(255, 0, 0)`
|
||||
|
||||
If the name is not found, the label is given standard index
|
||||
`std::size_t(-1)` and a random color.
|
||||
|
||||
\note Names are not used for identification: two labels in the
|
||||
same set can have the same name (but not the same handle).
|
||||
same set can have the same name but not the same handle. Each call
|
||||
to `add()` generates a new distinct label.
|
||||
|
||||
\param name name of the label.
|
||||
|
||||
|
|
@ -63,13 +134,80 @@ public:
|
|||
*/
|
||||
Label_handle add (const char* name)
|
||||
{
|
||||
Label_handle out (new Classification::Label (name));
|
||||
m_labels.push_back (out);
|
||||
return out;
|
||||
static std::unordered_map<std::string, std::pair<std::size_t, CGAL::Color> > init_map;
|
||||
if (init_map.empty())
|
||||
{
|
||||
init_map.insert (std::make_pair ("unassigned",
|
||||
std::make_pair (2, CGAL::Color (0, 0, 0))));
|
||||
init_map.insert (std::make_pair ("ground",
|
||||
std::make_pair (2, CGAL::Color (186, 189, 182))));
|
||||
init_map.insert (std::make_pair ("low_vegetation",
|
||||
std::make_pair (3, CGAL::Color (78, 154, 6))));
|
||||
init_map.insert (std::make_pair ("medium_vegetation",
|
||||
std::make_pair (4, CGAL::Color (138, 226, 52))));
|
||||
init_map.insert (std::make_pair ("high_vegetation",
|
||||
std::make_pair (5, CGAL::Color (204, 255, 201))));
|
||||
init_map.insert (std::make_pair ("building",
|
||||
std::make_pair (6, CGAL::Color (245, 121, 0))));
|
||||
init_map.insert (std::make_pair ("noise",
|
||||
std::make_pair (7, CGAL::Color (128, 0, 0))));
|
||||
init_map.insert (std::make_pair ("reserved",
|
||||
std::make_pair (8, CGAL::Color (233, 185, 110))));
|
||||
init_map.insert (std::make_pair ("water",
|
||||
std::make_pair (9, CGAL::Color (114, 159, 207))));
|
||||
init_map.insert (std::make_pair ("rail",
|
||||
std::make_pair (10, CGAL::Color (136, 46, 25))));
|
||||
init_map.insert (std::make_pair ("road_surface",
|
||||
std::make_pair (11, CGAL::Color (56, 56, 56))));
|
||||
init_map.insert (std::make_pair ("reserved_2",
|
||||
std::make_pair (12, CGAL::Color (193, 138, 51))));
|
||||
init_map.insert (std::make_pair ("wire_guard",
|
||||
std::make_pair (13, CGAL::Color (37, 61, 136))));
|
||||
init_map.insert (std::make_pair ("wire_conductor",
|
||||
std::make_pair (14, CGAL::Color (173, 127, 168))));
|
||||
init_map.insert (std::make_pair ("wire_conduct",
|
||||
std::make_pair (14, CGAL::Color (173, 127, 168))));
|
||||
init_map.insert (std::make_pair ("transmission_tower",
|
||||
std::make_pair (15, CGAL::Color (136, 138, 133))));
|
||||
init_map.insert (std::make_pair ("trans_tower",
|
||||
std::make_pair (15, CGAL::Color (136, 138, 133))));
|
||||
init_map.insert (std::make_pair ("wire_connect",
|
||||
std::make_pair (16, CGAL::Color (145, 64, 236))));
|
||||
init_map.insert (std::make_pair ("bridge_deck",
|
||||
std::make_pair (17, CGAL::Color (213, 93, 93))));
|
||||
init_map.insert (std::make_pair ("high_noise",
|
||||
std::make_pair (18, CGAL::Color (255, 0, 0))));
|
||||
|
||||
// Undocumented additions
|
||||
init_map.insert (std::make_pair ("low_veget",
|
||||
std::make_pair (3, CGAL::Color (78, 154, 6))));
|
||||
init_map.insert (std::make_pair ("medium_veget",
|
||||
std::make_pair (4, CGAL::Color (138, 226, 52))));
|
||||
init_map.insert (std::make_pair ("vegetation",
|
||||
std::make_pair (4, CGAL::Color (138, 226, 52))));
|
||||
init_map.insert (std::make_pair ("high_veget",
|
||||
std::make_pair (5, CGAL::Color (204, 255, 201))));
|
||||
init_map.insert (std::make_pair ("roof",
|
||||
std::make_pair (6, CGAL::Color (245, 121, 0))));
|
||||
init_map.insert (std::make_pair ("facade",
|
||||
std::make_pair (-1, CGAL::Color (77, 131, 186))));
|
||||
}
|
||||
|
||||
std::string sname (name);
|
||||
auto found = init_map.find (sname);
|
||||
if (found == init_map.end())
|
||||
return add (name,
|
||||
CGAL::Color ((unsigned char)(m_random.get_int(64, 192)),
|
||||
(unsigned char)(m_random.get_int(64, 192)),
|
||||
(unsigned char)(m_random.get_int(64, 192))));
|
||||
|
||||
// else
|
||||
return add (name, found->second.second, found->second.first);
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Removes a label.
|
||||
\brief removes a label.
|
||||
|
||||
\param label the handle to the label that must be removed.
|
||||
|
||||
|
|
@ -78,22 +216,38 @@ public:
|
|||
*/
|
||||
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))
|
||||
if (label->index() >= m_labels.size()
|
||||
|| m_labels[label->index()] != label)
|
||||
return false;
|
||||
|
||||
for (std::size_t i = label->index() + 1; i < m_labels.size(); ++ i)
|
||||
m_labels[i]->m_index --;
|
||||
m_labels.erase (m_labels.begin() + label->index());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Returns how many labels are defined.
|
||||
\brief removes all labels.
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_labels.clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
const_iterator begin() const { return m_labels.begin(); }
|
||||
iterator begin() { return m_labels.begin(); }
|
||||
const_iterator end() const { return m_labels.end(); }
|
||||
iterator end() { return m_labels.end(); }
|
||||
|
||||
/*!
|
||||
\brief returns how many labels are defined.
|
||||
*/
|
||||
std::size_t size () const
|
||||
{
|
||||
|
|
@ -101,22 +255,75 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the \f$i^{th}\f$ label.
|
||||
\brief returns the \f$i^{th}\f$ label.
|
||||
*/
|
||||
Label_handle operator[] (std::size_t i) const
|
||||
{
|
||||
return m_labels[i];
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \name Validity
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Removes all labels.
|
||||
\brief checks the validity of the ground truth with respect to the
|
||||
label set.
|
||||
|
||||
\param ground_truth range of label indices. This function checks
|
||||
that all these indices are either -1 (for unclassified) or a valid
|
||||
index of one of the labels. If at least one of the indices is out
|
||||
of range, this function returns `false`, otherwise it returns
|
||||
`true`.
|
||||
|
||||
\param verbose if set to `true`, the number of inliers of each
|
||||
label, the number of unclassified items and the potential number
|
||||
of out-of-range items are displayed. Otherwise, this function does
|
||||
not display anything.
|
||||
*/
|
||||
void clear ()
|
||||
template <typename LabelIndexRange>
|
||||
bool is_valid_ground_truth (const LabelIndexRange& ground_truth,
|
||||
bool verbose = false) const
|
||||
{
|
||||
m_labels.clear();
|
||||
std::vector<std::size_t> nb_inliers (m_labels.size() + 2, 0);
|
||||
std::size_t total = 0;
|
||||
|
||||
for (const auto& gt : ground_truth)
|
||||
{
|
||||
int g = int(gt);
|
||||
if (g == -1)
|
||||
++ nb_inliers[m_labels.size()];
|
||||
else if (g >= int(m_labels.size()))
|
||||
{
|
||||
++ nb_inliers[m_labels.size() + 1];
|
||||
if (!verbose)
|
||||
break;
|
||||
}
|
||||
else
|
||||
++ nb_inliers[std::size_t(gt)];
|
||||
++ total;
|
||||
}
|
||||
|
||||
bool valid = (nb_inliers[m_labels.size() + 1] == 0);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cout << "Ground truth is " << (valid ? "valid" : "invalid") << ":" << std::endl;
|
||||
std::cout << " * " << nb_inliers[m_labels.size()] << " unclassified item(s) ("
|
||||
<< 100. * (nb_inliers[m_labels.size()] / double(total)) << "%)" << std::endl;
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
std::cout << " * " << nb_inliers[i] << " " << m_labels[i]->name() << " inlier(s) ("
|
||||
<< 100. * (nb_inliers[i] / double(total)) << "%)" << std::endl;
|
||||
if (!valid)
|
||||
std::cout << " * " << nb_inliers[m_labels.size() + 1] << " item(s) with out-of-range index ("
|
||||
<< 100. * (nb_inliers[m_labels.size() + 1] / double(total)) << "%)" << std::endl;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <CGAL/license/Classification.h>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <CGAL/Classification/compressed_float.h>
|
||||
#include <CGAL/Search_traits_3.h>
|
||||
|
|
@ -28,7 +29,6 @@
|
|||
#include <CGAL/boost/graph/properties.h>
|
||||
|
||||
#include <boost/graph/graph_traits.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
#include <tbb/parallel_for.h>
|
||||
|
|
@ -57,7 +57,7 @@ namespace Classification {
|
|||
class Local_eigen_analysis
|
||||
{
|
||||
public:
|
||||
typedef std::array<float, 3> Eigenvalues; ///< Eigenvalues (sorted in ascending order)
|
||||
using Eigenvalues = std::array<float, 3>; ///< Eigenvalues (sorted in ascending order)
|
||||
|
||||
private:
|
||||
|
||||
|
|
@ -113,9 +113,9 @@ private:
|
|||
template <typename FaceListGraph, typename NeighborQuery, typename DiagonalizeTraits>
|
||||
class Compute_eigen_values_graph
|
||||
{
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_descriptor face_descriptor;
|
||||
typedef typename boost::property_map<FaceListGraph, CGAL::face_index_t>::type::value_type face_index;
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_iterator face_iterator;
|
||||
using face_descriptor = typename boost::graph_traits<FaceListGraph>::face_descriptor;
|
||||
using face_index = typename boost::property_map<FaceListGraph, CGAL::face_index_t>::type::value_type;
|
||||
using face_iterator = typename boost::graph_traits<FaceListGraph>::face_iterator;
|
||||
|
||||
Local_eigen_analysis& m_eigen;
|
||||
const FaceListGraph& m_input;
|
||||
|
|
@ -178,8 +178,8 @@ private:
|
|||
|
||||
inline void apply (std::size_t i) const
|
||||
{
|
||||
typedef typename ClusterRange::value_type Cluster;
|
||||
typedef typename Cluster::Item Item;
|
||||
using Cluster = typename ClusterRange::value_type;
|
||||
using Item = typename Cluster::Item;
|
||||
const Cluster& cluster = m_input[i];
|
||||
|
||||
std::vector<typename ClusterRange::value_type::Item> points;
|
||||
|
|
@ -191,9 +191,9 @@ private:
|
|||
|
||||
};
|
||||
|
||||
typedef std::array<float, 3> float3;
|
||||
typedef std::array<float, 2> float2;
|
||||
typedef std::array<compressed_float, 2> cfloat2;
|
||||
using float3 = std::array<float, 3>;
|
||||
using float2 = std::array<float, 2>;
|
||||
using cfloat2 = std::array<compressed_float, 2>;
|
||||
|
||||
struct Content
|
||||
{
|
||||
|
|
@ -203,7 +203,7 @@ private:
|
|||
float mean_range;
|
||||
};
|
||||
|
||||
boost::shared_ptr<Content> m_content; // To avoid copies with named constructors
|
||||
std::shared_ptr<Content> m_content; // To avoid copies with named constructors
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Computes the local eigen analysis of an input point set
|
||||
\brief computes the local eigen analysis of an input point set
|
||||
based on a local neighborhood.
|
||||
|
||||
\tparam PointRange model of `ConstRange`. Its iterator type is
|
||||
|
|
@ -257,7 +257,7 @@ public:
|
|||
const DiagonalizeTraits& = DiagonalizeTraits())
|
||||
{
|
||||
Local_eigen_analysis out;
|
||||
out.m_content = boost::make_shared<Content>();
|
||||
out.m_content = std::make_shared<Content>();
|
||||
out.m_content->eigenvalues.resize (input.size());
|
||||
out.m_content->centroids.resize (input.size());
|
||||
out.m_content->smallest_eigenvectors.resize (input.size());
|
||||
|
|
@ -265,10 +265,10 @@ public:
|
|||
out.m_content->mean_range = 0.;
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
CGAL_static_assertion_msg (!(std::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
if (std::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
std::mutex mutex;
|
||||
Compute_eigen_values<PointRange, PointMap, NeighborQuery, DiagonalizeTraits>
|
||||
|
|
@ -302,7 +302,7 @@ public:
|
|||
|
||||
|
||||
/*!
|
||||
\brief Computes the local eigen analysis of an input face graph
|
||||
\brief computes the local eigen analysis of an input face graph
|
||||
based on a local neighborhood.
|
||||
|
||||
\tparam FaceListGraph model of `FaceListGraph`.
|
||||
|
|
@ -336,13 +336,13 @@ public:
|
|||
const ConcurrencyTag& = ConcurrencyTag(),
|
||||
const DiagonalizeTraits& = DiagonalizeTraits())
|
||||
{
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_descriptor face_descriptor;
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_iterator face_iterator;
|
||||
typedef typename CGAL::Iterator_range<face_iterator> Face_range;
|
||||
typedef typename boost::property_map<FaceListGraph, CGAL::face_index_t>::type::value_type face_index;
|
||||
using face_descriptor = typename boost::graph_traits<FaceListGraph>::face_descriptor;
|
||||
using face_iterator = typename boost::graph_traits<FaceListGraph>::face_iterator;
|
||||
using Face_range = typename CGAL::Iterator_range<face_iterator>;
|
||||
using face_index = typename boost::property_map<FaceListGraph, CGAL::face_index_t>::type::value_type;
|
||||
|
||||
Local_eigen_analysis out;
|
||||
out.m_content = boost::make_shared<Content>();
|
||||
out.m_content = std::make_shared<Content>();
|
||||
|
||||
Face_range range (faces(input));
|
||||
|
||||
|
|
@ -353,10 +353,10 @@ public:
|
|||
out.m_content->mean_range = 0.;
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
CGAL_static_assertion_msg (!(std::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
if (std::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
std::mutex mutex;
|
||||
Compute_eigen_values_graph<FaceListGraph, NeighborQuery, DiagonalizeTraits>
|
||||
|
|
@ -385,7 +385,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Computes the local eigen analysis of an input set of point
|
||||
\brief computes the local eigen analysis of an input set of point
|
||||
clusters based on a local neighborhood.
|
||||
|
||||
\tparam ClusterRange model of `ConstRange`. Its iterator type is
|
||||
|
|
@ -418,7 +418,7 @@ public:
|
|||
const DiagonalizeTraits& = DiagonalizeTraits())
|
||||
{
|
||||
Local_eigen_analysis out;
|
||||
out.m_content = boost::make_shared<Content>();
|
||||
out.m_content = std::make_shared<Content>();
|
||||
|
||||
out.m_content->eigenvalues.resize (input.size());
|
||||
out.m_content->centroids.resize (input.size());
|
||||
|
|
@ -431,10 +431,10 @@ public:
|
|||
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
CGAL_static_assertion_msg (!(std::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
if (std::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, input.size ()), f);
|
||||
}
|
||||
|
|
@ -454,7 +454,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Returns the estimated unoriented normal vector of the point at position `index`.
|
||||
\brief returns the estimated unoriented normal vector of the point at position `index`.
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
*/
|
||||
template <typename GeomTraits>
|
||||
|
|
@ -467,7 +467,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the estimated local tangent plane of the point at position `index`.
|
||||
\brief returns the estimated local tangent plane of the point at position `index`.
|
||||
\tparam GeomTraits model of \cgal Kernel.
|
||||
*/
|
||||
template <typename GeomTraits>
|
||||
|
|
@ -481,7 +481,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the normalized eigenvalues of the point at position `index`.
|
||||
\brief returns the normalized eigenvalues of the point at position `index`.
|
||||
*/
|
||||
Eigenvalues eigenvalue (std::size_t index) const
|
||||
{
|
||||
|
|
@ -505,7 +505,7 @@ private:
|
|||
float face_radius (typename boost::graph_traits<FaceListGraph>::face_descriptor& fd,
|
||||
const FaceListGraph& g)
|
||||
{
|
||||
typedef typename boost::graph_traits<FaceListGraph>::halfedge_descriptor halfedge_descriptor;
|
||||
using halfedge_descriptor = typename boost::graph_traits<FaceListGraph>::halfedge_descriptor;
|
||||
|
||||
float out = 0.f;
|
||||
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, g), g))
|
||||
|
|
@ -520,7 +520,7 @@ 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;
|
||||
using Vector = typename Kernel_traits<Point>::Kernel::Vector_3;
|
||||
|
||||
if (neighbor_points.size() == 0)
|
||||
{
|
||||
|
|
@ -575,12 +575,12 @@ private:
|
|||
typename boost::graph_traits<FaceListGraph>::face_descriptor& query,
|
||||
std::vector<typename boost::property_map<FaceListGraph, CGAL::face_index_t>::type::value_type>& neighbor_faces)
|
||||
{
|
||||
typedef typename boost::property_map<FaceListGraph, boost::vertex_point_t>::type::value_type Point;
|
||||
typedef typename Kernel_traits<Point>::Kernel Kernel;
|
||||
typedef typename Kernel::Triangle_3 Triangle;
|
||||
using Point = typename boost::property_map<FaceListGraph, boost::vertex_point_t>::type::value_type;
|
||||
using Kernel = typename Kernel_traits<Point>::Kernel;
|
||||
using Triangle = typename Kernel::Triangle_3;
|
||||
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_descriptor face_descriptor;
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_iterator face_iterator;
|
||||
using face_descriptor = typename boost::graph_traits<FaceListGraph>::face_descriptor;
|
||||
using face_iterator = typename boost::graph_traits<FaceListGraph>::face_iterator;
|
||||
|
||||
if (neighbor_faces.size() == 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,19 +34,7 @@
|
|||
|
||||
#include <CGAL/bounding_box.h>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.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 <mutex>
|
||||
#endif // CGAL_LINKED_WITH_TBB
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
|
|
@ -101,55 +89,44 @@ class Mesh_feature_generator
|
|||
{
|
||||
|
||||
public:
|
||||
typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3;
|
||||
using Iso_cuboid_3 = typename GeomTraits::Iso_cuboid_3;
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_descriptor face_descriptor;
|
||||
typedef typename boost::graph_traits<FaceListGraph>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename boost::graph_traits<FaceListGraph>::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_iterator face_iterator;
|
||||
typedef typename CGAL::Iterator_range<face_iterator> Face_range;
|
||||
using face_descriptor = typename boost::graph_traits<FaceListGraph>::face_descriptor;
|
||||
using halfedge_descriptor = typename boost::graph_traits<FaceListGraph>::halfedge_descriptor;
|
||||
using vertex_descriptor = typename boost::graph_traits<FaceListGraph>::vertex_descriptor;
|
||||
using face_iterator = typename boost::graph_traits<FaceListGraph>::face_iterator;
|
||||
using Face_range = typename CGAL::Iterator_range<face_iterator>;
|
||||
|
||||
typedef typename PointMap::value_type Point;
|
||||
typedef CGAL::Identity_property_map<face_descriptor> Face_map;
|
||||
using Point = typename PointMap::value_type;
|
||||
using Face_map = CGAL::Identity_property_map<face_descriptor>;
|
||||
/// \endcond
|
||||
|
||||
|
||||
public:
|
||||
|
||||
typedef Classification::Planimetric_grid
|
||||
<GeomTraits, Face_range, PointMap> Planimetric_grid;
|
||||
typedef Classification::Mesh_neighborhood
|
||||
<FaceListGraph> Neighborhood;
|
||||
typedef Classification::Local_eigen_analysis Local_eigen_analysis;
|
||||
using Planimetric_grid = Classification::Planimetric_grid<GeomTraits, Face_range, PointMap>;
|
||||
using Neighborhood = Classification::Mesh_neighborhood<FaceListGraph>;
|
||||
using Local_eigen_analysis = Classification::Local_eigen_analysis;
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
typedef Classification::Feature_handle Feature_handle;
|
||||
|
||||
typedef Classification::Feature::Distance_to_plane
|
||||
<Face_range, PointMap> Distance_to_plane;
|
||||
typedef Classification::Feature::Elevation
|
||||
<GeomTraits, Face_range, PointMap> Elevation;
|
||||
typedef Classification::Feature::Height_below
|
||||
<GeomTraits, Face_range, PointMap> Height_below;
|
||||
typedef Classification::Feature::Height_above
|
||||
<GeomTraits, Face_range, PointMap> Height_above;
|
||||
typedef Classification::Feature::Vertical_range
|
||||
<GeomTraits, Face_range, PointMap> Vertical_range;
|
||||
typedef Classification::Feature::Vertical_dispersion
|
||||
<GeomTraits, Face_range, PointMap> Dispersion;
|
||||
typedef Classification::Feature::Verticality
|
||||
<GeomTraits> Verticality;
|
||||
typedef Classification::Feature::Eigenvalue Eigenvalue;
|
||||
using Feature_handle = Classification::Feature_handle;
|
||||
using Distance_to_plane = Classification::Feature::Distance_to_plane<Face_range, PointMap>;
|
||||
using Elevation = Classification::Feature::Elevation<GeomTraits, Face_range, PointMap>;
|
||||
using Height_below = Classification::Feature::Height_below<GeomTraits, Face_range, PointMap>;
|
||||
using Height_above = Classification::Feature::Height_above<GeomTraits, Face_range, PointMap>;
|
||||
using Vertical_range = Classification::Feature::Vertical_range<GeomTraits, Face_range, PointMap>;
|
||||
using Dispersion = Classification::Feature::Vertical_dispersion<GeomTraits, Face_range, PointMap>;
|
||||
using Verticality = Classification::Feature::Verticality<GeomTraits>;
|
||||
using Eigenvalue = Classification::Feature::Eigenvalue;
|
||||
/// \endcond
|
||||
|
||||
private:
|
||||
|
||||
struct Scale
|
||||
{
|
||||
Neighborhood* neighborhood;
|
||||
Planimetric_grid* grid;
|
||||
Local_eigen_analysis* eigen;
|
||||
std::unique_ptr<Neighborhood> neighborhood;
|
||||
std::unique_ptr<Planimetric_grid> grid;
|
||||
std::unique_ptr<Local_eigen_analysis> eigen;
|
||||
float voxel_size;
|
||||
|
||||
Scale (const FaceListGraph& input,
|
||||
|
|
@ -157,12 +134,13 @@ private:
|
|||
PointMap point_map,
|
||||
const Iso_cuboid_3& bbox, float voxel_size,
|
||||
std::size_t nb_scale,
|
||||
Planimetric_grid* lower_grid = nullptr)
|
||||
const std::unique_ptr<Planimetric_grid>& lower_grid
|
||||
= std::unique_ptr<Planimetric_grid>())
|
||||
: voxel_size (voxel_size)
|
||||
{
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
neighborhood = new Neighborhood (input);
|
||||
neighborhood = std::make_unique<Neighborhood> (input);
|
||||
t.stop();
|
||||
|
||||
CGAL_CLASSIFICATION_CERR << "Neighborhood computed in " << t.time() << " second(s)" << std::endl;
|
||||
|
|
@ -170,7 +148,7 @@ private:
|
|||
t.reset();
|
||||
t.start();
|
||||
|
||||
eigen = new Local_eigen_analysis
|
||||
eigen = std::make_unique<Local_eigen_analysis>
|
||||
(Local_eigen_analysis::create_from_face_graph
|
||||
(input, neighborhood->n_ring_neighbor_query(nb_scale + 1),
|
||||
ConcurrencyTag(), DiagonalizeTraits()));
|
||||
|
|
@ -183,42 +161,22 @@ private:
|
|||
t.reset();
|
||||
t.start();
|
||||
|
||||
if (lower_grid == nullptr)
|
||||
grid = new Planimetric_grid (range, point_map, bbox, this->voxel_size);
|
||||
if (!lower_grid)
|
||||
grid = std::make_unique<Planimetric_grid> (range, point_map, bbox, this->voxel_size);
|
||||
else
|
||||
grid = new Planimetric_grid(lower_grid);
|
||||
grid = std::make_unique<Planimetric_grid>(lower_grid.get());
|
||||
t.stop();
|
||||
CGAL_CLASSIFICATION_CERR << "Planimetric grid computed in " << t.time() << " second(s)" << std::endl;
|
||||
t.reset();
|
||||
}
|
||||
~Scale()
|
||||
{
|
||||
if (neighborhood != nullptr)
|
||||
delete neighborhood;
|
||||
if (grid != nullptr)
|
||||
delete grid;
|
||||
delete eigen;
|
||||
}
|
||||
|
||||
void reduce_memory_footprint(bool delete_neighborhood)
|
||||
{
|
||||
delete grid;
|
||||
grid = nullptr;
|
||||
if (delete_neighborhood)
|
||||
{
|
||||
delete neighborhood;
|
||||
neighborhood = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
float grid_resolution() const { return voxel_size; }
|
||||
float radius_neighbors() const { return voxel_size * 3; }
|
||||
float radius_dtm() const { return voxel_size * 10; }
|
||||
|
||||
};
|
||||
|
||||
Iso_cuboid_3 m_bbox;
|
||||
std::vector<Scale*> m_scales;
|
||||
std::vector<std::unique_ptr<Scale> > m_scales;
|
||||
|
||||
const FaceListGraph& m_input;
|
||||
Face_range m_range;
|
||||
|
|
@ -231,7 +189,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Initializes a feature generator from an input range.
|
||||
\brief initializes a feature generator from an input range.
|
||||
|
||||
If not provided by the user, The size of the smallest scale is
|
||||
automatically estimated using a method equivalent to
|
||||
|
|
@ -256,14 +214,14 @@ public:
|
|||
{
|
||||
|
||||
m_bbox = CGAL::bounding_box
|
||||
(boost::make_transform_iterator (m_range.begin(), CGAL::Property_map_to_unary_function<PointMap>(m_point_map)),
|
||||
boost::make_transform_iterator (m_range.end(), CGAL::Property_map_to_unary_function<PointMap>(m_point_map)));
|
||||
(CGAL::make_transform_iterator_from_property_map (m_range.begin(), m_point_map),
|
||||
CGAL::make_transform_iterator_from_property_map (m_range.end(), m_point_map));
|
||||
|
||||
CGAL::Real_timer t; t.start();
|
||||
|
||||
m_scales.reserve (nb_scales);
|
||||
|
||||
m_scales.push_back (new Scale (m_input, m_range, m_point_map, m_bbox, voxel_size, 0));
|
||||
m_scales.emplace_back (std::make_unique<Scale> (m_input, m_range, m_point_map, m_bbox, voxel_size, 0));
|
||||
|
||||
if (voxel_size == -1.f)
|
||||
voxel_size = m_scales[0]->grid_resolution();
|
||||
|
|
@ -271,7 +229,7 @@ public:
|
|||
for (std::size_t i = 1; i < nb_scales; ++ i)
|
||||
{
|
||||
voxel_size *= 2;
|
||||
m_scales.push_back (new Scale (m_input, m_range, m_point_map, m_bbox, voxel_size, i, m_scales[i-1]->grid));
|
||||
m_scales.emplace_back (std::make_unique<Scale> (m_input, m_range, m_point_map, m_bbox, voxel_size, i, m_scales[i-1]->grid));
|
||||
}
|
||||
t.stop();
|
||||
CGAL_CLASSIFICATION_CERR << "Scales computed in " << t.time() << " second(s)" << std::endl;
|
||||
|
|
@ -280,28 +238,11 @@ public:
|
|||
|
||||
/// @}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
virtual ~Mesh_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
|
||||
|
||||
|
||||
/// \name Feature Generation
|
||||
/// @{
|
||||
|
||||
|
||||
/*!
|
||||
\brief Generate geometric features based on face information.
|
||||
\brief generates geometric features based on face information.
|
||||
|
||||
At each scale, the following features are generated:
|
||||
|
||||
|
|
@ -320,7 +261,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Generate geometric features based on point position information.
|
||||
\brief generates geometric features based on point position information.
|
||||
|
||||
At each scale, the following features are generated by considering
|
||||
the mesh as a point cloud through `PointMap`:
|
||||
|
|
@ -356,19 +297,19 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Returns the bounding box of the input point set.
|
||||
\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`.
|
||||
\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`.
|
||||
\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`.
|
||||
\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); }
|
||||
|
||||
|
|
@ -378,26 +319,26 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Returns the number of scales that were computed.
|
||||
\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
|
||||
\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
|
||||
\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
|
||||
\brief returns the radius used for digital terrain modeling at
|
||||
scale `scale`. This radius represents the minimum size of a
|
||||
building at this scale.
|
||||
*/
|
||||
|
|
@ -405,15 +346,6 @@ public:
|
|||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
delete m_scales[i];
|
||||
m_scales.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#include <CGAL/Handle_hash_function.h>
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/boost/graph/properties.h>
|
||||
#include <CGAL/array.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
|
|
@ -41,21 +40,21 @@ template <typename FaceListGraph>
|
|||
class Mesh_neighborhood
|
||||
{
|
||||
public:
|
||||
typedef typename boost::graph_traits<FaceListGraph>::face_descriptor face_descriptor; ///<
|
||||
using face_descriptor = typename boost::graph_traits<FaceListGraph>::face_descriptor; ///<
|
||||
|
||||
private:
|
||||
typedef typename boost::graph_traits<FaceListGraph>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename boost::graph_traits<FaceListGraph>::vertex_descriptor vertex_descriptor;
|
||||
using halfedge_descriptor = typename boost::graph_traits<FaceListGraph>::halfedge_descriptor;
|
||||
using vertex_descriptor = typename boost::graph_traits<FaceListGraph>::vertex_descriptor;
|
||||
const FaceListGraph& m_mesh;
|
||||
|
||||
class Is_face_selected
|
||||
{
|
||||
public:
|
||||
typedef face_descriptor key_type;
|
||||
typedef bool value_type;
|
||||
typedef bool reference;
|
||||
typedef boost::read_write_property_map_tag category;
|
||||
typedef typename std::unordered_set<face_descriptor, CGAL::Handle_hash_function> Set;
|
||||
using key_type = face_descriptor;
|
||||
using value_type = bool;
|
||||
using reference = bool;
|
||||
using category = boost::read_write_property_map_tag;
|
||||
using Set = typename std::unordered_set<face_descriptor, CGAL::Handle_hash_function>;
|
||||
private:
|
||||
Set* m_set;
|
||||
|
||||
|
|
@ -85,14 +84,14 @@ public:
|
|||
class One_ring_neighbor_query
|
||||
{
|
||||
public:
|
||||
typedef typename Mesh_neighborhood::face_descriptor value_type; ///<
|
||||
using value_type = typename Mesh_neighborhood::face_descriptor; ///<
|
||||
private:
|
||||
const Mesh_neighborhood& neighborhood;
|
||||
|
||||
public:
|
||||
|
||||
/*!
|
||||
\brief Constructs a 1-ring neighbor query object.
|
||||
\brief constructs a 1-ring neighbor query object.
|
||||
\param neighborhood mesh neighborhood object.
|
||||
*/
|
||||
One_ring_neighbor_query (const Mesh_neighborhood& neighborhood)
|
||||
|
|
@ -118,7 +117,7 @@ public:
|
|||
class N_ring_neighbor_query
|
||||
{
|
||||
public:
|
||||
typedef typename Mesh_neighborhood::face_descriptor value_type; ///<
|
||||
using value_type = typename Mesh_neighborhood::face_descriptor; ///<
|
||||
private:
|
||||
const Mesh_neighborhood& neighborhood;
|
||||
const std::size_t n;
|
||||
|
|
@ -126,7 +125,7 @@ public:
|
|||
public:
|
||||
|
||||
/*!
|
||||
\brief Constructs a N-ring neighbor query object.
|
||||
\brief constructs a N-ring neighbor query object.
|
||||
\param neighborhood mesh neighborhood object.
|
||||
\param n size of neighborhood.
|
||||
*/
|
||||
|
|
@ -152,7 +151,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Constructs a neighborhood object based on the input mesh.
|
||||
\brief constructs a neighborhood object based on the input mesh.
|
||||
|
||||
\param mesh input mesh.
|
||||
*/
|
||||
|
|
@ -172,7 +171,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Returns a 1-ring neighbor query object.
|
||||
\brief returns a 1-ring neighbor query object.
|
||||
*/
|
||||
One_ring_neighbor_query one_ring_neighbor_query () const
|
||||
{
|
||||
|
|
@ -180,7 +179,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns an N-ring neighbor query object.
|
||||
\brief returns an N-ring neighbor query object.
|
||||
*/
|
||||
N_ring_neighbor_query n_ring_neighbor_query (const std::size_t n) const
|
||||
{
|
||||
|
|
@ -196,9 +195,7 @@ private:
|
|||
void direct_neighbors (const face_descriptor& query, OutputIterator output) const
|
||||
{
|
||||
for(halfedge_descriptor hd : halfedges_around_face(halfedge(query, m_mesh), m_mesh))
|
||||
{
|
||||
*(output ++ ) = face(opposite(hd, m_mesh), m_mesh);
|
||||
}
|
||||
*(output ++ ) = face(opposite(hd, m_mesh), m_mesh);
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Instantiates the classifier using the sets of `labels` and `features`.
|
||||
\brief instantiates the classifier using the sets of `labels` and `features`.
|
||||
|
||||
Parameters documentation is copy-pasted from [the official documentation of OpenCV](https://docs.opencv.org/2.4/modules/ml/doc/random_trees.html). For more details on this method, please refer to it.
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\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
|
||||
|
|
@ -147,6 +147,8 @@ public:
|
|||
template <typename LabelIndexRange>
|
||||
void train (const LabelIndexRange& ground_truth)
|
||||
{
|
||||
CGAL_precondition (m_labels.is_valid_ground_truth (ground_truth));
|
||||
|
||||
#if (CV_MAJOR_VERSION < 3)
|
||||
if (rtree != nullptr)
|
||||
delete rtree;
|
||||
|
|
@ -160,33 +162,46 @@ public:
|
|||
#endif
|
||||
|
||||
std::size_t nb_samples = 0;
|
||||
for (std::size_t i = 0; i < ground_truth.size(); ++ i)
|
||||
if (int(ground_truth[i]) != -1)
|
||||
for (const auto& gt_value : ground_truth)
|
||||
if (int(gt_value) != -1)
|
||||
++ nb_samples;
|
||||
|
||||
cv::Mat training_features (int(nb_samples), int(m_features.size()), CV_32FC1);
|
||||
cv::Mat training_labels (int(nb_samples), 1, CV_32FC1);
|
||||
|
||||
for (std::size_t i = 0, index = 0; i < ground_truth.size(); ++ i)
|
||||
if (int(ground_truth[i]) != -1)
|
||||
std::size_t i = 0, index = 0;
|
||||
for (const auto& gt_value : ground_truth)
|
||||
{
|
||||
if (int(gt_value) != -1)
|
||||
{
|
||||
for (std::size_t f = 0; f < m_features.size(); ++ f)
|
||||
training_features.at<float>(int(index), int(f)) = m_features[f]->value(i);
|
||||
training_labels.at<float>(int(index), 0) = static_cast<float>(ground_truth[i]);
|
||||
training_labels.at<float>(int(index), 0) = static_cast<float>(gt_value);
|
||||
++ index;
|
||||
}
|
||||
++ i;
|
||||
}
|
||||
|
||||
|
||||
#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
|
||||
);
|
||||
CvRTParams params;
|
||||
|
||||
if (m_forest_accuracy == 0.f)
|
||||
params = CvRTParams
|
||||
(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);
|
||||
else
|
||||
params = CvRTParams
|
||||
(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_EPS | CV_TERMCRIT_ITER);
|
||||
|
||||
cv::Mat var_type (m_features.size() + 1, 1, CV_8U);
|
||||
var_type.setTo (cv::Scalar(CV_VAR_NUMERICAL));
|
||||
|
|
@ -207,7 +222,12 @@ public:
|
|||
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);
|
||||
cv::TermCriteria criteria;
|
||||
if (m_forest_accuracy == 0.f)
|
||||
criteria = cv::TermCriteria (cv::TermCriteria::COUNT, m_max_number_of_trees_in_the_forest, m_forest_accuracy);
|
||||
else
|
||||
criteria = cv::TermCriteria (cv::TermCriteria::EPS + cv::TermCriteria::COUNT, m_max_number_of_trees_in_the_forest, m_forest_accuracy);
|
||||
|
||||
rtree->setTermCriteria (criteria);
|
||||
|
||||
cv::Ptr<cv::ml::TrainData> tdata = cv::ml::TrainData::create
|
||||
|
|
@ -262,7 +282,7 @@ public:
|
|||
|
||||
|
||||
/*!
|
||||
\brief Saves the current configuration in the file named `filename`.
|
||||
\brief saves the current configuration in the file named `filename`.
|
||||
|
||||
This allows to easily save and recover a specific classification
|
||||
configuration.
|
||||
|
|
@ -276,7 +296,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Loads a configuration from the file named `filename`.
|
||||
\brief loads a configuration from the file named `filename`.
|
||||
|
||||
The input file should be in the XML format written by the
|
||||
`save_configuration()` method. The feature set of the classifier
|
||||
|
|
|
|||
|
|
@ -51,12 +51,12 @@ 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;
|
||||
using Point_3 = typename GeomTraits::Point_3;
|
||||
using Iso_cuboid_3 = typename GeomTraits::Iso_cuboid_3;
|
||||
|
||||
private:
|
||||
typedef Image<std::vector<boost::uint32_t> > Image_indices;
|
||||
typedef Image<bool> Image_bool;
|
||||
using Image_indices = Image<std::vector<std::uint32_t> >;
|
||||
using Image_bool = Image<bool>;
|
||||
|
||||
const PointRange* m_points;
|
||||
PointMap m_point_map;
|
||||
|
|
@ -74,7 +74,7 @@ private:
|
|||
public:
|
||||
|
||||
#ifdef DOXYGEN_RUNNING
|
||||
typedef unspecified_type iterator; ///< A forward iterator with value type `std::size_t`.
|
||||
using iterator = unspecified_type; ///< A forward iterator with value type `std::size_t`.
|
||||
#else
|
||||
class iterator
|
||||
: public boost::iterator_facade<iterator,
|
||||
|
|
@ -194,7 +194,7 @@ public:
|
|||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Constructs a planimetric grid based on the input range.
|
||||
\brief constructs a planimetric grid based on the input range.
|
||||
|
||||
\param input point range.
|
||||
\param point_map property map to access the input points.
|
||||
|
|
@ -216,9 +216,9 @@ public:
|
|||
for (std::size_t i = 0; i < input.size(); ++ i)
|
||||
{
|
||||
const Point_3& p = get(point_map, *(input.begin()+i));
|
||||
std::size_t x = (boost::uint32_t)((p.x() - bbox.xmin()) / grid_resolution);
|
||||
std::size_t y = (boost::uint32_t)((p.y() - bbox.ymin()) / grid_resolution);
|
||||
m_grid(x,y).push_back (boost::uint32_t(i));
|
||||
std::size_t x = (std::uint32_t)((p.x() - bbox.xmin()) / grid_resolution);
|
||||
std::size_t y = (std::uint32_t)((p.y() - bbox.ymin()) / grid_resolution);
|
||||
m_grid(x,y).push_back (std::uint32_t(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,7 +266,7 @@ public:
|
|||
|
||||
|
||||
/*!
|
||||
\brief Returns the resolution of the grid.
|
||||
\brief returns the resolution of the grid.
|
||||
*/
|
||||
float resolution() const
|
||||
{
|
||||
|
|
@ -274,14 +274,14 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the number of cells along the X-axis.
|
||||
\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.
|
||||
\brief returns the number of cells along the Y-axis.
|
||||
*/
|
||||
std::size_t height() const
|
||||
{
|
||||
|
|
@ -300,7 +300,7 @@ public:
|
|||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns the begin iterator on the indices of the points
|
||||
\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
|
||||
|
|
@ -311,7 +311,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the past-the-end iterator on the indices of the points
|
||||
\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
|
||||
|
|
@ -322,7 +322,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns `false` if the cell at position `(x,y)` is empty, `true` otherwise.
|
||||
\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
|
||||
{
|
||||
|
|
@ -336,7 +336,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns the `x` grid coordinate of the point at position `index`.
|
||||
\brief returns the `x` grid coordinate of the point at position `index`.
|
||||
*/
|
||||
std::size_t x(std::size_t index) const
|
||||
{
|
||||
|
|
@ -350,7 +350,7 @@ public:
|
|||
return m_lower_scale->x(index) / 2;
|
||||
}
|
||||
/*!
|
||||
\brief Returns the `y` grid coordinate of the point at position `index`.
|
||||
\brief returns the `y` grid coordinate of the point at position `index`.
|
||||
*/
|
||||
std::size_t y(std::size_t index) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,24 +30,11 @@
|
|||
#include <CGAL/Classification/Feature/Height_above.h>
|
||||
#include <CGAL/Classification/Feature/Vertical_range.h>
|
||||
|
||||
// Experimental feature, not used officially
|
||||
#ifdef CGAL_CLASSIFICATION_USE_GRADIENT_OF_FEATURE
|
||||
#include <CGAL/Classification/Feature/Gradient_of_feature.h>
|
||||
#endif
|
||||
|
||||
#include <CGAL/Classification/Label.h>
|
||||
#include <CGAL/Classification/internal/verbosity.h>
|
||||
#include <CGAL/Classification/Feature_set.h>
|
||||
#include <CGAL/bounding_box.h>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.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>
|
||||
|
||||
|
||||
namespace CGAL {
|
||||
|
|
@ -102,71 +89,54 @@ class Point_set_feature_generator
|
|||
{
|
||||
|
||||
public:
|
||||
typedef typename GeomTraits::Iso_cuboid_3 Iso_cuboid_3;
|
||||
using Iso_cuboid_3 = typename GeomTraits::Iso_cuboid_3;
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
typedef typename PointRange::const_iterator Iterator;
|
||||
typedef typename PointMap::value_type Point;
|
||||
using Iterator = typename PointRange::const_iterator;
|
||||
using Point = typename PointMap::value_type;
|
||||
/// \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;
|
||||
using Planimetric_grid = Classification::Planimetric_grid<GeomTraits, PointRange, PointMap>;
|
||||
using Neighborhood = Classification::Point_set_neighborhood<GeomTraits, PointRange, PointMap>;
|
||||
using Local_eigen_analysis = Classification::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::Distance_to_plane
|
||||
<PointRange, PointMap> Distance_to_plane;
|
||||
typedef Classification::Feature::Elevation
|
||||
<GeomTraits, PointRange, PointMap> Elevation;
|
||||
typedef Classification::Feature::Height_below
|
||||
<GeomTraits, PointRange, PointMap> Height_below;
|
||||
typedef Classification::Feature::Height_above
|
||||
<GeomTraits, PointRange, PointMap> Height_above;
|
||||
typedef Classification::Feature::Vertical_range
|
||||
<GeomTraits, PointRange, PointMap> Vertical_range;
|
||||
typedef Classification::Feature::Vertical_dispersion
|
||||
<GeomTraits, PointRange, PointMap> Dispersion;
|
||||
typedef Classification::Feature::Verticality
|
||||
<GeomTraits> Verticality;
|
||||
typedef Classification::Feature::Eigenvalue Eigenvalue;
|
||||
|
||||
typedef typename Neighborhood::K_neighbor_query Neighbor_query;
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_USE_GRADIENT_OF_FEATURE
|
||||
typedef Classification::Feature::Gradient_of_feature
|
||||
<PointRange, PointMap, Neighbor_query> Gradient_of_feature;
|
||||
#endif
|
||||
using Feature_handle = Classification::Feature_handle;
|
||||
using Distance_to_plane = Classification::Feature::Distance_to_plane<PointRange, PointMap>;
|
||||
using Elevation = Classification::Feature::Elevation<GeomTraits, PointRange, PointMap>;
|
||||
using Height_below = Classification::Feature::Height_below<GeomTraits, PointRange, PointMap>;
|
||||
using Height_above = Classification::Feature::Height_above<GeomTraits, PointRange, PointMap>;
|
||||
using Vertical_range = Classification::Feature::Vertical_range<GeomTraits, PointRange, PointMap>;
|
||||
using Dispersion = Classification::Feature::Vertical_dispersion<GeomTraits, PointRange, PointMap>;
|
||||
using Verticality = Classification::Feature::Verticality<GeomTraits>;
|
||||
using Eigenvalue = Classification::Feature::Eigenvalue;
|
||||
using Neighbor_query = typename Neighborhood::K_neighbor_query;
|
||||
/// \endcond
|
||||
|
||||
private:
|
||||
|
||||
struct Scale
|
||||
{
|
||||
Neighborhood* neighborhood;
|
||||
Planimetric_grid* grid;
|
||||
Local_eigen_analysis* eigen;
|
||||
std::unique_ptr<Neighborhood> neighborhood;
|
||||
std::unique_ptr<Planimetric_grid> grid;
|
||||
std::unique_ptr<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 = nullptr)
|
||||
const std::unique_ptr<Planimetric_grid>& lower_grid
|
||||
= std::unique_ptr<Planimetric_grid>())
|
||||
: voxel_size (voxel_size)
|
||||
{
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
if (lower_grid == nullptr)
|
||||
neighborhood = new Neighborhood (input, point_map, ConcurrencyTag());
|
||||
if (!lower_grid)
|
||||
neighborhood = std::make_unique<Neighborhood> (input, point_map, ConcurrencyTag());
|
||||
else
|
||||
neighborhood = new Neighborhood (input, point_map, voxel_size, ConcurrencyTag());
|
||||
neighborhood = std::make_unique<Neighborhood> (input, point_map, voxel_size, ConcurrencyTag());
|
||||
t.stop();
|
||||
|
||||
if (lower_grid == nullptr)
|
||||
if (!lower_grid)
|
||||
CGAL_CLASSIFICATION_CERR << "Neighborhood computed in " << t.time() << " second(s)" << std::endl;
|
||||
else
|
||||
CGAL_CLASSIFICATION_CERR << "Neighborhood with voxel size " << voxel_size
|
||||
|
|
@ -174,7 +144,7 @@ private:
|
|||
t.reset();
|
||||
t.start();
|
||||
|
||||
eigen = new Local_eigen_analysis
|
||||
eigen = std::make_unique<Local_eigen_analysis>
|
||||
(Local_eigen_analysis::create_from_point_set
|
||||
(input, point_map, neighborhood->k_neighbor_query(12), ConcurrencyTag(), DiagonalizeTraits()));
|
||||
|
||||
|
|
@ -187,33 +157,14 @@ private:
|
|||
t.reset();
|
||||
t.start();
|
||||
|
||||
if (lower_grid == nullptr)
|
||||
grid = new Planimetric_grid (input, point_map, bbox, this->voxel_size);
|
||||
if (!lower_grid)
|
||||
grid = std::make_unique<Planimetric_grid> (input, point_map, bbox, this->voxel_size);
|
||||
else
|
||||
grid = new Planimetric_grid(lower_grid);
|
||||
grid = std::make_unique<Planimetric_grid>(lower_grid.get());
|
||||
t.stop();
|
||||
CGAL_CLASSIFICATION_CERR << "Planimetric grid computed in " << t.time() << " second(s)" << std::endl;
|
||||
t.reset();
|
||||
}
|
||||
~Scale()
|
||||
{
|
||||
if (neighborhood != nullptr)
|
||||
delete neighborhood;
|
||||
if (grid != nullptr)
|
||||
delete grid;
|
||||
delete eigen;
|
||||
}
|
||||
|
||||
void reduce_memory_footprint(bool delete_neighborhood)
|
||||
{
|
||||
delete grid;
|
||||
grid = nullptr;
|
||||
if (delete_neighborhood)
|
||||
{
|
||||
delete neighborhood;
|
||||
neighborhood = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
float grid_resolution() const { return voxel_size; }
|
||||
float radius_neighbors() const { return voxel_size * 3; }
|
||||
|
|
@ -222,7 +173,7 @@ private:
|
|||
};
|
||||
|
||||
Iso_cuboid_3 m_bbox;
|
||||
std::vector<Scale*> m_scales;
|
||||
std::vector<std::unique_ptr<Scale> > m_scales;
|
||||
|
||||
const PointRange& m_input;
|
||||
PointMap m_point_map;
|
||||
|
|
@ -233,7 +184,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Initializes a feature generator from an input range.
|
||||
\brief initializes a feature generator from an input range.
|
||||
|
||||
If not provided by the user, The size of the smallest scale is
|
||||
automatically estimated using a method equivalent to
|
||||
|
|
@ -256,14 +207,14 @@ public:
|
|||
: m_input (input), m_point_map (point_map)
|
||||
{
|
||||
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)));
|
||||
(CGAL::make_transform_iterator_from_property_map (m_input.begin(), m_point_map),
|
||||
CGAL::make_transform_iterator_from_property_map (m_input.end(), m_point_map));
|
||||
|
||||
CGAL::Real_timer t; t.start();
|
||||
|
||||
m_scales.reserve (nb_scales);
|
||||
|
||||
m_scales.push_back (new Scale (m_input, m_point_map, m_bbox, voxel_size));
|
||||
m_scales.emplace_back (std::make_unique<Scale> (m_input, m_point_map, m_bbox, voxel_size));
|
||||
|
||||
if (voxel_size == -1.f)
|
||||
voxel_size = m_scales[0]->grid_resolution();
|
||||
|
|
@ -271,7 +222,7 @@ public:
|
|||
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));
|
||||
m_scales.push_back (std::make_unique<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;
|
||||
|
|
@ -280,50 +231,78 @@ public:
|
|||
|
||||
/// @}
|
||||
|
||||
/// \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
|
||||
|
||||
/// \name Feature Generation
|
||||
/// @{
|
||||
|
||||
|
||||
/*!
|
||||
\brief Generate geometric features based on point position information.
|
||||
\brief generates geometric features based on point position information.
|
||||
|
||||
At each scale, the following features are generated:
|
||||
This is a meta-function that calls the following functions:
|
||||
|
||||
- `CGAL::Classification::Feature::Eigenvalue` with indices 0, 1 and 2
|
||||
- `CGAL::Classification::Feature::Distance_to_plane`
|
||||
- `CGAL::Classification::Feature::Elevation`
|
||||
- `CGAL::Classification::Feature::Height_above`
|
||||
- `CGAL::Classification::Feature::Height_below`
|
||||
- `CGAL::Classification::Feature::Vertical_dispersion`
|
||||
- `CGAL::Classification::Feature::Vertical_range`
|
||||
- The version of `CGAL::Classification::Feature::Verticality` based on eigenvalues
|
||||
- `generate_eigen_features()`
|
||||
- `generate_dispersion_features()`
|
||||
- `generate_elevation_features()`
|
||||
- The version of `generate_normal_based_features()` without a normal map
|
||||
|
||||
\param features the feature set where the features are instantiated.
|
||||
*/
|
||||
void generate_point_based_features (Feature_set& features)
|
||||
{
|
||||
generate_eigen_features (features);
|
||||
generate_dispersion_features (features);
|
||||
generate_elevation_features (features);
|
||||
generate_normal_based_features (features);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief generates geometric eigen features.
|
||||
|
||||
At each scale, features
|
||||
`CGAL::Classification::Feature::Eigenvalue` with indices 0, 1 and
|
||||
2 are generated.
|
||||
|
||||
\param features the feature set where the features are instantiated.
|
||||
*/
|
||||
void generate_eigen_features (Feature_set& features)
|
||||
{
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
features.add_with_scale_id<Eigenvalue> (i, m_input, eigen(i), (unsigned int)(j));
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief generates geometric features based on local dispersion information.
|
||||
|
||||
At each scale, the following features are generated:
|
||||
|
||||
- `CGAL::Classification::Feature::Distance_to_plane`
|
||||
- `CGAL::Classification::Feature::Vertical_dispersion`
|
||||
|
||||
\param features the feature set where the features are instantiated.
|
||||
*/
|
||||
void generate_dispersion_features (Feature_set& features)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
features.add_with_scale_id<Distance_to_plane> (i, m_input, m_point_map, eigen(i));
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
features.add_with_scale_id<Dispersion> (i, m_input, m_point_map, grid(i), radius_neighbors(i));
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief generates geometric features based on elevation information.
|
||||
|
||||
At each scale, the following features are generated:
|
||||
|
||||
- `CGAL::Classification::Feature::Elevation`
|
||||
- `CGAL::Classification::Feature::Height_above`
|
||||
- `CGAL::Classification::Feature::Height_below`
|
||||
- `CGAL::Classification::Feature::Vertical_range`
|
||||
|
||||
\param features the feature set where the features are instantiated.
|
||||
*/
|
||||
void generate_elevation_features (Feature_set& features)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
features.add_with_scale_id<Elevation> (i, m_input, m_point_map, grid(i), radius_dtm(i));
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
|
|
@ -332,12 +311,26 @@ public:
|
|||
features.add_with_scale_id<Height_above> (i, m_input, m_point_map, grid(i));
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
features.add_with_scale_id<Vertical_range> (i, m_input, m_point_map, grid(i));
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\brief generates geometric features based on normal analysis information.
|
||||
|
||||
At each scale, the version of
|
||||
`CGAL::Classification::Feature::Verticality` based on eigenvalue
|
||||
is generated.
|
||||
|
||||
\param features the feature set where the features are instantiated.
|
||||
*/
|
||||
void generate_normal_based_features (Feature_set& features)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
features.add_with_scale_id<Verticality> (i, m_input, eigen(i));
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Generate geometric features based on normal vector information.
|
||||
\brief generates geometric features based on normal vector information.
|
||||
|
||||
Generates the version of `CGAL::Classification::Feature::Verticality` based on normal vectors.
|
||||
|
||||
|
|
@ -347,7 +340,6 @@ public:
|
|||
|
||||
\param features the feature set where the features are instantiated.
|
||||
\param normal_map property map to access the normal vectors of the input points (if any).
|
||||
|
||||
*/
|
||||
template <typename VectorMap>
|
||||
void generate_normal_based_features(Feature_set& features, const VectorMap& normal_map)
|
||||
|
|
@ -356,7 +348,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Generate geometric features based on point color information.
|
||||
\brief generates geometric features based on point color information.
|
||||
|
||||
Generates `CGAL::Classification::Feature::Color_channel` with
|
||||
channels `HUE`, `SATURATION` and `VALUE`.
|
||||
|
|
@ -377,7 +369,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Generate geometric features based on echo information.
|
||||
\brief generates geometric features based on echo information.
|
||||
|
||||
At each scale, generates `CGAL::Classification::Feature::Echo_scatter`.
|
||||
|
||||
|
|
@ -402,19 +394,19 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Returns the bounding box of the input point set.
|
||||
\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`.
|
||||
\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`.
|
||||
\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`.
|
||||
\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); }
|
||||
|
||||
|
|
@ -424,26 +416,26 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Returns the number of scales that were computed.
|
||||
\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
|
||||
\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
|
||||
\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
|
||||
\brief returns the radius used for digital terrain modeling at
|
||||
scale `scale`. This radius represents the minimum size of a
|
||||
building at this scale.
|
||||
*/
|
||||
|
|
@ -451,51 +443,6 @@ public:
|
|||
|
||||
/// @}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||
delete m_scales[i];
|
||||
m_scales.clear();
|
||||
}
|
||||
|
||||
#ifdef CGAL_CLASSIFICATION_USE_GRADIENT_OF_FEATURE
|
||||
void generate_gradient_features(Feature_set& features)
|
||||
{
|
||||
std::size_t size = features->size();
|
||||
|
||||
for (std::size_t i = 0; i < size; ++ i)
|
||||
{
|
||||
for (int j = m_scales.size() - 1; j >= 0; -- j)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "_" << j;
|
||||
if ((*features)[i]->name().find (oss.str()))
|
||||
{
|
||||
const Neighbor_query& neighbor_query = neighborhood(std::size_t(j)).k_neighbor_query(6);
|
||||
features->template add<Gradient_of_feature> (m_input, m_point_map, (*features)[i], neighbor_query);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
const T& get_parameter (const T& t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Constant_property_map<Iterator, T>
|
||||
get_parameter (const Default&)
|
||||
{
|
||||
return Constant_property_map<Iterator, T>();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,37 +56,34 @@ namespace Classification {
|
|||
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||
class Point_set_neighborhood
|
||||
{
|
||||
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Point_3 Point;
|
||||
using FT = typename GeomTraits::FT;
|
||||
using Point = typename GeomTraits::Point_3;
|
||||
|
||||
class My_point_property_map{
|
||||
const PointRange* input;
|
||||
PointMap point_map;
|
||||
|
||||
public:
|
||||
typedef Point value_type;
|
||||
typedef const value_type& reference;
|
||||
typedef boost::uint32_t key_type;
|
||||
typedef boost::lvalue_property_map_tag category;
|
||||
using value_type = typename boost::property_traits<PointMap>::value_type;
|
||||
using reference = typename boost::property_traits<PointMap>::reference;
|
||||
using key_type = std::uint32_t;
|
||||
using category = typename boost::property_traits<PointMap>::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()+std::size_t(k))); }
|
||||
friend inline reference get (const My_point_property_map& ppmap, key_type i)
|
||||
{ return ppmap[i]; }
|
||||
friend reference get (const My_point_property_map& ppmap, key_type i)
|
||||
{ return get(ppmap.point_map, *(ppmap.input->begin()+std::size_t(i))); }
|
||||
};
|
||||
|
||||
typedef Search_traits_3<GeomTraits> SearchTraits_3;
|
||||
typedef Search_traits_adapter <boost::uint32_t, My_point_property_map, SearchTraits_3> Search_traits;
|
||||
typedef Sliding_midpoint<Search_traits> Splitter;
|
||||
typedef Distance_adapter<boost::uint32_t, My_point_property_map, Euclidean_distance<SearchTraits_3> > Distance;
|
||||
typedef Kd_tree<Search_traits, Splitter, Tag_true, Tag_true> Tree;
|
||||
typedef Fuzzy_sphere<Search_traits> Sphere;
|
||||
typedef Orthogonal_k_neighbor_search<Search_traits, Distance, Splitter, Tree> Knn;
|
||||
using Search_traits_base = Search_traits_3<GeomTraits>;
|
||||
using Search_traits = Search_traits_adapter<std::uint32_t, My_point_property_map, Search_traits_base>;
|
||||
using Splitter = Sliding_midpoint<Search_traits>;
|
||||
using Distance = Distance_adapter<std::uint32_t, My_point_property_map, Euclidean_distance<Search_traits_base> >;
|
||||
using Tree = Kd_tree<Search_traits, Splitter, Tag_true, Tag_true>;
|
||||
using Sphere = Fuzzy_sphere<Search_traits>;
|
||||
using Knn = Orthogonal_k_neighbor_search<Search_traits, Distance, Splitter, Tree>;
|
||||
|
||||
|
||||
Tree* m_tree;
|
||||
std::shared_ptr<Tree> m_tree;
|
||||
Distance m_distance;
|
||||
|
||||
public:
|
||||
|
|
@ -102,13 +99,13 @@ public:
|
|||
class K_neighbor_query
|
||||
{
|
||||
public:
|
||||
typedef typename Point_set_neighborhood::Point value_type; ///<
|
||||
using value_type = typename Point_set_neighborhood::Point; ///<
|
||||
private:
|
||||
const Point_set_neighborhood& neighborhood;
|
||||
unsigned int k;
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs a K neighbor query object.
|
||||
\brief constructs a K neighbor query object.
|
||||
\param neighborhood point set neighborhood object.
|
||||
\param k number of neighbors per query.
|
||||
*/
|
||||
|
|
@ -137,13 +134,13 @@ public:
|
|||
class Sphere_neighbor_query
|
||||
{
|
||||
public:
|
||||
typedef typename Point_set_neighborhood::Point value_type; ///<
|
||||
using value_type = typename Point_set_neighborhood::Point; ///<
|
||||
private:
|
||||
const Point_set_neighborhood& neighborhood;
|
||||
float radius;
|
||||
public:
|
||||
/*!
|
||||
\brief Constructs a range neighbor query object.
|
||||
\brief constructs a range neighbor query object.
|
||||
\param neighborhood point set neighborhood object.
|
||||
\param radius radius of the neighbor query sphere.
|
||||
*/
|
||||
|
|
@ -164,14 +161,14 @@ public:
|
|||
friend class K_neighbor_query;
|
||||
friend class Sphere_neighbor_query;
|
||||
|
||||
Point_set_neighborhood () : m_tree (nullptr) { }
|
||||
Point_set_neighborhood () { }
|
||||
/// \endcond
|
||||
|
||||
/// \name Constructors
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Constructs a neighborhood object based on the input range.
|
||||
\brief constructs a neighborhood object based on the input range.
|
||||
|
||||
\tparam ConcurrencyTag enables sequential versus parallel
|
||||
algorithm. Possible values are `Sequential_tag`, `Parallel_tag`,
|
||||
|
|
@ -201,17 +198,18 @@ public:
|
|||
void init (const PointRange& input, PointMap point_map)
|
||||
{
|
||||
My_point_property_map pmap (&input, point_map);
|
||||
m_tree = new Tree (boost::counting_iterator<boost::uint32_t> (0),
|
||||
boost::counting_iterator<boost::uint32_t> (boost::uint32_t(input.size())),
|
||||
Splitter(),
|
||||
Search_traits (pmap));
|
||||
m_tree = std::make_shared<Tree>
|
||||
(boost::counting_iterator<std::uint32_t> (0),
|
||||
boost::counting_iterator<std::uint32_t> (std::uint32_t(input.size())),
|
||||
Splitter(),
|
||||
Search_traits (pmap));
|
||||
m_distance = Distance (pmap);
|
||||
m_tree->template build<ConcurrencyTag>();
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Constructs a simplified neighborhood object based on the input range.
|
||||
\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
|
||||
|
|
@ -241,7 +239,6 @@ public:
|
|||
Point_set_neighborhood (const PointRange& input,
|
||||
PointMap point_map,
|
||||
float voxel_size)
|
||||
: m_tree (nullptr)
|
||||
{
|
||||
init<Parallel_if_available_tag> (input, point_map, voxel_size);
|
||||
}
|
||||
|
|
@ -250,13 +247,13 @@ public:
|
|||
void init (const PointRange& input, PointMap point_map, float voxel_size)
|
||||
{
|
||||
// First, simplify
|
||||
std::vector<boost::uint32_t> indices;
|
||||
std::vector<std::uint32_t> indices;
|
||||
My_point_property_map pmap (&input, point_map);
|
||||
voxelize_point_set(input.size(), indices, pmap, voxel_size);
|
||||
|
||||
m_tree = new Tree (indices.begin(), indices.end(),
|
||||
Splitter(),
|
||||
Search_traits (pmap));
|
||||
m_tree = std::make_shared<Tree> (indices.begin(), indices.end(),
|
||||
Splitter(),
|
||||
Search_traits (pmap));
|
||||
m_distance = Distance (pmap);
|
||||
m_tree->template build<ConcurrencyTag>();
|
||||
}
|
||||
|
|
@ -264,19 +261,11 @@ public:
|
|||
|
||||
/// @}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
~Point_set_neighborhood ()
|
||||
{
|
||||
if (m_tree != nullptr)
|
||||
delete m_tree;
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/// \name Queries
|
||||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Returns a neighbor query object with fixed number of neighbors `k`.
|
||||
\brief returns a neighbor query object with fixed number of neighbors `k`.
|
||||
*/
|
||||
K_neighbor_query k_neighbor_query (const unsigned int k) const
|
||||
{
|
||||
|
|
@ -284,7 +273,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Returns a neighbor query object with fixed radius `radius`.
|
||||
\brief returns a neighbor query object with fixed radius `radius`.
|
||||
*/
|
||||
Sphere_neighbor_query sphere_neighbor_query (const float radius) const
|
||||
{
|
||||
|
|
@ -313,34 +302,32 @@ private:
|
|||
}
|
||||
|
||||
template <typename Map>
|
||||
void voxelize_point_set (std::size_t nb_pts, std::vector<boost::uint32_t>& indices, Map point_map,
|
||||
void voxelize_point_set (std::size_t nb_pts, std::vector<std::uint32_t>& indices, Map point_map,
|
||||
float voxel_size)
|
||||
{
|
||||
std::map<Point, std::vector<boost::uint32_t> > grid;
|
||||
std::map<Point, std::vector<std::uint32_t> > grid;
|
||||
|
||||
for (boost::uint32_t i = 0; i < nb_pts; ++ i)
|
||||
for (std::uint32_t i = 0; i < nb_pts; ++ i)
|
||||
{
|
||||
const Point& p = get(point_map, 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<boost::uint32_t> >::iterator it;
|
||||
typename std::map<Point, std::vector<std::uint32_t> >::iterator it;
|
||||
boost::tie (it, boost::tuples::ignore)
|
||||
= grid.insert (std::make_pair (ref, std::vector<boost::uint32_t>()));
|
||||
= grid.insert (std::make_pair (ref, std::vector<std::uint32_t>()));
|
||||
it->second.push_back (i);
|
||||
}
|
||||
|
||||
for (typename std::map<Point, std::vector<boost::uint32_t> >::iterator
|
||||
for (typename std::map<Point, std::vector<std::uint32_t> >::iterator
|
||||
it = grid.begin(); it != grid.end(); ++ it)
|
||||
{
|
||||
const std::vector<boost::uint32_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)));
|
||||
boost::uint32_t chosen = 0;
|
||||
const std::vector<std::uint32_t>& pts = it->second;
|
||||
Point centroid = CGAL::centroid (CGAL::make_transform_iterator_from_property_map
|
||||
(pts.begin(), point_map),
|
||||
CGAL::make_transform_iterator_from_property_map
|
||||
(pts.end(), point_map));
|
||||
std::uint32_t chosen = 0;
|
||||
float min_dist = (std::numeric_limits<float>::max)();
|
||||
for (std::size_t i = 0; i < pts.size(); ++ i)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ public:
|
|||
|
||||
/*!
|
||||
|
||||
\brief Instantiates the classifier using the sets of `labels` and `features`.
|
||||
\brief instantiates 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
|
||||
|
|
@ -190,7 +190,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Sets the weight of `feature` (`weight` must be positive).
|
||||
\brief sets the weight of `feature` (`weight` must be positive).
|
||||
*/
|
||||
void set_weight (Feature_handle feature, float weight)
|
||||
{
|
||||
|
|
@ -204,7 +204,7 @@ public:
|
|||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns the weight of `feature`.
|
||||
\brief returns the weight of `feature`.
|
||||
*/
|
||||
float weight (Feature_handle feature) const
|
||||
{
|
||||
|
|
@ -218,7 +218,7 @@ public:
|
|||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Sets the `effect` of `feature` on `label`.
|
||||
\brief sets the `effect` of `feature` on `label`.
|
||||
*/
|
||||
void set_effect (Label_handle label, Feature_handle feature,
|
||||
Effect effect)
|
||||
|
|
@ -234,7 +234,7 @@ public:
|
|||
/// \endcond
|
||||
|
||||
/*!
|
||||
\brief Returns the `effect` of `feature` on `label`.
|
||||
\brief returns the `effect` of `feature` on `label`.
|
||||
*/
|
||||
Effect effect (Label_handle label, Feature_handle feature) const
|
||||
{
|
||||
|
|
@ -269,7 +269,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Runs the training algorithm.
|
||||
\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
|
||||
|
|
@ -298,14 +298,20 @@ public:
|
|||
float train (const LabelIndexRange& ground_truth,
|
||||
unsigned int nb_tests = 300)
|
||||
{
|
||||
CGAL_precondition (m_labels.is_valid_ground_truth (ground_truth));
|
||||
|
||||
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 (int(ground_truth[i]) != -1)
|
||||
std::size_t i = 0;
|
||||
for (const auto& gt_value : ground_truth)
|
||||
{
|
||||
if (int(gt_value) != -1)
|
||||
{
|
||||
training_sets[std::size_t(ground_truth[i])].push_back (i);
|
||||
training_sets[std::size_t(gt_value)].push_back (i);
|
||||
++ nb_tot;
|
||||
}
|
||||
++ i;
|
||||
}
|
||||
|
||||
#ifdef CLASSIFICATION_TRAINING_QUICK_ESTIMATION
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
|
|
@ -636,7 +642,7 @@ 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, that is to say:
|
||||
|
|
@ -694,7 +700,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Loads a configuration from the stream `input`. A
|
||||
\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
|
||||
|
|
@ -896,10 +902,10 @@ private:
|
|||
std::size_t gt = j;
|
||||
|
||||
#ifndef CGAL_LINKED_WITH_TBB
|
||||
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
CGAL_static_assertion_msg (!(std::is_convertible<ConcurrencyTag, Parallel_tag>::value),
|
||||
"Parallel_tag is enabled but TBB is unavailable.");
|
||||
#else
|
||||
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
if (std::is_convertible<ConcurrencyTag,Parallel_tag>::value)
|
||||
{
|
||||
std::vector<std::mutex> tp_mutex (m_labels.size());
|
||||
std::vector<std::mutex> fp_mutex (m_labels.size());
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/*!
|
||||
\brief Instantiates the classifier using the sets of `labels` and `features`.
|
||||
\brief instantiates the classifier using the sets of `labels` and `features`.
|
||||
|
||||
*/
|
||||
Neural_network_classifier (const Label_set& labels,
|
||||
|
|
@ -204,7 +204,7 @@ public:
|
|||
|
||||
/// @{
|
||||
/*!
|
||||
\brief Runs the training algorithm.
|
||||
\brief runs the training algorithm.
|
||||
|
||||
From the set of provided ground truth, this algorithm constructs a
|
||||
neural network and applies an Adam optimizer to set up the weights
|
||||
|
|
@ -251,6 +251,8 @@ public:
|
|||
const std::vector<std::size_t>& hidden_layers
|
||||
= std::vector<std::size_t>())
|
||||
{
|
||||
CGAL_precondition (m_labels.is_valid_ground_truth (ground_truth));
|
||||
|
||||
if (restart_from_scratch)
|
||||
clear();
|
||||
|
||||
|
|
@ -258,15 +260,17 @@ public:
|
|||
|
||||
std::vector<std::size_t> indices;
|
||||
std::vector<int> raw_gt;
|
||||
for (std::size_t i = 0; i < ground_truth.size(); ++ i)
|
||||
std::size_t i = 0;
|
||||
for (const auto& gt_value : ground_truth)
|
||||
{
|
||||
int gc = int(ground_truth[i]);
|
||||
int gc = int(gt_value);
|
||||
if (gc != -1)
|
||||
{
|
||||
indices.push_back (i);
|
||||
raw_gt.push_back (gc);
|
||||
random_indices[std::size_t(gc)].push_back (indices.size() - 1);
|
||||
}
|
||||
++ i;
|
||||
}
|
||||
|
||||
if (!initialized())
|
||||
|
|
@ -446,7 +450,7 @@ 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, that is to say:
|
||||
|
|
@ -527,7 +531,7 @@ public:
|
|||
}
|
||||
|
||||
/*!
|
||||
\brief Loads a configuration from the stream `input`.
|
||||
\brief loads a configuration from the stream `input`.
|
||||
|
||||
The input file should be in the XML format written by the
|
||||
`save_configuration()` method. The feature set of the classifier
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@
|
|||
|
||||
#include <CGAL/boost/graph/alpha_expansion_graphcut.h>
|
||||
#include <CGAL/Bbox_3.h>
|
||||
#include <CGAL/for_each.h>
|
||||
#include <CGAL/Classification/Label_set.h>
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/iterator.h>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
#include <tbb/parallel_for.h>
|
||||
|
|
@ -31,296 +33,10 @@ namespace CGAL {
|
|||
|
||||
namespace Classification {
|
||||
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
namespace internal {
|
||||
|
||||
template <typename Classifier, typename LabelIndexRange>
|
||||
class Classify_functor
|
||||
{
|
||||
const Label_set& m_labels;
|
||||
const Classifier& m_classifier;
|
||||
LabelIndexRange& m_out;
|
||||
|
||||
public:
|
||||
|
||||
Classify_functor (const Label_set& labels,
|
||||
const Classifier& classifier,
|
||||
LabelIndexRange& 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 = 0.f;
|
||||
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] = static_cast<typename LabelIndexRange::iterator::value_type>(nb_class_best);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Classifier, typename LabelIndexRange, typename ProbabilitiesRanges>
|
||||
class Classify_detailed_output_functor
|
||||
{
|
||||
const Label_set& m_labels;
|
||||
const Classifier& m_classifier;
|
||||
LabelIndexRange& m_out;
|
||||
ProbabilitiesRanges& m_prob;
|
||||
|
||||
public:
|
||||
|
||||
Classify_detailed_output_functor (const Label_set& labels,
|
||||
const Classifier& classifier,
|
||||
LabelIndexRange& out,
|
||||
ProbabilitiesRanges& prob)
|
||||
: m_labels (labels), m_classifier (classifier), m_out (out), m_prob (prob)
|
||||
{ }
|
||||
|
||||
#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 = 0.f;
|
||||
for(std::size_t k = 0; k < m_labels.size(); ++ k)
|
||||
{
|
||||
m_prob[k][s] = values[k];
|
||||
if(val_class_best < values[k])
|
||||
{
|
||||
val_class_best = values[k];
|
||||
nb_class_best = k;
|
||||
}
|
||||
}
|
||||
|
||||
m_out[s] = static_cast<typename LabelIndexRange::iterator::value_type>(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, typename LabelIndexRange>
|
||||
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;
|
||||
LabelIndexRange& 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,
|
||||
LabelIndexRange& 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 = 0.f;
|
||||
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] = static_cast<typename LabelIndexRange::iterator::value_type>(nb_class_best);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
template <typename ItemRange, typename ItemMap,
|
||||
typename Classifier, typename NeighborQuery,
|
||||
typename LabelIndexRange>
|
||||
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;
|
||||
LabelIndexRange& m_out;
|
||||
|
||||
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,
|
||||
LabelIndexRange& 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 = 0.f;
|
||||
for(std::size_t k = 0; k < m_labels.size(); ++ k)
|
||||
{
|
||||
float value = values[k];
|
||||
probability_matrix[k][j] = -std::log(value);
|
||||
|
||||
if(val_class_best < value)
|
||||
{
|
||||
val_class_best = value;
|
||||
nb_class_best = k;
|
||||
}
|
||||
}
|
||||
assigned_label[j] = nb_class_best;
|
||||
}
|
||||
|
||||
CGAL::alpha_expansion_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]] = static_cast<typename LabelIndexRange::iterator::value_type>(assigned_label[i]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// \endcond
|
||||
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationMain
|
||||
|
||||
\brief Runs the classification algorithm without any regularization.
|
||||
\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
|
||||
|
|
@ -355,23 +71,27 @@ namespace internal {
|
|||
const Classifier& classifier,
|
||||
LabelIndexRange& output)
|
||||
{
|
||||
internal::Classify_functor<Classifier, LabelIndexRange>
|
||||
f (labels, classifier, output);
|
||||
CGAL::for_each<ConcurrencyTag>
|
||||
(CGAL::make_counting_range<std::size_t> (0, input.size()),
|
||||
[&](const std::size_t& s) -> bool
|
||||
{
|
||||
std::size_t nb_class_best=0;
|
||||
std::vector<float> values;
|
||||
classifier (s, values);
|
||||
|
||||
#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);
|
||||
}
|
||||
float val_class_best = 0.f;
|
||||
for(std::size_t k = 0; k < labels.size(); ++ k)
|
||||
{
|
||||
if(val_class_best < values[k])
|
||||
{
|
||||
val_class_best = values[k];
|
||||
nb_class_best = k;
|
||||
}
|
||||
}
|
||||
output[s] = static_cast<typename LabelIndexRange::iterator::value_type>(nb_class_best);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
|
|
@ -387,30 +107,36 @@ namespace internal {
|
|||
LabelIndexRange& output,
|
||||
ProbabilitiesRanges& probabilities)
|
||||
{
|
||||
internal::Classify_detailed_output_functor<Classifier, LabelIndexRange, ProbabilitiesRanges>
|
||||
f (labels, classifier, output, probabilities);
|
||||
CGAL::for_each<ConcurrencyTag>
|
||||
(CGAL::make_counting_range<std::size_t> (0, input.size()),
|
||||
[&](const std::size_t& s) -> bool
|
||||
{
|
||||
std::size_t nb_class_best=0;
|
||||
std::vector<float> values;
|
||||
classifier (s, values);
|
||||
|
||||
#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);
|
||||
}
|
||||
float val_class_best = 0.f;
|
||||
for(std::size_t k = 0; k < labels.size(); ++ k)
|
||||
{
|
||||
probabilities[k][s] = values[k];
|
||||
if(val_class_best < values[k])
|
||||
{
|
||||
val_class_best = values[k];
|
||||
nb_class_best = k;
|
||||
}
|
||||
}
|
||||
|
||||
output[s] = static_cast<typename LabelIndexRange::iterator::value_type>(nb_class_best);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationMain
|
||||
|
||||
\brief Runs the classification algorithm with a local smoothing.
|
||||
\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
|
||||
|
|
@ -453,34 +179,53 @@ namespace internal {
|
|||
{
|
||||
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, LabelIndexRange>
|
||||
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);
|
||||
}
|
||||
CGAL::for_each<ConcurrencyTag>
|
||||
(CGAL::make_counting_range<std::size_t> (0, input.size()),
|
||||
[&](const std::size_t& s) -> bool
|
||||
{
|
||||
std::vector<float> v;
|
||||
classifier(s, v);
|
||||
for(std::size_t k = 0; k < labels.size(); ++ k)
|
||||
values[k][s] = v[k];
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
CGAL::for_each<ConcurrencyTag>
|
||||
(CGAL::make_counting_range<std::size_t> (0, input.size()),
|
||||
[&](const std::size_t& s) -> bool
|
||||
{
|
||||
std::vector<std::size_t> neighbors;
|
||||
neighbor_query (get (item_map, *(input.begin()+s)), std::back_inserter (neighbors));
|
||||
|
||||
std::vector<float> mean (values.size(), 0.);
|
||||
for (std::size_t n = 0; n < neighbors.size(); ++ n)
|
||||
for (std::size_t j = 0; j < values.size(); ++ j)
|
||||
mean[j] += values[j][neighbors[n]];
|
||||
|
||||
std::size_t nb_class_best=0;
|
||||
float val_class_best = 0.f;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
output[s] = static_cast<typename LabelIndexRange::iterator::value_type>(nb_class_best);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/*!
|
||||
\ingroup PkgClassificationMain
|
||||
|
||||
\brief Runs the classification algorithm with a global
|
||||
\brief runs the classification algorithm with a global
|
||||
regularization based on a graph cut.
|
||||
|
||||
The computed classification energy is globally regularized through
|
||||
|
|
@ -538,8 +283,8 @@ namespace internal {
|
|||
LabelIndexRange& 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)));
|
||||
(CGAL::make_transform_iterator_from_property_map (input.begin(), item_map),
|
||||
CGAL::make_transform_iterator_from_property_map (input.end(), item_map));
|
||||
|
||||
double Dx = double(bbox.xmax() - bbox.xmin());
|
||||
double Dy = double(bbox.ymax() - bbox.ymin());
|
||||
|
|
@ -587,23 +332,59 @@ namespace internal {
|
|||
CGAL_assertion_msg (i != bboxes.size(), "Point was not assigned to any subdivision.");
|
||||
}
|
||||
|
||||
internal::Classify_functor_graphcut<ItemRange, ItemMap, Classifier, NeighborQuery, LabelIndexRange>
|
||||
f (input, item_map, labels, classifier, neighbor_query, strength, indices, input_to_indices, output);
|
||||
CGAL::for_each<ConcurrencyTag>
|
||||
(CGAL::make_counting_range<std::size_t> (0, indices.size()),
|
||||
[&](const std::size_t& sub) -> bool
|
||||
{
|
||||
if (indices[sub].empty())
|
||||
return true;
|
||||
|
||||
#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);
|
||||
}
|
||||
std::vector<std::pair<std::size_t, std::size_t> > edges;
|
||||
std::vector<double> edge_weights;
|
||||
std::vector<std::vector<double> > probability_matrix
|
||||
(labels.size(), std::vector<double>(indices[sub].size(), 0.));
|
||||
std::vector<std::size_t> assigned_label (indices[sub].size());
|
||||
|
||||
for (std::size_t j = 0; j < indices[sub].size(); ++ j)
|
||||
{
|
||||
std::size_t s = indices[sub][j];
|
||||
|
||||
std::vector<std::size_t> neighbors;
|
||||
|
||||
neighbor_query (get(item_map, *(input.begin()+s)), std::back_inserter (neighbors));
|
||||
|
||||
for (std::size_t i = 0; i < neighbors.size(); ++ i)
|
||||
if (sub == input_to_indices[neighbors[i]].first
|
||||
&& j != input_to_indices[neighbors[i]].second)
|
||||
{
|
||||
edges.push_back (std::make_pair (j, input_to_indices[neighbors[i]].second));
|
||||
edge_weights.push_back (strength);
|
||||
}
|
||||
|
||||
std::vector<float> values;
|
||||
classifier(s, values);
|
||||
std::size_t nb_class_best = 0;
|
||||
float val_class_best = 0.f;
|
||||
for(std::size_t k = 0; k < labels.size(); ++ k)
|
||||
{
|
||||
float value = values[k];
|
||||
probability_matrix[k][j] = -std::log(value);
|
||||
|
||||
if(val_class_best < value)
|
||||
{
|
||||
val_class_best = value;
|
||||
nb_class_best = k;
|
||||
}
|
||||
}
|
||||
assigned_label[j] = nb_class_best;
|
||||
}
|
||||
|
||||
CGAL::alpha_expansion_graphcut (edges, edge_weights, probability_matrix, assigned_label);
|
||||
|
||||
for (std::size_t i = 0; i < assigned_label.size(); ++ i)
|
||||
output[indices[sub][i]] = static_cast<typename LabelIndexRange::iterator::value_type>(assigned_label[i]);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ Principal_component_analysis
|
|||
Principal_component_analysis_LGPL
|
||||
Profiling_tools
|
||||
Property_map
|
||||
Random_numbers
|
||||
STL_Extension
|
||||
Solver_interface
|
||||
Spatial_searching
|
||||
|
|
|
|||
|
|
@ -679,7 +679,6 @@ void CGAL::QGLViewer::setDefaultShortcuts() {
|
|||
setShortcut(qglviewer::FULL_SCREEN, ::Qt::ALT + ::Qt::Key_Return);
|
||||
setShortcut(qglviewer::ANIMATION, ::Qt::Key_Return);
|
||||
setShortcut(qglviewer::HELP, ::Qt::Key_H);
|
||||
setShortcut(qglviewer::EDIT_CAMERA, ::Qt::Key_C);
|
||||
setShortcut(qglviewer::MOVE_CAMERA_LEFT, ::Qt::Key_Left);
|
||||
setShortcut(qglviewer::MOVE_CAMERA_RIGHT, ::Qt::Key_Right);
|
||||
setShortcut(qglviewer::MOVE_CAMERA_UP, ::Qt::Key_Up);
|
||||
|
|
@ -701,9 +700,6 @@ void CGAL::QGLViewer::setDefaultShortcuts() {
|
|||
tr("Opens this help window", "HELP action description");
|
||||
keyboardActionDescription_[qglviewer::ANIMATION] =
|
||||
tr("Starts/stops the animation", "ANIMATION action description");
|
||||
keyboardActionDescription_[qglviewer::EDIT_CAMERA] =
|
||||
tr("Toggles camera paths display",
|
||||
"EDIT_CAMERA action description"); // TODO change
|
||||
keyboardActionDescription_[qglviewer::ENABLE_TEXT] =
|
||||
tr("Toggles the display of the text", "ENABLE_TEXT action description");
|
||||
keyboardActionDescription_[qglviewer::EXIT_VIEWER] =
|
||||
|
|
|
|||
|
|
@ -43,10 +43,35 @@ Release date: December 2020
|
|||
- Added an optional range parameter to `CGAL::Polygon_mesh_processing::stitch_borders()`,
|
||||
which can be used to specify which boundary cycles are eligible for stitching.
|
||||
|
||||
### [Classification](https://doc.cgal.org/5.2/Manual/packages.html#PkgClassification)
|
||||
|
||||
- **Breaking change**: new IO format for `ETHZ::Random_Forest` (a
|
||||
conversion function from the outdated format to the new one is
|
||||
provided)
|
||||
|
||||
- Additional functions for the `Evaluation` class: `append()` to
|
||||
enrich the evaluation with additional results; `confusion()` to
|
||||
access the confusion matrix; output functions to save the evaluation
|
||||
to and ASCII or HTML stream
|
||||
|
||||
- New operator `feature_cast<>` for easy conversions
|
||||
|
||||
- `Feature_set` and `Label_set` are now models of `Range`
|
||||
|
||||
- `Label` now has attributes `index`, `standard_index` and `color`,
|
||||
with automatic selection if the ASPRS standard names are used
|
||||
|
||||
- New functions in `Point_set_feature_iterator` to allow users to
|
||||
select which features should be generated
|
||||
|
||||
- New function `Label_set::is_valid_ground_truth()` to help users
|
||||
check if a ground truth matches a given label set
|
||||
|
||||
### Surface Mesh
|
||||
|
||||
- Added the functions `set_recycle_garbage()` and `does_recycle_garbage()` to the class `Surface_mesh`.
|
||||
|
||||
|
||||
### [dD Geometry Kernel](https://doc.cgal.org/5.2/Manual/packages.html#PkgKernelD)
|
||||
|
||||
- The kernels [`Epick_d`](https://doc.cgal.org/5.2/Kernel_d/structCGAL_1_1Epick__d.html)
|
||||
|
|
|
|||
|
|
@ -57,15 +57,6 @@ function(create_single_source_cgal_program firstfile )
|
|||
set(NO_TESTING TRUE)
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0064)
|
||||
# CMake 3.4 or later
|
||||
if(NOT NO_TESTING)
|
||||
cgal_add_test(${exe_name})
|
||||
else()
|
||||
cgal_add_test(${exe_name} NO_EXECUTION)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_to_cached_list( CGAL_EXECUTABLE_TARGETS ${exe_name} )
|
||||
|
||||
target_link_libraries(${exe_name} PRIVATE CGAL::CGAL)
|
||||
|
|
@ -78,6 +69,15 @@ function(create_single_source_cgal_program firstfile )
|
|||
target_link_libraries(${exe_name} PRIVATE ${CGAL_3RD_PARTY_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0064)
|
||||
# CMake 3.4 or later
|
||||
if(NOT NO_TESTING)
|
||||
cgal_add_test(${exe_name})
|
||||
else()
|
||||
cgal_add_test(${exe_name} NO_EXECUTION)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(AUTHOR_WARNING "The executable ${exe_name} will not be created because the source file ${firstfile} does not exist.")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ function(cgal_add_compilation_test exe_name)
|
|||
add_test(NAME "check_build_system"
|
||||
COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --target "cgal_check_build_system" --config "$<CONFIG>")
|
||||
set_property(TEST "check_build_system"
|
||||
APPEND PROPERTY LABELS "${PROJECT_NAME}")
|
||||
APPEND PROPERTY LABELS "CGAL_build_system")
|
||||
if(POLICY CMP0066) # cmake 3.7 or later
|
||||
set_property(TEST "check_build_system"
|
||||
PROPERTY FIXTURES_SETUP "check_build_system_SetupFixture")
|
||||
|
|
@ -120,6 +120,26 @@ function(cgal_add_compilation_test exe_name)
|
|||
set_property(TEST "compilation_of__${exe_name}"
|
||||
APPEND PROPERTY FIXTURES_REQUIRED "check_build_system_SetupFixture")
|
||||
endif()
|
||||
if(TARGET CGAL_Qt5_moc_and_resources) # if CGAL_Qt5 was searched, and is header-only
|
||||
get_property(linked_libraries TARGET "${exe_name}" PROPERTY LINK_LIBRARIES)
|
||||
# message(STATUS "${exe_name} depends on ${linked_libraries}")
|
||||
string(FIND "${linked_libraries}" "CGAL::CGAL_Qt5" link_with_CGAL_Qt5)
|
||||
if(link_with_CGAL_Qt5 STRGREATER "-1" AND
|
||||
NOT TARGET compilation_of__CGAL_Qt5_moc_and_resources)
|
||||
# This custom target is useless. It is used only as a flag to
|
||||
# detect that the test has already been created.
|
||||
add_custom_target(compilation_of__CGAL_Qt5_moc_and_resources)
|
||||
add_dependencies( compilation_of__CGAL_Qt5_moc_and_resources CGAL_Qt5_moc_and_resources )
|
||||
add_test(NAME "compilation_of__CGAL_Qt5_moc_and_resources"
|
||||
COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --target "compilation_of__CGAL_Qt5_moc_and_resources" --config "$<CONFIG>")
|
||||
set_property(TEST "compilation_of__CGAL_Qt5_moc_and_resources"
|
||||
APPEND PROPERTY LABELS "CGAL_build_system")
|
||||
set_property(TEST "compilation_of__CGAL_Qt5_moc_and_resources"
|
||||
PROPERTY FIXTURES_SETUP "check_build_system_SetupFixture")
|
||||
set_property(TEST "compilation_of__CGAL_Qt5_moc_and_resources"
|
||||
APPEND PROPERTY DEPENDS "check_build_system")
|
||||
endif()
|
||||
endif()
|
||||
endfunction(cgal_add_compilation_test)
|
||||
|
||||
option(CGAL_TEST_DRAW_FUNCTIONS "If set, the ctest command will not skip the tests of the draw functions.")
|
||||
|
|
|
|||
|
|
@ -46,11 +46,6 @@
|
|||
#include <boost/graph/adjacency_list.hpp>
|
||||
#include <CGAL/boost/graph/split_graph_into_polylines.h>
|
||||
|
||||
#define CGAL_CLASSIFICATION_ETHZ_ID "Random Forest (ETHZ)"
|
||||
#define CGAL_CLASSIFICATION_TENSORFLOW_ID "Neural Network (TensorFlow)"
|
||||
#define CGAL_CLASSIFICATION_OPENCV_ID "Random Forest (OpenCV)"
|
||||
#define CGAL_CLASSIFICATION_SOWF_ID "Sum of Weighted Features"
|
||||
|
||||
using namespace CGAL::Three;
|
||||
|
||||
class Polyhedron_demo_classification_plugin :
|
||||
|
|
@ -546,13 +541,13 @@ public Q_SLOTS:
|
|||
int get_classifier ()
|
||||
{
|
||||
if (classifier->text() == QString(CGAL_CLASSIFICATION_ETHZ_ID))
|
||||
return 1;
|
||||
return CGAL_CLASSIFICATION_ETHZ_NUMBER;
|
||||
if (classifier->text() == QString(CGAL_CLASSIFICATION_TENSORFLOW_ID))
|
||||
return 3;
|
||||
return CGAL_CLASSIFICATION_TENSORFLOW_NUMBER;
|
||||
if (classifier->text() == QString(CGAL_CLASSIFICATION_OPENCV_ID))
|
||||
return 2;
|
||||
return CGAL_CLASSIFICATION_OPENCV_NUMBER;
|
||||
if (classifier->text() == QString(CGAL_CLASSIFICATION_SOWF_ID))
|
||||
return 0;
|
||||
return CGAL_CLASSIFICATION_SOWF_NUMBER;
|
||||
|
||||
std::cerr << "Error: unknown classifier" << std::endl;
|
||||
return -1;
|
||||
|
|
@ -650,18 +645,18 @@ public Q_SLOTS:
|
|||
QString filename;
|
||||
|
||||
int classifier = get_classifier();
|
||||
if (classifier == 0) // Sum of Weighted Featuers
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER) // Sum of Weighted Featuers
|
||||
filename = QFileDialog::getSaveFileName(mw,
|
||||
tr("Save classification configuration"),
|
||||
tr("%1 (CGAL classif config).xml").arg(classif->item()->name()),
|
||||
"CGAL classification configuration (*.xml);;");
|
||||
else if (classifier == 1) // Random Forest (ETHZ)
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER) // Random Forest (ETHZ)
|
||||
filename = QFileDialog::getSaveFileName(mw,
|
||||
tr("Save classification configuration"),
|
||||
tr("%1 (ETHZ random forest config).gz").arg(classif->item()->name()),
|
||||
"Compressed ETHZ random forest configuration (*.gz);;");
|
||||
tr("%1 (ETHZ random forest config).bin").arg(classif->item()->name()),
|
||||
"ETHZ random forest configuration (*.bin);;");
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
else if (classifier == 2) // Random Forest (OpenCV)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER) // Random Forest (OpenCV)
|
||||
filename = QFileDialog::getSaveFileName(mw,
|
||||
tr("Save classification configuration"),
|
||||
tr("%1 (OpenCV %2.%3 random forest config).xml")
|
||||
|
|
@ -673,7 +668,7 @@ public Q_SLOTS:
|
|||
.arg(CV_MINOR_VERSION));
|
||||
#endif
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
else if (classifier == 3) // Neural Network (TensorFlow)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER) // Neural Network (TensorFlow)
|
||||
filename = QFileDialog::getSaveFileName(mw,
|
||||
tr("Save classification configuration"),
|
||||
tr("%1 (CGAL Neural Network config).xml").arg(classif->item()->name()),
|
||||
|
|
@ -704,18 +699,18 @@ public Q_SLOTS:
|
|||
QString filename;
|
||||
|
||||
int classifier = get_classifier();
|
||||
if (classifier == 0) // SOWF
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER) // Sum of Weighted Featuers
|
||||
filename = QFileDialog::getOpenFileName(mw,
|
||||
tr("Open CGAL classification configuration"),
|
||||
".",
|
||||
"CGAL classification configuration (*.xml);;All Files (*)");
|
||||
else if (classifier == 1) // ETHZ
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER) // Random Forest (ETHZ)
|
||||
filename = QFileDialog::getOpenFileName(mw,
|
||||
tr("Open ETHZ random forest configuration"),
|
||||
".",
|
||||
"Compressed ETHZ random forest configuration (*.gz);;All Files (*)");
|
||||
"ETHZ random forest configuration (*.bin);Deprecated compressed ETHZ random forest configuration (*.gz);All Files (*)");
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
else if (classifier == 2) // OpenCV
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER) // Random Forest (OpenCV)
|
||||
filename = QFileDialog::getOpenFileName(mw,
|
||||
tr("Open OpenCV %2.%3 random forest configuration")
|
||||
.arg(CV_MAJOR_VERSION)
|
||||
|
|
@ -726,7 +721,7 @@ public Q_SLOTS:
|
|||
.arg(CV_MINOR_VERSION));
|
||||
#endif
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
else if (classifier == 3) // TensorFlow
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER) // Neural Network (TensorFlow)
|
||||
filename = QFileDialog::getOpenFileName(mw,
|
||||
tr("Open CGAL Neural Network classification configuration"),
|
||||
".",
|
||||
|
|
@ -739,10 +734,8 @@ public Q_SLOTS:
|
|||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
classif->load_config (filename.toStdString().c_str(), classifier);
|
||||
|
||||
update_plugin_from_item(classif);
|
||||
run (classif, 0);
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
item_changed(classif->item());
|
||||
}
|
||||
|
|
@ -1347,13 +1340,14 @@ public Q_SLOTS:
|
|||
QMultipleInputDialog dialog ("Train Classifier", mw);
|
||||
|
||||
int classifier = get_classifier();
|
||||
if (classifier == 0) // SOWF
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER) // Sum of Weighted Featuers
|
||||
{
|
||||
QSpinBox* trials = dialog.add<QSpinBox> ("Number of trials: ", "trials");
|
||||
trials->setRange (1, 99999);
|
||||
trials->setValue (800);
|
||||
}
|
||||
else if (classifier == 1 || classifier == 2) // random forest
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER
|
||||
|| classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER) // random forest
|
||||
{
|
||||
QSpinBox* trees = dialog.add<QSpinBox> ("Number of trees: ", "num_trees");
|
||||
trees->setRange (1, 9999);
|
||||
|
|
@ -1362,7 +1356,7 @@ public Q_SLOTS:
|
|||
depth->setRange (1, 9999);
|
||||
depth->setValue (20);
|
||||
}
|
||||
else if (classifier == 3) // neural network
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER) // Neural Network (TensorFlow)
|
||||
{
|
||||
QSpinBox* trials = dialog.add<QSpinBox> ("Number of trials: ", "trials");
|
||||
trials->setRange (1, 99999);
|
||||
|
|
|
|||
|
|
@ -213,8 +213,6 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po
|
|||
oss << "label_" << i;
|
||||
new_label = m_labels.add(oss.str().c_str());
|
||||
}
|
||||
|
||||
m_label_colors.push_back (this->get_new_label_color (new_label->name()));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -223,9 +221,6 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po
|
|||
m_labels.add("vegetation");
|
||||
m_labels.add("roof");
|
||||
m_labels.add("facade");
|
||||
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
m_label_colors.push_back (this->get_new_label_color (m_labels[i]->name()));
|
||||
}
|
||||
|
||||
update_comments_of_point_set_item();
|
||||
|
|
@ -240,6 +235,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po
|
|||
#endif
|
||||
|
||||
// Compute neighborhood
|
||||
#if 0
|
||||
typedef CGAL::Triangulation_vertex_base_with_info_3<int, Kernel> Vb;
|
||||
typedef CGAL::Delaunay_triangulation_cell_base_3<Kernel> Cb;
|
||||
typedef CGAL::Triangulation_data_structure_3<Vb, Cb> Tds;
|
||||
|
|
@ -279,7 +275,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po
|
|||
m_clusters[std::size_t(it->first)].neighbors->push_back (std::size_t(it->second));
|
||||
m_clusters[std::size_t(it->second)].neighbors->push_back (std::size_t(it->first));
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -460,7 +456,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax)
|
|||
std::size_t c = m_clusters[cid].label();
|
||||
|
||||
if (c != std::size_t(-1))
|
||||
color = m_label_colors[c];
|
||||
color = label_qcolor (m_labels[std::size_t(c)]);
|
||||
}
|
||||
|
||||
m_points->point_set()->set_color(*it, color);
|
||||
|
|
@ -481,7 +477,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax)
|
|||
int c2 = m_clusters[cid].label();
|
||||
|
||||
if (c != -1)
|
||||
color = m_label_colors[std::size_t(c)];
|
||||
color = label_qcolor (m_labels[std::size_t(c)]);
|
||||
|
||||
if (c != c2)
|
||||
div = 2;
|
||||
|
|
@ -780,6 +776,7 @@ void Cluster_classification::add_remaining_point_set_properties_as_features(Feat
|
|||
prop[i] == "training" ||
|
||||
prop[i] == "label" ||
|
||||
prop[i] == "classification" ||
|
||||
prop[i] == "scan_direction_flag" ||
|
||||
prop[i] == "real_color" ||
|
||||
prop[i] == "shape" ||
|
||||
prop[i] == "red" || prop[i] == "green" || prop[i] == "blue" ||
|
||||
|
|
@ -841,14 +838,14 @@ void Cluster_classification::train(int classifier, const QMultipleInputDialog& d
|
|||
|
||||
std::vector<int> indices (m_clusters.size(), -1);
|
||||
|
||||
if (classifier == 0)
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER)
|
||||
{
|
||||
m_sowf->train<Concurrency_tag>(training, dialog.get<QSpinBox>("trials")->value());
|
||||
CGAL::Classification::classify<Concurrency_tag> (m_clusters,
|
||||
m_labels, *m_sowf,
|
||||
indices, m_label_probabilities);
|
||||
}
|
||||
else if (classifier == 1)
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER)
|
||||
{
|
||||
if (m_ethz != NULL)
|
||||
delete m_ethz;
|
||||
|
|
@ -860,7 +857,7 @@ void Cluster_classification::train(int classifier, const QMultipleInputDialog& d
|
|||
m_labels, *m_ethz,
|
||||
indices, m_label_probabilities);
|
||||
}
|
||||
else if (classifier == 2)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
if (m_random_forest != NULL)
|
||||
|
|
@ -874,7 +871,7 @@ void Cluster_classification::train(int classifier, const QMultipleInputDialog& d
|
|||
indices, m_label_probabilities);
|
||||
#endif
|
||||
}
|
||||
else if (classifier == 3)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
if (m_neural_network != NULL)
|
||||
|
|
@ -938,9 +935,9 @@ bool Cluster_classification::run (int method, int classifier,
|
|||
}
|
||||
reset_indices();
|
||||
|
||||
if (classifier == 0)
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER)
|
||||
run (method, *m_sowf, subdivisions, smoothing);
|
||||
else if (classifier == 1)
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER)
|
||||
{
|
||||
if (m_ethz == NULL)
|
||||
{
|
||||
|
|
@ -949,7 +946,7 @@ bool Cluster_classification::run (int method, int classifier,
|
|||
}
|
||||
run (method, *m_ethz, subdivisions, smoothing);
|
||||
}
|
||||
else if (classifier == 2)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
if (m_random_forest == NULL)
|
||||
|
|
@ -960,7 +957,7 @@ bool Cluster_classification::run (int method, int classifier,
|
|||
run (method, *m_random_forest, subdivisions, smoothing);
|
||||
#endif
|
||||
}
|
||||
else if (classifier == 3)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
if (m_neural_network == NULL)
|
||||
|
|
@ -974,4 +971,3 @@ bool Cluster_classification::run (int method, int classifier,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ class Cluster_classification : public Item_classification_base
|
|||
= new Scene_points_with_normal_item;
|
||||
|
||||
points_item->setName (QString("%1 (%2)").arg(name).arg(m_labels[label]->name().c_str()));
|
||||
points_item->setColor (m_label_colors[label]);
|
||||
points_item->setColor (label_qcolor (m_labels[label]));
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->end(); ++ it)
|
||||
{
|
||||
|
|
@ -231,7 +231,7 @@ class Cluster_classification : public Item_classification_base
|
|||
{
|
||||
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]);
|
||||
points_item[i]->setColor (label_qcolor (m_labels[i]));
|
||||
items.push_back (points_item[i]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,16 +10,32 @@
|
|||
|
||||
#include <CGAL/Classification/Feature_set.h>
|
||||
#include <CGAL/Classification/Label_set.h>
|
||||
#include <CGAL/Classification/Sum_of_weighted_features_classifier.h>
|
||||
|
||||
|
||||
#include <CGAL/Classification/ETHZ/Random_forest_classifier.h>
|
||||
#include <CGAL/Classification/Sum_of_weighted_features_classifier.h>
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
# include <CGAL/Classification/TensorFlow/Neural_network_classifier.h>
|
||||
#endif
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
#include <CGAL/Classification/OpenCV/Random_forest_classifier.h>
|
||||
#endif
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
#include <CGAL/Classification/TensorFlow/Neural_network_classifier.h>
|
||||
# include <CGAL/Classification/OpenCV/Random_forest_classifier.h>
|
||||
#endif
|
||||
|
||||
#define CGAL_CLASSIFICATION_ETHZ_ID "Random Forest (ETHZ)"
|
||||
#define CGAL_CLASSIFICATION_ETHZ_NUMBER 0
|
||||
|
||||
#define CGAL_CLASSIFICATION_TENSORFLOW_ID "Neural Network (TensorFlow)"
|
||||
#define CGAL_CLASSIFICATION_TENSORFLOW_NUMBER 1
|
||||
|
||||
#define CGAL_CLASSIFICATION_OPENCV_ID "Random Forest (OpenCV)"
|
||||
#define CGAL_CLASSIFICATION_OPENCV_NUMBER 2
|
||||
|
||||
#define CGAL_CLASSIFICATION_SOWF_ID "Sum of Weighted Features"
|
||||
#define CGAL_CLASSIFICATION_SOWF_NUMBER 3
|
||||
|
||||
|
||||
class Item_classification_base
|
||||
{
|
||||
public:
|
||||
|
|
@ -77,10 +93,14 @@ public:
|
|||
void set_effect (Label_handle l, Feature_handle f, Sum_of_weighted_features::Effect e)
|
||||
{ m_sowf->set_effect (l, f, e); }
|
||||
|
||||
QColor label_qcolor (Label_handle l) const
|
||||
{
|
||||
return QColor (l->color().red(), l->color().green(), l->color().blue());
|
||||
}
|
||||
|
||||
virtual QColor add_new_label (const char* name)
|
||||
{
|
||||
m_labels.add(name);
|
||||
m_label_colors.push_back (get_new_label_color (name));
|
||||
|
||||
delete m_sowf;
|
||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||
|
|
@ -98,12 +118,11 @@ public:
|
|||
m_neural_network = new Neural_network (m_labels, m_features);
|
||||
#endif
|
||||
|
||||
return m_label_colors.back();
|
||||
return label_qcolor (m_labels[m_labels.size() - 1]);
|
||||
}
|
||||
virtual void remove_label (std::size_t position)
|
||||
{
|
||||
m_labels.remove(m_labels[position]);
|
||||
m_label_colors.erase (m_label_colors.begin() + position);
|
||||
|
||||
delete m_sowf;
|
||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||
|
|
@ -125,7 +144,6 @@ public:
|
|||
virtual void clear_labels ()
|
||||
{
|
||||
m_labels.clear();
|
||||
m_label_colors.clear();
|
||||
|
||||
delete m_sowf;
|
||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||
|
|
@ -172,23 +190,25 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
if (classifier == 0)
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER)
|
||||
{
|
||||
std::ofstream f (filename);
|
||||
m_sowf->save_configuration (f);
|
||||
}
|
||||
else if (classifier == 1)
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER)
|
||||
{
|
||||
std::cerr << "D ";
|
||||
std::ofstream f (filename, std::ios_base::out | std::ios_base::binary);
|
||||
m_ethz->save_configuration (f);
|
||||
std::cerr << "E ";
|
||||
}
|
||||
else if (classifier == 2)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
m_random_forest->save_configuration (filename);
|
||||
#endif
|
||||
}
|
||||
else if (classifier == 3)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
std::ofstream f (filename);
|
||||
|
|
@ -204,25 +224,30 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
if (classifier == 0)
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER)
|
||||
{
|
||||
std::ifstream f (filename);
|
||||
m_sowf->load_configuration (f, true);
|
||||
}
|
||||
else if (classifier == 1)
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER)
|
||||
{
|
||||
if (m_ethz == NULL)
|
||||
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
||||
std::ifstream f (filename, std::ios_base::in | std::ios_base::binary);
|
||||
m_ethz->load_configuration (f);
|
||||
|
||||
// Handle deprecated files
|
||||
if (std::string(filename).find(".gz") != std::string::npos)
|
||||
m_ethz->load_deprecated_configuration(f);
|
||||
else
|
||||
m_ethz->load_configuration (f);
|
||||
}
|
||||
else if (classifier == 2)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
m_random_forest->load_configuration (filename);
|
||||
#endif
|
||||
}
|
||||
else if (classifier == 3)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
if (m_neural_network == NULL)
|
||||
|
|
@ -233,69 +258,24 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
const QColor& label_color(std::size_t i) const { return m_label_colors[i]; }
|
||||
QColor label_color(std::size_t i) const
|
||||
{
|
||||
return label_qcolor (m_labels[i]);
|
||||
}
|
||||
void change_label_color (std::size_t position, const QColor& color)
|
||||
{
|
||||
m_label_colors[position] = color;
|
||||
m_labels[position]->set_color
|
||||
(CGAL::Color (color.red(), color.green(), color.blue()));
|
||||
}
|
||||
void change_label_name (std::size_t position, const std::string& name)
|
||||
{
|
||||
m_labels[position]->set_name (name);
|
||||
}
|
||||
|
||||
QColor get_new_label_color (const std::string& name)
|
||||
{
|
||||
QColor color (64 + rand() % 192,
|
||||
64 + rand() % 192,
|
||||
64 + rand() % 192);
|
||||
|
||||
if (name == "ground")
|
||||
color = QColor (186, 189, 182);
|
||||
else if (name == "low_veget")
|
||||
color = QColor (78, 154, 6);
|
||||
else if (name == "med_veget"
|
||||
|| name == "vegetation")
|
||||
color = QColor (138, 226, 52);
|
||||
else if (name == "high_veget")
|
||||
color = QColor (204, 255, 201);
|
||||
else if (name == "building"
|
||||
|| name == "roof")
|
||||
color = QColor (245, 121, 0);
|
||||
else if (name == "noise")
|
||||
color = QColor (0, 0, 0);
|
||||
else if (name == "reserved")
|
||||
color = QColor (233, 185, 110);
|
||||
else if (name == "water")
|
||||
color = QColor (114, 159, 207);
|
||||
else if (name == "rail")
|
||||
color = QColor (136, 46, 25);
|
||||
else if (name == "road_surface")
|
||||
color = QColor (56, 56, 56);
|
||||
else if (name == "reserved_2")
|
||||
color = QColor (193, 138, 51);
|
||||
else if (name == "wire_guard")
|
||||
color = QColor (37, 61, 136);
|
||||
else if (name == "wire_conduct")
|
||||
color = QColor (173, 127, 168);
|
||||
else if (name == "trans_tower")
|
||||
color = QColor (136, 138, 133);
|
||||
else if (name == "wire_connect")
|
||||
color = QColor (145, 64, 236);
|
||||
else if (name == "bridge_deck")
|
||||
color = QColor (213, 93, 93);
|
||||
else if (name == "high_noise")
|
||||
color = QColor (255, 0, 0);
|
||||
else if (name == "facade")
|
||||
color = QColor (77, 131, 186);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
Label_set m_labels;
|
||||
Feature_set m_features;
|
||||
std::vector<QColor> m_label_colors;
|
||||
Sum_of_weighted_features* m_sowf;
|
||||
ETHZ_random_forest* m_ethz;
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
|
|
|
|||
|
|
@ -200,8 +200,6 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n
|
|||
oss << "label_" << i;
|
||||
new_label = m_labels.add(oss.str().c_str());
|
||||
}
|
||||
|
||||
m_label_colors.push_back (this->get_new_label_color (new_label->name()));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -210,9 +208,6 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n
|
|||
m_labels.add("vegetation");
|
||||
m_labels.add("roof");
|
||||
m_labels.add("facade");
|
||||
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
m_label_colors.push_back (this->get_new_label_color (m_labels[i]->name()));
|
||||
}
|
||||
|
||||
update_comments_of_point_set_item();
|
||||
|
|
@ -247,7 +242,13 @@ Point_set_item_classification::~Point_set_item_classification()
|
|||
if (m_points != NULL)
|
||||
{
|
||||
// For LAS saving, convert classification info in the LAS standard
|
||||
if (m_input_is_las)
|
||||
QString filename = m_points->property("source filename").toString();
|
||||
|
||||
if (m_input_is_las ||
|
||||
(!filename.isEmpty() && (filename.endsWith(QString(".las")) ||
|
||||
filename.endsWith(QString(".LAS")) ||
|
||||
filename.endsWith(QString(".laz")) ||
|
||||
filename.endsWith(QString(".LAZ")))))
|
||||
{
|
||||
Point_set::Property_map<unsigned char> las_classif
|
||||
= m_points->point_set()->add_property_map<unsigned char>("classification", 0).first;
|
||||
|
|
@ -388,7 +389,7 @@ void Point_set_item_classification::change_color (int index, float* vmin, float*
|
|||
std::size_t c = m_classif[*it];
|
||||
|
||||
if (c != std::size_t(-1))
|
||||
color = m_label_colors[c];
|
||||
color = label_qcolor (m_labels[c]);
|
||||
|
||||
m_points->point_set()->set_color(*it, color);
|
||||
}
|
||||
|
|
@ -403,7 +404,7 @@ void Point_set_item_classification::change_color (int index, float* vmin, float*
|
|||
int c2 = m_classif[*it];
|
||||
|
||||
if (c != -1)
|
||||
color = m_label_colors[std::size_t(c)];
|
||||
color = label_qcolor (m_labels[std::size_t(c)]);
|
||||
|
||||
float div = 1;
|
||||
if (c != c2)
|
||||
|
|
@ -670,6 +671,7 @@ void Point_set_item_classification::add_remaining_point_set_properties_as_featur
|
|||
prop[i] == "training" ||
|
||||
prop[i] == "label" ||
|
||||
prop[i] == "classification" ||
|
||||
prop[i] == "scan_direction_flag" ||
|
||||
prop[i] == "real_color" ||
|
||||
prop[i] == "shape" ||
|
||||
prop[i] == "red" || prop[i] == "green" || prop[i] == "blue" ||
|
||||
|
|
@ -731,14 +733,14 @@ void Point_set_item_classification::train(int classifier, const QMultipleInputDi
|
|||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
std::cerr << " * " << m_labels[i]->name() << ": " << nb_label[i] << " point(s)" << std::endl;
|
||||
|
||||
if (classifier == 0)
|
||||
{
|
||||
m_sowf->train<Concurrency_tag>(training, dialog.get<QSpinBox>("trials")->value());
|
||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||
m_labels, *m_sowf,
|
||||
indices, m_label_probabilities);
|
||||
}
|
||||
else if (classifier == 1)
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER)
|
||||
{
|
||||
m_sowf->train<Concurrency_tag>(training, dialog.get<QSpinBox>("trials")->value());
|
||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||
m_labels, *m_sowf,
|
||||
indices, m_label_probabilities);
|
||||
}
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER)
|
||||
{
|
||||
if (m_ethz != NULL)
|
||||
delete m_ethz;
|
||||
|
|
@ -750,7 +752,7 @@ void Point_set_item_classification::train(int classifier, const QMultipleInputDi
|
|||
m_labels, *m_ethz,
|
||||
indices, m_label_probabilities);
|
||||
}
|
||||
else if (classifier == 2)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
if (m_random_forest != NULL)
|
||||
|
|
@ -764,7 +766,7 @@ void Point_set_item_classification::train(int classifier, const QMultipleInputDi
|
|||
indices, m_label_probabilities);
|
||||
#endif
|
||||
}
|
||||
else if (classifier == 3)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
if (m_neural_network != NULL)
|
||||
|
|
@ -829,9 +831,9 @@ bool Point_set_item_classification::run (int method, int classifier,
|
|||
}
|
||||
reset_indices();
|
||||
|
||||
if (classifier == 0)
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER)
|
||||
run (method, *m_sowf, subdivisions, smoothing);
|
||||
else if (classifier == 1)
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER)
|
||||
{
|
||||
if (m_ethz == NULL)
|
||||
{
|
||||
|
|
@ -840,7 +842,7 @@ bool Point_set_item_classification::run (int method, int classifier,
|
|||
}
|
||||
run (method, *m_ethz, subdivisions, smoothing);
|
||||
}
|
||||
else if (classifier == 2)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
if (m_random_forest == NULL)
|
||||
|
|
@ -851,7 +853,7 @@ bool Point_set_item_classification::run (int method, int classifier,
|
|||
run (method, *m_random_forest, subdivisions, smoothing);
|
||||
#endif
|
||||
}
|
||||
else if (classifier == 3)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
if (m_neural_network == NULL)
|
||||
|
|
@ -865,4 +867,3 @@ bool Point_set_item_classification::run (int method, int classifier,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -224,7 +224,8 @@ class Point_set_item_classification : public Item_classification_base
|
|||
= new Scene_points_with_normal_item;
|
||||
|
||||
points_item->setName (QString("%1 (%2)").arg(name).arg(m_labels[label]->name().c_str()));
|
||||
points_item->setColor (m_label_colors[label]);
|
||||
points_item->setColor (label_qcolor (m_labels[label]));
|
||||
|
||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||
it != m_points->point_set()->end(); ++ it)
|
||||
{
|
||||
|
|
@ -243,7 +244,7 @@ class Point_set_item_classification : public Item_classification_base
|
|||
{
|
||||
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]);
|
||||
points_item[i]->setColor (label_qcolor (m_labels[i]));
|
||||
items.push_back (points_item[i]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ Surface_mesh_item_classification::Surface_mesh_item_classification(Scene_surface
|
|||
m_labels.add("roof");
|
||||
m_labels.add("facade");
|
||||
|
||||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
m_label_colors.push_back (this->get_new_label_color (m_labels[i]->name()));
|
||||
|
||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||
m_ethz = NULL;
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
|
|
@ -108,7 +105,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo
|
|||
std::size_t c = m_classif[fd];
|
||||
|
||||
if (c != std::size_t(-1))
|
||||
color = m_label_colors[c];
|
||||
color = label_qcolor (m_labels[c]);
|
||||
|
||||
m_color[fd] = CGAL::Color(color.red(), color.green(), color.blue());
|
||||
}
|
||||
|
|
@ -122,7 +119,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo
|
|||
std::size_t c2 = m_classif[fd];
|
||||
|
||||
if (c != std::size_t(-1))
|
||||
color = m_label_colors[c];
|
||||
color = label_qcolor(m_labels[c]);
|
||||
|
||||
float div = 1;
|
||||
if (c != c2)
|
||||
|
|
@ -296,14 +293,14 @@ void Surface_mesh_item_classification::train (int classifier, const QMultipleInp
|
|||
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||
std::cerr << " * " << m_labels[i]->name() << ": " << nb_label[i] << " face(s)" << std::endl;
|
||||
|
||||
if (classifier == 0)
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER)
|
||||
{
|
||||
m_sowf->train<Concurrency_tag>(training, dialog.get<QSpinBox>("trials")->value());
|
||||
CGAL::Classification::classify<Concurrency_tag> (m_mesh->polyhedron()->faces(),
|
||||
m_labels, *m_sowf,
|
||||
indices, m_label_probabilities);
|
||||
}
|
||||
else if (classifier == 1)
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER)
|
||||
{
|
||||
if (m_ethz != NULL)
|
||||
delete m_ethz;
|
||||
|
|
@ -315,7 +312,7 @@ void Surface_mesh_item_classification::train (int classifier, const QMultipleInp
|
|||
m_labels, *m_ethz,
|
||||
indices, m_label_probabilities);
|
||||
}
|
||||
else if (classifier == 2)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
if (m_random_forest != NULL)
|
||||
|
|
@ -330,7 +327,7 @@ void Surface_mesh_item_classification::train (int classifier, const QMultipleInp
|
|||
indices, m_label_probabilities);
|
||||
#endif
|
||||
}
|
||||
else if (classifier == 3)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
if (m_neural_network != NULL)
|
||||
|
|
@ -392,9 +389,9 @@ bool Surface_mesh_item_classification::run (int method, int classifier,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (classifier == 0)
|
||||
if (classifier == CGAL_CLASSIFICATION_SOWF_NUMBER)
|
||||
run (method, *m_sowf, subdivisions, smoothing);
|
||||
else if (classifier == 1)
|
||||
else if (classifier == CGAL_CLASSIFICATION_ETHZ_NUMBER)
|
||||
{
|
||||
if (m_ethz == NULL)
|
||||
{
|
||||
|
|
@ -403,7 +400,7 @@ bool Surface_mesh_item_classification::run (int method, int classifier,
|
|||
}
|
||||
run (method, *m_ethz, subdivisions, smoothing);
|
||||
}
|
||||
else if (classifier == 2)
|
||||
else if (classifier == CGAL_CLASSIFICATION_OPENCV_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||
if (m_random_forest == NULL)
|
||||
|
|
@ -414,7 +411,7 @@ bool Surface_mesh_item_classification::run (int method, int classifier,
|
|||
run (method, *m_random_forest, subdivisions, smoothing);
|
||||
#endif
|
||||
}
|
||||
else if (classifier == 3)
|
||||
else if (classifier == CGAL_CLASSIFICATION_TENSORFLOW_NUMBER)
|
||||
{
|
||||
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||
if (m_neural_network == NULL)
|
||||
|
|
|
|||
|
|
@ -1738,7 +1738,7 @@ void Scene::updatePrimitiveIds(CGAL::Three::Scene_item* it)
|
|||
}
|
||||
}
|
||||
}
|
||||
bool Scene::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer, const QVector3D& scaler)
|
||||
bool Scene::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer)
|
||||
{
|
||||
CGAL::Three::Scene_item *i = item(mainSelectionIndex());
|
||||
if(!i)
|
||||
|
|
@ -1746,7 +1746,7 @@ bool Scene::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_inte
|
|||
Scene_print_item_interface* spit= qobject_cast<Scene_print_item_interface*>(i);
|
||||
if(spit && i->visible())
|
||||
{
|
||||
bool res = spit->testDisplayId(x,y,z, viewer, scaler);
|
||||
bool res = spit->testDisplayId(x,y,z, viewer);
|
||||
return res;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ public:
|
|||
void printAllIds() Q_DECL_OVERRIDE;
|
||||
//!Re-computes the primitiveIds for `item`
|
||||
void updatePrimitiveIds(Scene_item *item) Q_DECL_OVERRIDE;
|
||||
bool testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer, const QVector3D& scaler) Q_DECL_OVERRIDE;
|
||||
bool testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer) Q_DECL_OVERRIDE;
|
||||
Bbox bbox() const Q_DECL_OVERRIDE;
|
||||
void computeBbox();
|
||||
double len_diagonal() const Q_DECL_OVERRIDE
|
||||
|
|
|
|||
|
|
@ -2254,9 +2254,9 @@ void Scene_polyhedron_selection_item::printAllIds()
|
|||
{
|
||||
d->item->polyhedron_item()->printAllIds();
|
||||
}
|
||||
bool Scene_polyhedron_selection_item::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer, const QVector3D& scaler)const
|
||||
bool Scene_polyhedron_selection_item::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer)const
|
||||
{
|
||||
return d->item->polyhedron_item()->testDisplayId(x, y, z, viewer, scaler);
|
||||
return d->item->polyhedron_item()->testDisplayId(x, y, z, viewer);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ public:
|
|||
bool printEdgeIds() const;
|
||||
bool printFaceIds() const;
|
||||
void printAllIds();
|
||||
bool testDisplayId(double, double, double, CGAL::Three::Viewer_interface*, const QVector3D&)const;
|
||||
bool testDisplayId(double, double, double, CGAL::Three::Viewer_interface*)const;
|
||||
bool shouldDisplayIds(CGAL::Three::Scene_item *current_item) const;
|
||||
QString defaultSaveName() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2130,7 +2130,7 @@ void Scene_surface_mesh_item::printAllIds()
|
|||
d->killIds();
|
||||
}
|
||||
|
||||
bool Scene_surface_mesh_item::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer, const QVector3D& scaler)const
|
||||
bool Scene_surface_mesh_item::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer)const
|
||||
{
|
||||
const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
|
||||
EPICK::Point_3 src(x - offset.x,
|
||||
|
|
@ -2138,6 +2138,7 @@ bool Scene_surface_mesh_item::testDisplayId(double x, double y, double z, CGAL::
|
|||
z - offset.z);
|
||||
|
||||
CGAL::qglviewer::Camera* cam = viewer->camera();
|
||||
const QVector3D& scaler = viewer->scaler();
|
||||
EPICK::Point_3 dest( cam->position().x/scaler.x() - offset.x,
|
||||
cam->position().y/scaler.y() - offset.y,
|
||||
cam->position().z/scaler.z() - offset.z);
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ public:
|
|||
bool printFaceIds()const Q_DECL_OVERRIDE;
|
||||
void printAllIds() Q_DECL_OVERRIDE;
|
||||
bool shouldDisplayIds(CGAL::Three::Scene_item *current_item) const Q_DECL_OVERRIDE;
|
||||
bool testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface*, const QVector3D &scaler)const Q_DECL_OVERRIDE;
|
||||
bool testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface*)const Q_DECL_OVERRIDE;
|
||||
float alpha() const Q_DECL_OVERRIDE;
|
||||
void setAlpha(int alpha) Q_DECL_OVERRIDE;
|
||||
QSlider* alphaSlider();
|
||||
|
|
|
|||
|
|
@ -318,15 +318,14 @@ void Viewer::doBindings()
|
|||
|
||||
//modify mouse bindings that have been updated
|
||||
setMouseBinding(Qt::Key(0), Qt::NoModifier, Qt::LeftButton, CGAL::qglviewer::RAP_FROM_PIXEL, true, Qt::RightButton);
|
||||
setMouseBinding(Qt::ShiftModifier, Qt::RightButton, CGAL::qglviewer::NO_CLICK_ACTION, false, Qt::NoButton);
|
||||
setMouseBindingDescription(Qt::ShiftModifier, Qt::RightButton,
|
||||
tr("Select and pop context menu"));
|
||||
setMouseBinding(Qt::Key_R, Qt::NoModifier, Qt::LeftButton, CGAL::qglviewer::RAP_FROM_PIXEL);
|
||||
|
||||
//use the new API for these
|
||||
setMouseBinding(Qt::ShiftModifier, Qt::LeftButton, CGAL::qglviewer::SELECT);
|
||||
|
||||
setMouseBindingDescription(Qt::Key(0), Qt::ShiftModifier, Qt::LeftButton,
|
||||
tr("Selects and display context "
|
||||
"menu of the selected item"));
|
||||
setMouseBindingDescription(Qt::Key_I, Qt::NoModifier, Qt::LeftButton,
|
||||
tr("Show/hide the primitive ID of the types selected in the context menu of the picked item."));
|
||||
setMouseBindingDescription(Qt::Key_D, Qt::NoModifier, Qt::LeftButton,
|
||||
|
|
@ -1483,7 +1482,7 @@ void Viewer::wheelEvent(QWheelEvent* e)
|
|||
|
||||
bool Viewer::testDisplayId(double x, double y, double z)
|
||||
{
|
||||
return d->scene->testDisplayId(x,y,z,this, d->scaler);
|
||||
return d->scene->testDisplayId(x,y,z,this);
|
||||
}
|
||||
|
||||
QPainter* Viewer::getPainter(){return d->painter;}
|
||||
|
|
@ -2117,4 +2116,6 @@ void Viewer::onTextMessageSocketReceived(QString message)
|
|||
update();
|
||||
}
|
||||
#endif
|
||||
|
||||
const QVector3D& Viewer::scaler()const { return d->scaler; }
|
||||
#include "Viewer.moc"
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ public:
|
|||
//!Set total number of depth peeling passes.
|
||||
void setTotalPass(int);
|
||||
void resetFov();
|
||||
const QVector3D& scaler() const;
|
||||
Q_SIGNALS:
|
||||
void sendMessage(QString);
|
||||
void doneInitGL(CGAL::Three::Viewer_interface*);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <utility> // defines std::pair
|
||||
|
||||
#include <CGAL/boost/iterator/counting_iterator.hpp>
|
||||
#include <CGAL/boost/iterator/transform_iterator.hpp>
|
||||
#include <CGAL/Iterator_range.h>
|
||||
#include <CGAL/Cartesian_converter_fwd.h>
|
||||
|
|
@ -603,6 +604,15 @@ make_transform_range_from_property_map (Range& range, Pmap pmap)
|
|||
(make_transform_iterator_from_property_map (range.begin(), pmap),
|
||||
make_transform_iterator_from_property_map (range.end(), pmap));
|
||||
}
|
||||
|
||||
template <typename SizeType>
|
||||
CGAL::Iterator_range<boost::counting_iterator<SizeType> >
|
||||
make_counting_range (const SizeType begin, const SizeType end)
|
||||
{
|
||||
return CGAL::make_range (boost::counting_iterator<SizeType>(begin),
|
||||
boost::counting_iterator<SizeType>(end));
|
||||
}
|
||||
|
||||
/// \endcond
|
||||
|
||||
} // namespace CGAL
|
||||
|
|
|
|||
|
|
@ -19,31 +19,69 @@
|
|||
#define CGAL_IO_BINARY_FILE_IO_H
|
||||
|
||||
#include <CGAL/config.h>
|
||||
#include <CGAL/assertions.h>
|
||||
#include <iostream>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
inline void
|
||||
I_Binary_write_integer32(std::ostream& out, boost::int32_t i) {
|
||||
I_Binary_write_uinteger32(std::ostream& out, std::uint32_t u) {
|
||||
out.write( (char*)(&u), 4);
|
||||
}
|
||||
// Special function to write size_t in 32b integer to ensure files
|
||||
// written by 64b systems are still readable by 32b ones
|
||||
inline void
|
||||
I_Binary_write_size_t_into_uinteger32 (std::ostream& out, std::size_t s) {
|
||||
CGAL_assertion_msg
|
||||
(s <= static_cast<std::size_t>((std::numeric_limits<std::uint32_t>::max)()),
|
||||
"Trying to write size_t that does not fit in uint32_t");
|
||||
I_Binary_write_uinteger32 (out, static_cast<std::uint32_t>(s));
|
||||
}
|
||||
inline void
|
||||
I_Binary_write_integer32(std::ostream& out, std::int32_t i) {
|
||||
out.write( (char*)(&i), 4);
|
||||
}
|
||||
inline void
|
||||
I_Binary_write_float32(std::ostream& out, float f) {
|
||||
out.write( (char*)(&f), 4);
|
||||
}
|
||||
|
||||
inline void
|
||||
I_Binary_read_integer32(std::istream& in, boost::int32_t& i) {
|
||||
I_Binary_write_bool(std::ostream& out, bool b) {
|
||||
char c = (b ? 1 : 0);
|
||||
out.write(&c, 1);
|
||||
}
|
||||
|
||||
// Special function to read size_t from 32b integer to ensure files
|
||||
inline void
|
||||
I_Binary_read_uinteger32(std::istream& in, std::uint32_t& u) {
|
||||
in.read( (char*)(&u), 4);
|
||||
}
|
||||
// written by 64b systems are still readable by 32b ones
|
||||
inline void
|
||||
I_Binary_read_size_t_from_uinteger32(std::istream& in, std::size_t& s) {
|
||||
std::uint32_t s32;
|
||||
I_Binary_read_uinteger32 (in, s32);
|
||||
s = static_cast<std::size_t>(s32);
|
||||
}
|
||||
inline void
|
||||
I_Binary_read_integer32(std::istream& in, std::int32_t& i) {
|
||||
in.read( (char*)(&i), 4);
|
||||
}
|
||||
inline void
|
||||
I_Binary_read_float32(std::istream& in, float& f) {
|
||||
in.read( (char*)(&f), 4);
|
||||
}
|
||||
inline void
|
||||
I_Binary_read_bool(std::istream& in, bool& b) {
|
||||
char c;
|
||||
in.read(&c, 1);
|
||||
b = (c != 0);
|
||||
}
|
||||
|
||||
inline void
|
||||
I_swap_to_big_endian( boost::uint32_t& u) {
|
||||
I_swap_to_big_endian( std::uint32_t& u) {
|
||||
(void) u;
|
||||
#ifdef CGAL_LITTLE_ENDIAN
|
||||
u = ((u >> 24) | (u << 24) | ((u >> 8) & 0xff00) | ((u << 8) & 0xff0000));
|
||||
|
|
@ -51,14 +89,14 @@ I_swap_to_big_endian( boost::uint32_t& u) {
|
|||
}
|
||||
|
||||
inline void
|
||||
I_swap_to_big_endian( boost::int32_t& i) {
|
||||
I_swap_to_big_endian( std::int32_t& i) {
|
||||
// We need to use a union instead of the 2 lines below,
|
||||
// otherwise we get aliasing issues.
|
||||
// boost::uint32_t& u = (boost::uint32_t&)i;
|
||||
// std::uint32_t& u = (std::uint32_t&)i;
|
||||
// I_swap_to_big_endian( u);
|
||||
union {
|
||||
boost::int32_t in;
|
||||
boost::uint32_t ui;
|
||||
std::int32_t in;
|
||||
std::uint32_t ui;
|
||||
} u;
|
||||
u.in = i;
|
||||
I_swap_to_big_endian(u.ui);
|
||||
|
|
@ -69,10 +107,10 @@ inline void
|
|||
I_swap_to_big_endian( float& f) {
|
||||
// We need to use a union instead of the 2 lines below,
|
||||
// otherwise we get aliasing issues.
|
||||
// boost::uint32_t& u = (boost::uint32_t&)f;
|
||||
// std::uint32_t& u = (std::uint32_t&)f;
|
||||
// I_swap_to_big_endian( u);
|
||||
union {
|
||||
boost::uint32_t ui;
|
||||
std::uint32_t ui;
|
||||
float fl;
|
||||
} u;
|
||||
u.fl = f;
|
||||
|
|
@ -81,7 +119,7 @@ I_swap_to_big_endian( float& f) {
|
|||
}
|
||||
|
||||
inline void
|
||||
I_Binary_write_big_endian_integer32(std::ostream& out, boost::int32_t i) {
|
||||
I_Binary_write_big_endian_integer32(std::ostream& out, std::int32_t i) {
|
||||
I_swap_to_big_endian( i);
|
||||
out.write( (char*)(&i), 4);
|
||||
}
|
||||
|
|
@ -92,7 +130,7 @@ I_Binary_write_big_endian_float32(std::ostream& out, float f) {
|
|||
}
|
||||
|
||||
inline void
|
||||
I_Binary_read_big_endian_integer32(std::istream& in, boost::int32_t& i) {
|
||||
I_Binary_read_big_endian_integer32(std::istream& in, std::int32_t& i) {
|
||||
in.read( (char*)(&i), 4);
|
||||
I_swap_to_big_endian( i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1630,7 +1630,7 @@ private:
|
|||
|
||||
/*!
|
||||
* @brief runs the pseudo Constrained Delaunay Triangulation at each proxy region,
|
||||
* and stores the extracted indexed triangles in @a tris.
|
||||
* and stores the extracted indexed triangles in `tris`.
|
||||
* @pre all anchors are found, i.e. all boundary cycles have been visited
|
||||
* and attached with at least 3 anchors.
|
||||
*/
|
||||
|
|
@ -1829,7 +1829,7 @@ private:
|
|||
}
|
||||
|
||||
/*!
|
||||
* @brief subdivides a chord recursively in range `[@a chord_begin, @a chord_end).`
|
||||
* @brief subdivides a chord recursively in range `[chord_begin, chord_end)`.
|
||||
* @param chord_begin begin iterator of the chord
|
||||
* @param chord_end end iterator of the chord
|
||||
* @param subdivision_ratio the chord recursive split error threshold
|
||||
|
|
|
|||
|
|
@ -394,14 +394,14 @@ public:
|
|||
|
||||
/// During the local remeshing step, a triangle will be split
|
||||
/// if it has an angle larger than `max_triangle_angle()`.
|
||||
double max_triangle_angle()
|
||||
double max_triangle_angle() const
|
||||
{
|
||||
return m_alpha_TH;
|
||||
}
|
||||
|
||||
/// During the local remeshing step, an edge will be collapse
|
||||
/// if it is length is less than `min_edge_length()`.
|
||||
double min_edge_length()
|
||||
double min_edge_length() const
|
||||
{
|
||||
return m_min_edge_length;
|
||||
}
|
||||
|
|
@ -423,7 +423,7 @@ public:
|
|||
/// @{
|
||||
|
||||
/// Maximum number of iterations performed by `contract_until_convergence()`.
|
||||
std::size_t max_iterations()
|
||||
std::size_t max_iterations() const
|
||||
{
|
||||
return m_max_iterations;
|
||||
}
|
||||
|
|
@ -432,7 +432,7 @@ public:
|
|||
/// the meso-skeleton after one iteration is smaller than
|
||||
/// `area_variation_factor()*original_area` where `original_area` is the area of the input
|
||||
/// triangle mesh.
|
||||
double area_variation_factor()
|
||||
double area_variation_factor() const
|
||||
{
|
||||
return m_delta_area;
|
||||
}
|
||||
|
|
@ -460,7 +460,7 @@ public:
|
|||
/// faster, but results in a skeleton of lower quality.
|
||||
/// This parameter corresponds to \f$ w_H \f$ in the original publication.
|
||||
/// \cgalAdvancedEnd
|
||||
double quality_speed_tradeoff()
|
||||
double quality_speed_tradeoff() const
|
||||
{
|
||||
return m_omega_H;
|
||||
}
|
||||
|
|
@ -469,7 +469,7 @@ public:
|
|||
/// of the medial axis of the mesh during the contraction steps, so will be the result skeleton.
|
||||
// (an additional energy is used during the contraction using the Voronoi poles of the input triangulated mesh
|
||||
// as attractors).
|
||||
bool is_medially_centered()
|
||||
bool is_medially_centered() const
|
||||
{
|
||||
return m_is_medially_centered;
|
||||
}
|
||||
|
|
@ -482,7 +482,7 @@ public:
|
|||
/// It is only used if `is_medially_centered()==true`.
|
||||
/// This parameter corresponds to \f$ w_M \f$ in the original publication.
|
||||
/// \cgalAdvancedEnd
|
||||
double medially_centered_speed_tradeoff()
|
||||
double medially_centered_speed_tradeoff() const
|
||||
{
|
||||
return m_omega_P;
|
||||
}
|
||||
|
|
@ -526,7 +526,7 @@ public:
|
|||
* @param fixed_points
|
||||
* return the positions of fixed points
|
||||
*/
|
||||
void fixed_points(std::vector<Point>& fixed_points)
|
||||
void fixed_points(std::vector<Point>& fixed_points) const
|
||||
{
|
||||
fixed_points.clear();
|
||||
for(vertex_descriptor vd : vertices(m_tmesh))
|
||||
|
|
@ -542,7 +542,7 @@ public:
|
|||
* @param non_fixed_points
|
||||
* return the positions of non-fixed points
|
||||
*/
|
||||
void non_fixed_points(std::vector<Point>& non_fixed_points)
|
||||
void non_fixed_points(std::vector<Point>& non_fixed_points) const
|
||||
{
|
||||
non_fixed_points.clear();
|
||||
for(vertex_descriptor vd : vertices(m_tmesh))
|
||||
|
|
@ -558,7 +558,7 @@ public:
|
|||
* @param max_poles
|
||||
* for each surface mesh vertex, record its correspondent Voronoi pole position
|
||||
*/
|
||||
void poles(std::vector<Point>& max_poles)
|
||||
void poles(std::vector<Point>& max_poles) const
|
||||
{
|
||||
max_poles.resize(num_vertices(m_tmesh));
|
||||
int cnt = 0;
|
||||
|
|
@ -867,9 +867,6 @@ private:
|
|||
m_vertex_id_count = static_cast<int>(num_vertices(m_tmesh));
|
||||
m_max_id = m_vertex_id_count;
|
||||
|
||||
if (m_is_medially_centered)
|
||||
compute_voronoi_pole();
|
||||
|
||||
init_args();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -309,6 +309,77 @@ public:
|
|||
m_visitor->after_sweep();
|
||||
}
|
||||
|
||||
/*!
|
||||
runs the indexed sweep-line algorithm on a given range of
|
||||
x-monotone curves and accessor. The main difference from the
|
||||
original `sweep()` function is that the accessor allows to get
|
||||
indices of end-points and avoid checking several times in the
|
||||
event queue when the same vertex has several incident edges.
|
||||
|
||||
\param edges A range of edges
|
||||
\param accessor An object providing, for a value_type `e` of
|
||||
range `edges`, the following methods:
|
||||
- size_t min_end_index(e) -> the index of the min end of curve c
|
||||
- size_t max_end_index(e) -> the index of the max end of curve c
|
||||
- X_monotone_curve_2 curve(e) -> the x-monotone curve associated to e
|
||||
- size_t nb_vertices() -> the total number of points/events
|
||||
- void before_init() -> called before initialization
|
||||
- void after_init() -> called after initialization
|
||||
*/
|
||||
template <typename EdgeRange, typename Accessor>
|
||||
void indexed_sweep (const EdgeRange& edges,
|
||||
const Accessor& accessor)
|
||||
{
|
||||
m_visitor->before_sweep();
|
||||
accessor.before_init();
|
||||
_init_indexed_sweep(edges, accessor);
|
||||
accessor.after_init();
|
||||
_sweep();
|
||||
_complete_sweep();
|
||||
m_visitor->after_sweep();
|
||||
}
|
||||
|
||||
/*!
|
||||
runs the indexed sweep-line algorithm on a given range of
|
||||
x-monotone curves and accessor. The main difference from the
|
||||
original `sweep()` function is that the accessor allows to get
|
||||
indices of end-points and avoid checking several times in the
|
||||
event queue when the same vertex has several incident edges.
|
||||
|
||||
Variant with action event points (if a curve passed through an
|
||||
action point, it will be split).
|
||||
|
||||
\param edges A range of edges
|
||||
\param accessor An object providing, for a value_type `e` of
|
||||
range `edges`, the following methods:
|
||||
- size_t min_end_index(e) -> the index of the min end of curve c
|
||||
- size_t max_end_index(e) -> the index of the max end of curve c
|
||||
- X_monotone_curve_2 curve(e) -> the x-monotone curve associated to e
|
||||
- size_t nb_vertices() -> the total number of points/events
|
||||
- void before_init() -> called before initialization
|
||||
- void after_init() -> called after initialization
|
||||
\param points_begin An iterator for the first point in the range.
|
||||
\param points_end A past-the-end iterator for this range.
|
||||
\pre The value-type of PointInputIterator is the
|
||||
traits-class Point_2.
|
||||
*/
|
||||
template <typename EdgeRange, typename Accessor,
|
||||
typename PointInputIterator>
|
||||
void indexed_sweep (const EdgeRange& edges,
|
||||
const Accessor& accessor,
|
||||
PointInputIterator action_points_begin,
|
||||
PointInputIterator action_points_end)
|
||||
{
|
||||
m_visitor->before_sweep();
|
||||
accessor.before_init();
|
||||
_init_indexed_sweep(edges, accessor);
|
||||
accessor.after_init();
|
||||
_init_points(action_points_begin, action_points_end, Event::ACTION);
|
||||
_sweep();
|
||||
_complete_sweep();
|
||||
m_visitor->after_sweep();
|
||||
}
|
||||
|
||||
/*! Get an iterator for the first subcurve in the status line. */
|
||||
Status_line_iterator status_line_begin() { return m_statusLine.begin(); }
|
||||
|
||||
|
|
@ -376,6 +447,32 @@ protected:
|
|||
_init_curve(*cit, index);
|
||||
}
|
||||
|
||||
/*! Create a Subcurve object and two Event objects for each curve. */
|
||||
template <typename EdgeRange, typename Accessor>
|
||||
void _init_indexed_curves(const EdgeRange& edges,
|
||||
const Accessor& accessor)
|
||||
{
|
||||
std::vector<Event_queue_iterator> events (accessor.nb_vertices());
|
||||
|
||||
unsigned int index = 0;
|
||||
for (const auto& e : edges)
|
||||
{
|
||||
std::size_t max_end = accessor.max_end_index(e);
|
||||
std::size_t min_end = accessor.min_end_index(e);
|
||||
const X_monotone_curve_2& curve = accessor.curve (e);
|
||||
|
||||
// Construct and initialize a subcurve object.
|
||||
std::allocator_traits<Subcurve_alloc>::construct(m_subCurveAlloc, m_subCurves + index, m_masterSubcurve );
|
||||
(m_subCurves + index)->set_hint(this->m_statusLine.end());
|
||||
(m_subCurves + index)->init (curve);
|
||||
|
||||
_init_curve_end(curve, ARR_MAX_END, m_subCurves + index, events, max_end);
|
||||
_init_curve_end(curve, ARR_MIN_END, m_subCurves + index, events, min_end);
|
||||
|
||||
++ index;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Initiliaze the sweep algorithm. */
|
||||
template <typename CurveInputIterator>
|
||||
void _init_sweep(CurveInputIterator curves_begin,
|
||||
|
|
@ -387,6 +484,17 @@ protected:
|
|||
_init_curves(curves_begin, curves_end); // initialize the curves
|
||||
}
|
||||
|
||||
/*! Initiliaze the sweep algorithm. */
|
||||
template <typename EdgeRange, typename Accessor>
|
||||
void _init_indexed_sweep(const EdgeRange& edges,
|
||||
const Accessor& accessor)
|
||||
{
|
||||
m_num_of_subCurves =
|
||||
static_cast<unsigned int>(std::distance(edges.begin(), edges.end()));
|
||||
_init_structures();
|
||||
_init_indexed_curves(edges, accessor); // initialize the curves
|
||||
}
|
||||
|
||||
/*! Initialize the data structures for the sweep-line algorithm. */
|
||||
virtual void _init_structures();
|
||||
|
||||
|
|
@ -413,6 +521,11 @@ protected:
|
|||
void _init_curve_end(const X_monotone_curve_2& cv, Arr_curve_end ind,
|
||||
Subcurve* sc);
|
||||
|
||||
// Variant keeping track of indexed events
|
||||
void _init_curve_end(const X_monotone_curve_2& cv, Arr_curve_end ind,
|
||||
Subcurve* sc,
|
||||
std::vector<Event_queue_iterator>& events, std::size_t index);
|
||||
|
||||
/*! Handle the subcurves that are to the left of the event point (i.e.,
|
||||
* subcurves that we are done with).
|
||||
*/
|
||||
|
|
@ -485,6 +598,14 @@ protected:
|
|||
Arr_parameter_space ps_y,
|
||||
Subcurve* sc = nullptr);
|
||||
|
||||
// Variant keeping track of indexed events
|
||||
std::pair<Event*, bool> _push_event(const Point_2& pt, Attribute type,
|
||||
Arr_parameter_space ps_x,
|
||||
Arr_parameter_space ps_y,
|
||||
Subcurve* sc,
|
||||
std::vector<Event_queue_iterator>& events,
|
||||
std::size_t index);
|
||||
|
||||
/*! Push an event point associated with a curve end into the event queue.
|
||||
* \param cv The x-monotone curve.
|
||||
* \param ind The relevant curve end.
|
||||
|
|
@ -503,6 +624,17 @@ protected:
|
|||
Arr_parameter_space ps_y,
|
||||
Subcurve* sc = nullptr);
|
||||
|
||||
// Variant keeping track of indexed events
|
||||
std::pair<Event*, bool> _push_event(const X_monotone_curve_2& cv,
|
||||
Arr_curve_end ind,
|
||||
Attribute type,
|
||||
Arr_parameter_space ps_x,
|
||||
Arr_parameter_space ps_y,
|
||||
Subcurve* sc,
|
||||
const Point_2& pt,
|
||||
std::vector<Event_queue_iterator>& events,
|
||||
std::size_t index);
|
||||
|
||||
void _update_event_at_open_boundary(Event* e,
|
||||
const X_monotone_curve_2& cv,
|
||||
Arr_curve_end ind,
|
||||
|
|
|
|||
|
|
@ -301,6 +301,47 @@ _init_curve_end(const X_monotone_curve_2& cv, Arr_curve_end ind, Subcurve* sc)
|
|||
}
|
||||
}
|
||||
|
||||
template <typename Vis>
|
||||
void No_intersection_surface_sweep_2<Vis>::
|
||||
_init_curve_end(const X_monotone_curve_2& cv, Arr_curve_end ind, Subcurve* sc,
|
||||
std::vector<Event_queue_iterator>& events, std::size_t index)
|
||||
{
|
||||
// Get the boundary conditions of the curve end.
|
||||
const Attribute end_attr =
|
||||
(ind == ARR_MIN_END) ? Event::LEFT_END : Event::RIGHT_END;
|
||||
|
||||
Arr_parameter_space ps_x = m_traits->parameter_space_in_x_2_object()(cv, ind);
|
||||
Arr_parameter_space ps_y = m_traits->parameter_space_in_y_2_object()(cv, ind);
|
||||
|
||||
// Create the corresponding event and push it into the event queue.
|
||||
std::pair<Event*, bool> pair_res;
|
||||
|
||||
if (m_traits->is_closed_2_object()(cv, ind)) {
|
||||
// The curve end is closed and thus associated with a valid endpoint.
|
||||
const Point_2& pt = (ind == ARR_MIN_END) ?
|
||||
m_traits->construct_min_vertex_2_object()(cv) :
|
||||
m_traits->construct_max_vertex_2_object()(cv);
|
||||
|
||||
pair_res = ((ps_x == ARR_INTERIOR) && (ps_y == ARR_INTERIOR)) ?
|
||||
_push_event(pt, end_attr, ps_x, ps_y, sc, events, index) :
|
||||
_push_event(cv, ind, end_attr, ps_x, ps_y, sc, pt, events, index);
|
||||
|
||||
// Inform the visitor in case we updated an existing event.
|
||||
Event* e = pair_res.first;
|
||||
CGAL_assertion(e->is_closed());
|
||||
m_visitor->update_event(e, pt, cv, ind, pair_res.second);
|
||||
}
|
||||
else {
|
||||
// The curve end is open, insert it into the event queue.
|
||||
pair_res = _push_event(cv, ind, end_attr, ps_x, ps_y, sc);
|
||||
|
||||
// Inform the visitor in case we updated an existing event.
|
||||
Event* e = pair_res.first;
|
||||
CGAL_assertion(! e->is_closed());
|
||||
_update_event_at_open_boundary(e, cv, ind, pair_res.second);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Handle the subcurves to the left of the current event point.
|
||||
//
|
||||
|
|
@ -673,6 +714,72 @@ No_intersection_surface_sweep_2<Vis>::_push_event(const Point_2& pt,
|
|||
return (std::make_pair(e, !exist));
|
||||
}
|
||||
|
||||
template <typename Vis>
|
||||
std::pair<typename No_intersection_surface_sweep_2<Vis>::Event*, bool>
|
||||
No_intersection_surface_sweep_2<Vis>::_push_event(const Point_2& pt,
|
||||
Attribute type,
|
||||
Arr_parameter_space ps_x,
|
||||
Arr_parameter_space ps_y,
|
||||
Subcurve* sc,
|
||||
std::vector<Event_queue_iterator>& events,
|
||||
std::size_t index)
|
||||
{
|
||||
Event* e;
|
||||
|
||||
std::pair<Event_queue_iterator, bool>
|
||||
pair_res = std::make_pair (events[index], true);
|
||||
|
||||
// If event does not exist
|
||||
if (events[index] == Event_queue_iterator())
|
||||
{
|
||||
// Still look for the curve end in the event queue in case two
|
||||
// point are the the same in the vertex range
|
||||
m_queueEventLess.set_parameter_space_in_x(ps_x);
|
||||
m_queueEventLess.set_parameter_space_in_y(ps_y);
|
||||
pair_res = m_queue->find_lower(pt, m_queueEventLess);
|
||||
}
|
||||
|
||||
bool exist = pair_res.second;
|
||||
if (! exist) {
|
||||
// The point is not found in the event queue - create a new event and
|
||||
// insert it into the queue.
|
||||
e = _allocate_event(pt, type, ps_x, ps_y);
|
||||
}
|
||||
else {
|
||||
events[index] = pair_res.first;
|
||||
// The event associated with the given point already exists in the queue,
|
||||
// so we just have to update it.
|
||||
e = *(pair_res.first);
|
||||
CGAL_assertion(e->is_closed());
|
||||
|
||||
e->set_attribute(type);
|
||||
}
|
||||
|
||||
// If we are given a subcurve that the event represents one of its
|
||||
// endpoints, update the event and the subcurve records accordingly.
|
||||
// Note that this must be done before we actually insert the new event
|
||||
// into the event queue.
|
||||
_add_curve(e, sc, type);
|
||||
|
||||
// Insert the new event into the queue using the hint we got when we
|
||||
// looked for it.
|
||||
if (! exist)
|
||||
events[index] = m_queue->insert_before(pair_res.first, e);
|
||||
|
||||
#ifdef CGAL_SS_VERBOSE
|
||||
if (! exist) {
|
||||
CGAL_SS_PRINT_NEW_EVENT(pt, e);
|
||||
}
|
||||
else {
|
||||
CGAL_SS_PRINT_UPDATE_EVENT(pt, e);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return the resulting event and a flag indicating whether we have created
|
||||
// a new event.
|
||||
return (std::make_pair(e, !exist));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Push an event point associated with a curve end into the event queue.
|
||||
//
|
||||
|
|
@ -736,6 +843,72 @@ No_intersection_surface_sweep_2<Vis>::_push_event(const X_monotone_curve_2& cv,
|
|||
return (std::make_pair(e, !exist));
|
||||
}
|
||||
|
||||
template <typename Vis>
|
||||
std::pair<typename No_intersection_surface_sweep_2<Vis>::Event*, bool>
|
||||
No_intersection_surface_sweep_2<Vis>::_push_event(const X_monotone_curve_2& cv,
|
||||
Arr_curve_end ind,
|
||||
Attribute type,
|
||||
Arr_parameter_space ps_x,
|
||||
Arr_parameter_space ps_y,
|
||||
Subcurve* sc,
|
||||
const Point_2& pt,
|
||||
std::vector<Event_queue_iterator>& events,
|
||||
std::size_t index)
|
||||
{
|
||||
Event* e;
|
||||
|
||||
std::pair<Event_queue_iterator, bool>
|
||||
pair_res = std::make_pair (events[index], true);
|
||||
|
||||
// If event does not exist
|
||||
if (events[index] == Event_queue_iterator())
|
||||
{
|
||||
// Still look for the curve end in the event queue in case two
|
||||
// point are the the same in the vertex range
|
||||
|
||||
m_queueEventLess.set_parameter_space_in_x(ps_x);
|
||||
m_queueEventLess.set_parameter_space_in_y(ps_y);
|
||||
m_queueEventLess.set_index(ind);
|
||||
|
||||
pair_res =
|
||||
m_queue->find_lower(cv, m_queueEventLess);
|
||||
}
|
||||
|
||||
bool exist = pair_res.second;
|
||||
|
||||
if (! exist) {
|
||||
// The curve end is not found in the event queue - create a new event and
|
||||
// insert it into the queue.
|
||||
// The curve end is closed and so it is associated with a valid
|
||||
// point.
|
||||
e = _allocate_event(pt, type, ps_x, ps_y);
|
||||
}
|
||||
else {
|
||||
events[index] = pair_res.first;
|
||||
|
||||
// The event associated with the given curve end already exists in the
|
||||
// queue, so we just have to update it.
|
||||
e = *(pair_res.first);
|
||||
CGAL_assertion((e->parameter_space_in_x() == ps_x) &&
|
||||
(e->parameter_space_in_y() == ps_y));
|
||||
|
||||
e->set_attribute(type);
|
||||
}
|
||||
|
||||
// If we are given a subcurve that the event represents one of its
|
||||
// endpoints, update the event and the subcurve records accordingly.
|
||||
// Note that this must be done before we actually insert the new event
|
||||
// into the event queue.
|
||||
_add_curve(e, sc, type);
|
||||
|
||||
// Insert the new event into the queue using the hint we got when we
|
||||
// looked for it.
|
||||
if (! exist)
|
||||
events[index] = m_queue->insert_before(pair_res.first, e);
|
||||
|
||||
return (std::make_pair(e, !exist));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// add a curve as a right curve or left curve when the event is created
|
||||
// or updated.
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public:
|
|||
* \param z the Z coordinate of theTextItem's position.
|
||||
* \param viewer the viewer used to display the Scene.
|
||||
* \return true if the TextItem is visible. */
|
||||
virtual bool testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer, const QVector3D& scaler) = 0;
|
||||
virtual bool testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer) = 0;
|
||||
|
||||
///\brief displays all the vertices ids if there are less than max_textItems.
|
||||
virtual void printVertexIds() = 0;
|
||||
|
|
@ -72,7 +72,7 @@ public:
|
|||
///\brief displays all the primitive ids if there are less than max_textItems.
|
||||
virtual void printAllIds() = 0;
|
||||
|
||||
//!\brief moves the camera orthogonally to the picked sface.
|
||||
//!\brief moves the camera orthogonally to the picked face.
|
||||
//!
|
||||
//! \param point the picked point
|
||||
//! \param viewer the active viewer
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public:
|
|||
//!
|
||||
//! \returns true if the Id should be displayed
|
||||
//! \returns false if the Id should not be displayed (if it is hidden for example)
|
||||
virtual bool testDisplayId(double, double, double, CGAL::Three::Viewer_interface*, const QVector3D& scaler)const = 0;
|
||||
virtual bool testDisplayId(double, double, double, CGAL::Three::Viewer_interface*)const = 0;
|
||||
|
||||
//! \brief Tests if this item should display its ids.
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ public:
|
|||
{
|
||||
}
|
||||
//!Draws all the `TextItem`s
|
||||
void draw(CGAL::Three::Viewer_interface* viewer, const QVector3D& scaler);
|
||||
void draw(CGAL::Three::Viewer_interface* viewer, const QVector3D &scaler);
|
||||
//!\brief Adds a single TextItem to TextRenderer::local_textItems
|
||||
//!
|
||||
//! @see addText(float p_x, float p_y, float p_z, QString p_text, bool p_3D = true, QFont font = QFont(), QColor p_color = Qt::black)
|
||||
|
|
|
|||
|
|
@ -284,6 +284,9 @@ public:
|
|||
virtual void makeCurrent() = 0;
|
||||
virtual QVector4D* clipBox() const =0;
|
||||
virtual bool isClipping() const = 0;
|
||||
//! A vector indicating the scaling factors to apply to the scene when displaying it.
|
||||
//! It can be useful when a scene is very large along one of it's coordinates, making it hard to visualize it.
|
||||
virtual const QVector3D& scaler() const = 0;
|
||||
}; // end class Viewer_interface
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue