mirror of https://github.com/CGAL/cgal
Merge branch 'Periodic_4_hyperbolic_triangulation_2-IIordanov' of github.com:imiordanov/cgal into Periodic_4_hyperbolic_triangulation_2-IIordanov
This commit is contained in:
commit
2e2e3cf434
|
|
@ -1,5 +1,5 @@
|
||||||
language: cpp
|
language: cpp
|
||||||
dist: trusty
|
dist: xenial
|
||||||
sudo: required
|
sudo: required
|
||||||
git:
|
git:
|
||||||
depth: 3
|
depth: 3
|
||||||
|
|
@ -52,19 +52,19 @@ env:
|
||||||
- PACKAGE='Triangulation Triangulation_2 Triangulation_3 '
|
- PACKAGE='Triangulation Triangulation_2 Triangulation_3 '
|
||||||
- PACKAGE='Union_find Visibility_2 Voronoi_diagram_2 '
|
- PACKAGE='Union_find Visibility_2 Voronoi_diagram_2 '
|
||||||
- PACKAGE='wininst '
|
- PACKAGE='wininst '
|
||||||
compiler: clang-3.6
|
compiler: clang
|
||||||
install:
|
install:
|
||||||
- echo "$PWD"
|
- echo "$PWD"
|
||||||
- if [ -n "$TRAVIS_PULL_REQUEST" ] && [ "$PACKAGE" != CHECK ]; then DO_IGNORE=FALSE; for ARG in $(echo "$PACKAGE");do if [ "$ARG" = "Maintenance" ]; then continue; fi; . $PWD/.travis/test_package.sh "$PWD" "$ARG"; echo "DO_IGNORE is $DO_IGNORE"; if [ "$DO_IGNORE" = "FALSE" ]; then break; fi; done; if [ "$DO_IGNORE" = "TRUE" ]; then travis_terminate 0; fi;fi
|
- if [ -n "$TRAVIS_PULL_REQUEST" ] && [ "$PACKAGE" != CHECK ]; then DO_IGNORE=FALSE; for ARG in $(echo "$PACKAGE");do if [ "$ARG" = "Maintenance" ]; then continue; fi; . $PWD/.travis/test_package.sh "$PWD" "$ARG"; echo "DO_IGNORE is $DO_IGNORE"; if [ "$DO_IGNORE" = "FALSE" ]; then break; fi; done; if [ "$DO_IGNORE" = "TRUE" ]; then travis_terminate 0; fi;fi
|
||||||
- bash .travis/install.sh
|
- bash .travis/install.sh
|
||||||
- export CXX=clang++-3.6 CC=clang-3.6;
|
- export CXX=clang++ CC=clang;
|
||||||
before_script:
|
before_script:
|
||||||
- wget -O doxygen_exe https://cgal.geometryfactory.com/~mgimeno/doxygen_exe
|
- wget -O doxygen_exe https://cgal.geometryfactory.com/~mgimeno/doxygen_exe
|
||||||
- sudo mv doxygen_exe /usr/bin/doxygen
|
- sudo mv doxygen_exe /usr/bin/doxygen
|
||||||
- sudo chmod +x /usr/bin/doxygen
|
- sudo chmod +x /usr/bin/doxygen
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
- cd build
|
- cd build
|
||||||
- cmake -DCMAKE_CXX_FLAGS="-std=c++11" -DCGAL_HEADER_ONLY=ON -DQt5_DIR="/opt/qt55/lib/cmake/Qt5" -DQt5Svg_DIR="/opt/qt55/lib/cmake/Qt5Svg" -DQt5OpenGL_DIR="/opt/qt55/lib/cmake/Qt5OpenGL" -DCMAKE_CXX_FLAGS_RELEASE=-DCGAL_NDEBUG -DWITH_examples=ON -DWITH_demos=ON -DWITH_tests=ON ..
|
- cmake -DCMAKE_CXX_FLAGS="-std=c++11" -DCGAL_HEADER_ONLY=ON -DCMAKE_CXX_FLAGS_RELEASE=-DCGAL_NDEBUG -DWITH_examples=ON -DWITH_demos=ON -DWITH_tests=ON ..
|
||||||
- make
|
- make
|
||||||
- sudo make install &>/dev/null
|
- sudo make install &>/dev/null
|
||||||
- cd ..
|
- cd ..
|
||||||
|
|
|
||||||
|
|
@ -18,40 +18,18 @@ function build_tests {
|
||||||
function build_demo {
|
function build_demo {
|
||||||
mkdir -p build-travis
|
mkdir -p build-travis
|
||||||
cd build-travis
|
cd build-travis
|
||||||
if [ $NEED_3D = 1 ]; then
|
|
||||||
#install libqglviewer
|
|
||||||
git clone --depth=4 -b v2.6.3 --single-branch https://github.com/GillesDebunne/libQGLViewer.git ./qglviewer
|
|
||||||
pushd ./qglviewer/QGLViewer
|
|
||||||
#use qt5 instead of qt4
|
|
||||||
# export QT_SELECT=5
|
|
||||||
qmake NO_QT_VERSION_SUFFIX=yes
|
|
||||||
make -j2
|
|
||||||
if [ ! -f libQGLViewer.so ]; then
|
|
||||||
echo "libQGLViewer.so not made"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "QGLViewer built successfully"
|
|
||||||
fi
|
|
||||||
#end install qglviewer
|
|
||||||
popd
|
|
||||||
fi
|
|
||||||
EXTRA_CXX_FLAGS=
|
EXTRA_CXX_FLAGS=
|
||||||
case "$CC" in
|
case "$CC" in
|
||||||
clang*)
|
clang*)
|
||||||
EXTRA_CXX_FLAGS="-Werror=inconsistent-missing-override"
|
EXTRA_CXX_FLAGS="-Werror=inconsistent-missing-override"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
if [ $NEED_3D = 1 ]; then
|
cmake -DCGAL_DIR="/usr/local/lib/cmake/CGAL" -DCGAL_DONT_OVERRIDE_CMAKE_FLAGS:BOOL=ON -DCMAKE_CXX_FLAGS="${CXX_FLAGS} ${EXTRA_CXX_FLAGS}" ..
|
||||||
QGLVIEWERROOT=$PWD/qglviewer
|
|
||||||
export QGLVIEWERROOT
|
|
||||||
fi
|
|
||||||
cmake -DCGAL_DIR="/usr/local/lib/cmake/CGAL" -DQt5_DIR="/opt/qt55/lib/cmake/Qt5" -DQt5Svg_DIR="/opt/qt55/lib/cmake/Qt5Svg" -DQt5OpenGL_DIR="/opt/qt55/lib/cmake/Qt5OpenGL" -DCGAL_DONT_OVERRIDE_CMAKE_FLAGS:BOOL=ON -DCMAKE_CXX_FLAGS="${CXX_FLAGS} ${EXTRA_CXX_FLAGS}" ..
|
|
||||||
make -j2
|
make -j2
|
||||||
}
|
}
|
||||||
old_IFS=$IFS
|
old_IFS=$IFS
|
||||||
IFS=$' '
|
IFS=$' '
|
||||||
ROOT="$PWD/.."
|
ROOT="$PWD/.."
|
||||||
NEED_3D=0
|
|
||||||
for ARG in $(echo "$@")
|
for ARG in $(echo "$@")
|
||||||
do
|
do
|
||||||
#skip package maintenance
|
#skip package maintenance
|
||||||
|
|
@ -146,12 +124,6 @@ cd $ROOT
|
||||||
EXAMPLES="$ARG/examples/$ARG"
|
EXAMPLES="$ARG/examples/$ARG"
|
||||||
TEST="$ARG/test/$ARG"
|
TEST="$ARG/test/$ARG"
|
||||||
DEMOS=$ROOT/$ARG/demo/*
|
DEMOS=$ROOT/$ARG/demo/*
|
||||||
if [ "$ARG" = AABB_tree ] || [ "$ARG" = Alpha_shapes_3 ] ||\
|
|
||||||
[ "$ARG" = Circular_kernel_3 ] || [ "$ARG" = Linear_cell_complex ] ||\
|
|
||||||
[ "$ARG" = Periodic_3_triangulation_3 ] || [ "$ARG" = Principal_component_analysis ] ||\
|
|
||||||
[ "$ARG" = Surface_mesher ] || [ "$ARG" = Triangulation_3 ]; then
|
|
||||||
NEED_3D=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "$ROOT/$EXAMPLES" ]
|
if [ -d "$ROOT/$EXAMPLES" ]
|
||||||
then
|
then
|
||||||
|
|
@ -206,10 +178,9 @@ cd $ROOT
|
||||||
done
|
done
|
||||||
if [ "$ARG" = Polyhedron_demo ]; then
|
if [ "$ARG" = Polyhedron_demo ]; then
|
||||||
DEMO=Polyhedron/demo/Polyhedron
|
DEMO=Polyhedron/demo/Polyhedron
|
||||||
NEED_3D=1
|
|
||||||
cd "$ROOT/$DEMO"
|
cd "$ROOT/$DEMO"
|
||||||
build_demo
|
build_demo
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
IFS=$old_IFS
|
IFS=$old_IFS
|
||||||
# Local Variables:
|
# Local Variables:
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ done
|
||||||
COPY=0
|
COPY=0
|
||||||
for LINE in $(cat "$PWD/.travis/template.txt")
|
for LINE in $(cat "$PWD/.travis/template.txt")
|
||||||
do
|
do
|
||||||
if [ "$LINE" = "compiler: clang-3.6" ]
|
if [ "$LINE" = "compiler: clang" ]
|
||||||
then
|
then
|
||||||
COPY=1
|
COPY=1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,13 @@
|
||||||
|
|
||||||
[ -n "$CGAL_DEBUG_TRAVIS" ] && set -x
|
[ -n "$CGAL_DEBUG_TRAVIS" ] && set -x
|
||||||
DONE=0
|
DONE=0
|
||||||
|
sudo apt-get update
|
||||||
while [ $DONE = 0 ]
|
while [ $DONE = 0 ]
|
||||||
do
|
do
|
||||||
DONE=1 && sudo -E apt-add-repository -y "ppa:ppsspp/cmake" || DONE=0 && sleep 5
|
DONE=1 && sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install clang zsh \
|
||||||
done
|
flex bison cmake graphviz libgmp-dev libmpfr-dev libmpfi-dev zlib1g-dev libeigen3-dev libboost-dev \
|
||||||
DONE=0
|
libboost-system-dev libboost-program-options-dev libboost-thread-dev libboost-iostreams-dev \
|
||||||
while [ $DONE = 0 ]
|
qtbase5-dev libqt5opengl5-dev qtscript5-dev libqt5svg5-dev qttools5-dev qttools5-dev-tools qml-module-qtgraphicaleffects libopencv-dev mesa-common-dev libmetis-dev libglu1-mesa-dev \
|
||||||
do
|
|
||||||
DONE=1 && sudo -E apt-add-repository -y "ppa:hedges/qt5.5" || DONE=0 && sleep 5
|
|
||||||
done
|
|
||||||
|
|
||||||
DONE=0
|
|
||||||
while [ $DONE = 0 ]
|
|
||||||
do
|
|
||||||
DONE=1 && sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install clang-3.6 zsh \
|
|
||||||
flex bison cmake graphviz libgmp-dev libmpfr-dev libmpfi-dev zlib1g-dev libeigen3-dev libboost1.55-dev \
|
|
||||||
libboost-system1.55-dev libboost-program-options1.55-dev libboost-thread1.55-dev libboost-iostreams1.55-dev \
|
|
||||||
qt55base qt55script qt55svg qt55tools qt55graphicaleffects libopencv-dev mesa-common-dev libmetis-dev libglu1-mesa-dev \
|
|
||||||
|| DONE=0 && sudo apt-get update
|
|| DONE=0 && sudo apt-get update
|
||||||
done
|
done
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
language: cpp
|
language: cpp
|
||||||
dist: trusty
|
dist: xenial
|
||||||
sudo: required
|
sudo: required
|
||||||
git:
|
git:
|
||||||
depth: 3
|
depth: 3
|
||||||
|
|
@ -7,19 +7,19 @@ env:
|
||||||
matrix:
|
matrix:
|
||||||
PACKAGES_MATRIX
|
PACKAGES_MATRIX
|
||||||
|
|
||||||
compiler: clang-3.6
|
compiler: clang
|
||||||
install:
|
install:
|
||||||
- echo "$PWD"
|
- echo "$PWD"
|
||||||
- if [ -n "$TRAVIS_PULL_REQUEST" ] && [ "$PACKAGE" != CHECK ]; then DO_IGNORE=FALSE; for ARG in $(echo "$PACKAGE");do if [ "$ARG" = "Maintenance" ]; then continue; fi; . $PWD/.travis/test_package.sh "$PWD" "$ARG"; echo "DO_IGNORE is $DO_IGNORE"; if [ "$DO_IGNORE" = "FALSE" ]; then break; fi; done; if [ "$DO_IGNORE" = "TRUE" ]; then travis_terminate 0; fi;fi
|
- if [ -n "$TRAVIS_PULL_REQUEST" ] && [ "$PACKAGE" != CHECK ]; then DO_IGNORE=FALSE; for ARG in $(echo "$PACKAGE");do if [ "$ARG" = "Maintenance" ]; then continue; fi; . $PWD/.travis/test_package.sh "$PWD" "$ARG"; echo "DO_IGNORE is $DO_IGNORE"; if [ "$DO_IGNORE" = "FALSE" ]; then break; fi; done; if [ "$DO_IGNORE" = "TRUE" ]; then travis_terminate 0; fi;fi
|
||||||
- bash .travis/install.sh
|
- bash .travis/install.sh
|
||||||
- export CXX=clang++-3.6 CC=clang-3.6;
|
- export CXX=clang++ CC=clang;
|
||||||
before_script:
|
before_script:
|
||||||
- wget -O doxygen_exe https://cgal.geometryfactory.com/~mgimeno/doxygen_exe
|
- wget -O doxygen_exe https://cgal.geometryfactory.com/~mgimeno/doxygen_exe
|
||||||
- sudo mv doxygen_exe /usr/bin/doxygen
|
- sudo mv doxygen_exe /usr/bin/doxygen
|
||||||
- sudo chmod +x /usr/bin/doxygen
|
- sudo chmod +x /usr/bin/doxygen
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
- cd build
|
- cd build
|
||||||
- cmake -DCMAKE_CXX_FLAGS="-std=c++11" -DCGAL_HEADER_ONLY=ON -DQt5_DIR="/opt/qt55/lib/cmake/Qt5" -DQt5Svg_DIR="/opt/qt55/lib/cmake/Qt5Svg" -DQt5OpenGL_DIR="/opt/qt55/lib/cmake/Qt5OpenGL" -DCMAKE_CXX_FLAGS_RELEASE=-DCGAL_NDEBUG -DWITH_examples=ON -DWITH_demos=ON -DWITH_tests=ON ..
|
- cmake -DCMAKE_CXX_FLAGS="-std=c++11" -DCGAL_HEADER_ONLY=ON -DCMAKE_CXX_FLAGS_RELEASE=-DCGAL_NDEBUG -DWITH_examples=ON -DWITH_demos=ON -DWITH_tests=ON ..
|
||||||
- make
|
- make
|
||||||
- sudo make install &>/dev/null
|
- sudo make install &>/dev/null
|
||||||
- cd ..
|
- cd ..
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <boost/iterator.hpp>
|
#include <boost/iterator/iterator_adaptor.hpp>
|
||||||
|
|
||||||
#include <CGAL/Simple_cartesian.h>
|
#include <CGAL/Simple_cartesian.h>
|
||||||
#include <CGAL/AABB_tree.h>
|
#include <CGAL/AABB_tree.h>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
|
|
||||||
// Author(s) : Camille Wormser, Pierre Alliez
|
// Author(s) : Camille Wormser, Pierre Alliez
|
||||||
// Example of an AABB tree used with a simple list of
|
// Example of an AABB tree used with a simple list of
|
||||||
// triangles (a triangle soup) stored into an array of points.
|
// triangles (a triangle soup) stored into an array of points.
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/iterator.hpp>
|
#include <boost/iterator/iterator_adaptor.hpp>
|
||||||
|
|
||||||
#include <CGAL/Simple_cartesian.h>
|
#include <CGAL/Simple_cartesian.h>
|
||||||
#include <CGAL/AABB_tree.h>
|
#include <CGAL/AABB_tree.h>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ INPUT += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/Euler_operations.h \
|
||||||
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/METIS/partition_graph.h \
|
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/METIS/partition_graph.h \
|
||||||
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/METIS/partition_dual_graph.h
|
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/METIS/partition_dual_graph.h
|
||||||
|
|
||||||
|
|
||||||
EXAMPLE_PATH = ${CGAL_Surface_mesh_skeletonization_EXAMPLE_DIR} \
|
EXAMPLE_PATH = ${CGAL_Surface_mesh_skeletonization_EXAMPLE_DIR} \
|
||||||
${CGAL_Surface_mesh_segmentation_EXAMPLE_DIR} \
|
${CGAL_Surface_mesh_segmentation_EXAMPLE_DIR} \
|
||||||
${CGAL_Polygon_mesh_processing_EXAMPLE_DIR} \
|
${CGAL_Polygon_mesh_processing_EXAMPLE_DIR} \
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,13 @@ being marked or not.\n
|
||||||
<b>Default:</b> a default property map where no edge is constrained
|
<b>Default:</b> a default property map where no edge is constrained
|
||||||
\cgalNPEnd
|
\cgalNPEnd
|
||||||
|
|
||||||
|
\cgalNPBegin{use_binary_mode} \anchor BGL_use_binary_mode
|
||||||
|
is a Boolean indicating whether the binary mode or the ASCII mode should be used
|
||||||
|
when writing data into a stream.\n
|
||||||
|
<b>Type:</b> `bool`\n
|
||||||
|
<b>Default:</b> Function specific.
|
||||||
|
\cgalNPEnd
|
||||||
|
|
||||||
\cgalNPBegin{METIS_options} \anchor BGL_METIS_options
|
\cgalNPBegin{METIS_options} \anchor BGL_METIS_options
|
||||||
is a parameter used in `partition_graph()` and `partition_dual_graph()`
|
is a parameter used in `partition_graph()` and `partition_dual_graph()`
|
||||||
to pass options to the METIS graph partitioner. The many options of METIS
|
to pass options to the METIS graph partitioner. The many options of METIS
|
||||||
|
|
|
||||||
|
|
@ -709,6 +709,7 @@ user might encounter.
|
||||||
- \link PkgBGLIOFct CGAL::read_off() \endlink
|
- \link PkgBGLIOFct CGAL::read_off() \endlink
|
||||||
- \link PkgBGLIOFct CGAL::write_off() \endlink
|
- \link PkgBGLIOFct CGAL::write_off() \endlink
|
||||||
- \link PkgBGLIOFct CGAL::write_wrl() \endlink
|
- \link PkgBGLIOFct CGAL::write_wrl() \endlink
|
||||||
|
- `CGAL::write_vtp()`
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
#include <CGAL/boost/graph/helpers.h>
|
#include <CGAL/boost/graph/helpers.h>
|
||||||
#include <CGAL/boost/graph/named_params_helper.h>
|
#include <CGAL/boost/graph/named_params_helper.h>
|
||||||
#include <CGAL/boost/graph/named_function_params.h>
|
#include <CGAL/boost/graph/named_function_params.h>
|
||||||
|
#include <CGAL/IO/write_vtk.h>
|
||||||
|
|
||||||
namespace CGAL {
|
namespace CGAL {
|
||||||
/*!
|
/*!
|
||||||
|
|
@ -401,6 +402,278 @@ bool write_inp(std::ostream& os,
|
||||||
{
|
{
|
||||||
return write_inp(os, g, name, type, parameters::all_default());
|
return write_inp(os, g, name, type, parameters::all_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
namespace write_vtp {
|
||||||
|
|
||||||
|
// writes the polys appended data at the end of the .vtp file
|
||||||
|
template <class Mesh,
|
||||||
|
typename NamedParameters>
|
||||||
|
void
|
||||||
|
write_polys(std::ostream& os,
|
||||||
|
const Mesh & mesh,
|
||||||
|
const NamedParameters& np)
|
||||||
|
{
|
||||||
|
typedef typename boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
|
||||||
|
typedef typename boost::graph_traits<Mesh>::face_iterator face_iterator;
|
||||||
|
typedef typename CGAL::Polygon_mesh_processing::GetVertexIndexMap<Mesh, NamedParameters>::type Vimap;
|
||||||
|
Vimap V = choose_param(get_param(np, CGAL::internal_np::vertex_index),
|
||||||
|
get_const_property_map(CGAL::internal_np::vertex_index, mesh));
|
||||||
|
|
||||||
|
std::vector<std::size_t> connectivity_table;
|
||||||
|
std::vector<std::size_t> offsets;
|
||||||
|
std::vector<unsigned char> cell_type(num_faces(mesh),5); // triangle == 5
|
||||||
|
|
||||||
|
std::size_t off = 0;
|
||||||
|
for( face_iterator fit = faces(mesh).begin() ;
|
||||||
|
fit != faces(mesh).end() ;
|
||||||
|
++fit )
|
||||||
|
{
|
||||||
|
off += 3;
|
||||||
|
offsets.push_back(off);
|
||||||
|
BOOST_FOREACH(vertex_descriptor v,
|
||||||
|
vertices_around_face(halfedge(*fit, mesh), mesh))
|
||||||
|
connectivity_table.push_back(V[v]);
|
||||||
|
}
|
||||||
|
write_vector<std::size_t>(os,connectivity_table);
|
||||||
|
write_vector<std::size_t>(os,offsets);
|
||||||
|
write_vector<unsigned char>(os,cell_type);
|
||||||
|
}
|
||||||
|
//todo use named params for maps
|
||||||
|
template <class Mesh,
|
||||||
|
typename NamedParameters>
|
||||||
|
void
|
||||||
|
write_polys_tag(std::ostream& os,
|
||||||
|
const Mesh & mesh,
|
||||||
|
bool binary,
|
||||||
|
std::size_t& offset,
|
||||||
|
const NamedParameters& np)
|
||||||
|
{
|
||||||
|
typedef typename boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
|
||||||
|
typedef typename boost::graph_traits<Mesh>::face_iterator face_iterator;
|
||||||
|
typedef typename CGAL::Polygon_mesh_processing::GetVertexIndexMap<Mesh, NamedParameters>::type Vimap;
|
||||||
|
Vimap V = choose_param(get_param(np, CGAL::internal_np::vertex_index),
|
||||||
|
get_const_property_map(CGAL::internal_np::vertex_index, mesh));
|
||||||
|
|
||||||
|
std::string formatattribute =
|
||||||
|
binary ? " format=\"appended\"" : " format=\"ascii\"";
|
||||||
|
|
||||||
|
std::string typeattribute;
|
||||||
|
switch(sizeof(std::size_t)) {
|
||||||
|
case 8: typeattribute = " type=\"UInt64\""; break;
|
||||||
|
case 4: typeattribute = " type=\"UInt32\""; break;
|
||||||
|
default: CGAL_error_msg("Unknown size of std::size_t");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write connectivity table
|
||||||
|
os << " <Polys>\n"
|
||||||
|
<< " <DataArray Name=\"connectivity\""
|
||||||
|
<< formatattribute << typeattribute;
|
||||||
|
|
||||||
|
if (binary) { // if binary output, just write the xml tag
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += (3 * num_faces(mesh)+ 1) * sizeof(std::size_t);
|
||||||
|
// 3 indices (size_t) per triangle + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for( face_iterator fit = faces(mesh).begin() ;
|
||||||
|
fit != faces(mesh).end() ;
|
||||||
|
++fit )
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(vertex_descriptor v,
|
||||||
|
vertices_around_face(halfedge(*fit, mesh), mesh))
|
||||||
|
os << V[v] << " ";
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write offsets
|
||||||
|
os << " <DataArray Name=\"offsets\""
|
||||||
|
<< formatattribute << typeattribute;
|
||||||
|
|
||||||
|
if (binary) { // if binary output, just write the xml tag
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += (num_faces(mesh) + 1) * sizeof(std::size_t);
|
||||||
|
// 1 offset (size_t) per triangle + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
std::size_t polys_offset = 0;
|
||||||
|
for( face_iterator fit = faces(mesh).begin() ;
|
||||||
|
fit != faces(mesh).end() ;
|
||||||
|
++fit )
|
||||||
|
{
|
||||||
|
polys_offset += 3;
|
||||||
|
os << polys_offset << " ";
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write cell type (triangle == 5)
|
||||||
|
os << " <DataArray Name=\"types\""
|
||||||
|
<< formatattribute << " type=\"UInt8\"";
|
||||||
|
|
||||||
|
if (binary) {
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += num_faces(mesh) + sizeof(std::size_t);
|
||||||
|
// 1 unsigned char per cell + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for(std::size_t i = 0; i< num_faces(mesh); ++i)
|
||||||
|
os << "5 ";
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
os << " </Polys>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo : use namedparams for points and ids
|
||||||
|
//overload for facegraph
|
||||||
|
template <class Mesh,
|
||||||
|
typename NamedParameters>
|
||||||
|
void
|
||||||
|
write_points_tag(std::ostream& os,
|
||||||
|
const Mesh & mesh,
|
||||||
|
bool binary,
|
||||||
|
std::size_t& offset,
|
||||||
|
const NamedParameters& np)
|
||||||
|
{
|
||||||
|
typedef typename boost::graph_traits<Mesh>::vertex_iterator vertex_iterator;
|
||||||
|
typedef typename CGAL::Polygon_mesh_processing::GetVertexPointMap<Mesh, NamedParameters>::const_type Vpmap;
|
||||||
|
Vpmap vpm = choose_param(get_param(np, CGAL::vertex_point),
|
||||||
|
get_const_property_map(CGAL::vertex_point, mesh));
|
||||||
|
typedef typename boost::property_traits<Vpmap>::value_type Point_t;
|
||||||
|
typedef typename CGAL::Kernel_traits<Point_t>::Kernel Gt;
|
||||||
|
typedef typename Gt::FT FT;
|
||||||
|
|
||||||
|
std::string format = binary ? "appended" : "ascii";
|
||||||
|
std::string type = (sizeof(FT) == 8) ? "Float64" : "Float32";
|
||||||
|
|
||||||
|
os << " <Points>\n"
|
||||||
|
<< " <DataArray type =\"" << type << "\" NumberOfComponents=\"3\" format=\""
|
||||||
|
<< format;
|
||||||
|
|
||||||
|
if (binary) {
|
||||||
|
os << "\" offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += 3 * num_vertices(mesh) * sizeof(FT) + sizeof(std::size_t);
|
||||||
|
// 3 coords per points + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for( vertex_iterator vit = vertices(mesh).begin();
|
||||||
|
vit != vertices(mesh).end();
|
||||||
|
++vit)
|
||||||
|
{
|
||||||
|
os << get(vpm, *vit).x() << " " << get(vpm, *vit).y() << " "
|
||||||
|
<< get(vpm, *vit).z() << " ";
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
os << " </Points>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// writes the points appended data at the end of the .vtp file
|
||||||
|
template <class Mesh,
|
||||||
|
class NamedParameters>
|
||||||
|
void
|
||||||
|
write_polys_points(std::ostream& os,
|
||||||
|
const Mesh & mesh,
|
||||||
|
const NamedParameters& np)
|
||||||
|
{
|
||||||
|
typedef typename boost::graph_traits<Mesh>::vertex_iterator vertex_iterator;
|
||||||
|
typedef typename CGAL::Polygon_mesh_processing::GetVertexPointMap<Mesh, NamedParameters>::const_type Vpmap;
|
||||||
|
Vpmap vpm = choose_param(get_param(np, CGAL::vertex_point),
|
||||||
|
get_const_property_map(CGAL::vertex_point, mesh));
|
||||||
|
typedef typename boost::property_traits<Vpmap>::value_type Point_t;
|
||||||
|
typedef typename CGAL::Kernel_traits<Point_t>::Kernel Gt;
|
||||||
|
typedef typename Gt::FT FT;
|
||||||
|
std::vector<FT> coordinates;
|
||||||
|
for( vertex_iterator vit = vertices(mesh).begin();
|
||||||
|
vit != vertices(mesh).end();
|
||||||
|
++vit)
|
||||||
|
{
|
||||||
|
coordinates.push_back(get(vpm, *vit).x());
|
||||||
|
coordinates.push_back(get(vpm, *vit).y());
|
||||||
|
coordinates.push_back(get(vpm, *vit).z());
|
||||||
|
}
|
||||||
|
write_vector<FT>(os,coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace CGAL::internal::write_vtp
|
||||||
|
} // end namespace CGAL::internal
|
||||||
|
|
||||||
|
/*!\ingroup PkgBGLIOFct
|
||||||
|
*
|
||||||
|
* \brief writes a triangulated surface mesh in the `PolyData` XML format.
|
||||||
|
*
|
||||||
|
* \tparam TriangleMesh a model of `FaceListGraph` with only triangle faces.
|
||||||
|
* \tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||||
|
*
|
||||||
|
* \param os the stream used for writing.
|
||||||
|
* \param mesh the triangle mesh to be written.
|
||||||
|
* \param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the
|
||||||
|
* ones listed below
|
||||||
|
*
|
||||||
|
* \cgalNamedParamsBegin
|
||||||
|
* \cgalParamBegin{use_binary_mode} a Boolean indicating if the
|
||||||
|
* data should be written in binary (`true`, the default) or in ASCII (`false`).
|
||||||
|
* \cgalParamEnd
|
||||||
|
* \cgalParamBegin{vertex_point_map} the property map with the points associated to
|
||||||
|
* the vertices of `mesh`. If this parameter is omitted, an internal property map for
|
||||||
|
* `CGAL::vertex_point_t` must be available in `TriangleMesh`.
|
||||||
|
* \cgalParamEnd
|
||||||
|
* \cgalParamBegin{vertex_index_map} the property map with the indices associated to
|
||||||
|
* the vertices of `mesh`. If this parameter is omitted, an internal property map for
|
||||||
|
* `CGAL::vertex_index_t` must be available in `TriangleMesh`.
|
||||||
|
* \cgalParamEnd
|
||||||
|
* \cgalNamedParamsEnd
|
||||||
|
*/
|
||||||
|
template<class TriangleMesh,
|
||||||
|
class NamedParameters>
|
||||||
|
void write_vtp(std::ostream& os,
|
||||||
|
const TriangleMesh& mesh,
|
||||||
|
const NamedParameters& np)
|
||||||
|
{
|
||||||
|
os << "<?xml version=\"1.0\"?>\n"
|
||||||
|
<< "<VTKFile type=\"PolyData\" version=\"0.1\"";
|
||||||
|
#ifdef CGAL_LITTLE_ENDIAN
|
||||||
|
os << " byte_order=\"LittleEndian\"";
|
||||||
|
#else // CGAL_BIG_ENDIAN
|
||||||
|
os << " byte_order=\"BigEndian\"";
|
||||||
|
#endif
|
||||||
|
switch(sizeof(std::size_t)) {
|
||||||
|
case 4: os << " header_type=\"UInt32\""; break;
|
||||||
|
case 8: os << " header_type=\"UInt64\""; break;
|
||||||
|
default: CGAL_error_msg("Unknown size of std::size_t");
|
||||||
|
}
|
||||||
|
os << ">\n"
|
||||||
|
<< " <PolyData>" << "\n";
|
||||||
|
|
||||||
|
os << " <Piece NumberOfPoints=\"" << num_vertices(mesh)
|
||||||
|
<< "\" NumberOfPolys=\"" << num_faces(mesh) << "\">\n";
|
||||||
|
std::size_t offset = 0;
|
||||||
|
const bool binary = boost::choose_param(boost::get_param(np, internal_np::use_binary_mode), true);
|
||||||
|
internal::write_vtp::write_points_tag(os,mesh,binary,offset, np);
|
||||||
|
internal::write_vtp::write_polys_tag(os,mesh,binary,offset, np);
|
||||||
|
os << " </Piece>\n"
|
||||||
|
<< " </PolyData>\n";
|
||||||
|
if (binary) {
|
||||||
|
os << "<AppendedData encoding=\"raw\">\n_";
|
||||||
|
internal::write_vtp::write_polys_points(os,mesh, np);
|
||||||
|
internal::write_vtp::write_polys(os,mesh, np);
|
||||||
|
}
|
||||||
|
os << "</VTKFile>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TriangleMesh>
|
||||||
|
void write_vtp(std::ostream& os,
|
||||||
|
const TriangleMesh& mesh)
|
||||||
|
{
|
||||||
|
write_vtp(os, mesh, CGAL::parameters::all_default());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace CGAL
|
} // namespace CGAL
|
||||||
|
|
||||||
#endif // CGAL_BOOST_GRAPH_IO_H
|
#endif // CGAL_BOOST_GRAPH_IO_H
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ CGAL_add_named_parameter(edge_is_constrained_t, edge_is_constrained, edge_is_con
|
||||||
CGAL_add_named_parameter(first_index_t, first_index, first_index)
|
CGAL_add_named_parameter(first_index_t, first_index, first_index)
|
||||||
CGAL_add_named_parameter(number_of_iterations_t, number_of_iterations, number_of_iterations)
|
CGAL_add_named_parameter(number_of_iterations_t, number_of_iterations, number_of_iterations)
|
||||||
CGAL_add_named_parameter(verbosity_level_t, verbosity_level, verbosity_level)
|
CGAL_add_named_parameter(verbosity_level_t, verbosity_level, verbosity_level)
|
||||||
|
CGAL_add_named_parameter(use_binary_mode_t, use_binary_mode, use_binary_mode)
|
||||||
|
|
||||||
CGAL_add_named_parameter(metis_options_t, METIS_options, METIS_options)
|
CGAL_add_named_parameter(metis_options_t, METIS_options, METIS_options)
|
||||||
CGAL_add_named_parameter(vertex_partition_id_t, vertex_partition_id, vertex_partition_id_map)
|
CGAL_add_named_parameter(vertex_partition_id_t, vertex_partition_id, vertex_partition_id_map)
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ void test(const NamedParameters& np)
|
||||||
assert(get_param(np, CGAL::internal_np::weight_calculator).v == 39);
|
assert(get_param(np, CGAL::internal_np::weight_calculator).v == 39);
|
||||||
assert(get_param(np, CGAL::internal_np::preserve_genus).v == 40);
|
assert(get_param(np, CGAL::internal_np::preserve_genus).v == 40);
|
||||||
assert(get_param(np, CGAL::internal_np::verbosity_level).v == 41);
|
assert(get_param(np, CGAL::internal_np::verbosity_level).v == 41);
|
||||||
|
assert(get_param(np, CGAL::internal_np::use_binary_mode).v == 51);
|
||||||
assert(get_param(np, CGAL::internal_np::projection_functor).v == 42);
|
assert(get_param(np, CGAL::internal_np::projection_functor).v == 42);
|
||||||
assert(get_param(np, CGAL::internal_np::apply_per_connected_component).v == 46);
|
assert(get_param(np, CGAL::internal_np::apply_per_connected_component).v == 46);
|
||||||
assert(get_param(np, CGAL::internal_np::output_iterator).v == 47);
|
assert(get_param(np, CGAL::internal_np::output_iterator).v == 47);
|
||||||
|
|
@ -182,6 +183,7 @@ void test(const NamedParameters& np)
|
||||||
check_same_type<39>(get_param(np, CGAL::internal_np::weight_calculator));
|
check_same_type<39>(get_param(np, CGAL::internal_np::weight_calculator));
|
||||||
check_same_type<40>(get_param(np, CGAL::internal_np::preserve_genus));
|
check_same_type<40>(get_param(np, CGAL::internal_np::preserve_genus));
|
||||||
check_same_type<41>(get_param(np, CGAL::internal_np::verbosity_level));
|
check_same_type<41>(get_param(np, CGAL::internal_np::verbosity_level));
|
||||||
|
check_same_type<51>(get_param(np, CGAL::internal_np::use_binary_mode));
|
||||||
check_same_type<42>(get_param(np, CGAL::internal_np::projection_functor));
|
check_same_type<42>(get_param(np, CGAL::internal_np::projection_functor));
|
||||||
check_same_type<46>(get_param(np, CGAL::internal_np::apply_per_connected_component));
|
check_same_type<46>(get_param(np, CGAL::internal_np::apply_per_connected_component));
|
||||||
check_same_type<47>(get_param(np, CGAL::internal_np::output_iterator));
|
check_same_type<47>(get_param(np, CGAL::internal_np::output_iterator));
|
||||||
|
|
@ -241,6 +243,7 @@ int main()
|
||||||
.weight_calculator(A<39>(39))
|
.weight_calculator(A<39>(39))
|
||||||
.preserve_genus(A<40>(40))
|
.preserve_genus(A<40>(40))
|
||||||
.verbosity_level(A<41>(41))
|
.verbosity_level(A<41>(41))
|
||||||
|
.use_binary_mode(A<51>(51))
|
||||||
.projection_functor(A<42>(42))
|
.projection_functor(A<42>(42))
|
||||||
.throw_on_self_intersection(A<43>(43))
|
.throw_on_self_intersection(A<43>(43))
|
||||||
.clip_volume(A<44>(44))
|
.clip_volume(A<44>(44))
|
||||||
|
|
|
||||||
|
|
@ -322,7 +322,6 @@ inline bool setRationalReduceFlag(bool f) {
|
||||||
inline void CORE_init(long d) {
|
inline void CORE_init(long d) {
|
||||||
get_static_defAbsPrec() = CORE_posInfty;
|
get_static_defAbsPrec() = CORE_posInfty;
|
||||||
get_static_defOutputDigits() = d;
|
get_static_defOutputDigits() = d;
|
||||||
std::setprecision(get_static_defOutputDigits());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// change to scientific output format
|
/// change to scientific output format
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,13 @@ message( "== CMake setup (DONE) ==\n" )
|
||||||
|
|
||||||
# Enable testing with BUILD_TESTING
|
# Enable testing with BUILD_TESTING
|
||||||
option(BUILD_TESTING "Build the testing tree." OFF)
|
option(BUILD_TESTING "Build the testing tree." OFF)
|
||||||
include(CTest)
|
|
||||||
if(BUILD_TESTING AND NOT POLICY CMP0064)
|
if(BUILD_TESTING AND NOT POLICY CMP0064)
|
||||||
message(FATAL_ERROR "CGAL support of CTest requires CMake version 3.4 or later.
|
message(FATAL_ERROR "CGAL support of CTest requires CMake version 3.4 or later.
|
||||||
The variable BUILD_TESTING must be set of OFF.")
|
The variable BUILD_TESTING must be set of OFF.")
|
||||||
endif()
|
endif()
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
endif()
|
||||||
|
|
||||||
# and finally start actual build
|
# and finally start actual build
|
||||||
add_subdirectory( Installation )
|
add_subdirectory( Installation )
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,10 @@ The following code snippet shows how to instantiate such data structures from an
|
||||||
- [Distance_to_plane](@ref CGAL::Classification::Feature::Distance_to_plane) measures how far away a point is from a locally estimated plane;
|
- [Distance_to_plane](@ref CGAL::Classification::Feature::Distance_to_plane) measures how far away a point is from a locally estimated plane;
|
||||||
- [Eigenvalue](@ref CGAL::Classification::Feature::Eigenvalue) measures one of the three local eigenvalues;
|
- [Eigenvalue](@ref CGAL::Classification::Feature::Eigenvalue) measures one of the three local eigenvalues;
|
||||||
- [Elevation](@ref CGAL::Classification::Feature::Elevation) computes the local distance to an estimation of the ground;
|
- [Elevation](@ref CGAL::Classification::Feature::Elevation) computes the local distance to an estimation of the ground;
|
||||||
|
- [Height_above](@ref CGAL::Classification::Feature::Elevation) computes the distance between the local highest point and the point;
|
||||||
|
- [Height_below](@ref CGAL::Classification::Feature::Elevation) computes the distance between the point and the local lowest point;
|
||||||
- [Vertical_dispersion](@ref CGAL::Classification::Feature::Vertical_dispersion) computes how noisy the point set is on a local Z-cylinder;
|
- [Vertical_dispersion](@ref CGAL::Classification::Feature::Vertical_dispersion) computes how noisy the point set is on a local Z-cylinder;
|
||||||
|
- [Vertical_range](@ref CGAL::Classification::Feature::Elevation) computes the distance between the local highest and lowest points;
|
||||||
- [Verticality](@ref CGAL::Classification::Feature::Verticality) compares the local normal vector to the vertical vector.
|
- [Verticality](@ref CGAL::Classification::Feature::Verticality) compares the local normal vector to the vertical vector.
|
||||||
|
|
||||||
These features are designed for point sets but can easily be used with surface meshes as well (see \ref Classification_meshes). For more details about how these different features can help to identify one label or the other, please refer to their associated reference manual pages.
|
These features are designed for point sets but can easily be used with surface meshes as well (see \ref Classification_meshes). For more details about how these different features can help to identify one label or the other, please refer to their associated reference manual pages.
|
||||||
|
|
@ -108,6 +111,8 @@ Multiple scales that are sequentially larger can be used to increase the quality
|
||||||
|
|
||||||
Note that using this class in order to generate features is not mandatory, as features and data structures can all be handled by hand. It is mainly provided to make the specific case of point sets simpler to handle. Users can still add their own features within their feature set.
|
Note that using this class in order to generate features is not mandatory, as features and data structures can all be handled by hand. It is mainly provided to make the specific case of point sets simpler to handle. Users can still add their own features within their feature set.
|
||||||
|
|
||||||
|
Some data structure instantiated by the generator will be used by feature: for this reason, the generator should be instantiated _within the same scope_ as the feature set and should not be deleted before the feature set.
|
||||||
|
|
||||||
The following snippet shows how to use the point set feature generator:
|
The following snippet shows how to use the point set feature generator:
|
||||||
|
|
||||||
\snippet Classification/example_generation_and_training.cpp Generator
|
\snippet Classification/example_generation_and_training.cpp Generator
|
||||||
|
|
@ -196,13 +201,17 @@ Example of cluster classification mesh (left: input, middle: clusters computed f
|
||||||
|
|
||||||
%Classification relies on a classifier: this classifier is an object that, from the set of values taken by the features at an input item, computes the probability that an input item belongs to one label or another. A model of the concept `CGAL::Classification::Classifier` must take the index of an input item and store the probability associated to each label in a vector. If a classifier returns the value 1 for a pair of label and input item, it means that this item belongs to this label with certainty; values close to 0 mean that this item is not likely to belong to this label.
|
%Classification relies on a classifier: this classifier is an object that, from the set of values taken by the features at an input item, computes the probability that an input item belongs to one label or another. A model of the concept `CGAL::Classification::Classifier` must take the index of an input item and store the probability associated to each label in a vector. If a classifier returns the value 1 for a pair of label and input item, it means that this item belongs to this label with certainty; values close to 0 mean that this item is not likely to belong to this label.
|
||||||
|
|
||||||
\cgal provides three models for this concept, [ETHZ_random_forest_classifier](@ref CGAL::Classification::ETHZ_random_forest_classifier), [OpenCV_random_forest_classifier](@ref CGAL::Classification::OpenCV_random_forest_classifier) and [Sum_of_weighted_features_classifier](@ref CGAL::Classification::Sum_of_weighted_features_classifier).
|
\cgal provides four models for this concept, [ETHZ::Random_forest_classifier](@ref CGAL::Classification::ETHZ::Random_forest_classifier), [OpenCV::Random_forest_classifier](@ref CGAL::Classification::OpenCV::Random_forest_classifier), [TensorFlow::Neural_network_classifier](@ref CGAL::Classification::TensorFlow::Neural_network_classifier) and [Sum_of_weighted_features_classifier](@ref CGAL::Classification::Sum_of_weighted_features_classifier).
|
||||||
|
|
||||||
To perform classification based on these classifiers, please refer to \ref Classification_classification_functions.
|
\note Currently, [ETHZ::Random_forest_classifier](@ref CGAL::Classification::ETHZ::Random_forest_classifier)
|
||||||
|
is the best classifier available in \cgal and we strongly advise users
|
||||||
|
to use it.
|
||||||
|
|
||||||
|
To perform classification based on four classifiers, please refer to \ref Classification_classification_functions.
|
||||||
|
|
||||||
\subsection Classification_ETHZ_random_forest ETHZ Random Forest
|
\subsection Classification_ETHZ_random_forest ETHZ Random Forest
|
||||||
|
|
||||||
\cgal provides [ETHZ_random_forest_classifier](@ref CGAL::Classification::ETHZ_random_forest_classifier),
|
\cgal provides [ETHZ::Random_forest_classifier](@ref CGAL::Classification::ETHZ::Random_forest_classifier),
|
||||||
a classifier based on the Random Forest Template Library developed by
|
a classifier based on the Random Forest Template Library developed by
|
||||||
Stefan Walk at ETH Zurich \cgalCite{cgal:w-erftl-14} (the library is
|
Stefan Walk at ETH Zurich \cgalCite{cgal:w-erftl-14} (the library is
|
||||||
distributed under the MIT license and is included with the \cgal release,
|
distributed under the MIT license and is included with the \cgal release,
|
||||||
|
|
@ -210,9 +219,6 @@ the user does not have to install anything more). This classifier uses
|
||||||
a ground truth training set to construct several decision trees that
|
a ground truth training set to construct several decision trees that
|
||||||
are then used to assign a label to each input item.
|
are then used to assign a label to each input item.
|
||||||
|
|
||||||
__This classifier is currently the best available in \cgal and we
|
|
||||||
strongly advise users to use it.__
|
|
||||||
|
|
||||||
This classifier cannot be set up by hand and requires a ground truth
|
This classifier cannot be set up by hand and requires a ground truth
|
||||||
training set. The training algorithm is fast but usually requires a
|
training set. The training algorithm is fast but usually requires a
|
||||||
high number of inliers. The training algorithm uses more memory at
|
high number of inliers. The training algorithm uses more memory at
|
||||||
|
|
@ -227,14 +233,13 @@ to README provided in the [ETH Zurich's code archive](https://www.ethz.ch/conten
|
||||||
|
|
||||||
\subsection Classification_OpenCV_random_forest OpenCV Random Forest
|
\subsection Classification_OpenCV_random_forest OpenCV Random Forest
|
||||||
|
|
||||||
The second classifier is [OpenCV_random_forest_classifier](@ref CGAL::Classification::OpenCV_random_forest_classifier).
|
The second classifier is [OpenCV::Random_forest_classifier](@ref CGAL::Classification::OpenCV::Random_forest_classifier).
|
||||||
It uses the \ref thirdpartyOpenCV library, more specifically the
|
It uses the \ref thirdpartyOpenCV library, more specifically the
|
||||||
[Random Trees](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html)
|
[Random Trees](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html)
|
||||||
package.
|
package.
|
||||||
|
|
||||||
Note that this classifier usually produces results with a lower
|
Note that this classifier usually produces results with a lower
|
||||||
quality than [ETHZ_random_forest_classifier](@ref CGAL::Classification::ETHZ_random_forest_classifier).
|
quality than [ETHZ::Random_forest_classifier](@ref CGAL::Classification::ETHZ::Random_forest_classifier).
|
||||||
|
|
||||||
It is provided for the sake of completeness and for testing purposes,
|
It is provided for the sake of completeness and for testing purposes,
|
||||||
but if you are not sure what to use, we advise using the ETHZ Random
|
but if you are not sure what to use, we advise using the ETHZ Random
|
||||||
Forest instead.
|
Forest instead.
|
||||||
|
|
@ -244,6 +249,31 @@ use this classifier. For more details about the algorithm, please refer
|
||||||
to [the official documentation](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html)
|
to [the official documentation](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html)
|
||||||
of OpenCV.
|
of OpenCV.
|
||||||
|
|
||||||
|
\subsection Classification_TensorFlow_neural_network TensorFlow Neural Network
|
||||||
|
|
||||||
|
\cgal provides [TensorFlow::Neural_network_classifier](@ref CGAL::Classification::TensorFlow::Neural_network_classifier).
|
||||||
|
|
||||||
|
It uses the C++ API of the \ref thirdpartyTensorFlow library.
|
||||||
|
|
||||||
|
\warning This feature is still experimental: it may not be stable
|
||||||
|
and is likely to undergo substantial changes in future releases of
|
||||||
|
\cgal. The API changes will be announced in the release notes.
|
||||||
|
|
||||||
|
The provided interface is a feature-based neural network: a set of
|
||||||
|
features is used as an input layer followed by a user-specified number
|
||||||
|
of hidden layers with a user-specified activation function. The output
|
||||||
|
layer is a softmax layer providing, for each label, the probability
|
||||||
|
that an input item belongs to it.
|
||||||
|
|
||||||
|
This classifier cannot be set up by hand and requires a ground truth
|
||||||
|
training set. The training algorithm usually requires a higher number
|
||||||
|
of inliers than random forest. The quality of the results, so far, is
|
||||||
|
comparable to random forest.
|
||||||
|
|
||||||
|
An [example](\ref Classification_example_tensorflow_neural_network) shows how to
|
||||||
|
use this classifier. For more details about the algorithm, please refer
|
||||||
|
to [the TensorFlow tutorials](https://www.tensorflow.org/tutorials/).
|
||||||
|
|
||||||
\subsection Classification_sowf Sum of Weighted Features
|
\subsection Classification_sowf Sum of Weighted Features
|
||||||
|
|
||||||
This latest classifier defines the following attributes:
|
This latest classifier defines the following attributes:
|
||||||
|
|
@ -334,7 +364,7 @@ Top-Left: input point set. Top-Right: raw output classification represented by a
|
||||||
|
|
||||||
Mathematical details are provided hereafter.
|
Mathematical details are provided hereafter.
|
||||||
|
|
||||||
\subsection Classification_classify Raw classification
|
\subsection Classification_classify Raw Classification
|
||||||
|
|
||||||
- `CGAL::Classification::classify()`: this is the fastest method
|
- `CGAL::Classification::classify()`: this is the fastest method
|
||||||
that provides acceptable but usually noisy results (see Figure
|
that provides acceptable but usually noisy results (see Figure
|
||||||
|
|
@ -478,23 +508,31 @@ The following example:
|
||||||
|
|
||||||
\subsection Classification_example_ethz_random_forest ETHZ Random Forest
|
\subsection Classification_example_ethz_random_forest ETHZ Random Forest
|
||||||
|
|
||||||
The following example shows how to use the classifier [ETHZ_random_forest_classifier](@ref CGAL::Classification::ETHZ_random_forest_classifier) using an input training set.
|
The following example shows how to use the classifier [ETHZ::Random_forest_classifier](@ref CGAL::Classification::ETHZ::Random_forest_classifier) using an input training set.
|
||||||
|
|
||||||
\cgalExample{Classification/example_ethz_random_forest.cpp}
|
\cgalExample{Classification/example_ethz_random_forest.cpp}
|
||||||
|
|
||||||
\subsection Classification_example_opencv_random_forest OpenCV Random Forest
|
\subsection Classification_example_opencv_random_forest OpenCV Random Forest
|
||||||
|
|
||||||
The following example shows how to use the classifier [OpenCV_random_forest_classifier](@ref CGAL::Classification::OpenCV_random_forest_classifier) using an input training set.
|
The following example shows how to use the classifier [OpenCV::Random_forest_classifier](@ref CGAL::Classification::OpenCV::Random_forest_classifier) using an input training set.
|
||||||
|
|
||||||
\cgalExample{Classification/example_opencv_random_forest.cpp}
|
\cgalExample{Classification/example_opencv_random_forest.cpp}
|
||||||
|
|
||||||
|
\subsection Classification_example_tensorflow_neural_network TensorFlow Neural Network
|
||||||
|
|
||||||
|
The following example shows how to use the classifier
|
||||||
|
[TensorFlow::Neural_network_classifier](@ref CGAL::Classification::TensorFlow::Neural_network_classifier)
|
||||||
|
using an input training set.
|
||||||
|
|
||||||
|
\cgalExample{Classification/example_tensorflow_neural_network.cpp}
|
||||||
|
|
||||||
\subsection Classification_example_mesh Mesh Classification
|
\subsection Classification_example_mesh Mesh Classification
|
||||||
|
|
||||||
The following example:
|
The following example:
|
||||||
|
|
||||||
- reads a mesh in OFF format;
|
- reads a mesh in OFF format;
|
||||||
- automatically generates features on 5 scales;
|
- automatically generates features on 5 scales;
|
||||||
- loads a configuration file for classifier [ETHZ_random_forest_classifier](@ref CGAL::Classification::ETHZ_random_forest_classifier);
|
- loads a configuration file for classifier [ETHZ::Random_forest_classifier](@ref CGAL::Classification::ETHZ::Random_forest_classifier);
|
||||||
- runs the algorithm using the graphcut regularization.
|
- runs the algorithm using the graphcut regularization.
|
||||||
|
|
||||||
\cgalExample{Classification/example_mesh_classification.cpp}
|
\cgalExample{Classification/example_mesh_classification.cpp}
|
||||||
|
|
@ -509,7 +547,7 @@ The following example:
|
||||||
- detects plane using the algorithm `CGAL::Shape_detection_3::Region_growing`;
|
- detects plane using the algorithm `CGAL::Shape_detection_3::Region_growing`;
|
||||||
- creates [Cluster](@ref CGAL::Classification::Cluster) objects from these detected planes;
|
- creates [Cluster](@ref CGAL::Classification::Cluster) objects from these detected planes;
|
||||||
- computes cluster features from the pointwise features;
|
- computes cluster features from the pointwise features;
|
||||||
- loads a configuration file for classifier [ETHZ_random_forest_classifier](@ref CGAL::Classification::ETHZ_random_forest_classifier);
|
- loads a configuration file for classifier [ETHZ::Random_forest_classifier](@ref CGAL::Classification::ETHZ::Random_forest_classifier);
|
||||||
- runs the algorithm using the raw algorithm.
|
- runs the algorithm using the raw algorithm.
|
||||||
|
|
||||||
\cgalExample{Classification/example_cluster_classification.cpp}
|
\cgalExample{Classification/example_cluster_classification.cpp}
|
||||||
|
|
@ -517,7 +555,7 @@ The following example:
|
||||||
|
|
||||||
\section Classification_history History
|
\section Classification_history History
|
||||||
|
|
||||||
This package is based on a research code by [Florent Lafarge](https://www-sop.inria.fr/members/Florent.Lafarge/) that was generalized, extended and packaged by [Simon Giraudot](http://geometryfactory.com/who-we-are/) in \cgal 4.12. %Classification of surface meshes and of clusters were introduced in \cgal 4.13.
|
This package is based on a research code by [Florent Lafarge](https://www-sop.inria.fr/members/Florent.Lafarge/) that was generalized, extended and packaged by [Simon Giraudot](http://geometryfactory.com/who-we-are/) in \cgal 4.12. %Classification of surface meshes and of clusters were introduced in \cgal 4.13. The Neural Network classifier was introduced in \cgal 4.14.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,9 @@ Concept describing a classifier used by classification functions (see
|
||||||
`CGAL::Classification::classify_with_graphcut()`).
|
`CGAL::Classification::classify_with_graphcut()`).
|
||||||
|
|
||||||
\cgalHasModel `CGAL::Classification::Sum_of_weighted_features_classifier`
|
\cgalHasModel `CGAL::Classification::Sum_of_weighted_features_classifier`
|
||||||
\cgalHasModel `CGAL::Classification::ETHZ_random_forest_classifier`
|
\cgalHasModel `CGAL::Classification::ETHZ::Random_forest_classifier`
|
||||||
\cgalHasModel `CGAL::Classification::OpenCV_random_forest_classifier`
|
\cgalHasModel `CGAL::Classification::OpenCV::Random_forest_classifier`
|
||||||
|
\cgalHasModel `CGAL::Classification::TensorFlow::Neural_network_classifier`
|
||||||
|
|
||||||
*/
|
*/
|
||||||
class Classifier
|
class Classifier
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,21 @@ Functions that perform classification based on a set of labels and a classifier,
|
||||||
|
|
||||||
Classifiers are functors that, given a label set and an input item, associate this input item with an energy for each label. This energy measures the likelihood of the item to belong to this label.
|
Classifiers are functors that, given a label set and an input item, associate this input item with an energy for each label. This energy measures the likelihood of the item to belong to this label.
|
||||||
|
|
||||||
|
\defgroup PkgClassificationClassifiersETHZ ETHZ
|
||||||
|
\ingroup PkgClassificationClassifiers
|
||||||
|
|
||||||
|
Classifiers that use the ETHZ library.
|
||||||
|
|
||||||
|
\defgroup PkgClassificationClassifiersOpenCV OpenCV
|
||||||
|
\ingroup PkgClassificationClassifiers
|
||||||
|
|
||||||
|
Classifiers that use the \ref thirdpartyOpenCV library.
|
||||||
|
|
||||||
|
\defgroup PkgClassificationClassifiersTensorFlow TensorFlow
|
||||||
|
\ingroup PkgClassificationClassifiers
|
||||||
|
|
||||||
|
Classifiers that use the \ref thirdpartyTensorFlow library.
|
||||||
|
|
||||||
\defgroup PkgClassificationDataStructures Common Data Structures
|
\defgroup PkgClassificationDataStructures Common Data Structures
|
||||||
\ingroup PkgClassificationRef
|
\ingroup PkgClassificationRef
|
||||||
|
|
||||||
|
|
@ -86,9 +101,10 @@ Data structures specialized to classify clusters.
|
||||||
|
|
||||||
## Classifiers ##
|
## Classifiers ##
|
||||||
|
|
||||||
|
- `CGAL::Classification::ETHZ::Random_forest_classifier`
|
||||||
|
- `CGAL::Classification::OpenCV::Random_forest_classifier`
|
||||||
|
- `CGAL::Classification::TensorFlow::Neural_network_classifier<ActivationFunction>`
|
||||||
- `CGAL::Classification::Sum_of_weighted_features_classifier`
|
- `CGAL::Classification::Sum_of_weighted_features_classifier`
|
||||||
- `CGAL::Classification::ETHZ_random_forest_classifier`
|
|
||||||
- `CGAL::Classification::OpenCV_random_forest_classifier`
|
|
||||||
|
|
||||||
## Common Data Structures ##
|
## Common Data Structures ##
|
||||||
|
|
||||||
|
|
@ -115,8 +131,11 @@ Data structures specialized to classify clusters.
|
||||||
- `CGAL::Classification::Feature::Echo_scatter<GeomTraits, PointRange, PointMap, EchoMap>`
|
- `CGAL::Classification::Feature::Echo_scatter<GeomTraits, PointRange, PointMap, EchoMap>`
|
||||||
- `CGAL::Classification::Feature::Eigenvalue`
|
- `CGAL::Classification::Feature::Eigenvalue`
|
||||||
- `CGAL::Classification::Feature::Elevation<GeomTraits, PointRange, PointMap>`
|
- `CGAL::Classification::Feature::Elevation<GeomTraits, PointRange, PointMap>`
|
||||||
|
- `CGAL::Classification::Feature::Height_above<GeomTraits, PointRange, PointMap>`
|
||||||
|
- `CGAL::Classification::Feature::Height_below<GeomTraits, PointRange, PointMap>`
|
||||||
- `CGAL::Classification::Feature::Simple_feature<InputRange, PropertyMap>`
|
- `CGAL::Classification::Feature::Simple_feature<InputRange, PropertyMap>`
|
||||||
- `CGAL::Classification::Feature::Vertical_dispersion<GeomTraits, PointRange, PointMap>`
|
- `CGAL::Classification::Feature::Vertical_dispersion<GeomTraits, PointRange, PointMap>`
|
||||||
|
- `CGAL::Classification::Feature::Vertical_range<GeomTraits, PointRange, PointMap>`
|
||||||
- `CGAL::Classification::Feature::Verticality<GeomTraits>`
|
- `CGAL::Classification::Feature::Verticality<GeomTraits>`
|
||||||
|
|
||||||
## Point Set Classification ##
|
## Point Set Classification ##
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
\example Classification/example_generation_and_training.cpp
|
\example Classification/example_generation_and_training.cpp
|
||||||
\example Classification/example_ethz_random_forest.cpp
|
\example Classification/example_ethz_random_forest.cpp
|
||||||
\example Classification/example_opencv_random_forest.cpp
|
\example Classification/example_opencv_random_forest.cpp
|
||||||
|
\example Classification/example_tensorflow_neural_network.cpp
|
||||||
\example Classification/example_mesh_classification.cpp
|
\example Classification/example_mesh_classification.cpp
|
||||||
\example Classification/example_cluster_classification.cpp
|
\example Classification/example_cluster_classification.cpp
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,6 @@ if( WIN32 )
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(OpenCV QUIET)
|
|
||||||
if (OpenCV_FOUND)
|
if (OpenCV_FOUND)
|
||||||
message(STATUS "Found OpenCV ${OpenCV_VERSION}")
|
message(STATUS "Found OpenCV ${OpenCV_VERSION}")
|
||||||
include_directories(${OpenCV_INCLUDE_DIRS})
|
include_directories(${OpenCV_INCLUDE_DIRS})
|
||||||
|
|
@ -108,6 +107,21 @@ else()
|
||||||
message(STATUS "NOTICE: OpenCV was not found. OpenCV random forest predicate for classification won't be available.")
|
message(STATUS "NOTICE: OpenCV was not found. OpenCV random forest predicate for classification won't be available.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(TensorFlow QUIET)
|
||||||
|
if (TensorFlow_FOUND)
|
||||||
|
message(STATUS "Found TensorFlow")
|
||||||
|
include_directories( ${TensorFlow_INCLUDE_DIR} )
|
||||||
|
set(classification_linked_libraries ${classification_linked_libraries}
|
||||||
|
${TensorFlow_LIBRARY})
|
||||||
|
set(classification_compile_definitions ${classification_compile_definitions}
|
||||||
|
"-DCGAL_LINKED_WITH_TENSORFLOW")
|
||||||
|
|
||||||
|
set(targets ${targets} example_tensorflow_neural_network)
|
||||||
|
else()
|
||||||
|
message(STATUS "NOTICE: TensorFlow not found, Neural Network predicate for classification won't be available.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# Creating targets with correct libraries and flags
|
# Creating targets with correct libraries and flags
|
||||||
foreach(target ${targets})
|
foreach(target ${targets})
|
||||||
create_single_source_cgal_program( "${target}.cpp" CXX_FEATURES ${needed_cxx_features} )
|
create_single_source_cgal_program( "${target}.cpp" CXX_FEATURES ${needed_cxx_features} )
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ int main (int argc, char** argv)
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////
|
||||||
//! [Classify]
|
//! [Classify]
|
||||||
std::vector<std::size_t> label_indices;
|
std::vector<int> label_indices (pts.size(), -1);
|
||||||
|
|
||||||
CGAL::Real_timer t;
|
CGAL::Real_timer t;
|
||||||
t.start();
|
t.start();
|
||||||
|
|
@ -200,7 +200,7 @@ int main (int argc, char** argv)
|
||||||
{
|
{
|
||||||
f << pts[i] << " ";
|
f << pts[i] << " ";
|
||||||
|
|
||||||
Label_handle label = labels[label_indices[i]];
|
Label_handle label = labels[std::size_t(label_indices[i])];
|
||||||
if (label == ground)
|
if (label == ground)
|
||||||
f << "245 180 0" << std::endl;
|
f << "245 180 0" << std::endl;
|
||||||
else if (label == vegetation)
|
else if (label == vegetation)
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ int main (int argc, char** argv)
|
||||||
std::vector<int> label_indices(clusters.size(), -1);
|
std::vector<int> label_indices(clusters.size(), -1);
|
||||||
|
|
||||||
std::cerr << "Using ETHZ Random Forest Classifier" << std::endl;
|
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::cerr << "Loading configuration" << std::endl;
|
||||||
std::ifstream in_config (filename_config, std::ios_base::in | std::ios_base::binary);
|
std::ifstream in_config (filename_config, std::ios_base::in | std::ios_base::binary);
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ int main (int argc, char** argv)
|
||||||
std::vector<int> label_indices(pts.size(), -1);
|
std::vector<int> label_indices(pts.size(), -1);
|
||||||
|
|
||||||
std::cerr << "Using ETHZ Random Forest Classifier" << std::endl;
|
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 << "Training" << std::endl;
|
std::cerr << "Training" << std::endl;
|
||||||
t.reset();
|
t.reset();
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ int main (int argc, char** argv)
|
||||||
classifier.set_effect (b, my_feature, Classifier::PENALIZING);
|
classifier.set_effect (b, my_feature, Classifier::PENALIZING);
|
||||||
|
|
||||||
std::cerr << "Classifying" << std::endl;
|
std::cerr << "Classifying" << std::endl;
|
||||||
std::vector<std::size_t> label_indices(pts.size(), -1);
|
std::vector<int> label_indices(pts.size(), -1);
|
||||||
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
||||||
(pts, Pmap(), labels, classifier,
|
(pts, Pmap(), labels, classifier,
|
||||||
neighborhood.k_neighbor_query(12),
|
neighborhood.k_neighbor_query(12),
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ int main (int argc, char** argv)
|
||||||
std::vector<int> label_indices(mesh.number_of_faces(), -1);
|
std::vector<int> label_indices(mesh.number_of_faces(), -1);
|
||||||
|
|
||||||
std::cerr << "Using ETHZ Random Forest Classifier" << std::endl;
|
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::cerr << "Loading configuration" << std::endl;
|
||||||
std::ifstream in_config (filename_config, std::ios_base::in | std::ios_base::binary);
|
std::ifstream in_config (filename_config, std::ios_base::in | std::ios_base::binary);
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ int main (int argc, char** argv)
|
||||||
std::vector<int> label_indices(pts.size(), -1);
|
std::vector<int> label_indices(pts.size(), -1);
|
||||||
|
|
||||||
std::cerr << "Using OpenCV Random Forest Classifier" << std::endl;
|
std::cerr << "Using OpenCV Random Forest Classifier" << std::endl;
|
||||||
Classification::OpenCV_random_forest_classifier classifier (labels, features);
|
Classification::OpenCV::Random_forest_classifier classifier (labels, features);
|
||||||
|
|
||||||
std::cerr << "Training" << std::endl;
|
std::cerr << "Training" << std::endl;
|
||||||
t.reset();
|
t.reset();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
#if defined (_MSC_VER) && !defined (_WIN64)
|
||||||
|
#pragma warning(disable:4244) // boost::number_distance::distance()
|
||||||
|
// converts 64 to 32 bits integers
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
#include <CGAL/Classification.h>
|
||||||
|
#include <CGAL/Point_set_3.h>
|
||||||
|
#include <CGAL/Point_set_3/IO.h>
|
||||||
|
|
||||||
|
#include <CGAL/Real_timer.h>
|
||||||
|
|
||||||
|
typedef CGAL::Simple_cartesian<double> Kernel;
|
||||||
|
typedef Kernel::Point_3 Point;
|
||||||
|
typedef CGAL::Point_set_3<Point> Point_set;
|
||||||
|
typedef Kernel::Iso_cuboid_3 Iso_cuboid_3;
|
||||||
|
|
||||||
|
typedef Point_set::Point_map Pmap;
|
||||||
|
typedef Point_set::Property_map<int> Imap;
|
||||||
|
typedef Point_set::Property_map<unsigned char> UCmap;
|
||||||
|
|
||||||
|
namespace Classification = CGAL::Classification;
|
||||||
|
|
||||||
|
typedef Classification::Label_handle Label_handle;
|
||||||
|
typedef Classification::Feature_handle Feature_handle;
|
||||||
|
typedef Classification::Label_set Label_set;
|
||||||
|
typedef Classification::Feature_set Feature_set;
|
||||||
|
|
||||||
|
typedef Classification::Point_set_feature_generator<Kernel, Point_set, Pmap> Feature_generator;
|
||||||
|
|
||||||
|
|
||||||
|
int main (int argc, char** argv)
|
||||||
|
{
|
||||||
|
std::string filename = "data/b9_training.ply";
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
filename = argv[1];
|
||||||
|
|
||||||
|
std::ifstream in (filename.c_str(), std::ios::binary);
|
||||||
|
Point_set pts;
|
||||||
|
|
||||||
|
std::cerr << "Reading input" << std::endl;
|
||||||
|
in >> pts;
|
||||||
|
|
||||||
|
Imap label_map;
|
||||||
|
bool lm_found = false;
|
||||||
|
boost::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;
|
||||||
|
CGAL::Real_timer t;
|
||||||
|
t.start();
|
||||||
|
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
|
||||||
|
Label_set labels;
|
||||||
|
Label_handle ground = labels.add ("ground");
|
||||||
|
Label_handle vegetation = labels.add ("vegetation");
|
||||||
|
Label_handle roof = labels.add ("roof");
|
||||||
|
|
||||||
|
std::vector<int> label_indices(pts.size(), -1);
|
||||||
|
|
||||||
|
std::cerr << "Using TensorFlow neural network Classifier" << std::endl;
|
||||||
|
Classification::TensorFlow::Neural_network_classifier<> classifier (labels, features);
|
||||||
|
|
||||||
|
std::cerr << "Training" << std::endl;
|
||||||
|
t.reset();
|
||||||
|
t.start();
|
||||||
|
classifier.train (ground_truth,
|
||||||
|
true, // restart from scratch
|
||||||
|
100); // 100 iterations
|
||||||
|
t.stop();
|
||||||
|
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
|
||||||
|
|
||||||
|
t.reset();
|
||||||
|
t.start();
|
||||||
|
Classification::classify_with_graphcut<CGAL::Sequential_tag>
|
||||||
|
(pts, pts.point_map(), labels, classifier,
|
||||||
|
generator.neighborhood().k_neighbor_query(12),
|
||||||
|
0.2f, 1, label_indices);
|
||||||
|
t.stop();
|
||||||
|
|
||||||
|
std::cerr << "Classification with graphcut done in " << t.time() << " second(s)" << std::endl;
|
||||||
|
|
||||||
|
std::cerr << "Precision, recall, F1 scores and IoU:" << std::endl;
|
||||||
|
Classification::Evaluation evaluation (labels, ground_truth, label_indices);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < labels.size(); ++ i)
|
||||||
|
{
|
||||||
|
std::cerr << " * " << labels[i]->name() << ": "
|
||||||
|
<< evaluation.precision(labels[i]) << " ; "
|
||||||
|
<< evaluation.recall(labels[i]) << " ; "
|
||||||
|
<< evaluation.f1_score(labels[i]) << " ; "
|
||||||
|
<< evaluation.intersection_over_union(labels[i]) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Accuracy = " << evaluation.accuracy() << std::endl
|
||||||
|
<< "Mean F1 score = " << evaluation.mean_f1_score() << std::endl
|
||||||
|
<< "Mean IoU = " << evaluation.mean_intersection_over_union() << std::endl;
|
||||||
|
|
||||||
|
// Color point set according to class
|
||||||
|
UCmap red = pts.add_property_map<unsigned char>("red", 0).first;
|
||||||
|
UCmap green = pts.add_property_map<unsigned char>("green", 0).first;
|
||||||
|
UCmap blue = pts.add_property_map<unsigned char>("blue", 0).first;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < label_indices.size(); ++ i)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write result
|
||||||
|
std::ofstream f ("classification.ply");
|
||||||
|
f.precision(18);
|
||||||
|
f << pts;
|
||||||
|
|
||||||
|
std::cerr << "All done" << std::endl;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -25,10 +25,14 @@
|
||||||
|
|
||||||
#include <CGAL/Classification/classify.h>
|
#include <CGAL/Classification/classify.h>
|
||||||
#include <CGAL/Classification/Sum_of_weighted_features_classifier.h>
|
#include <CGAL/Classification/Sum_of_weighted_features_classifier.h>
|
||||||
#include <CGAL/Classification/ETHZ_random_forest_classifier.h>
|
#include <CGAL/Classification/ETHZ/Random_forest_classifier.h>
|
||||||
|
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
#include <CGAL/Classification/OpenCV_random_forest_classifier.h>
|
#include <CGAL/Classification/OpenCV/Random_forest_classifier.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
#include <CGAL/Classification/TensorFlow/Neural_network_classifier.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <CGAL/Classification/Cluster.h>
|
#include <CGAL/Classification/Cluster.h>
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include <CGAL/Classification/Feature_set.h>
|
#include <CGAL/Classification/Feature_set.h>
|
||||||
#include <CGAL/Classification/Label_set.h>
|
#include <CGAL/Classification/Label_set.h>
|
||||||
|
#include <CGAL/Classification/internal/verbosity.h>
|
||||||
|
|
||||||
#ifdef CGAL_CLASSIFICATION_VERBOSE
|
#ifdef CGAL_CLASSIFICATION_VERBOSE
|
||||||
#define VERBOSE_TREE_PROGRESS 1
|
#define VERBOSE_TREE_PROGRESS 1
|
||||||
|
|
@ -41,8 +42,10 @@
|
||||||
# pragma warning(disable:4996)
|
# pragma warning(disable:4996)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <CGAL/Classification/internal/auxiliary/random-forest/node-gini.hpp>
|
#include <CGAL/Classification/ETHZ/internal/random-forest/node-gini.hpp>
|
||||||
#include <CGAL/Classification/internal/auxiliary/random-forest/forest.hpp>
|
#include <CGAL/Classification/ETHZ/internal/random-forest/forest.hpp>
|
||||||
|
|
||||||
|
#include <CGAL/tags.h>
|
||||||
|
|
||||||
#include <boost/archive/text_iarchive.hpp>
|
#include <boost/archive/text_iarchive.hpp>
|
||||||
#include <boost/archive/text_oarchive.hpp>
|
#include <boost/archive/text_oarchive.hpp>
|
||||||
|
|
@ -58,16 +61,18 @@ namespace CGAL {
|
||||||
|
|
||||||
namespace Classification {
|
namespace Classification {
|
||||||
|
|
||||||
/*!
|
namespace ETHZ {
|
||||||
\ingroup PkgClassificationClassifiers
|
|
||||||
|
|
||||||
\brief %Classifier based on the ETH Zurich version of random forest algorithm \cgalCite{cgal:w-erftl-14}.
|
/*!
|
||||||
|
\ingroup PkgClassificationClassifiersETHZ
|
||||||
|
|
||||||
|
\brief %Classifier based on the ETH Zurich version of the random forest algorithm \cgalCite{cgal:w-erftl-14}.
|
||||||
|
|
||||||
\note This classifier is distributed under the MIT license.
|
\note This classifier is distributed under the MIT license.
|
||||||
|
|
||||||
\cgalModels `CGAL::Classification::Classifier`
|
\cgalModels `CGAL::Classification::Classifier`
|
||||||
*/
|
*/
|
||||||
class ETHZ_random_forest_classifier
|
class Random_forest_classifier
|
||||||
{
|
{
|
||||||
typedef CGAL::internal::liblearning::RandomForest::RandomForest
|
typedef CGAL::internal::liblearning::RandomForest::RandomForest
|
||||||
< CGAL::internal::liblearning::RandomForest::NodeGini
|
< CGAL::internal::liblearning::RandomForest::NodeGini
|
||||||
|
|
@ -83,16 +88,36 @@ public:
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Instantiate the classifier using the sets of `labels` and `features`.
|
\brief Instantiates the classifier using the sets of `labels` and `features`.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
ETHZ_random_forest_classifier (const Label_set& labels,
|
Random_forest_classifier (const Label_set& labels,
|
||||||
const Feature_set& features)
|
const Feature_set& features)
|
||||||
: m_labels (labels), m_features (features), m_rfc (NULL)
|
: m_labels (labels), m_features (features), m_rfc (NULL)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Copies the `other` classifier's configuration using another
|
||||||
|
set of `features`.
|
||||||
|
|
||||||
|
This constructor can be used to apply a trained random forest to
|
||||||
|
another data set.
|
||||||
|
|
||||||
|
\warning The feature set should be composed of the same features
|
||||||
|
than the ones used by `other`, and in the same order.
|
||||||
|
|
||||||
|
*/
|
||||||
|
Random_forest_classifier (const Random_forest_classifier& other,
|
||||||
|
const Feature_set& features)
|
||||||
|
: m_labels (other.m_labels), m_features (features), m_rfc (NULL)
|
||||||
|
{
|
||||||
|
std::stringstream stream;
|
||||||
|
other.save_configuration(stream);
|
||||||
|
this->load_configuration(stream);
|
||||||
|
}
|
||||||
|
|
||||||
/// \cond SKIP_IN_MANUAL
|
/// \cond SKIP_IN_MANUAL
|
||||||
~ETHZ_random_forest_classifier ()
|
~Random_forest_classifier ()
|
||||||
{
|
{
|
||||||
if (m_rfc != NULL)
|
if (m_rfc != NULL)
|
||||||
delete m_rfc;
|
delete m_rfc;
|
||||||
|
|
@ -102,8 +127,23 @@ public:
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// \name Training
|
/// \name Training
|
||||||
|
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
template <typename LabelIndexRange>
|
||||||
|
void train (const LabelIndexRange& ground_truth,
|
||||||
|
bool reset_trees = true,
|
||||||
|
std::size_t num_trees = 25,
|
||||||
|
std::size_t max_depth = 20)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
train<CGAL::Parallel_tag>(ground_truth, reset_trees, num_trees, max_depth);
|
||||||
|
#else
|
||||||
|
train<CGAL::Sequential_tag>(ground_truth, reset_trees, num_trees, max_depth);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/// \endcond
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Runs the training algorithm.
|
\brief Runs the training algorithm.
|
||||||
|
|
||||||
|
|
@ -114,6 +154,11 @@ public:
|
||||||
\pre At least one ground truth item should be assigned to each
|
\pre At least one ground truth item should be assigned to each
|
||||||
label.
|
label.
|
||||||
|
|
||||||
|
\tparam ConcurrencyTag enables sequential versus parallel
|
||||||
|
algorithm. Possible values are `Parallel_tag` (default value is
|
||||||
|
%CGAL is linked with TBB) or `Sequential_tag` (default value
|
||||||
|
otherwise).
|
||||||
|
|
||||||
\param ground_truth vector of label indices. It should contain for
|
\param ground_truth vector of label indices. It should contain for
|
||||||
each input item, in the same order as the input set, the index of
|
each input item, in the same order as the input set, the index of
|
||||||
the corresponding label in the `Label_set` provided in the
|
the corresponding label in the `Label_set` provided in the
|
||||||
|
|
@ -135,7 +180,7 @@ public:
|
||||||
will underfit the test data and conversely an overly high value
|
will underfit the test data and conversely an overly high value
|
||||||
will likely overfit.
|
will likely overfit.
|
||||||
*/
|
*/
|
||||||
template <typename LabelIndexRange>
|
template <typename ConcurrencyTag, typename LabelIndexRange>
|
||||||
void train (const LabelIndexRange& ground_truth,
|
void train (const LabelIndexRange& ground_truth,
|
||||||
bool reset_trees = true,
|
bool reset_trees = true,
|
||||||
std::size_t num_trees = 25,
|
std::size_t num_trees = 25,
|
||||||
|
|
@ -159,7 +204,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "Using " << gt.size() << " inliers" << std::endl;
|
CGAL_CLASSIFICATION_CERR << "Using " << gt.size() << " inliers" << std::endl;
|
||||||
|
|
||||||
CGAL::internal::liblearning::DataView2D<int> label_vector (&(gt[0]), gt.size(), 1);
|
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());
|
CGAL::internal::liblearning::DataView2D<float> feature_vector(&(ft[0]), gt.size(), ft.size() / gt.size());
|
||||||
|
|
@ -175,7 +220,8 @@ public:
|
||||||
|
|
||||||
CGAL::internal::liblearning::RandomForest::AxisAlignedRandomSplitGenerator generator;
|
CGAL::internal::liblearning::RandomForest::AxisAlignedRandomSplitGenerator generator;
|
||||||
|
|
||||||
m_rfc->train(feature_vector, label_vector, CGAL::internal::liblearning::DataView2D<int>(), generator, 0, false, reset_trees);
|
m_rfc->train<ConcurrencyTag>
|
||||||
|
(feature_vector, label_vector, CGAL::internal::liblearning::DataView2D<int>(), generator, 0, reset_trees, m_labels.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \cond SKIP_IN_MANUAL
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
|
@ -195,10 +241,46 @@ public:
|
||||||
for (std::size_t i = 0; i < out.size(); ++ i)
|
for (std::size_t i = 0; i < out.size(); ++ i)
|
||||||
out[i] = (std::min) (1.f, (std::max) (0.f, prob[i]));
|
out[i] = (std::min) (1.f, (std::max) (0.f, prob[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \endcond
|
/// \endcond
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
/// \name Miscellaneous
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\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
|
||||||
|
data set using at each node one of the input features. This method
|
||||||
|
counts, for each feature, how many times it was selected by the
|
||||||
|
training algorithm as a split criterion.
|
||||||
|
|
||||||
|
This method allows to evaluate how useful a feature was with
|
||||||
|
respect to a training set: if a feature is used a lot, that means
|
||||||
|
that it has a strong discriminative power with respect to how the
|
||||||
|
labels are represented by the feature set; on the contrary, if a
|
||||||
|
feature is not used very often, its discriminative power is
|
||||||
|
probably low; if a feature is _never_ used, it likely has no
|
||||||
|
interest at all and is completely uncorrelated to the label
|
||||||
|
segmentation of the training set.
|
||||||
|
|
||||||
|
\param count vector where the result is stored. After running the
|
||||||
|
method, it contains, for each feature, the number of nodes in the
|
||||||
|
forest that use it as a split criterion, in the same order as the
|
||||||
|
feature set order.
|
||||||
|
*/
|
||||||
|
void get_feature_usage (std::vector<std::size_t>& count) const
|
||||||
|
{
|
||||||
|
count.clear();
|
||||||
|
count.resize(m_features.size(), 0);
|
||||||
|
return m_rfc->get_feature_usage(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
/// \name Input/Output
|
/// \name Input/Output
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
|
@ -211,7 +293,7 @@ public:
|
||||||
The output file is written in an GZIP container that is readable
|
The output file is written in an GZIP container that is readable
|
||||||
by the `load_configuration()` method.
|
by the `load_configuration()` method.
|
||||||
*/
|
*/
|
||||||
void save_configuration (std::ostream& output)
|
void save_configuration (std::ostream& output) const
|
||||||
{
|
{
|
||||||
boost::iostreams::filtering_ostream outs;
|
boost::iostreams::filtering_ostream outs;
|
||||||
outs.push(boost::iostreams::gzip_compressor());
|
outs.push(boost::iostreams::gzip_compressor());
|
||||||
|
|
@ -247,6 +329,13 @@ public:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
// Backward compatibility
|
||||||
|
typedef ETHZ::Random_forest_classifier ETHZ_random_forest_classifier;
|
||||||
|
/// \endcond
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CGAL_CLASSIFICATION_ETHZ_RANDOM_FOREST_CLASSIFIER_H
|
#endif // CGAL_CLASSIFICATION_ETHZ_RANDOM_FOREST_CLASSIFIER_H
|
||||||
|
|
@ -27,6 +27,16 @@
|
||||||
// Modifications from original library:
|
// Modifications from original library:
|
||||||
// * changed inclusion protection tag
|
// * changed inclusion protection tag
|
||||||
// * moved to namespace CGAL::internal::
|
// * moved to namespace CGAL::internal::
|
||||||
|
// * init_feature_class_data() does not resize anymore (it's done
|
||||||
|
// later directly in the splitter). WARNING: all splitters other
|
||||||
|
// than the default won't be working correctly (but experimentally
|
||||||
|
// they are less good and we don't use them - we keep them just in
|
||||||
|
// case)
|
||||||
|
// * sample reduction is now 36.8% (to account for the correction of
|
||||||
|
// the randomization of the input which used to implicitly ignore
|
||||||
|
// this proportion of items)
|
||||||
|
// * map_points() in axis aligned splitter now only uses a subset of
|
||||||
|
// the points for evaluation (for timing optimization=
|
||||||
|
|
||||||
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_COMMON_LIBRARIES_H
|
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_COMMON_LIBRARIES_H
|
||||||
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_COMMON_LIBRARIES_H
|
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_COMMON_LIBRARIES_H
|
||||||
|
|
@ -62,9 +72,9 @@ namespace liblearning {
|
||||||
namespace RandomForest {
|
namespace RandomForest {
|
||||||
|
|
||||||
typedef std::vector< std::pair<float, int> > FeatureClassDataFloat;
|
typedef std::vector< std::pair<float, int> > FeatureClassDataFloat;
|
||||||
inline void init_feature_class_data(FeatureClassDataFloat& data, int /*n_classes*/, int n_samples)
|
inline void init_feature_class_data(FeatureClassDataFloat& /*data*/, int /*n_classes*/, int /* n_samples */)
|
||||||
{
|
{
|
||||||
data.resize(n_samples);
|
// data.resize(n_samples);
|
||||||
}
|
}
|
||||||
typedef boost::unordered_set<int> FeatureSet;
|
typedef boost::unordered_set<int> FeatureSet;
|
||||||
|
|
||||||
|
|
@ -97,7 +107,7 @@ struct ForestParams {
|
||||||
max_depth(42),
|
max_depth(42),
|
||||||
n_trees(100),
|
n_trees(100),
|
||||||
min_samples_per_node(5),
|
min_samples_per_node(5),
|
||||||
sample_reduction(0)
|
sample_reduction(0.368f)
|
||||||
{}
|
{}
|
||||||
template <typename Archive>
|
template <typename Archive>
|
||||||
void serialize(Archive& ar, unsigned /*version*/)
|
void serialize(Archive& ar, unsigned /*version*/)
|
||||||
|
|
@ -222,15 +232,21 @@ struct AxisAlignedSplitter {
|
||||||
int n_samples,
|
int n_samples,
|
||||||
FeatureClassData& data_points) const
|
FeatureClassData& data_points) const
|
||||||
{
|
{
|
||||||
for (int i_sample = 0; i_sample < n_samples; ++i_sample) {
|
std::size_t size = (std::min)(std::size_t(5000), std::size_t(n_samples));
|
||||||
// determine index of this sample ...
|
data_points.clear();
|
||||||
int sample_idx = sample_idxes[i_sample];
|
data_points.reserve(size);
|
||||||
// determine class ...
|
|
||||||
int sample_class = labels(sample_idx, 0);
|
std::size_t step = n_samples / size;
|
||||||
// determine value of the selected feature for this sample
|
|
||||||
FeatureType sample_fval = samples(sample_idx, feature);
|
for (int i_sample = 0; i_sample < n_samples; i_sample += step) {
|
||||||
data_points[i_sample] = std::make_pair(sample_fval, sample_class);
|
// determine index of this sample ...
|
||||||
}
|
int sample_idx = sample_idxes[i_sample];
|
||||||
|
// determine class ...
|
||||||
|
int sample_class = labels(sample_idx, 0);
|
||||||
|
// determine value of the selected feature for this sample
|
||||||
|
FeatureType sample_fval = samples(sample_idx, feature);
|
||||||
|
data_points.push_back(std::make_pair(sample_fval, sample_class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
template <typename Archive>
|
template <typename Archive>
|
||||||
void serialize(Archive& ar, unsigned /*version*/)
|
void serialize(Archive& ar, unsigned /*version*/)
|
||||||
|
|
@ -29,6 +29,12 @@
|
||||||
// * moved to namespace CGAL::internal::
|
// * moved to namespace CGAL::internal::
|
||||||
// * add parameter "reset_trees" to train() to be able to construct
|
// * add parameter "reset_trees" to train() to be able to construct
|
||||||
// forest with several iterations
|
// forest with several iterations
|
||||||
|
// * training algorithm has been parallelized with Intel TBB
|
||||||
|
// * remove the unused feature "register_obb"
|
||||||
|
// * add option to not count labels (if it's know before)
|
||||||
|
// * fix the randomization of input (which was implicitly losing
|
||||||
|
// samples)
|
||||||
|
// * add method to get feature usage
|
||||||
|
|
||||||
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_FOREST_H
|
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_FOREST_H
|
||||||
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_FOREST_H
|
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_FOREST_H
|
||||||
|
|
@ -39,11 +45,80 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <CGAL/tags.h>
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
|
#include <tbb/parallel_for.h>
|
||||||
|
#include <tbb/blocked_range.h>
|
||||||
|
#include <tbb/scalable_allocator.h>
|
||||||
|
#include <tbb/mutex.h>
|
||||||
|
#endif // CGAL_LINKED_WITH_TBB
|
||||||
|
|
||||||
|
|
||||||
namespace CGAL { namespace internal {
|
namespace CGAL { namespace internal {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace liblearning {
|
namespace liblearning {
|
||||||
namespace RandomForest {
|
namespace RandomForest {
|
||||||
|
|
||||||
|
template <typename NodeT, typename SplitGenerator>
|
||||||
|
class Tree_training_functor
|
||||||
|
{
|
||||||
|
typedef typename NodeT::ParamType ParamType;
|
||||||
|
typedef typename NodeT::FeatureType FeatureType;
|
||||||
|
typedef Tree<NodeT> TreeType;
|
||||||
|
|
||||||
|
std::size_t seed_start;
|
||||||
|
const std::vector<int>& sample_idxes;
|
||||||
|
boost::ptr_vector<Tree<NodeT> >& trees;
|
||||||
|
DataView2D<FeatureType> samples;
|
||||||
|
DataView2D<int> labels;
|
||||||
|
std::size_t n_in_bag_samples;
|
||||||
|
const SplitGenerator& split_generator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Tree_training_functor(std::size_t seed_start,
|
||||||
|
const std::vector<int>& sample_idxes,
|
||||||
|
boost::ptr_vector<Tree<NodeT> >& trees,
|
||||||
|
DataView2D<FeatureType> samples,
|
||||||
|
DataView2D<int> labels,
|
||||||
|
std::size_t n_in_bag_samples,
|
||||||
|
const SplitGenerator& split_generator)
|
||||||
|
: seed_start (seed_start)
|
||||||
|
, sample_idxes (sample_idxes)
|
||||||
|
, trees (trees)
|
||||||
|
, samples (samples)
|
||||||
|
, labels (labels)
|
||||||
|
, n_in_bag_samples(n_in_bag_samples)
|
||||||
|
, split_generator(split_generator)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
#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 i_tree) const
|
||||||
|
{
|
||||||
|
// initialize random generator with sequential seeds (one for each
|
||||||
|
// tree)
|
||||||
|
RandomGen gen(seed_start + i_tree);
|
||||||
|
std::vector<int> in_bag_samples = sample_idxes;
|
||||||
|
|
||||||
|
// Bagging: draw random sample indexes used for this tree
|
||||||
|
std::random_shuffle (in_bag_samples.begin(),in_bag_samples.end());
|
||||||
|
|
||||||
|
// Train the tree
|
||||||
|
trees[i_tree].train(samples, labels, &in_bag_samples[0], n_in_bag_samples, split_generator, gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
template <typename NodeT>
|
template <typename NodeT>
|
||||||
class RandomForest {
|
class RandomForest {
|
||||||
public:
|
public:
|
||||||
|
|
@ -52,28 +127,29 @@ public:
|
||||||
typedef Tree<NodeT> TreeType;
|
typedef Tree<NodeT> TreeType;
|
||||||
ParamType params;
|
ParamType params;
|
||||||
|
|
||||||
std::vector<uint8_t> was_oob_data;
|
|
||||||
DataView2D<uint8_t> was_oob;
|
|
||||||
|
|
||||||
boost::ptr_vector< Tree<NodeT> > trees;
|
boost::ptr_vector< Tree<NodeT> > trees;
|
||||||
|
|
||||||
RandomForest() {}
|
RandomForest() {}
|
||||||
RandomForest(ParamType const& params) : params(params) {}
|
RandomForest(ParamType const& params) : params(params) {}
|
||||||
|
|
||||||
template<typename SplitGenerator>
|
template<typename ConcurrencyTag, typename SplitGenerator>
|
||||||
void train(DataView2D<FeatureType> samples,
|
void train(DataView2D<FeatureType> samples,
|
||||||
DataView2D<int> labels,
|
DataView2D<int> labels,
|
||||||
DataView2D<int> train_sample_idxes,
|
DataView2D<int> train_sample_idxes,
|
||||||
SplitGenerator const& split_generator,
|
SplitGenerator const& split_generator,
|
||||||
size_t seed_start = 1,
|
size_t seed_start = 1,
|
||||||
bool register_oob = true,
|
bool reset_trees = true,
|
||||||
bool reset_trees = true
|
std::size_t n_classes = std::size_t(-1)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (reset_trees)
|
if (reset_trees)
|
||||||
trees.clear();
|
trees.clear();
|
||||||
|
|
||||||
params.n_classes = *std::max_element(&labels(0,0), &labels(0,0)+labels.num_elements()) + 1;
|
if (n_classes == std::size_t(-1))
|
||||||
|
params.n_classes = *std::max_element(&labels(0,0), &labels(0,0)+labels.num_elements()) + 1;
|
||||||
|
else
|
||||||
|
params.n_classes = n_classes;
|
||||||
|
|
||||||
params.n_features = samples.cols;
|
params.n_features = samples.cols;
|
||||||
params.n_samples = samples.rows;
|
params.n_samples = samples.rows;
|
||||||
|
|
||||||
|
|
@ -93,42 +169,31 @@ public:
|
||||||
size_t n_idxes = sample_idxes.size();
|
size_t n_idxes = sample_idxes.size();
|
||||||
params.n_in_bag_samples = n_idxes * (1 - params.sample_reduction);
|
params.n_in_bag_samples = n_idxes * (1 - params.sample_reduction);
|
||||||
|
|
||||||
// Random distribution over indexes
|
|
||||||
UniformIntDist dist(0, n_idxes - 1);
|
|
||||||
|
|
||||||
// Store for each sample and each tree if sample was used for tree
|
|
||||||
if (register_oob) {
|
|
||||||
was_oob_data.assign(n_idxes*params.n_trees, 1);
|
|
||||||
was_oob = DataView2D<uint8_t>(&was_oob_data[0], n_idxes, params.n_trees);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t nb_trees = trees.size();
|
std::size_t nb_trees = trees.size();
|
||||||
for (size_t i_tree = nb_trees; i_tree < nb_trees + params.n_trees; ++i_tree) {
|
for (std::size_t i_tree = nb_trees; i_tree < nb_trees + params.n_trees; ++ i_tree)
|
||||||
|
trees.push_back (new TreeType(¶ms));
|
||||||
|
|
||||||
|
Tree_training_functor<NodeT, SplitGenerator>
|
||||||
|
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),
|
||||||
|
"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>(nb_trees, nb_trees + params.n_trees), f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
for (size_t i_tree = nb_trees; i_tree < nb_trees + params.n_trees; ++i_tree)
|
||||||
|
{
|
||||||
#if VERBOSE_TREE_PROGRESS
|
#if VERBOSE_TREE_PROGRESS
|
||||||
std::printf("Training tree %zu/%zu, max depth %zu\n", i_tree+1, nb_trees + params.n_trees, params.max_depth);
|
std::printf("Training tree %zu/%zu, max depth %zu\n", i_tree+1, nb_trees + params.n_trees, params.max_depth);
|
||||||
#endif
|
#endif
|
||||||
// new tree
|
f.apply(i_tree);
|
||||||
trees.push_back(new TreeType(¶ms));
|
}
|
||||||
// initialize random generator with sequential seeds (one for each
|
|
||||||
// tree)
|
|
||||||
RandomGen gen(seed_start + i_tree);
|
|
||||||
// Bagging: draw random sample indexes used for this tree
|
|
||||||
std::vector<int> in_bag_samples(params.n_in_bag_samples);
|
|
||||||
for (size_t i_sample = 0; i_sample < in_bag_samples.size(); ++i_sample) {
|
|
||||||
int random_idx = dist(gen);
|
|
||||||
in_bag_samples[i_sample] = sample_idxes[random_idx];
|
|
||||||
if (register_oob && was_oob(random_idx, i_tree)) {
|
|
||||||
was_oob(random_idx, i_tree) = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef TREE_GRAPHVIZ_STREAM
|
|
||||||
TREE_GRAPHVIZ_STREAM << "digraph Tree {" << std::endl;
|
|
||||||
#endif
|
|
||||||
// Train the tree
|
|
||||||
trees.back().train(samples, labels, &in_bag_samples[0], in_bag_samples.size(), split_generator, gen);
|
|
||||||
#ifdef TREE_GRAPHVIZ_STREAM
|
|
||||||
TREE_GRAPHVIZ_STREAM << "}" << std::endl << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int evaluate(FeatureType const* sample, float* results) {
|
int evaluate(FeatureType const* sample, float* results) {
|
||||||
|
|
@ -177,6 +242,12 @@ public:
|
||||||
ar & BOOST_SERIALIZATION_NVP(params);
|
ar & BOOST_SERIALIZATION_NVP(params);
|
||||||
ar & BOOST_SERIALIZATION_NVP(trees);
|
ar & BOOST_SERIALIZATION_NVP(trees);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void get_feature_usage (std::vector<std::size_t>& count) const
|
||||||
|
{
|
||||||
|
for (std::size_t i_tree = 0; i_tree < trees.size(); ++i_tree)
|
||||||
|
trees[i_tree].get_feature_usage(count);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +28,11 @@
|
||||||
// * changed inclusion protection tag
|
// * changed inclusion protection tag
|
||||||
// * moved to namespace CGAL::internal::
|
// * moved to namespace CGAL::internal::
|
||||||
|
|
||||||
|
// * improve sorting algorithm by only comparing the first of pair
|
||||||
|
// (second is useless)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_NODE_GINI_H
|
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_NODE_GINI_H
|
||||||
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_NODE_GINI_H
|
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_NODE_GINI_H
|
||||||
#include "node.hpp"
|
#include "node.hpp"
|
||||||
|
|
@ -79,7 +84,13 @@ public:
|
||||||
n_r += 1;
|
n_r += 1;
|
||||||
}
|
}
|
||||||
// sort data so thresholding is easy based on position in array
|
// sort data so thresholding is easy based on position in array
|
||||||
std::sort(data_points.begin(), data_points.end());
|
std::sort(data_points.begin(), data_points.end(),
|
||||||
|
[&](const std::pair<float, int>& a,
|
||||||
|
const std::pair<float, int>& b) -> bool
|
||||||
|
{
|
||||||
|
return a.first < b.first;
|
||||||
|
});
|
||||||
|
|
||||||
// loop over data, update class distributions left&right
|
// loop over data, update class distributions left&right
|
||||||
for (size_t i_point = 1; i_point < data_points.size(); ++i_point) {
|
for (size_t i_point = 1; i_point < data_points.size(); ++i_point) {
|
||||||
int cls = data_points[i_point-1].second;
|
int cls = data_points[i_point-1].second;
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
// * fix computation of node_dist[label] so that results are always <= 1.0
|
// * fix computation of node_dist[label] so that results are always <= 1.0
|
||||||
// * change serialization functions to avoid a bug with boost and some
|
// * change serialization functions to avoid a bug with boost and some
|
||||||
// compilers (that leads to dereferencing a null pointer)
|
// compilers (that leads to dereferencing a null pointer)
|
||||||
|
// * add a method to get feature usage
|
||||||
|
|
||||||
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFORESTS_NODE_H
|
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFORESTS_NODE_H
|
||||||
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFORESTS_NODE_H
|
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFORESTS_NODE_H
|
||||||
|
|
@ -257,6 +258,16 @@ public:
|
||||||
ar & BOOST_SERIALIZATION_NVP(right);
|
ar & BOOST_SERIALIZATION_NVP(right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void get_feature_usage (std::vector<std::size_t>& count) const
|
||||||
|
{
|
||||||
|
if (!is_leaf)
|
||||||
|
{
|
||||||
|
count[std::size_t(splitter.feature)] ++;
|
||||||
|
left->get_feature_usage(count);
|
||||||
|
right->get_feature_usage(count);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
// Modifications from original library:
|
// Modifications from original library:
|
||||||
// * changed inclusion protection tag
|
// * changed inclusion protection tag
|
||||||
// * moved to namespace CGAL::internal::
|
// * moved to namespace CGAL::internal::
|
||||||
|
// * add a method to get feature usage
|
||||||
|
|
||||||
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_TREE_H
|
#ifndef CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_TREE_H
|
||||||
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_TREE_H
|
#define CGAL_INTERNAL_LIBLEARNING_RANDOMFOREST_TREE_H
|
||||||
|
|
@ -135,6 +136,10 @@ public:
|
||||||
ar & BOOST_SERIALIZATION_NVP(params);
|
ar & BOOST_SERIALIZATION_NVP(params);
|
||||||
ar & BOOST_SERIALIZATION_NVP(root_node);
|
ar & BOOST_SERIALIZATION_NVP(root_node);
|
||||||
}
|
}
|
||||||
|
void get_feature_usage (std::vector<std::size_t>& count) const
|
||||||
|
{
|
||||||
|
root_node->get_feature_usage(count);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +85,8 @@ public:
|
||||||
: grid (grid)
|
: grid (grid)
|
||||||
{
|
{
|
||||||
this->set_name ("echo_scatter");
|
this->set_name ("echo_scatter");
|
||||||
|
if (radius_neighbors < 0.)
|
||||||
|
radius_neighbors = 3.f * grid.resolution();
|
||||||
|
|
||||||
if (grid.width() * grid.height() > input.size())
|
if (grid.width() * grid.height() > input.size())
|
||||||
echo_scatter.resize(input.size(), compressed_float(0));
|
echo_scatter.resize(input.size(), compressed_float(0));
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ public:
|
||||||
{
|
{
|
||||||
this->set_name ("elevation");
|
this->set_name ("elevation");
|
||||||
if (radius_dtm < 0.)
|
if (radius_dtm < 0.)
|
||||||
radius_dtm = 100.f * grid.resolution();
|
radius_dtm = 10.f * grid.resolution();
|
||||||
|
|
||||||
//DEM
|
//DEM
|
||||||
Image_float dem(grid.width(),grid.height());
|
Image_float dem(grid.width(),grid.height());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||||
|
// Copyright (c) 2017 GeometryFactory Sarl (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
// You can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
// General Public License as published by the Free Software Foundation,
|
||||||
|
// either version 3 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Licensees holding a valid commercial license may use this file in
|
||||||
|
// accordance with the commercial license agreement provided with the software.
|
||||||
|
//
|
||||||
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
//
|
||||||
|
// Author(s) : Florent Lafarge, Simon Giraudot
|
||||||
|
|
||||||
|
#ifndef CGAL_CLASSIFICATION_FEATURE_HEIGHT_ABOVE_H
|
||||||
|
#define CGAL_CLASSIFICATION_FEATURE_HEIGHT_ABOVE_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Classification.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <CGAL/Classification/Feature_base.h>
|
||||||
|
#include <CGAL/Classification/compressed_float.h>
|
||||||
|
#include <CGAL/Classification/Image.h>
|
||||||
|
#include <CGAL/Classification/Planimetric_grid.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
|
||||||
|
namespace Classification {
|
||||||
|
|
||||||
|
namespace Feature {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\ingroup PkgClassificationFeatures
|
||||||
|
|
||||||
|
%Feature based on local height distribution This feature computes
|
||||||
|
the distance between the maximum height on the local cell of the
|
||||||
|
planimetric grid and a point's height.
|
||||||
|
|
||||||
|
Its default name is "height_above".
|
||||||
|
|
||||||
|
\tparam GeomTraits model of \cgal Kernel.
|
||||||
|
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||||
|
is `RandomAccessIterator` and its value type is the key type of
|
||||||
|
`PointMap`.
|
||||||
|
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||||
|
type is the value type of the iterator of `PointRange` and value type
|
||||||
|
is `GeomTraits::Point_3`.
|
||||||
|
|
||||||
|
*/
|
||||||
|
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||||
|
class 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;
|
||||||
|
|
||||||
|
const PointRange& input;
|
||||||
|
PointMap point_map;
|
||||||
|
const Grid& grid;
|
||||||
|
Image_float dtm;
|
||||||
|
std::vector<float> values;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
\brief Constructs the feature.
|
||||||
|
|
||||||
|
\param input point range.
|
||||||
|
\param point_map property map to access the input points.
|
||||||
|
\param grid precomputed `Planimetric_grid`.
|
||||||
|
*/
|
||||||
|
Height_above (const PointRange& input,
|
||||||
|
PointMap point_map,
|
||||||
|
const Grid& grid)
|
||||||
|
: input(input), point_map(point_map), grid(grid)
|
||||||
|
{
|
||||||
|
this->set_name ("height_above");
|
||||||
|
|
||||||
|
dtm = Image_float(grid.width(),grid.height());
|
||||||
|
|
||||||
|
for (std::size_t j = 0; j < grid.height(); ++ j)
|
||||||
|
for (std::size_t i = 0; i < grid.width(); ++ i)
|
||||||
|
if (grid.has_points(i,j))
|
||||||
|
{
|
||||||
|
float z_max = -std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
typename Grid::iterator end = grid.indices_end(i,j);
|
||||||
|
for (typename Grid::iterator it = grid.indices_begin(i,j); it != end; ++ it)
|
||||||
|
{
|
||||||
|
float z = float(get(point_map, *(input.begin()+(*it))).z());
|
||||||
|
z_max = (std::max(z_max, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
dtm(i,j) = z_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid.width() * grid.height() > input.size())
|
||||||
|
{
|
||||||
|
values.resize (input.size(), 0.f);
|
||||||
|
for (std::size_t i = 0; i < input.size(); ++ i)
|
||||||
|
{
|
||||||
|
std::size_t I = grid.x(i);
|
||||||
|
std::size_t J = grid.y(i);
|
||||||
|
values[i] = float(dtm(I,J) - get (point_map, *(input.begin() + i)).z());
|
||||||
|
}
|
||||||
|
dtm.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
virtual float value (std::size_t pt_index)
|
||||||
|
{
|
||||||
|
if (values.empty())
|
||||||
|
{
|
||||||
|
std::size_t I = grid.x(pt_index);
|
||||||
|
std::size_t J = grid.y(pt_index);
|
||||||
|
return dtm(I,J) - float(get (point_map, *(input.begin() + pt_index)).z());
|
||||||
|
}
|
||||||
|
|
||||||
|
return values[pt_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \endcond
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Feature
|
||||||
|
|
||||||
|
} // namespace Classification
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_CLASSIFICATION_FEATURE_HEIGHT_ABOVE_H
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||||
|
// Copyright (c) 2017 GeometryFactory Sarl (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
// You can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
// General Public License as published by the Free Software Foundation,
|
||||||
|
// either version 3 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Licensees holding a valid commercial license may use this file in
|
||||||
|
// accordance with the commercial license agreement provided with the software.
|
||||||
|
//
|
||||||
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
//
|
||||||
|
// Author(s) : Florent Lafarge, Simon Giraudot
|
||||||
|
|
||||||
|
#ifndef CGAL_CLASSIFICATION_FEATURE_HEIGHT_BELOW_H
|
||||||
|
#define CGAL_CLASSIFICATION_FEATURE_HEIGHT_BELOW_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Classification.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <CGAL/Classification/Feature_base.h>
|
||||||
|
#include <CGAL/Classification/compressed_float.h>
|
||||||
|
#include <CGAL/Classification/Image.h>
|
||||||
|
#include <CGAL/Classification/Planimetric_grid.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
|
||||||
|
namespace Classification {
|
||||||
|
|
||||||
|
namespace Feature {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\ingroup PkgClassificationFeatures
|
||||||
|
|
||||||
|
%Feature based on local height distribution This feature computes
|
||||||
|
the distance between a point's height and the minimum height on
|
||||||
|
the local cell of the planimetric grid.
|
||||||
|
|
||||||
|
Its default name is "height_below".
|
||||||
|
|
||||||
|
\tparam GeomTraits model of \cgal Kernel.
|
||||||
|
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||||
|
is `RandomAccessIterator` and its value type is the key type of
|
||||||
|
`PointMap`.
|
||||||
|
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||||
|
type is the value type of the iterator of `PointRange` and value type
|
||||||
|
is `GeomTraits::Point_3`.
|
||||||
|
|
||||||
|
*/
|
||||||
|
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||||
|
class 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;
|
||||||
|
|
||||||
|
const PointRange& input;
|
||||||
|
PointMap point_map;
|
||||||
|
const Grid& grid;
|
||||||
|
Image_float dtm;
|
||||||
|
std::vector<float> values;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
\brief Constructs the feature.
|
||||||
|
|
||||||
|
\param input point range.
|
||||||
|
\param point_map property map to access the input points.
|
||||||
|
\param grid precomputed `Planimetric_grid`.
|
||||||
|
*/
|
||||||
|
Height_below (const PointRange& input,
|
||||||
|
PointMap point_map,
|
||||||
|
const Grid& grid)
|
||||||
|
: input(input), point_map(point_map), grid(grid)
|
||||||
|
{
|
||||||
|
this->set_name ("height_below");
|
||||||
|
|
||||||
|
dtm = Image_float(grid.width(),grid.height());
|
||||||
|
|
||||||
|
for (std::size_t j = 0; j < grid.height(); ++ j)
|
||||||
|
for (std::size_t i = 0; i < grid.width(); ++ i)
|
||||||
|
if (grid.has_points(i,j))
|
||||||
|
{
|
||||||
|
float z_min = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
typename Grid::iterator end = grid.indices_end(i,j);
|
||||||
|
for (typename Grid::iterator it = grid.indices_begin(i,j); it != end; ++ it)
|
||||||
|
{
|
||||||
|
float z = float(get(point_map, *(input.begin()+(*it))).z());
|
||||||
|
z_min = (std::min(z_min, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
dtm(i,j) = z_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid.width() * grid.height() > input.size())
|
||||||
|
{
|
||||||
|
values.resize (input.size(), 0.f);
|
||||||
|
for (std::size_t i = 0; i < input.size(); ++ i)
|
||||||
|
{
|
||||||
|
std::size_t I = grid.x(i);
|
||||||
|
std::size_t J = grid.y(i);
|
||||||
|
values[i] = float(get (point_map, *(input.begin() + i)).z() - dtm(I,J));
|
||||||
|
}
|
||||||
|
dtm.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
virtual float value (std::size_t pt_index)
|
||||||
|
{
|
||||||
|
if (values.empty())
|
||||||
|
{
|
||||||
|
std::size_t I = grid.x(pt_index);
|
||||||
|
std::size_t J = grid.y(pt_index);
|
||||||
|
return float(get (point_map, *(input.begin() + pt_index)).z() - dtm(I,J));
|
||||||
|
}
|
||||||
|
|
||||||
|
return values[pt_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \endcond
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Feature
|
||||||
|
|
||||||
|
} // namespace Classification
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_CLASSIFICATION_FEATURE_HEIGHT_BELOW_H
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
// Copyright (c) 2012 INRIA Sophia-Antipolis (France).
|
||||||
|
// Copyright (c) 2017 GeometryFactory Sarl (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
// You can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
// General Public License as published by the Free Software Foundation,
|
||||||
|
// either version 3 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Licensees holding a valid commercial license may use this file in
|
||||||
|
// accordance with the commercial license agreement provided with the software.
|
||||||
|
//
|
||||||
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
//
|
||||||
|
// Author(s) : Florent Lafarge, Simon Giraudot
|
||||||
|
|
||||||
|
#ifndef CGAL_CLASSIFICATION_FEATURE_VERTICAL_RANGE_H
|
||||||
|
#define CGAL_CLASSIFICATION_FEATURE_VERTICAL_RANGE_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Classification.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <CGAL/Classification/Feature_base.h>
|
||||||
|
#include <CGAL/Classification/compressed_float.h>
|
||||||
|
#include <CGAL/Classification/Image.h>
|
||||||
|
#include <CGAL/Classification/Planimetric_grid.h>
|
||||||
|
|
||||||
|
namespace CGAL {
|
||||||
|
|
||||||
|
namespace Classification {
|
||||||
|
|
||||||
|
namespace Feature {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\ingroup PkgClassificationFeatures
|
||||||
|
|
||||||
|
%Feature based on local height distribution. This feature computes
|
||||||
|
the distance between the maximum and the minimum height on the
|
||||||
|
local cell of the planimetric grid.
|
||||||
|
|
||||||
|
Its default name is "vertical_range".
|
||||||
|
|
||||||
|
\tparam GeomTraits model of \cgal Kernel.
|
||||||
|
\tparam PointRange model of `ConstRange`. Its iterator type
|
||||||
|
is `RandomAccessIterator` and its value type is the key type of
|
||||||
|
`PointMap`.
|
||||||
|
\tparam PointMap model of `ReadablePropertyMap` whose key
|
||||||
|
type is the value type of the iterator of `PointRange` and value type
|
||||||
|
is `GeomTraits::Point_3`.
|
||||||
|
|
||||||
|
*/
|
||||||
|
template <typename GeomTraits, typename PointRange, typename PointMap>
|
||||||
|
class Vertical_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;
|
||||||
|
|
||||||
|
const PointRange& input;
|
||||||
|
PointMap point_map;
|
||||||
|
const Grid& grid;
|
||||||
|
Image_float dtm;
|
||||||
|
std::vector<float> values;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
\brief Constructs the feature.
|
||||||
|
|
||||||
|
\param input point range.
|
||||||
|
\param point_map property map to access the input points.
|
||||||
|
\param grid precomputed `Planimetric_grid`.
|
||||||
|
*/
|
||||||
|
Vertical_range (const PointRange& input,
|
||||||
|
PointMap point_map,
|
||||||
|
const Grid& grid)
|
||||||
|
: input(input), point_map(point_map), grid(grid)
|
||||||
|
{
|
||||||
|
this->set_name ("vertical_range");
|
||||||
|
|
||||||
|
dtm = Image_float(grid.width(),grid.height());
|
||||||
|
|
||||||
|
for (std::size_t j = 0; j < grid.height(); ++ j)
|
||||||
|
for (std::size_t i = 0; i < grid.width(); ++ i)
|
||||||
|
if (grid.has_points(i,j))
|
||||||
|
{
|
||||||
|
float z_max = -std::numeric_limits<float>::max();
|
||||||
|
float z_min = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
typename Grid::iterator end = grid.indices_end(i,j);
|
||||||
|
for (typename Grid::iterator it = grid.indices_begin(i,j); it != end; ++ it)
|
||||||
|
{
|
||||||
|
float z = float(get(point_map, *(input.begin()+(*it))).z());
|
||||||
|
z_max = (std::max(z_max, z));
|
||||||
|
z_min = (std::min(z_min, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
dtm(i,j) = z_max - z_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid.width() * grid.height() > input.size())
|
||||||
|
{
|
||||||
|
values.resize (input.size(), 0.f);
|
||||||
|
for (std::size_t i = 0; i < input.size(); ++ i)
|
||||||
|
{
|
||||||
|
std::size_t I = grid.x(i);
|
||||||
|
std::size_t J = grid.y(i);
|
||||||
|
values[i] = dtm(I,J);
|
||||||
|
}
|
||||||
|
dtm.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
virtual float value (std::size_t pt_index)
|
||||||
|
{
|
||||||
|
if (values.empty())
|
||||||
|
{
|
||||||
|
std::size_t I = grid.x(pt_index);
|
||||||
|
std::size_t J = grid.y(pt_index);
|
||||||
|
return dtm(I,J);
|
||||||
|
}
|
||||||
|
|
||||||
|
return values[pt_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \endcond
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Feature
|
||||||
|
|
||||||
|
} // namespace Classification
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace CGAL
|
||||||
|
|
||||||
|
#endif // CGAL_CLASSIFICATION_FEATURE_VERTICAL_RANGE_H
|
||||||
|
|
@ -41,6 +41,8 @@ class Image
|
||||||
|
|
||||||
std::size_t m_width;
|
std::size_t m_width;
|
||||||
std::size_t m_height;
|
std::size_t m_height;
|
||||||
|
std::size_t m_depth;
|
||||||
|
|
||||||
boost::shared_ptr<Vector> m_raw;
|
boost::shared_ptr<Vector> m_raw;
|
||||||
boost::shared_ptr<Map> m_sparse;
|
boost::shared_ptr<Map> m_sparse;
|
||||||
Type m_default;
|
Type m_default;
|
||||||
|
|
@ -52,18 +54,19 @@ class Image
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Image () : m_width(0), m_height(0), m_raw (NULL)
|
Image () : m_width(0), m_height(0), m_depth(0), m_raw (NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Image (std::size_t width, std::size_t height)
|
Image (std::size_t width, std::size_t height, std::size_t depth = 1)
|
||||||
: m_width (width),
|
: m_width (width)
|
||||||
m_height (height)
|
, m_height (height)
|
||||||
|
, m_depth (depth)
|
||||||
{
|
{
|
||||||
if (m_width * m_height > 0)
|
if (m_width * m_height * m_depth > 0)
|
||||||
{
|
{
|
||||||
if (m_width * m_height < CGAL_CLASSIFICATION_IMAGE_SIZE_LIMIT)
|
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_raw = boost::shared_ptr<Vector> (new Vector(m_width * m_height * m_depth));
|
||||||
else
|
else
|
||||||
m_sparse = boost::shared_ptr<Map> (new Map());
|
m_sparse = boost::shared_ptr<Map> (new Map());
|
||||||
}
|
}
|
||||||
|
|
@ -85,33 +88,41 @@ public:
|
||||||
m_sparse = other.m_sparse;
|
m_sparse = other.m_sparse;
|
||||||
m_width = other.width();
|
m_width = other.width();
|
||||||
m_height = other.height();
|
m_height = other.height();
|
||||||
|
m_depth = other.depth();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t width() const { return m_width; }
|
std::size_t width() const { return m_width; }
|
||||||
std::size_t height() const { return m_height; }
|
std::size_t height() const { return m_height; }
|
||||||
|
std::size_t depth() const { return m_depth; }
|
||||||
|
|
||||||
Type& operator() (const std::size_t& x, const std::size_t& y)
|
inline std::size_t coord (const std::size_t& x, const std::size_t& y, const std::size_t& z) const
|
||||||
|
{
|
||||||
|
return z + (m_depth * y) + (m_depth * m_height * x);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 == boost::shared_ptr<Vector>()) // sparse case
|
||||||
{
|
{
|
||||||
typename Map::iterator inserted = m_sparse->insert (std::make_pair (x * m_height + y, Type())).first;
|
typename Map::iterator inserted = m_sparse->insert
|
||||||
|
(std::make_pair (coord(x,y,z), Type())).first;
|
||||||
return inserted->second;
|
return inserted->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*m_raw)[x * m_height + y];
|
return (*m_raw)[coord(x,y,z)];
|
||||||
}
|
}
|
||||||
const Type& operator() (const std::size_t& x, const std::size_t& y) const
|
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 == boost::shared_ptr<Vector>()) // sparse case
|
||||||
{
|
{
|
||||||
typename Map::iterator found = m_sparse->find (x * m_height + y);
|
typename Map::iterator found = m_sparse->find (coord(x,y,z));
|
||||||
if (found != m_sparse->end())
|
if (found != m_sparse->end())
|
||||||
return found->second;
|
return found->second;
|
||||||
return m_default;
|
return m_default;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*m_raw)[x * m_height + y];
|
return (*m_raw)[coord(x,y,z)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,10 @@ public:
|
||||||
Label (std::string name) : m_name (name) { }
|
Label (std::string name) : m_name (name) { }
|
||||||
|
|
||||||
const std::string& name() const { return m_name; }
|
const std::string& name() const { return m_name; }
|
||||||
|
|
||||||
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
void set_name (const std::string& name) { m_name = name; }
|
||||||
|
/// \endcond
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef DOXYGEN_RUNNING
|
#ifdef DOXYGEN_RUNNING
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@
|
||||||
#include <CGAL/Classification/Feature/Verticality.h>
|
#include <CGAL/Classification/Feature/Verticality.h>
|
||||||
#include <CGAL/Classification/Feature/Eigenvalue.h>
|
#include <CGAL/Classification/Feature/Eigenvalue.h>
|
||||||
#include <CGAL/Classification/Feature/Color_channel.h>
|
#include <CGAL/Classification/Feature/Color_channel.h>
|
||||||
|
#include <CGAL/Classification/Feature/Height_below.h>
|
||||||
|
#include <CGAL/Classification/Feature/Height_above.h>
|
||||||
|
#include <CGAL/Classification/Feature/Vertical_range.h>
|
||||||
#include <CGAL/Classification/internal/verbosity.h>
|
#include <CGAL/Classification/internal/verbosity.h>
|
||||||
|
|
||||||
#include <CGAL/bounding_box.h>
|
#include <CGAL/bounding_box.h>
|
||||||
|
|
@ -65,15 +68,20 @@ namespace Classification {
|
||||||
\brief Generates a set of generic features for surface mesh
|
\brief Generates a set of generic features for surface mesh
|
||||||
classification.
|
classification.
|
||||||
|
|
||||||
This class takes care of computing all necessary data structures and
|
This class takes care of computing and storing all necessary data
|
||||||
of generating a set of generic features at multiple scales to
|
structures and of generating a set of generic features at multiple
|
||||||
increase the reliability of the classification.
|
scales to increase the reliability of the classification.
|
||||||
|
|
||||||
A `PointMap` is required: this map should associate each face of the
|
A `PointMap` is required: this map should associate each face of the
|
||||||
mesh to a representative point (for example, the center of mass of
|
mesh to a representative point (for example, the center of mass of
|
||||||
the face). It is used to generate point set features by considering
|
the face). It is used to generate point set features by considering
|
||||||
the mesh as a point set.
|
the mesh as a point set.
|
||||||
|
|
||||||
|
\warning The generated features use data structures that are stored
|
||||||
|
inside the generator. For this reason, the generator should be
|
||||||
|
instantiated _within the same scope_ as the feature set and should
|
||||||
|
not be deleted before the feature set.
|
||||||
|
|
||||||
\tparam GeomTraits model of \cgal Kernel.
|
\tparam GeomTraits model of \cgal Kernel.
|
||||||
\tparam FaceListGraph model of `FaceListGraph`.
|
\tparam FaceListGraph model of `FaceListGraph`.
|
||||||
\tparam PointMap model of `ReadablePropertyMap` whose key type is
|
\tparam PointMap model of `ReadablePropertyMap` whose key type is
|
||||||
|
|
@ -134,6 +142,12 @@ public:
|
||||||
<Face_range, PointMap> Distance_to_plane;
|
<Face_range, PointMap> Distance_to_plane;
|
||||||
typedef Classification::Feature::Elevation
|
typedef Classification::Feature::Elevation
|
||||||
<GeomTraits, Face_range, PointMap> 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
|
typedef Classification::Feature::Vertical_dispersion
|
||||||
<GeomTraits, Face_range, PointMap> Dispersion;
|
<GeomTraits, Face_range, PointMap> Dispersion;
|
||||||
typedef Classification::Feature::Verticality
|
typedef Classification::Feature::Verticality
|
||||||
|
|
@ -212,8 +226,8 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
float grid_resolution() const { return voxel_size; }
|
float grid_resolution() const { return voxel_size; }
|
||||||
float radius_neighbors() const { return voxel_size * 5; }
|
float radius_neighbors() const { return voxel_size * 3; }
|
||||||
float radius_dtm() const { return voxel_size * 100; }
|
float radius_dtm() const { return voxel_size * 10; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -327,7 +341,10 @@ public:
|
||||||
|
|
||||||
- `CGAL::Classification::Feature::Distance_to_plane`
|
- `CGAL::Classification::Feature::Distance_to_plane`
|
||||||
- `CGAL::Classification::Feature::Elevation`
|
- `CGAL::Classification::Feature::Elevation`
|
||||||
|
- `CGAL::Classification::Feature::Height_above`
|
||||||
|
- `CGAL::Classification::Feature::Height_below`
|
||||||
- `CGAL::Classification::Feature::Vertical_dispersion`
|
- `CGAL::Classification::Feature::Vertical_dispersion`
|
||||||
|
- `CGAL::Classification::Feature::Vertical_range`
|
||||||
|
|
||||||
\param features the feature set where the features are instantiated.
|
\param features the feature set where the features are instantiated.
|
||||||
*/
|
*/
|
||||||
|
|
@ -339,6 +356,12 @@ public:
|
||||||
features.add_with_scale_id<Dispersion> (i, m_range, m_point_map, grid(i), radius_neighbors(i));
|
features.add_with_scale_id<Dispersion> (i, m_range, m_point_map, grid(i), radius_neighbors(i));
|
||||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||||
features.add_with_scale_id<Elevation> (i, m_range, m_point_map, grid(i), radius_dtm(i));
|
features.add_with_scale_id<Elevation> (i, m_range, m_point_map, grid(i), radius_dtm(i));
|
||||||
|
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||||
|
features.add_with_scale_id<Height_below> (i, m_range, m_point_map, grid(i));
|
||||||
|
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||||
|
features.add_with_scale_id<Height_above> (i, m_range, 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_range, m_point_map, grid(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
||||||
|
|
@ -37,16 +37,18 @@ namespace CGAL {
|
||||||
|
|
||||||
namespace Classification {
|
namespace Classification {
|
||||||
|
|
||||||
/*!
|
namespace OpenCV {
|
||||||
\ingroup PkgClassificationClassifiers
|
|
||||||
|
|
||||||
\brief %Classifier based on the OpenCV version of random forest algorithm.
|
/*!
|
||||||
|
\ingroup PkgClassificationClassifiersOpenCV
|
||||||
|
|
||||||
|
\brief %Classifier based on the OpenCV version of the random forest algorithm.
|
||||||
|
|
||||||
\note This class requires the \ref thirdpartyOpenCV library.
|
\note This class requires the \ref thirdpartyOpenCV library.
|
||||||
|
|
||||||
\cgalModels `CGAL::Classification::Classifier`
|
\cgalModels `CGAL::Classification::Classifier`
|
||||||
*/
|
*/
|
||||||
class OpenCV_random_forest_classifier
|
class Random_forest_classifier
|
||||||
{
|
{
|
||||||
const Label_set& m_labels;
|
const Label_set& m_labels;
|
||||||
const Feature_set& m_features;
|
const Feature_set& m_features;
|
||||||
|
|
@ -68,7 +70,7 @@ public:
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Instantiate 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](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html). For more details on this method, please refer to it.
|
Parameters documentation is copy-pasted from [the official documentation of OpenCV](http://docs.opencv.org/2.4/modules/ml/doc/random_trees.html). For more details on this method, please refer to it.
|
||||||
|
|
||||||
|
|
@ -80,13 +82,13 @@ public:
|
||||||
\param max_number_of_trees_in_the_forest The maximum number of trees in the forest (surprise, surprise). Typically the more trees you have the better the accuracy. However, the improvement in accuracy generally diminishes and asymptotes pass a certain number of trees. Also to keep in mind, the number of tree increases the prediction time linearly.
|
\param max_number_of_trees_in_the_forest The maximum number of trees in the forest (surprise, surprise). Typically the more trees you have the better the accuracy. However, the improvement in accuracy generally diminishes and asymptotes pass a certain number of trees. Also to keep in mind, the number of tree increases the prediction time linearly.
|
||||||
\param forest_accuracy Sufficient accuracy (OOB error).
|
\param forest_accuracy Sufficient accuracy (OOB error).
|
||||||
*/
|
*/
|
||||||
OpenCV_random_forest_classifier (const Label_set& labels,
|
Random_forest_classifier (const Label_set& labels,
|
||||||
const Feature_set& features,
|
const Feature_set& features,
|
||||||
int max_depth = 20,
|
int max_depth = 20,
|
||||||
int min_sample_count = 5,
|
int min_sample_count = 5,
|
||||||
int max_categories = 15,
|
int max_categories = 15,
|
||||||
int max_number_of_trees_in_the_forest = 100,
|
int max_number_of_trees_in_the_forest = 100,
|
||||||
float forest_accuracy = 0.01f)
|
float forest_accuracy = 0.01f)
|
||||||
: m_labels (labels), m_features (features),
|
: m_labels (labels), m_features (features),
|
||||||
m_max_depth (max_depth), m_min_sample_count (min_sample_count),
|
m_max_depth (max_depth), m_min_sample_count (min_sample_count),
|
||||||
m_max_categories (max_categories),
|
m_max_categories (max_categories),
|
||||||
|
|
@ -98,7 +100,7 @@ public:
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/// \cond SKIP_IN_MANUAL
|
/// \cond SKIP_IN_MANUAL
|
||||||
~OpenCV_random_forest_classifier ()
|
~Random_forest_classifier ()
|
||||||
{
|
{
|
||||||
#if (CV_MAJOR_VERSION < 3)
|
#if (CV_MAJOR_VERSION < 3)
|
||||||
if (rtree != NULL)
|
if (rtree != NULL)
|
||||||
|
|
@ -298,6 +300,13 @@ public:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
// Backward compatibility
|
||||||
|
typedef OpenCV::Random_forest_classifier OpenCV_random_forest_classifier;
|
||||||
|
/// \endcond
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CGAL_CLASSIFICATION_OPENCV_RANDOM_FOREST_CLASSIFIER_H
|
#endif // CGAL_CLASSIFICATION_OPENCV_RANDOM_FOREST_CLASSIFIER_H
|
||||||
|
|
@ -35,6 +35,9 @@
|
||||||
#include <CGAL/Classification/Feature/Verticality.h>
|
#include <CGAL/Classification/Feature/Verticality.h>
|
||||||
#include <CGAL/Classification/Feature/Eigenvalue.h>
|
#include <CGAL/Classification/Feature/Eigenvalue.h>
|
||||||
#include <CGAL/Classification/Feature/Color_channel.h>
|
#include <CGAL/Classification/Feature/Color_channel.h>
|
||||||
|
#include <CGAL/Classification/Feature/Height_below.h>
|
||||||
|
#include <CGAL/Classification/Feature/Height_above.h>
|
||||||
|
#include <CGAL/Classification/Feature/Vertical_range.h>
|
||||||
|
|
||||||
// Experimental feature, not used officially
|
// Experimental feature, not used officially
|
||||||
#ifdef CGAL_CLASSIFICATION_USE_GRADIENT_OF_FEATURE
|
#ifdef CGAL_CLASSIFICATION_USE_GRADIENT_OF_FEATURE
|
||||||
|
|
@ -67,9 +70,14 @@ namespace Classification {
|
||||||
\brief Generates a set of generic features for point set
|
\brief Generates a set of generic features for point set
|
||||||
classification.
|
classification.
|
||||||
|
|
||||||
This class takes care of computing all necessary data structures and
|
This class takes care of computing and storing all necessary data
|
||||||
of generating a set of generic features at multiple scales to
|
structures and of generating a set of generic features at multiple
|
||||||
increase the reliability of the classification.
|
scales to increase the reliability of the classification.
|
||||||
|
|
||||||
|
\warning The generated features use data structures that are stored
|
||||||
|
inside the generator. For this reason, the generator should be
|
||||||
|
instantiated _within the same scope_ as the feature set and should
|
||||||
|
not be deleted before the feature set.
|
||||||
|
|
||||||
\tparam GeomTraits model of \cgal Kernel.
|
\tparam GeomTraits model of \cgal Kernel.
|
||||||
\tparam PointRange model of `ConstRange`. Its iterator type is
|
\tparam PointRange model of `ConstRange`. Its iterator type is
|
||||||
|
|
@ -128,6 +136,12 @@ public:
|
||||||
<PointRange, PointMap> Distance_to_plane;
|
<PointRange, PointMap> Distance_to_plane;
|
||||||
typedef Classification::Feature::Elevation
|
typedef Classification::Feature::Elevation
|
||||||
<GeomTraits, PointRange, PointMap> 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
|
typedef Classification::Feature::Vertical_dispersion
|
||||||
<GeomTraits, PointRange, PointMap> Dispersion;
|
<GeomTraits, PointRange, PointMap> Dispersion;
|
||||||
typedef Classification::Feature::Verticality
|
typedef Classification::Feature::Verticality
|
||||||
|
|
@ -166,7 +180,7 @@ private:
|
||||||
neighborhood = new Neighborhood (input, point_map, voxel_size);
|
neighborhood = new Neighborhood (input, point_map, voxel_size);
|
||||||
t.stop();
|
t.stop();
|
||||||
|
|
||||||
if (voxel_size < 0.)
|
if (lower_grid == NULL)
|
||||||
CGAL_CLASSIFICATION_CERR << "Neighborhood computed in " << t.time() << " second(s)" << std::endl;
|
CGAL_CLASSIFICATION_CERR << "Neighborhood computed in " << t.time() << " second(s)" << std::endl;
|
||||||
else
|
else
|
||||||
CGAL_CLASSIFICATION_CERR << "Neighborhood with voxel size " << voxel_size
|
CGAL_CLASSIFICATION_CERR << "Neighborhood with voxel size " << voxel_size
|
||||||
|
|
@ -216,8 +230,8 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
float grid_resolution() const { return voxel_size; }
|
float grid_resolution() const { return voxel_size; }
|
||||||
float radius_neighbors() const { return voxel_size * 5; }
|
float radius_neighbors() const { return voxel_size * 3; }
|
||||||
float radius_dtm() const { return voxel_size * 100; }
|
float radius_dtm() const { return voxel_size * 10; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -365,7 +379,10 @@ public:
|
||||||
- `CGAL::Classification::Feature::Eigenvalue` with indices 0, 1 and 2
|
- `CGAL::Classification::Feature::Eigenvalue` with indices 0, 1 and 2
|
||||||
- `CGAL::Classification::Feature::Distance_to_plane`
|
- `CGAL::Classification::Feature::Distance_to_plane`
|
||||||
- `CGAL::Classification::Feature::Elevation`
|
- `CGAL::Classification::Feature::Elevation`
|
||||||
|
- `CGAL::Classification::Feature::Height_above`
|
||||||
|
- `CGAL::Classification::Feature::Height_below`
|
||||||
- `CGAL::Classification::Feature::Vertical_dispersion`
|
- `CGAL::Classification::Feature::Vertical_dispersion`
|
||||||
|
- `CGAL::Classification::Feature::Vertical_range`
|
||||||
- The version of `CGAL::Classification::Feature::Verticality` based on eigenvalues
|
- The version of `CGAL::Classification::Feature::Verticality` based on eigenvalues
|
||||||
|
|
||||||
\param features the feature set where the features are instantiated.
|
\param features the feature set where the features are instantiated.
|
||||||
|
|
@ -381,6 +398,12 @@ public:
|
||||||
features.add_with_scale_id<Dispersion> (i, m_input, m_point_map, grid(i), radius_neighbors(i));
|
features.add_with_scale_id<Dispersion> (i, m_input, m_point_map, grid(i), radius_neighbors(i));
|
||||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
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));
|
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)
|
||||||
|
features.add_with_scale_id<Height_below> (i, m_input, m_point_map, grid(i));
|
||||||
|
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||||
|
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));
|
||||||
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
for (std::size_t i = 0; i < m_scales.size(); ++ i)
|
||||||
features.add_with_scale_id<Verticality> (i, m_input, eigen(i));
|
features.add_with_scale_id<Verticality> (i, m_input, eigen(i));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ public:
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
||||||
\brief Instantiate 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
|
\note If the label set of the feature set are modified after
|
||||||
instantiating this object (addition of removal of a label and/or of
|
instantiating this object (addition of removal of a label and/or of
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -87,6 +87,53 @@ namespace internal {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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>
|
template <typename Classifier>
|
||||||
class Classify_functor_local_smoothing_preprocessing
|
class Classify_functor_local_smoothing_preprocessing
|
||||||
{
|
{
|
||||||
|
|
@ -323,8 +370,6 @@ namespace internal {
|
||||||
const Classifier& classifier,
|
const Classifier& classifier,
|
||||||
LabelIndexRange& output)
|
LabelIndexRange& output)
|
||||||
{
|
{
|
||||||
output.resize(input.size());
|
|
||||||
|
|
||||||
internal::Classify_functor<Classifier, LabelIndexRange>
|
internal::Classify_functor<Classifier, LabelIndexRange>
|
||||||
f (labels, classifier, output);
|
f (labels, classifier, output);
|
||||||
|
|
||||||
|
|
@ -344,6 +389,39 @@ namespace internal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \cond SKIP_IN_MANUAL
|
||||||
|
// variant to get a detailed output (not documented yet)
|
||||||
|
template <typename ConcurrencyTag,
|
||||||
|
typename ItemRange,
|
||||||
|
typename Classifier,
|
||||||
|
typename LabelIndexRange,
|
||||||
|
typename ProbabilitiesRanges>
|
||||||
|
void classify (const ItemRange& input,
|
||||||
|
const Label_set& labels,
|
||||||
|
const Classifier& classifier,
|
||||||
|
LabelIndexRange& output,
|
||||||
|
ProbabilitiesRanges& probabilities)
|
||||||
|
{
|
||||||
|
internal::Classify_detailed_output_functor<Classifier, LabelIndexRange, ProbabilitiesRanges>
|
||||||
|
f (labels, classifier, output, probabilities);
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// \endcond
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\ingroup PkgClassificationMain
|
\ingroup PkgClassificationMain
|
||||||
|
|
||||||
|
|
@ -388,8 +466,6 @@ namespace internal {
|
||||||
const NeighborQuery& neighbor_query,
|
const NeighborQuery& neighbor_query,
|
||||||
LabelIndexRange& output)
|
LabelIndexRange& output)
|
||||||
{
|
{
|
||||||
output.resize(input.size());
|
|
||||||
|
|
||||||
std::vector<std::vector<float> > values
|
std::vector<std::vector<float> > values
|
||||||
(labels.size(), std::vector<float> (input.size(), -1.));
|
(labels.size(), std::vector<float> (input.size(), -1.));
|
||||||
internal::Classify_functor_local_smoothing_preprocessing<Classifier>
|
internal::Classify_functor_local_smoothing_preprocessing<Classifier>
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ int main (int, char**)
|
||||||
color_map, echo_map);
|
color_map, echo_map);
|
||||||
|
|
||||||
assert (generator.number_of_scales() == 5);
|
assert (generator.number_of_scales() == 5);
|
||||||
assert (features.size() == 44);
|
assert (features.size() == 59);
|
||||||
|
|
||||||
Label_set labels;
|
Label_set labels;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ typedef Classification::Feature_handle
|
||||||
typedef Classification::Label_set Label_set;
|
typedef Classification::Label_set Label_set;
|
||||||
typedef Classification::Feature_set Feature_set;
|
typedef Classification::Feature_set Feature_set;
|
||||||
|
|
||||||
typedef Classification::ETHZ_random_forest_classifier Classifier;
|
typedef Classification::ETHZ::Random_forest_classifier Classifier;
|
||||||
|
|
||||||
typedef Classification::Planimetric_grid<Kernel, Point_set, Point_map> Planimetric_grid;
|
typedef Classification::Planimetric_grid<Kernel, Point_set, Point_map> Planimetric_grid;
|
||||||
typedef Classification::Point_set_neighborhood<Kernel, Point_set, Point_map> Neighborhood;
|
typedef Classification::Point_set_neighborhood<Kernel, Point_set, Point_map> Neighborhood;
|
||||||
|
|
@ -87,13 +87,18 @@ int main (int, char**)
|
||||||
std::ifstream inf ("output_config.gz", std::ios::binary);
|
std::ifstream inf ("output_config.gz", std::ios::binary);
|
||||||
classifier2.load_configuration(inf);
|
classifier2.load_configuration(inf);
|
||||||
|
|
||||||
std::vector<std::size_t> label_indices;
|
Classifier classifier3 (classifier, features);
|
||||||
std::vector<std::size_t> label_indices_2;
|
|
||||||
|
std::vector<std::size_t> label_indices (points.size());
|
||||||
|
std::vector<std::size_t> label_indices_2 (points.size());
|
||||||
|
std::vector<std::size_t> label_indices_3 (points.size());
|
||||||
|
|
||||||
Classification::classify<CGAL::Sequential_tag> (points, labels, classifier, label_indices);
|
Classification::classify<CGAL::Sequential_tag> (points, labels, classifier, label_indices);
|
||||||
Classification::classify<CGAL::Sequential_tag> (points, labels, classifier2, label_indices_2);
|
Classification::classify<CGAL::Sequential_tag> (points, labels, classifier2, label_indices_2);
|
||||||
|
Classification::classify<CGAL::Sequential_tag> (points, labels, classifier3, label_indices_3);
|
||||||
|
|
||||||
assert (label_indices == label_indices_2);
|
assert (label_indices == label_indices_2);
|
||||||
|
assert (label_indices == label_indices_3);
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ int main (int, char**)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
assert (generator.number_of_scales() == 5);
|
assert (generator.number_of_scales() == 5);
|
||||||
assert (features.size() == 44);
|
assert (features.size() == 59);
|
||||||
|
|
||||||
Label_set labels;
|
Label_set labels;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,22 @@ In \cgal, \sc{OpenCV} is used by the \ref PkgClassificationRef package.
|
||||||
|
|
||||||
The \sc{OpenCV} web site is <A HREF="http://opencv.org/">`http://opencv.org/`</A>.
|
The \sc{OpenCV} web site is <A HREF="http://opencv.org/">`http://opencv.org/`</A>.
|
||||||
|
|
||||||
|
\subsection thirdpartyTensorFlow TensorFlow
|
||||||
|
|
||||||
|
\sc{TensorFlow} is a library designed for machine learning and deep learning.
|
||||||
|
|
||||||
|
In \cgal, the C++ API of \sc{TensorFlow} is used by the \ref
|
||||||
|
PkgClassificationRef package for neural network. The C++ API can be
|
||||||
|
compiled using CMake: it is distributed as part of the official
|
||||||
|
package and is located in `tensorflow/contrib/cmake`. Be sure to
|
||||||
|
enable and compile the following targets:
|
||||||
|
|
||||||
|
- `tensorflow_BUILD_ALL_KERNELS`
|
||||||
|
- `tensorflow_BUILD_PYTHON_BINDINGS`
|
||||||
|
- `tensorflow_BUILD_SHARED_LIB`.
|
||||||
|
|
||||||
|
The \sc{TensorFlow} web site is <A HREF="https://www.tensorflow.org/">`https://www.tensorflow.org/`</A>.
|
||||||
|
|
||||||
\subsection thirdpartyMETIS METIS
|
\subsection thirdpartyMETIS METIS
|
||||||
|
|
||||||
\sc{METIS} is a library developed by the <A HREF="http://glaros.dtc.umn.edu/gkhome/">Karypis Lab</A>
|
\sc{METIS} is a library developed by the <A HREF="http://glaros.dtc.umn.edu/gkhome/">Karypis Lab</A>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <CGAL/Random.h>
|
#include <CGAL/Random.h>
|
||||||
#include <CGAL/point_generators_2.h>
|
#include <CGAL/point_generators_2.h>
|
||||||
#include <CGAL/Timer.h>
|
#include <CGAL/Timer.h>
|
||||||
|
#include <CGAL/IO/write_vtu.h>
|
||||||
|
|
||||||
// Qt headers
|
// Qt headers
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
|
|
@ -84,12 +85,12 @@ discoverInfiniteComponent(const CDT & ct)
|
||||||
Face_handle fh = queue.front();
|
Face_handle fh = queue.front();
|
||||||
queue.pop_front();
|
queue.pop_front();
|
||||||
fh->set_in_domain(false);
|
fh->set_in_domain(false);
|
||||||
|
|
||||||
for(int i = 0; i < 3; i++)
|
for(int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
Face_handle fi = fh->neighbor(i);
|
Face_handle fi = fh->neighbor(i);
|
||||||
if(fi->is_in_domain()
|
if(fi->is_in_domain()
|
||||||
&& !ct.is_constrained(CDT::Edge(fh,i)))
|
&& !ct.is_constrained(CDT::Edge(fh,i)))
|
||||||
queue.push_back(fi);
|
queue.push_back(fi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -269,7 +270,7 @@ MainWindow::MainWindow()
|
||||||
dgi->setFacesInDomainBrush(facesColor);
|
dgi->setFacesInDomainBrush(facesColor);
|
||||||
|
|
||||||
QObject::connect(this, SIGNAL(changed()),
|
QObject::connect(this, SIGNAL(changed()),
|
||||||
dgi, SLOT(modelChanged()));
|
dgi, SLOT(modelChanged()));
|
||||||
dgi->setVerticesPen(
|
dgi->setVerticesPen(
|
||||||
QPen(Qt::red, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
QPen(Qt::red, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
dgi->setVoronoiPen(
|
dgi->setVoronoiPen(
|
||||||
|
|
@ -285,8 +286,8 @@ MainWindow::MainWindow()
|
||||||
// the signal/slot mechanism
|
// the signal/slot mechanism
|
||||||
pi = new CGAL::Qt::GraphicsViewPolylineInput<K>(this, &scene, 0, true); // inputs polylines which are not closed
|
pi = new CGAL::Qt::GraphicsViewPolylineInput<K>(this, &scene, 0, true); // inputs polylines which are not closed
|
||||||
QObject::connect(pi, SIGNAL(generate(CGAL::Object)),
|
QObject::connect(pi, SIGNAL(generate(CGAL::Object)),
|
||||||
this, SLOT(processInput(CGAL::Object)));
|
this, SLOT(processInput(CGAL::Object)));
|
||||||
|
|
||||||
tcc = new CGAL::Qt::TriangulationCircumcircle<CDT>(&scene, &cdt, this);
|
tcc = new CGAL::Qt::TriangulationCircumcircle<CDT>(&scene, &cdt, this);
|
||||||
tcc->setPen(QPen(Qt::red, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
tcc->setPen(QPen(Qt::red, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
|
||||||
|
|
@ -298,8 +299,8 @@ MainWindow::MainWindow()
|
||||||
// Manual handling of actions
|
// Manual handling of actions
|
||||||
//
|
//
|
||||||
QObject::connect(this->actionQuit, SIGNAL(triggered()),
|
QObject::connect(this->actionQuit, SIGNAL(triggered()),
|
||||||
this, SLOT(close()));
|
this, SLOT(close()));
|
||||||
|
|
||||||
// We put mutually exclusive actions in an QActionGroup
|
// We put mutually exclusive actions in an QActionGroup
|
||||||
QActionGroup* ag = new QActionGroup(this);
|
QActionGroup* ag = new QActionGroup(this);
|
||||||
ag->addAction(this->actionInsertPolyline);
|
ag->addAction(this->actionInsertPolyline);
|
||||||
|
|
@ -338,7 +339,7 @@ MainWindow::MainWindow()
|
||||||
|
|
||||||
this->addRecentFiles(this->menuFile, this->actionQuit);
|
this->addRecentFiles(this->menuFile, this->actionQuit);
|
||||||
connect(this, SIGNAL(openRecentFile(QString)),
|
connect(this, SIGNAL(openRecentFile(QString)),
|
||||||
this, SLOT(open(QString)));
|
this, SLOT(open(QString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -534,11 +535,11 @@ void
|
||||||
MainWindow::on_actionLoadConstraints_triggered()
|
MainWindow::on_actionLoadConstraints_triggered()
|
||||||
{
|
{
|
||||||
QString fileName = QFileDialog::getOpenFileName(this,
|
QString fileName = QFileDialog::getOpenFileName(this,
|
||||||
tr("Open Constraint File"),
|
tr("Open Constraint File"),
|
||||||
".",
|
".",
|
||||||
tr("Edge files (*.edg);;"
|
tr("Edge files (*.edg);;"
|
||||||
"Plg files (*.plg);;"
|
"Plg files (*.plg);;"
|
||||||
"Poly files (*.poly)"));
|
"Poly files (*.poly)"));
|
||||||
open(fileName);
|
open(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -655,12 +656,13 @@ void
|
||||||
MainWindow::on_actionSaveConstraints_triggered()
|
MainWindow::on_actionSaveConstraints_triggered()
|
||||||
{
|
{
|
||||||
QString fileName = QFileDialog::getSaveFileName(this,
|
QString fileName = QFileDialog::getSaveFileName(this,
|
||||||
tr("Save Constraints"),
|
tr("Save Constraints"),
|
||||||
".",
|
".",
|
||||||
tr("Poly files (*.poly)\n"
|
tr("Poly files (*.poly)\n"
|
||||||
"Edge files (*.edg)"));
|
"Edge files (*.edg)\n"
|
||||||
|
"VTU files (*.vtu)"));
|
||||||
if(! fileName.isEmpty()){
|
if(! fileName.isEmpty()){
|
||||||
saveConstraints(fileName);
|
saveConstraints(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -669,7 +671,13 @@ void
|
||||||
MainWindow::saveConstraints(QString fileName)
|
MainWindow::saveConstraints(QString fileName)
|
||||||
{
|
{
|
||||||
std::ofstream output(qPrintable(fileName));
|
std::ofstream output(qPrintable(fileName));
|
||||||
if (output) output << cdt;
|
|
||||||
|
if(!fileName.endsWith("vtu") && output)
|
||||||
|
output << cdt;
|
||||||
|
else if (output)
|
||||||
|
{
|
||||||
|
CGAL::write_vtu(output, cdt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -796,15 +804,15 @@ MainWindow::on_actionInsertRandomPoints_triggered()
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
const int number_of_points =
|
const int number_of_points =
|
||||||
QInputDialog::getInt(this,
|
QInputDialog::getInt(this,
|
||||||
tr("Number of random points"),
|
tr("Number of random points"),
|
||||||
tr("Enter number of random points"),
|
tr("Enter number of random points"),
|
||||||
100,
|
100,
|
||||||
0,
|
0,
|
||||||
(std::numeric_limits<int>::max)(),
|
(std::numeric_limits<int>::max)(),
|
||||||
1,
|
1,
|
||||||
&ok);
|
&ok);
|
||||||
|
|
||||||
if(!ok) {
|
if(!ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,24 @@ Release date: March 2019
|
||||||
original behavior (using one unique and automatically selected seed) is
|
original behavior (using one unique and automatically selected seed) is
|
||||||
kept if this parameter is not used.
|
kept if this parameter is not used.
|
||||||
|
|
||||||
|
### Classification
|
||||||
|
|
||||||
|
- Added a new experimental classifier
|
||||||
|
`TensorFlow::Neural_network_classifier`.
|
||||||
|
|
||||||
|
- For uniformity, `ETHZ_random_forest_classifier` is renamed
|
||||||
|
`ETHZ::Random_forest_classifier` and `OpenCV_random_forest_classifier`
|
||||||
|
is renamed `OpenCV::Random_forest_classifier`.
|
||||||
|
|
||||||
|
- The training algorithm of `ETHZ::Random_forest_classifier` was
|
||||||
|
parallelized.
|
||||||
|
|
||||||
|
- Added a constructor to copy a `ETHZ::Random_forest_classifier` using a
|
||||||
|
different data set as input.
|
||||||
|
|
||||||
|
- Added 3 new geometric features, `Height_above`, `Height_below` and
|
||||||
|
`Vertical_range`.
|
||||||
|
|
||||||
### 3D Fast Intersection and Distance Computation
|
### 3D Fast Intersection and Distance Computation
|
||||||
|
|
||||||
- The primitives `AABB_face_graph_triangle_primitive` and
|
- The primitives `AABB_face_graph_triangle_primitive` and
|
||||||
|
|
@ -86,6 +104,12 @@ Release date: March 2019
|
||||||
`Arr_polyline_traits_2`, `Arr_polycurve_traits_2`, and
|
`Arr_polyline_traits_2`, `Arr_polycurve_traits_2`, and
|
||||||
`Arr_polycurve_basic_traits_2`.
|
`Arr_polycurve_basic_traits_2`.
|
||||||
|
|
||||||
|
### 2D and 3D Mesh Generation
|
||||||
|
|
||||||
|
- Added two functions for writing in XML VTK formats:
|
||||||
|
- `CGAL::write_vtu()`, that writes a 2D mesh in a `.vtu` file,
|
||||||
|
- `CGAL::output_to_vtu()`, that writes a 3D mesh in a `.vtu` file.
|
||||||
|
|
||||||
### 2D Minkowski Sums
|
### 2D Minkowski Sums
|
||||||
|
|
||||||
- Fixed a bug in the function that computed the Minkowski sum using the
|
- Fixed a bug in the function that computed the Minkowski sum using the
|
||||||
|
|
@ -94,7 +118,10 @@ Release date: March 2019
|
||||||
|
|
||||||
### CGAL and the Boost Graph Library (BGL)
|
### CGAL and the Boost Graph Library (BGL)
|
||||||
|
|
||||||
- Add function `write_wrl()` for writing into VRML 2.0 format.
|
- Added function `write_wrl()` for writing into VRML 2.0 format.
|
||||||
|
- Added functions `CGAL::write_vtp()` for writing a triangulated
|
||||||
|
face graph in a `.vtp` file (XML VTK format).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Release 4.13
|
Release 4.13
|
||||||
|
|
|
||||||
|
|
@ -108,11 +108,13 @@ else ( CGAL_BRANCH_BUILD )
|
||||||
|
|
||||||
# Enable testing with BUILD_TESTING
|
# Enable testing with BUILD_TESTING
|
||||||
option(BUILD_TESTING "Build the testing tree." OFF)
|
option(BUILD_TESTING "Build the testing tree." OFF)
|
||||||
include(CTest)
|
|
||||||
if(BUILD_TESTING AND NOT POLICY CMP0064)
|
if(BUILD_TESTING AND NOT POLICY CMP0064)
|
||||||
message(FATAL_ERROR "CGAL support of CTest requires CMake version 3.4 or later.
|
message(FATAL_ERROR "CGAL support of CTest requires CMake version 3.4 or later.
|
||||||
The variable BUILD_TESTING must be set of OFF.")
|
The variable BUILD_TESTING must be set of OFF.")
|
||||||
endif()
|
endif()
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
endif()
|
||||||
endif (CGAL_BRANCH_BUILD )
|
endif (CGAL_BRANCH_BUILD )
|
||||||
|
|
||||||
#message(STATUS "Packages found: ${CGAL_CONFIGURED_PACKAGES}")
|
#message(STATUS "Packages found: ${CGAL_CONFIGURED_PACKAGES}")
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ endif()
|
||||||
# See the release notes of CGAL-4.10: CGAL_Core now requires
|
# See the release notes of CGAL-4.10: CGAL_Core now requires
|
||||||
# Boost.Thread, with all compilers but MSVC.
|
# Boost.Thread, with all compilers but MSVC.
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
find_package( Boost 1.48 REQUIRED thread system )
|
find_package( Boost 1.48 REQUIRED COMPONENTS thread system )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
function(CGAL_setup_CGAL_Core_dependencies target)
|
function(CGAL_setup_CGAL_Core_dependencies target)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ if(CGAL_add_test_included)
|
||||||
endif(CGAL_add_test_included)
|
endif(CGAL_add_test_included)
|
||||||
set(CGAL_add_test_included TRUE)
|
set(CGAL_add_test_included TRUE)
|
||||||
|
|
||||||
|
option(BUILD_TESTING "Build the testing tree." OFF)
|
||||||
|
|
||||||
if(NOT POLICY CMP0064)
|
if(NOT POLICY CMP0064)
|
||||||
# CMake <= 3.3
|
# CMake <= 3.3
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
|
|
@ -19,7 +21,9 @@ if(NOT POLICY CMP0064)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(CTest)
|
if(BUILD_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
endif()
|
||||||
|
|
||||||
cmake_policy(SET CMP0064 NEW)
|
cmake_policy(SET CMP0064 NEW)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
unset(TENSORFLOW_FOUND)
|
||||||
|
|
||||||
|
find_path(TensorFlow_INCLUDE_DIR
|
||||||
|
NAMES
|
||||||
|
tensorflow/core
|
||||||
|
tensorflow/cc
|
||||||
|
third_party
|
||||||
|
HINTS
|
||||||
|
/usr/include/
|
||||||
|
/usr/local/include/)
|
||||||
|
|
||||||
|
find_library(TensorFlow_LIBRARY NAMES tensorflow_all
|
||||||
|
HINTS
|
||||||
|
/usr/lib
|
||||||
|
/usr/local/lib)
|
||||||
|
|
||||||
|
find_package_handle_standard_args(TensorFlow DEFAULT_MSG TensorFlow_INCLUDE_DIR TensorFlow_LIBRARY)
|
||||||
|
|
||||||
|
if(TENSORFLOW_FOUND)
|
||||||
|
set(TensorFlow_LIBRARIES ${TensorFlow_LIBRARY})
|
||||||
|
set(TensorFlow_INCLUDE_DIRS ${TensorFlow_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
set(list_of_whitelisted_headers_txt [=[
|
set(list_of_whitelisted_headers_txt [=[
|
||||||
|
CGAL/Classification/TensorFlow/Neural_network_classifier.h
|
||||||
CGAL/Linear_cell_complex_constructors.h
|
CGAL/Linear_cell_complex_constructors.h
|
||||||
CGAL/CGAL_Ipelet_base.h
|
CGAL/CGAL_Ipelet_base.h
|
||||||
CGAL/IO/read_las_points.h
|
CGAL/IO/read_las_points.h
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
namespace CGAL{
|
||||||
|
//!\ingroup PkgMesh2IO
|
||||||
|
//! \brief writes the faces of a domain and its constrained edges embedded in
|
||||||
|
//! a 2D constrained Delaunay triangulation using the `PolyData` XML format.
|
||||||
|
//!
|
||||||
|
//! The faces output are those for which `DelaunayMeshFaceBase_2::is_in_domain()` returns `true`,
|
||||||
|
//! the edges are those for which `ConstrainedTriangulationFaceBase_2::is_constrained()` returns `true`.
|
||||||
|
//! \tparam CDT a `Constrained_Delaunay_triangulation_2` with face type model of `DelaunayMeshFaceBase_2`.
|
||||||
|
//!
|
||||||
|
//! \param os the stream used for writing.
|
||||||
|
//! \param tr the triangulated domain to be written.
|
||||||
|
//! \param mode decides if the data should be written in binary (`IO::BINARY`)
|
||||||
|
//! or in ASCII (`IO::ASCII`).
|
||||||
|
//!
|
||||||
|
template <class CDT>
|
||||||
|
void write_vtu(std::ostream& os,
|
||||||
|
const CDT& tr,
|
||||||
|
IO::Mode mode = IO::BINARY);
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,11 @@
|
||||||
/// \defgroup PkgMesh2Enum Enumerations
|
/// \defgroup PkgMesh2Enum Enumerations
|
||||||
/// \ingroup PkgMesh2Ref
|
/// \ingroup PkgMesh2Ref
|
||||||
|
|
||||||
|
/// \defgroup PkgMesh2IO I/O Functions
|
||||||
|
/// \ingroup PkgMesh2Ref
|
||||||
|
/// In addition to the stream extraction and insertion operators for
|
||||||
|
/// %CGAL 2D triangulations, the following functions can be used.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\addtogroup PkgMesh2Ref
|
\addtogroup PkgMesh2Ref
|
||||||
\cgalPkgDescriptionBegin{2D Conforming Triangulations and Meshes,PkgMesh2}
|
\cgalPkgDescriptionBegin{2D Conforming Triangulations and Meshes,PkgMesh2}
|
||||||
|
|
@ -58,6 +63,9 @@ The package can handle intersecting input constraints and set no restriction on
|
||||||
- `CGAL::refine_Delaunay_mesh_2`
|
- `CGAL::refine_Delaunay_mesh_2`
|
||||||
- `CGAL::lloyd_optimize_mesh_2`
|
- `CGAL::lloyd_optimize_mesh_2`
|
||||||
|
|
||||||
|
## I/O Functions ##
|
||||||
|
- `CGAL::write_vtu()`
|
||||||
|
|
||||||
## Enumerations ##
|
## Enumerations ##
|
||||||
- `CGAL::Mesh_optimization_return_code`
|
- `CGAL::Mesh_optimization_return_code`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,387 @@
|
||||||
|
// Copyright (c) 2018 GeometryFactory (France).
|
||||||
|
// Copyright (c) 2004-2006 INRIA Sophia-Antipolis (France).
|
||||||
|
// Copyright (c) 2009 INRIA Sophia-Antipolis (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
// You can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
// General Public License as published by the Free Software Foundation,
|
||||||
|
// either version 3 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Licensees holding a valid commercial license may use this file in
|
||||||
|
// accordance with the commercial license agreement provided with the software.
|
||||||
|
//
|
||||||
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Author(s) : Laurent RINEAU, Stephane Tayeb, Maxime Gimeno
|
||||||
|
|
||||||
|
#ifndef CGAL_WRITE_VTU_H
|
||||||
|
#define CGAL_WRITE_VTU_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Mesh_2.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <CGAL/assertions.h>
|
||||||
|
#include <CGAL/IO/io.h>
|
||||||
|
|
||||||
|
//todo try to factorize with functors
|
||||||
|
namespace CGAL{
|
||||||
|
// writes the appended data into the .vtu file
|
||||||
|
template <class FT>
|
||||||
|
void
|
||||||
|
write_vector(std::ostream& os,
|
||||||
|
const std::vector<FT>& vect)
|
||||||
|
{
|
||||||
|
const char* buffer = reinterpret_cast<const char*>(&(vect[0]));
|
||||||
|
std::size_t size = vect.size()*sizeof(FT);
|
||||||
|
|
||||||
|
os.write(reinterpret_cast<const char *>(&size), sizeof(std::size_t)); // number of bytes encoded
|
||||||
|
os.write(buffer, vect.size()*sizeof(FT)); // encoded data
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the cells tags before binary data is appended
|
||||||
|
|
||||||
|
template <class CDT>
|
||||||
|
void
|
||||||
|
write_cells_tag_2(std::ostream& os,
|
||||||
|
const CDT & tr,
|
||||||
|
std::size_t number_of_triangles,
|
||||||
|
std::map<typename CDT::Vertex_handle, std::size_t> & V,
|
||||||
|
bool binary,
|
||||||
|
std::size_t& offset)
|
||||||
|
{
|
||||||
|
std::string formatattribute =
|
||||||
|
binary ? " format=\"appended\"" : " format=\"ascii\"";
|
||||||
|
|
||||||
|
std::string typeattribute;
|
||||||
|
switch(sizeof(std::size_t)) {
|
||||||
|
case 8: typeattribute = " type=\"UInt64\""; break;
|
||||||
|
case 4: typeattribute = " type=\"UInt32\""; break;
|
||||||
|
default: CGAL_error_msg("Unknown size of std::size_t");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write connectivity table
|
||||||
|
os << " <Cells>\n"
|
||||||
|
<< " <DataArray Name=\"connectivity\""
|
||||||
|
<< formatattribute << typeattribute;
|
||||||
|
|
||||||
|
if (binary) { // if binary output, just write the xml tag
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
// 3 indices (size_t) per triangle + length of the encoded data (size_t)
|
||||||
|
offset += (3 * number_of_triangles + 1) * sizeof(std::size_t);
|
||||||
|
// 2 indices (size_t) per edge (size_t)
|
||||||
|
offset += (2 * std::distance(tr.constrained_edges_begin(),
|
||||||
|
tr.constrained_edges_end())) * sizeof(std::size_t);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for(typename CDT::Finite_faces_iterator
|
||||||
|
fit = tr.finite_faces_begin(),
|
||||||
|
end = tr.finite_faces_end();
|
||||||
|
fit != end; ++fit)
|
||||||
|
{
|
||||||
|
if(fit->is_in_domain())
|
||||||
|
{
|
||||||
|
os << V[fit->vertex(0)] << " ";
|
||||||
|
os << V[fit->vertex(2)] << " ";
|
||||||
|
os << V[fit->vertex(1)] << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write offsets
|
||||||
|
os << " <DataArray Name=\"offsets\""
|
||||||
|
<< formatattribute << typeattribute;
|
||||||
|
|
||||||
|
if (binary) { // if binary output, just write the xml tag
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += (number_of_triangles +std::distance(tr.constrained_edges_begin(),
|
||||||
|
tr.constrained_edges_end()) + 1)
|
||||||
|
* sizeof(std::size_t);
|
||||||
|
// 1 offset (size_t) per cell + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
std::size_t cells_offset = 0;
|
||||||
|
for(typename CDT::Finite_faces_iterator fit =
|
||||||
|
tr.finite_faces_begin() ;
|
||||||
|
fit != tr.finite_faces_end() ;
|
||||||
|
++fit )
|
||||||
|
{
|
||||||
|
if(fit->is_in_domain())
|
||||||
|
{
|
||||||
|
cells_offset += 3;
|
||||||
|
os << cells_offset << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write cell type (triangles == 5)
|
||||||
|
os << " <DataArray Name=\"types\""
|
||||||
|
<< formatattribute << " type=\"UInt8\"";
|
||||||
|
|
||||||
|
if (binary) {
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += number_of_triangles
|
||||||
|
+ std::distance(tr.constrained_edges_begin(),
|
||||||
|
tr.constrained_edges_end())
|
||||||
|
+ sizeof(std::size_t);
|
||||||
|
// 1 unsigned char per cell + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for(typename CDT::Finite_faces_iterator fit =
|
||||||
|
tr.finite_faces_begin() ;
|
||||||
|
fit != tr.finite_faces_end() ;
|
||||||
|
++fit )
|
||||||
|
{
|
||||||
|
if(fit->is_in_domain())
|
||||||
|
{
|
||||||
|
os << "5 ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
os << " </Cells>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the cells appended data at the end of the .vtu file
|
||||||
|
template <class CDT>
|
||||||
|
void
|
||||||
|
write_cells_2(std::ostream& os,
|
||||||
|
const CDT & tr,
|
||||||
|
std::size_t number_of_triangles,
|
||||||
|
std::map<typename CDT::Vertex_handle, std::size_t> & V)
|
||||||
|
{
|
||||||
|
std::vector<std::size_t> connectivity_table;
|
||||||
|
std::vector<std::size_t> offsets;
|
||||||
|
std::vector<unsigned char> cell_type(number_of_triangles,5); // triangles == 5
|
||||||
|
cell_type.resize(cell_type.size() + std::distance(tr.constrained_edges_begin(),
|
||||||
|
tr.constrained_edges_end()), 3); // line == 3
|
||||||
|
|
||||||
|
std::size_t off = 0;
|
||||||
|
for(typename CDT::Finite_faces_iterator
|
||||||
|
fit = tr.finite_faces_begin(),
|
||||||
|
end = tr.finite_faces_end();
|
||||||
|
fit != end; ++fit)
|
||||||
|
{
|
||||||
|
if(fit->is_in_domain())
|
||||||
|
{
|
||||||
|
off += 3;
|
||||||
|
offsets.push_back(off);
|
||||||
|
connectivity_table.push_back(V[fit->vertex(0)]);
|
||||||
|
connectivity_table.push_back(V[fit->vertex(2)]);
|
||||||
|
connectivity_table.push_back(V[fit->vertex(1)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(typename CDT::Constrained_edges_iterator
|
||||||
|
cei = tr.constrained_edges_begin(),
|
||||||
|
end = tr.constrained_edges_end();
|
||||||
|
cei != end; ++cei)
|
||||||
|
{
|
||||||
|
off += 2;
|
||||||
|
offsets.push_back(off);
|
||||||
|
for(int i=0; i<3; ++i)
|
||||||
|
{
|
||||||
|
if(i != cei->second)
|
||||||
|
connectivity_table.push_back(V[cei->first->vertex(i)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_vector<std::size_t>(os,connectivity_table);
|
||||||
|
write_vector<std::size_t>(os,offsets);
|
||||||
|
write_vector<unsigned char>(os,cell_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the points tags before binary data is appended
|
||||||
|
template <class Tr>
|
||||||
|
void
|
||||||
|
write_cdt_points_tag(std::ostream& os,
|
||||||
|
const Tr & tr,
|
||||||
|
std::map<typename Tr::Vertex_handle, std::size_t> & V,
|
||||||
|
bool binary,
|
||||||
|
std::size_t& offset)
|
||||||
|
{
|
||||||
|
std::size_t dim = 2;
|
||||||
|
typedef typename Tr::Finite_vertices_iterator Finite_vertices_iterator;
|
||||||
|
typedef typename Tr::Geom_traits Gt;
|
||||||
|
typedef typename Gt::FT FT;
|
||||||
|
|
||||||
|
std::size_t inum = 0;
|
||||||
|
std::string format = binary ? "appended" : "ascii";
|
||||||
|
std::string type = (sizeof(FT) == 8) ? "Float64" : "Float32";
|
||||||
|
|
||||||
|
os << " <Points>\n"
|
||||||
|
<< " <DataArray type =\"" << type << "\" NumberOfComponents=\"3\" format=\""
|
||||||
|
<< format;
|
||||||
|
|
||||||
|
if (binary) {
|
||||||
|
os << "\" offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += 3 * tr.number_of_vertices() * sizeof(FT) + sizeof(std::size_t);
|
||||||
|
// dim coords per points + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for( Finite_vertices_iterator vit = tr.finite_vertices_begin();
|
||||||
|
vit != tr.finite_vertices_end();
|
||||||
|
++vit)
|
||||||
|
{
|
||||||
|
V[vit] = inum++;
|
||||||
|
os << vit->point()[0] << " ";
|
||||||
|
os << vit->point()[1] << " ";
|
||||||
|
if(dim == 3)
|
||||||
|
os << vit->point()[2] << " ";
|
||||||
|
else
|
||||||
|
os << 0.0 << " ";
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
os << " </Points>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the points appended data at the end of the .vtu file
|
||||||
|
template <class Tr>
|
||||||
|
void
|
||||||
|
write_cdt_points(std::ostream& os,
|
||||||
|
const Tr & tr,
|
||||||
|
std::map<typename Tr::Vertex_handle,
|
||||||
|
std::size_t> & V)
|
||||||
|
{
|
||||||
|
std::size_t dim = 2;
|
||||||
|
typedef typename Tr::Finite_vertices_iterator Finite_vertices_iterator;
|
||||||
|
typedef typename Tr::Geom_traits Gt;
|
||||||
|
typedef typename Gt::FT FT;
|
||||||
|
|
||||||
|
std::size_t inum = 0;
|
||||||
|
std::vector<FT> coordinates;
|
||||||
|
for( Finite_vertices_iterator vit = tr.finite_vertices_begin();
|
||||||
|
vit != tr.finite_vertices_end();
|
||||||
|
++vit)
|
||||||
|
{
|
||||||
|
V[vit] = inum++; // binary output => the map has not been filled yet
|
||||||
|
coordinates.push_back(vit->point()[0]);
|
||||||
|
coordinates.push_back(vit->point()[1]);
|
||||||
|
coordinates.push_back(dim == 3 ? vit->point()[2] : 0.0);
|
||||||
|
}
|
||||||
|
write_vector<FT>(os,coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the attribute tags before binary data is appended
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
write_attribute_tag_2 (std::ostream& os,
|
||||||
|
const std::string& attr_name,
|
||||||
|
const std::vector<T>& attribute,
|
||||||
|
bool binary,
|
||||||
|
std::size_t& offset)
|
||||||
|
{
|
||||||
|
std::string format = binary ? "appended" : "ascii";
|
||||||
|
std::string type = (sizeof(T) == 8) ? "Float64" : "Float32";
|
||||||
|
os << " <DataArray type=\"" << type << "\" Name=\"" << attr_name << "\" format=\"" << format;
|
||||||
|
|
||||||
|
if (binary) {
|
||||||
|
os << "\" offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += attribute.size() * sizeof(T) + sizeof(std::size_t);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
typedef typename std::vector<T>::const_iterator Iterator;
|
||||||
|
os << "\">\n";
|
||||||
|
for (Iterator it = attribute.begin();
|
||||||
|
it != attribute.end();
|
||||||
|
++it )
|
||||||
|
os << *it << " ";
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the attributes appended data at the end of the .vtu file
|
||||||
|
template <typename FT>
|
||||||
|
void
|
||||||
|
write_attributes_2(std::ostream& os,
|
||||||
|
const std::vector<FT>& att)
|
||||||
|
{
|
||||||
|
write_vector(os,att);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class CDT>
|
||||||
|
void write_vtu_with_attributes(std::ostream& os,
|
||||||
|
const CDT& tr,
|
||||||
|
std::vector<std::pair<const char*, const std::vector<double>*> >& attributes,
|
||||||
|
IO::Mode mode = IO::BINARY)
|
||||||
|
{
|
||||||
|
typedef typename CDT::Vertex_handle Vertex_handle;
|
||||||
|
std::map<Vertex_handle, std::size_t> V;
|
||||||
|
//write header
|
||||||
|
os << "<?xml version=\"1.0\"?>\n"
|
||||||
|
<< "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"";
|
||||||
|
#ifdef CGAL_LITTLE_ENDIAN
|
||||||
|
os << " byte_order=\"LittleEndian\"";
|
||||||
|
#else // CGAL_BIG_ENDIAN
|
||||||
|
os << " byte_order=\"BigEndian\"";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch(sizeof(std::size_t)) {
|
||||||
|
case 4: os << " header_type=\"UInt32\""; break;
|
||||||
|
case 8: os << " header_type=\"UInt64\""; break;
|
||||||
|
default: CGAL_error_msg("Unknown size of std::size_t");
|
||||||
|
}
|
||||||
|
os << ">\n"
|
||||||
|
<< " <UnstructuredGrid>" << "\n";
|
||||||
|
|
||||||
|
int number_of_triangles = 0;
|
||||||
|
for(typename CDT::Finite_faces_iterator
|
||||||
|
fit = tr.finite_faces_begin(),
|
||||||
|
end = tr.finite_faces_end();
|
||||||
|
fit != end; ++fit)
|
||||||
|
{
|
||||||
|
if(fit->is_in_domain()) ++number_of_triangles;
|
||||||
|
}
|
||||||
|
os << " <Piece NumberOfPoints=\"" << tr.number_of_vertices()
|
||||||
|
<< "\" NumberOfCells=\"" << number_of_triangles + std::distance(tr.constrained_edges_begin(), tr.constrained_edges_end()) << "\">\n";
|
||||||
|
std::size_t offset = 0;
|
||||||
|
const bool binary = (mode == IO::BINARY);
|
||||||
|
write_cdt_points_tag(os,tr,V,binary,offset);
|
||||||
|
write_cells_tag_2(os,tr,number_of_triangles, V,binary,offset);
|
||||||
|
if(attributes.empty())
|
||||||
|
os << " <CellData >\n";
|
||||||
|
else
|
||||||
|
os << " <CellData Scalars=\""<<attributes.front().first<<"\">\n";
|
||||||
|
for(std::size_t i = 0; i< attributes.size(); ++i)
|
||||||
|
{
|
||||||
|
write_attribute_tag_2(os,attributes[i].first, *attributes[i].second, binary,offset);
|
||||||
|
}
|
||||||
|
os << " </CellData>\n";
|
||||||
|
os << " </Piece>\n"
|
||||||
|
<< " </UnstructuredGrid>\n";
|
||||||
|
if (binary) {
|
||||||
|
os << "<AppendedData encoding=\"raw\">\n_";
|
||||||
|
write_cdt_points(os,tr,V); // write points before cells to fill the std::map V
|
||||||
|
write_cells_2(os,tr, number_of_triangles, V);
|
||||||
|
for(std::size_t i = 0; i< attributes.size(); ++i)
|
||||||
|
write_attributes_2(os, *attributes[i].second);
|
||||||
|
}
|
||||||
|
os << "</VTKFile>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class CDT>
|
||||||
|
void write_vtu(std::ostream& os,
|
||||||
|
const CDT& tr,
|
||||||
|
IO::Mode mode = IO::BINARY)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<const char*, const std::vector<double>*> > dummy_atts;
|
||||||
|
write_vtu_with_attributes(os, tr, dummy_atts, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //end CGAL
|
||||||
|
#endif // CGAL_WRITE_VTU_H
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace CGAL{
|
||||||
|
//! \ingroup PkgMesh3IOFunctions
|
||||||
|
//!
|
||||||
|
//! \brief writes a tetrahedron mesh using the `UnstructuredGrid` XML format.
|
||||||
|
//!
|
||||||
|
//! \tparam C3T3 a model of `MeshComplexWithFeatures_3InTriangulation_3`.
|
||||||
|
//!
|
||||||
|
//! \param os the stream used for writing.
|
||||||
|
//! \param c3t3 the instance of `C3T3` to be written.
|
||||||
|
//! \param mode decides if the data should be written in binary (`IO::BINARY`)
|
||||||
|
//! or in ASCII (`IO::ASCII`).
|
||||||
|
//!
|
||||||
|
template <class C3T3>
|
||||||
|
void output_to_vtu(std::ostream& os,
|
||||||
|
const C3T3& c3t3,
|
||||||
|
IO::Mode mode = IO::BINARY);
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,19 @@
|
||||||
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
|
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
|
||||||
|
|
||||||
|
# macros to be used inside the code
|
||||||
|
ALIASES += "cgalNamedParamsBegin=<dl class=\"params\"><dt>Named Parameters</dt><dd> <table class=\"params\">"
|
||||||
|
ALIASES += "cgalNamedParamsEnd=</table> </dd> </dl>"
|
||||||
|
ALIASES += "cgalParamBegin{1}=<tr><td class=\"paramname\">\ref PMP_\1 \"\1\"</td><td>"
|
||||||
|
ALIASES += "cgalParamEnd=</td></tr>"
|
||||||
|
ALIASES += "cgalDescribePolylineType=A polyline is defined as a sequence of points, each pair of contiguous points defines a segment of the polyline. If the first and last points of the polyline are identical, the polyline is closed."
|
||||||
|
|
||||||
|
#macros for NamedParameters.txt
|
||||||
|
ALIASES += "cgalNPTableBegin=<dl class=\"params\"><dt></dt><dd> <table class=\"params\">"
|
||||||
|
ALIASES += "cgalNPTableEnd=</table> </dd> </dl>"
|
||||||
|
ALIASES += "cgalNPBegin{1}=<tr><td class=\"paramname\">\1 </td><td>"
|
||||||
|
ALIASES += "cgalNPEnd=</td></tr>"
|
||||||
|
|
||||||
|
|
||||||
INPUT += \
|
INPUT += \
|
||||||
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Polyhedral_complex_mesh_domain_3.h \
|
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Polyhedral_complex_mesh_domain_3.h \
|
||||||
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Mesh_domain_with_polyline_features_3.h
|
${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Mesh_domain_with_polyline_features_3.h
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,6 @@ and their associated classes:
|
||||||
|
|
||||||
## Input/Output Functions ##
|
## Input/Output Functions ##
|
||||||
- `CGAL::output_to_medit()`
|
- `CGAL::output_to_medit()`
|
||||||
|
- `CGAL::output_to_vtu()`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ output_c3t3_to_vtk_unstructured_grid(const C3T3& c3t3,
|
||||||
vtkCellArray* const vtk_facets = vtkCellArray::New();
|
vtkCellArray* const vtk_facets = vtkCellArray::New();
|
||||||
vtkCellArray* const vtk_cells = vtkCellArray::New();
|
vtkCellArray* const vtk_cells = vtkCellArray::New();
|
||||||
|
|
||||||
vtk_points->Allocate(c3t3.triangulation().number_of_vertices());
|
vtk_points->Allocate(c3t3.triangulation().number_of_vertices()- c3t3.number_of_far_points());
|
||||||
vtk_facets->Allocate(c3t3.number_of_facets_in_complex());
|
vtk_facets->Allocate(c3t3.number_of_facets_in_complex());
|
||||||
vtk_cells->Allocate(c3t3.number_of_cells_in_complex());
|
vtk_cells->Allocate(c3t3.number_of_cells_in_complex());
|
||||||
|
|
||||||
|
|
@ -64,11 +64,14 @@ output_c3t3_to_vtk_unstructured_grid(const C3T3& c3t3,
|
||||||
++vit)
|
++vit)
|
||||||
{
|
{
|
||||||
typedef typename Triangulation::Weighted_point Weighted_point;
|
typedef typename Triangulation::Weighted_point Weighted_point;
|
||||||
const Weighted_point& p = tr.point(vit);
|
if(vit->in_dimension() > -1)
|
||||||
vtk_points->InsertNextPoint(CGAL::to_double(p.x()),
|
{
|
||||||
CGAL::to_double(p.y()),
|
const Weighted_point& p = tr.point(vit);
|
||||||
CGAL::to_double(p.z()));
|
vtk_points->InsertNextPoint(CGAL::to_double(p.x()),
|
||||||
V[vit] = inum++;
|
CGAL::to_double(p.y()),
|
||||||
|
CGAL::to_double(p.z()));
|
||||||
|
V[vit] = inum++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for(typename C3T3::Facets_in_complex_iterator
|
for(typename C3T3::Facets_in_complex_iterator
|
||||||
fit = c3t3.facets_in_complex_begin(),
|
fit = c3t3.facets_in_complex_begin(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,336 @@
|
||||||
|
// Copyright (c) 2018 GeometryFactory (France).
|
||||||
|
// Copyright (c) 2004-2006 INRIA Sophia-Antipolis (France).
|
||||||
|
// Copyright (c) 2009 INRIA Sophia-Antipolis (France).
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This file is part of CGAL (www.cgal.org).
|
||||||
|
// You can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
// General Public License as published by the Free Software Foundation,
|
||||||
|
// either version 3 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Licensees holding a valid commercial license may use this file in
|
||||||
|
// accordance with the commercial license agreement provided with the software.
|
||||||
|
//
|
||||||
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||||
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
//
|
||||||
|
// $URL$
|
||||||
|
// $Id$
|
||||||
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
//
|
||||||
|
// Author(s) : Laurent RINEAU, Stephane Tayeb, Maxime Gimeno
|
||||||
|
|
||||||
|
#ifndef CGAL_OUTPUT_TO_VTU_H
|
||||||
|
#define CGAL_OUTPUT_TO_VTU_H
|
||||||
|
|
||||||
|
#include <CGAL/license/Mesh_3.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <CGAL/assertions.h>
|
||||||
|
#include <CGAL/IO/io.h>
|
||||||
|
#include <CGAL/IO/write_vtk.h>
|
||||||
|
|
||||||
|
//todo try to factorize with functors
|
||||||
|
namespace CGAL{
|
||||||
|
|
||||||
|
template <class C3T3>
|
||||||
|
void
|
||||||
|
write_cells_tag(std::ostream& os,
|
||||||
|
const C3T3 & c3t3,
|
||||||
|
std::map<typename C3T3::Triangulation::Vertex_handle, std::size_t> & V,
|
||||||
|
bool binary,
|
||||||
|
std::size_t& offset)
|
||||||
|
{
|
||||||
|
typedef typename C3T3::Cells_in_complex_iterator Cell_iterator;
|
||||||
|
std::string formatattribute =
|
||||||
|
binary ? " format=\"appended\"" : " format=\"ascii\"";
|
||||||
|
|
||||||
|
std::string typeattribute;
|
||||||
|
switch(sizeof(std::size_t)) {
|
||||||
|
case 8: typeattribute = " type=\"UInt64\""; break;
|
||||||
|
case 4: typeattribute = " type=\"UInt32\""; break;
|
||||||
|
default: CGAL_error_msg("Unknown size of std::size_t");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write connectivity table
|
||||||
|
os << " <Cells>\n"
|
||||||
|
<< " <DataArray Name=\"connectivity\""
|
||||||
|
<< formatattribute << typeattribute;
|
||||||
|
|
||||||
|
if (binary) { // if binary output, just write the xml tag
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += (4 * c3t3.number_of_cells() + 1) * sizeof(std::size_t);
|
||||||
|
// 4 indices (size_t) per cell + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for( Cell_iterator cit = c3t3.cells_in_complex_begin() ;
|
||||||
|
cit != c3t3.cells_in_complex_end() ;
|
||||||
|
++cit )
|
||||||
|
{
|
||||||
|
for (int i=0; i<4; i++)
|
||||||
|
os << V[cit->vertex(i)] << " ";
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write offsets
|
||||||
|
os << " <DataArray Name=\"offsets\""
|
||||||
|
<< formatattribute << typeattribute;
|
||||||
|
|
||||||
|
if (binary) { // if binary output, just write the xml tag
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += (c3t3.number_of_cells() + 1) * sizeof(std::size_t);
|
||||||
|
// 1 offset (size_t) per cell + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
std::size_t cells_offset = 0;
|
||||||
|
for( Cell_iterator cit = c3t3.cells_in_complex_begin() ;
|
||||||
|
cit != c3t3.cells_in_complex_end() ;
|
||||||
|
++cit )
|
||||||
|
{
|
||||||
|
cells_offset += 4;
|
||||||
|
os << cells_offset << " ";
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write cell type (tetrahedra == 10)
|
||||||
|
os << " <DataArray Name=\"types\""
|
||||||
|
<< formatattribute << " type=\"UInt8\"";
|
||||||
|
|
||||||
|
if (binary) {
|
||||||
|
os << " offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += c3t3.number_of_cells() + sizeof(std::size_t);
|
||||||
|
// 1 unsigned char per cell + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for( Cell_iterator cit = c3t3.cells_in_complex_begin() ;
|
||||||
|
cit != c3t3.cells_in_complex_end() ;
|
||||||
|
++cit )
|
||||||
|
os << "10 ";
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
os << " </Cells>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class C3T3>
|
||||||
|
void
|
||||||
|
write_cells(std::ostream& os,
|
||||||
|
const C3T3 & c3t3,
|
||||||
|
std::map<typename C3T3::Triangulation::Vertex_handle, std::size_t> & V)
|
||||||
|
{
|
||||||
|
typedef typename C3T3::Cells_in_complex_iterator Cell_iterator;
|
||||||
|
std::vector<std::size_t> connectivity_table;
|
||||||
|
std::vector<std::size_t> offsets;
|
||||||
|
std::vector<unsigned char> cell_type(c3t3.number_of_cells(),10); // tetrahedra == 10
|
||||||
|
std::size_t off = 0;
|
||||||
|
for( Cell_iterator cit = c3t3.cells_in_complex_begin() ;
|
||||||
|
cit != c3t3.cells_in_complex_end() ;
|
||||||
|
++cit )
|
||||||
|
{
|
||||||
|
off += 4;
|
||||||
|
offsets.push_back(off);
|
||||||
|
for (int i=0; i<4; i++)
|
||||||
|
connectivity_table.push_back(V[cit->vertex(i)]);
|
||||||
|
}
|
||||||
|
write_vector<std::size_t>(os,connectivity_table);
|
||||||
|
write_vector<std::size_t>(os,offsets);
|
||||||
|
write_vector<unsigned char>(os,cell_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class Tr>
|
||||||
|
void
|
||||||
|
write_c3t3_points_tag(std::ostream& os,
|
||||||
|
const Tr & tr,
|
||||||
|
std::size_t size_of_vertices,
|
||||||
|
std::map<typename Tr::Vertex_handle, std::size_t> & V,
|
||||||
|
bool binary,
|
||||||
|
std::size_t& offset)
|
||||||
|
{
|
||||||
|
std::size_t dim = 3;
|
||||||
|
typedef typename Tr::Finite_vertices_iterator Finite_vertices_iterator;
|
||||||
|
typedef typename Tr::Geom_traits Gt;
|
||||||
|
typedef typename Gt::FT FT;
|
||||||
|
|
||||||
|
std::size_t inum = 0;
|
||||||
|
std::string format = binary ? "appended" : "ascii";
|
||||||
|
std::string type = (sizeof(FT) == 8) ? "Float64" : "Float32";
|
||||||
|
|
||||||
|
os << " <Points>\n"
|
||||||
|
<< " <DataArray type =\"" << type << "\" NumberOfComponents=\"3\" format=\""
|
||||||
|
<< format;
|
||||||
|
|
||||||
|
if (binary) {
|
||||||
|
os << "\" offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += 3 * size_of_vertices * sizeof(FT) + sizeof(std::size_t);
|
||||||
|
// dim coords per points + length of the encoded data (size_t)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
os << "\">\n";
|
||||||
|
for( Finite_vertices_iterator vit = tr.finite_vertices_begin();
|
||||||
|
vit != tr.finite_vertices_end();
|
||||||
|
++vit)
|
||||||
|
{
|
||||||
|
if(vit->in_dimension() <= -1) continue;
|
||||||
|
V[vit] = inum++;
|
||||||
|
os << vit->point()[0] << " ";
|
||||||
|
os << vit->point()[1] << " ";
|
||||||
|
if(dim == 3)
|
||||||
|
os << vit->point()[2] << " ";
|
||||||
|
else
|
||||||
|
os << 0.0 << " ";
|
||||||
|
}
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
os << " </Points>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the points appended data at the end of the .vtu file
|
||||||
|
template <class Tr>
|
||||||
|
void
|
||||||
|
write_c3t3_points(std::ostream& os,
|
||||||
|
const Tr & tr,
|
||||||
|
std::map<typename Tr::Vertex_handle,
|
||||||
|
std::size_t> & V)
|
||||||
|
{
|
||||||
|
std::size_t dim = 3;
|
||||||
|
typedef typename Tr::Finite_vertices_iterator Finite_vertices_iterator;
|
||||||
|
typedef typename Tr::Geom_traits Gt;
|
||||||
|
typedef typename Gt::FT FT;
|
||||||
|
|
||||||
|
std::size_t inum = 0;
|
||||||
|
std::vector<FT> coordinates;
|
||||||
|
for( Finite_vertices_iterator vit = tr.finite_vertices_begin();
|
||||||
|
vit != tr.finite_vertices_end();
|
||||||
|
++vit)
|
||||||
|
{
|
||||||
|
if(vit->in_dimension() <= -1) continue;
|
||||||
|
V[vit] = inum++; // binary output => the map has not been filled yet
|
||||||
|
coordinates.push_back(vit->point()[0]);
|
||||||
|
coordinates.push_back(vit->point()[1]);
|
||||||
|
coordinates.push_back(dim == 3 ? vit->point()[2] : 0.0);
|
||||||
|
}
|
||||||
|
write_vector<FT>(os,coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the attribute tags before binary data is appended
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
write_attribute_tag(std::ostream& os,
|
||||||
|
const std::string& attr_name,
|
||||||
|
const std::vector<T>& attribute,
|
||||||
|
bool binary,
|
||||||
|
std::size_t& offset)
|
||||||
|
{
|
||||||
|
std::string format = binary ? "appended" : "ascii";
|
||||||
|
std::string type = (sizeof(T) == 8) ? "Float64" : "Float32";
|
||||||
|
os << " <DataArray type=\"" << type << "\" Name=\"" << attr_name << "\" format=\"" << format;
|
||||||
|
|
||||||
|
if (binary) {
|
||||||
|
os << "\" offset=\"" << offset << "\"/>\n";
|
||||||
|
offset += attribute.size() * sizeof(T) + sizeof(std::size_t);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
typedef typename std::vector<T>::const_iterator Iterator;
|
||||||
|
os << "\">\n";
|
||||||
|
for (Iterator it = attribute.begin();
|
||||||
|
it != attribute.end();
|
||||||
|
++it )
|
||||||
|
os << *it << " ";
|
||||||
|
os << " </DataArray>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes the attributes appended data at the end of the .vtu file
|
||||||
|
template <typename FT>
|
||||||
|
void
|
||||||
|
write_attributes(std::ostream& os,
|
||||||
|
const std::vector<FT>& att)
|
||||||
|
{
|
||||||
|
write_vector(os,att);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class C3T3>
|
||||||
|
void output_to_vtu_with_attributes(std::ostream& os,
|
||||||
|
const C3T3& c3t3,
|
||||||
|
std::vector<std::pair<const char*, const std::vector<double>*> >& attributes,
|
||||||
|
IO::Mode mode = IO::BINARY)
|
||||||
|
{
|
||||||
|
typedef typename C3T3::Triangulation Tr;
|
||||||
|
typedef typename Tr::Vertex_handle Vertex_handle;
|
||||||
|
const Tr& tr = c3t3.triangulation();
|
||||||
|
std::map<Vertex_handle, std::size_t> V;
|
||||||
|
//write header
|
||||||
|
os << "<?xml version=\"1.0\"?>\n"
|
||||||
|
<< "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"";
|
||||||
|
#ifdef CGAL_LITTLE_ENDIAN
|
||||||
|
os << " byte_order=\"LittleEndian\"";
|
||||||
|
#else // CGAL_BIG_ENDIAN
|
||||||
|
os << " byte_order=\"BigEndian\"";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch(sizeof(std::size_t)) {
|
||||||
|
case 4: os << " header_type=\"UInt32\""; break;
|
||||||
|
case 8: os << " header_type=\"UInt64\""; break;
|
||||||
|
default: CGAL_error_msg("Unknown size of std::size_t");
|
||||||
|
}
|
||||||
|
os << ">\n"
|
||||||
|
<< " <UnstructuredGrid>" << "\n";
|
||||||
|
const std::size_t number_of_vertices =
|
||||||
|
tr.number_of_vertices() - c3t3.number_of_far_points();
|
||||||
|
os << " <Piece NumberOfPoints=\"" << number_of_vertices
|
||||||
|
<< "\" NumberOfCells=\"" << c3t3.number_of_cells() << "\">\n";
|
||||||
|
std::size_t offset = 0;
|
||||||
|
|
||||||
|
const bool binary = (mode == IO::BINARY);
|
||||||
|
write_c3t3_points_tag(os,tr,number_of_vertices,V,binary,offset);
|
||||||
|
write_cells_tag(os,c3t3,V,binary,offset); // fills V if the mode is ASCII
|
||||||
|
os << " <CellData Scalars=\""<<attributes.front().first<<"\">\n";
|
||||||
|
for(std::size_t i = 0; i< attributes.size(); ++i)
|
||||||
|
{
|
||||||
|
write_attribute_tag(os,attributes[i].first, *attributes[i].second, binary,offset);
|
||||||
|
}
|
||||||
|
os << " </CellData>\n";
|
||||||
|
os << " </Piece>\n"
|
||||||
|
<< " </UnstructuredGrid>\n";
|
||||||
|
if (binary) {
|
||||||
|
os << "<AppendedData encoding=\"raw\">\n_";
|
||||||
|
write_c3t3_points(os,tr,V); // fills V if the mode is BINARY
|
||||||
|
write_cells(os,c3t3,V);
|
||||||
|
for(std::size_t i = 0; i< attributes.size(); ++i)
|
||||||
|
write_attributes(os, *attributes[i].second);
|
||||||
|
}
|
||||||
|
os << "</VTKFile>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//public API
|
||||||
|
template <class C3T3>
|
||||||
|
void output_to_vtu(std::ostream& os,
|
||||||
|
const C3T3& c3t3,
|
||||||
|
IO::Mode mode = IO::BINARY)
|
||||||
|
{
|
||||||
|
typedef typename C3T3::Cells_in_complex_iterator Cell_iterator;
|
||||||
|
std::vector<double> mids;
|
||||||
|
for( Cell_iterator cit = c3t3.cells_in_complex_begin() ;
|
||||||
|
cit != c3t3.cells_in_complex_end() ;
|
||||||
|
++cit )
|
||||||
|
{
|
||||||
|
mids.push_back(cit->subdomain_index());
|
||||||
|
}
|
||||||
|
std::vector<std::pair<const char*, const std::vector<double>* > > atts;
|
||||||
|
atts.push_back(std::make_pair("MeshDomain", &mids));
|
||||||
|
output_to_vtu_with_attributes(os, c3t3, atts, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //end CGAL
|
||||||
|
#endif // CGAL_VTK_IO_H
|
||||||
|
|
@ -389,7 +389,9 @@ public:
|
||||||
hit->reset_fcit();
|
hit->reset_fcit();
|
||||||
for (Isolated_vertex_iterator vit = iv_begin(); vit!=iv_end(); ++vit)
|
for (Isolated_vertex_iterator vit = iv_begin(); vit!=iv_end(); ++vit)
|
||||||
vit->reset_ivit();
|
vit->reset_ivit();
|
||||||
FC.clear(); IV.clear(); }
|
FC.clear(); IV.clear();
|
||||||
|
_e=Halfedge_handle();
|
||||||
|
}
|
||||||
|
|
||||||
/*{\Mtext There are the same iterator ranges defined for the const
|
/*{\Mtext There are the same iterator ranges defined for the const
|
||||||
iterators |Hole_const_iterator|, |Isolated_vertex_const_iterator|.
|
iterators |Hole_const_iterator|, |Isolated_vertex_const_iterator|.
|
||||||
|
|
|
||||||
|
|
@ -473,7 +473,7 @@ check_integrity_and_topological_planarity(bool faces) const
|
||||||
int fc_num(0),iv_num(0);
|
int fc_num(0),iv_num(0);
|
||||||
Face_const_iterator fit;
|
Face_const_iterator fit;
|
||||||
for (fit = faces_begin(); fit != faces_end(); ++fit) {
|
for (fit = faces_begin(); fit != faces_end(); ++fit) {
|
||||||
if (!first) {
|
if (!first && halfedge(fit)!=Halfedge_const_handle()) {
|
||||||
CGAL_assertion( face(halfedge(fit))==fit ); ++fc_num;
|
CGAL_assertion( face(halfedge(fit))==fit ); ++fc_num;
|
||||||
}
|
}
|
||||||
Hole_const_iterator fcit;
|
Hole_const_iterator fcit;
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@
|
||||||
|
|
||||||
# include <CGAL/enable_warnings.h>
|
# include <CGAL/enable_warnings.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CGAL_USE_MPFR
|
||||||
|
# include <mpfr.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO: work on the coercions (end of the file)
|
// TODO: work on the coercions (end of the file)
|
||||||
|
|
||||||
|
|
@ -136,6 +139,13 @@ struct AST_boost_mp <NT, boost::mpl::int_<boost::multiprecision::number_kind_rat
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
||||||
|
struct Algebraic_structure_traits<boost::multiprecision::number<Backend, Eto> >
|
||||||
|
: AST_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
||||||
|
template <class T1,class T2,class T3,class T4,class T5>
|
||||||
|
struct Algebraic_structure_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
||||||
|
: Algebraic_structure_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
||||||
|
|
||||||
// Real_embeddable_traits
|
// Real_embeddable_traits
|
||||||
|
|
||||||
template <class NT>
|
template <class NT>
|
||||||
|
|
@ -193,11 +203,22 @@ struct RET_boost_mp_base
|
||||||
: public CGAL::cpp98::unary_function< Type, std::pair< double, double > > {
|
: public CGAL::cpp98::unary_function< Type, std::pair< double, double > > {
|
||||||
std::pair<double, double>
|
std::pair<double, double>
|
||||||
operator()(const Type& x) const {
|
operator()(const Type& x) const {
|
||||||
|
// See if https://github.com/boostorg/multiprecision/issues/108 suggests anything better
|
||||||
// assume the conversion is within 1 ulp
|
// assume the conversion is within 1 ulp
|
||||||
// adding IA::smallest() doesn't work because inf-e=inf, even rounded down.
|
// adding IA::smallest() doesn't work because inf-e=inf, even rounded down.
|
||||||
double d = x.template convert_to<double>();
|
double i = x.template convert_to<double>();
|
||||||
|
double s = i;
|
||||||
double inf = std::numeric_limits<double>::infinity();
|
double inf = std::numeric_limits<double>::infinity();
|
||||||
return std::pair<double, double> (nextafter (d, -inf), nextafter (d, inf));
|
int cmp = x.compare(i);
|
||||||
|
if (cmp > 0) {
|
||||||
|
s = nextafter(s, +inf);
|
||||||
|
CGAL_assertion(x.compare(s) < 0);
|
||||||
|
}
|
||||||
|
else if (cmp < 0) {
|
||||||
|
i = nextafter(i, -inf);
|
||||||
|
CGAL_assertion(x.compare(i) > 0);
|
||||||
|
}
|
||||||
|
return std::pair<double, double> (i, s);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -211,26 +232,93 @@ struct RET_boost_mp <NT, boost::mpl::int_<boost::multiprecision::number_kind_int
|
||||||
|
|
||||||
template <class NT>
|
template <class NT>
|
||||||
struct RET_boost_mp <NT, boost::mpl::int_<boost::multiprecision::number_kind_rational> >
|
struct RET_boost_mp <NT, boost::mpl::int_<boost::multiprecision::number_kind_rational> >
|
||||||
: RET_boost_mp_base <NT> {
|
: RET_boost_mp_base <NT> {};
|
||||||
#if BOOST_VERSION < 105700
|
|
||||||
typedef NT Type;
|
#ifdef CGAL_USE_MPFR
|
||||||
|
// Because of these full specializations, things get instantiated more eagerly. Make them artificially partial if necessary.
|
||||||
|
template <>
|
||||||
|
struct RET_boost_mp <boost::multiprecision::mpz_int>
|
||||||
|
: RET_boost_mp_base <boost::multiprecision::mpz_int> {
|
||||||
|
typedef boost::multiprecision::mpz_int Type;
|
||||||
struct To_interval
|
struct To_interval
|
||||||
: public CGAL::cpp98::unary_function< Type, std::pair< double, double > > {
|
: public CGAL::cpp98::unary_function< Type, std::pair< double, double > > {
|
||||||
std::pair<double, double>
|
std::pair<double, double>
|
||||||
operator()(const Type& x) const {
|
operator()(const Type& x) const {
|
||||||
std::pair<double,double> p_num = CGAL::to_interval (numerator (x));
|
#if MPFR_VERSION_MAJOR >= 3
|
||||||
std::pair<double,double> p_den = CGAL::to_interval (denominator (x));
|
MPFR_DECL_INIT (y, 53); /* Assume IEEE-754 */
|
||||||
typedef Interval_nt<false> IA;
|
int r = mpfr_set_z (y, x.backend().data(), MPFR_RNDA);
|
||||||
IA::Protector P;
|
double i = mpfr_get_d (y, MPFR_RNDA); /* EXACT but can overflow */
|
||||||
// assume the conversion is within 1 ulp for integers, but the conversion from rational may be unsafe, see boost trac #10085
|
if (r == 0 && is_finite (i))
|
||||||
IA i_num (p_num.first, p_num.second);
|
return std::pair<double, double>(i, i);
|
||||||
IA i_den (p_den.first, p_den.second);
|
else
|
||||||
IA i = i_num / i_den;
|
{
|
||||||
return std::pair<double, double>(i.inf(), i.sup());
|
double s = nextafter (i, 0);
|
||||||
|
if (i < 0)
|
||||||
|
return std::pair<double, double>(i, s);
|
||||||
|
else
|
||||||
|
return std::pair<double, double>(s, i);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
mpfr_t y;
|
||||||
|
mpfr_init2 (y, 53); /* Assume IEEE-754 */
|
||||||
|
mpfr_set_z (y, x.backend().data(), GMP_RNDD);
|
||||||
|
double i = mpfr_get_d (y, GMP_RNDD); /* EXACT but can overflow */
|
||||||
|
mpfr_set_z (y, x.backend().data(), GMP_RNDU);
|
||||||
|
double s = mpfr_get_d (y, GMP_RNDU); /* EXACT but can overflow */
|
||||||
|
mpfr_clear (y);
|
||||||
|
return std::pair<double, double>(i, s);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
template <>
|
||||||
|
struct RET_boost_mp <boost::multiprecision::mpq_rational>
|
||||||
|
: RET_boost_mp_base <boost::multiprecision::mpq_rational> {
|
||||||
|
typedef boost::multiprecision::mpq_rational Type;
|
||||||
|
struct To_interval
|
||||||
|
: public CGAL::cpp98::unary_function< Type, std::pair< double, double > > {
|
||||||
|
std::pair<double, double>
|
||||||
|
operator()(const Type& x) const {
|
||||||
|
# if MPFR_VERSION_MAJOR >= 3
|
||||||
|
mpfr_exp_t emin = mpfr_get_emin();
|
||||||
|
mpfr_set_emin(-1073);
|
||||||
|
MPFR_DECL_INIT (y, 53); /* Assume IEEE-754 */
|
||||||
|
int r = mpfr_set_q (y, x.backend().data(), MPFR_RNDA);
|
||||||
|
r = mpfr_subnormalize (y, r, MPFR_RNDA); /* Round subnormals */
|
||||||
|
double i = mpfr_get_d (y, MPFR_RNDA); /* EXACT but can overflow */
|
||||||
|
mpfr_set_emin(emin); /* Restore old value, users may care */
|
||||||
|
// With mpfr_set_emax(1024) we could drop the is_finite test
|
||||||
|
if (r == 0 && is_finite (i))
|
||||||
|
return std::pair<double, double>(i, i);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double s = nextafter (i, 0);
|
||||||
|
if (i < 0)
|
||||||
|
return std::pair<double, double>(i, s);
|
||||||
|
else
|
||||||
|
return std::pair<double, double>(s, i);
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
mpfr_t y;
|
||||||
|
mpfr_init2 (y, 53); /* Assume IEEE-754 */
|
||||||
|
mpfr_set_q (y, x.backend().data(), GMP_RNDD);
|
||||||
|
double i = mpfr_get_d (y, GMP_RNDD); /* EXACT but can overflow */
|
||||||
|
mpfr_set_q (y, x.backend().data(), GMP_RNDU);
|
||||||
|
double s = mpfr_get_d (y, GMP_RNDU); /* EXACT but can overflow */
|
||||||
|
mpfr_clear (y);
|
||||||
|
return std::pair<double, double>(i, s);
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
||||||
|
struct Real_embeddable_traits<boost::multiprecision::number<Backend, Eto> >
|
||||||
|
: RET_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
||||||
|
template <class T1,class T2,class T3,class T4,class T5>
|
||||||
|
struct Real_embeddable_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
||||||
|
: Real_embeddable_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
||||||
|
|
||||||
// Modular_traits
|
// Modular_traits
|
||||||
|
|
||||||
|
|
@ -262,6 +350,13 @@ struct MT_boost_mp <T, boost::mpl::int_<boost::multiprecision::number_kind_integ
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
||||||
|
struct Modular_traits<boost::multiprecision::number<Backend, Eto> >
|
||||||
|
: MT_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
||||||
|
template <class T1,class T2,class T3,class T4,class T5>
|
||||||
|
struct Modular_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
||||||
|
: Modular_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
||||||
|
|
||||||
// Split_double
|
// Split_double
|
||||||
|
|
||||||
template <class NT, class = boost::mpl::int_<boost::multiprecision::number_category<NT>::value> >
|
template <class NT, class = boost::mpl::int_<boost::multiprecision::number_category<NT>::value> >
|
||||||
|
|
@ -284,6 +379,13 @@ struct SD_boost_mp <NT, boost::mpl::int_<boost::multiprecision::number_kind_inte
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
||||||
|
struct Split_double<boost::multiprecision::number<Backend, Eto> >
|
||||||
|
: SD_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
||||||
|
template <class T1,class T2,class T3,class T4,class T5>
|
||||||
|
struct Split_double<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
||||||
|
: Split_double<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
||||||
|
|
||||||
|
|
||||||
// Fraction_traits
|
// Fraction_traits
|
||||||
|
|
||||||
|
|
@ -335,27 +437,6 @@ struct FT_boost_mp <NT, boost::mpl::int_<boost::multiprecision::number_kind_rati
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
|
||||||
struct Algebraic_structure_traits<boost::multiprecision::number<Backend, Eto> >
|
|
||||||
: AST_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
|
||||||
template <class T1,class T2,class T3,class T4,class T5>
|
|
||||||
struct Algebraic_structure_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
|
||||||
: Algebraic_structure_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
|
||||||
|
|
||||||
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
|
||||||
struct Real_embeddable_traits<boost::multiprecision::number<Backend, Eto> >
|
|
||||||
: RET_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
|
||||||
template <class T1,class T2,class T3,class T4,class T5>
|
|
||||||
struct Real_embeddable_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
|
||||||
: Real_embeddable_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
|
||||||
|
|
||||||
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
|
||||||
struct Modular_traits<boost::multiprecision::number<Backend, Eto> >
|
|
||||||
: MT_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
|
||||||
template <class T1,class T2,class T3,class T4,class T5>
|
|
||||||
struct Modular_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
|
||||||
: Modular_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
|
||||||
|
|
||||||
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
||||||
struct Fraction_traits<boost::multiprecision::number<Backend, Eto> >
|
struct Fraction_traits<boost::multiprecision::number<Backend, Eto> >
|
||||||
: FT_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
: FT_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
||||||
|
|
@ -363,13 +444,6 @@ template <class T1,class T2,class T3,class T4,class T5>
|
||||||
struct Fraction_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
struct Fraction_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
||||||
: Fraction_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
: Fraction_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
||||||
|
|
||||||
template <class Backend, boost::multiprecision::expression_template_option Eto>
|
|
||||||
struct Split_double<boost::multiprecision::number<Backend, Eto> >
|
|
||||||
: SD_boost_mp <boost::multiprecision::number<Backend, Eto> > {};
|
|
||||||
template <class T1,class T2,class T3,class T4,class T5>
|
|
||||||
struct Split_double<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> >
|
|
||||||
: Split_double<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type > {};
|
|
||||||
|
|
||||||
|
|
||||||
// Coercions
|
// Coercions
|
||||||
|
|
||||||
|
|
@ -440,7 +514,7 @@ typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type,
|
||||||
boost::multiprecision::number<B, E> >
|
boost::multiprecision::number<B, E> >
|
||||||
{ };
|
{ };
|
||||||
|
|
||||||
// TODO: coercion with expressions, fix existing coercions
|
// TODO: fix existing coercions
|
||||||
// (double -> rational is implicit only for 1.56+, see ticket #10082)
|
// (double -> rational is implicit only for 1.56+, see ticket #10082)
|
||||||
// The real solution would be to avoid specializing Coercion_traits for all pairs of number types and let it auto-detect what works, so only broken types need an explicit specialization.
|
// The real solution would be to avoid specializing Coercion_traits for all pairs of number types and let it auto-detect what works, so only broken types need an explicit specialization.
|
||||||
|
|
||||||
|
|
@ -458,7 +532,13 @@ struct Coercion_traits<boost::multiprecision::number<B1, E1>, int> { \
|
||||||
}; \
|
}; \
|
||||||
template <class B1, boost::multiprecision::expression_template_option E1> \
|
template <class B1, boost::multiprecision::expression_template_option E1> \
|
||||||
struct Coercion_traits<int, boost::multiprecision::number<B1, E1> > \
|
struct Coercion_traits<int, boost::multiprecision::number<B1, E1> > \
|
||||||
: Coercion_traits<boost::multiprecision::number<B1, E1>, int> {}
|
: Coercion_traits<boost::multiprecision::number<B1, E1>, int> {}; \
|
||||||
|
template <class T1, class T2, class T3, class T4, class T5> \
|
||||||
|
struct Coercion_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>, int> \
|
||||||
|
: Coercion_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type, int>{}; \
|
||||||
|
template <class T1, class T2, class T3, class T4, class T5> \
|
||||||
|
struct Coercion_traits<int, boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> > \
|
||||||
|
: Coercion_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type, int>{}
|
||||||
|
|
||||||
CGAL_COERCE_INT(short);
|
CGAL_COERCE_INT(short);
|
||||||
CGAL_COERCE_INT(int);
|
CGAL_COERCE_INT(int);
|
||||||
|
|
@ -479,7 +559,13 @@ struct Coercion_traits<boost::multiprecision::number<B1, E1>, float> { \
|
||||||
}; \
|
}; \
|
||||||
template <class B1, boost::multiprecision::expression_template_option E1> \
|
template <class B1, boost::multiprecision::expression_template_option E1> \
|
||||||
struct Coercion_traits<float, boost::multiprecision::number<B1, E1> > \
|
struct Coercion_traits<float, boost::multiprecision::number<B1, E1> > \
|
||||||
: Coercion_traits<boost::multiprecision::number<B1, E1>, float> {}
|
: Coercion_traits<boost::multiprecision::number<B1, E1>, float> {}; \
|
||||||
|
template <class T1, class T2, class T3, class T4, class T5> \
|
||||||
|
struct Coercion_traits<boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>, float> \
|
||||||
|
: Coercion_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type, float>{}; \
|
||||||
|
template <class T1, class T2, class T3, class T4, class T5> \
|
||||||
|
struct Coercion_traits<float, boost::multiprecision::detail::expression<T1,T2,T3,T4,T5> > \
|
||||||
|
: Coercion_traits<typename boost::multiprecision::detail::expression<T1,T2,T3,T4,T5>::result_type, float>{}
|
||||||
|
|
||||||
CGAL_COERCE_FLOAT(float);
|
CGAL_COERCE_FLOAT(float);
|
||||||
CGAL_COERCE_FLOAT(double);
|
CGAL_COERCE_FLOAT(double);
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,10 @@ struct Exact_field_selector
|
||||||
# endif
|
# endif
|
||||||
#elif defined(CGAL_USE_LEDA)
|
#elif defined(CGAL_USE_LEDA)
|
||||||
{ typedef leda_rational Type; };
|
{ typedef leda_rational Type; };
|
||||||
#elif defined(CGAL_USE_BOOST_MP)
|
#elif 0 && defined(CGAL_USE_BOOST_MP)
|
||||||
{ typedef boost::multiprecision::cpp_rational Type; };
|
// See the discussion in https://github.com/CGAL/cgal/pull/3614
|
||||||
|
// This is disabled for now because cpp_rational is even slower than Quotient<MP_Float>. Quotient<cpp_int> will be a good candidate after some polishing.
|
||||||
|
{ typedef BOOST_cpp_arithmetic_kernel::Rational Type; };
|
||||||
#else
|
#else
|
||||||
{ typedef Quotient<MP_Float> Type; };
|
{ typedef Quotient<MP_Float> Type; };
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <CGAL/Lazy_exact_nt.h>
|
#include <CGAL/Lazy_exact_nt.h>
|
||||||
#include <CGAL/Interval_nt.h>
|
#include <CGAL/Interval_nt.h>
|
||||||
#include <CGAL/Sqrt_extension.h>
|
#include <CGAL/Sqrt_extension.h>
|
||||||
|
#include <CGAL/boost_mp.h>
|
||||||
|
|
||||||
#ifdef CGAL_USE_GMP
|
#ifdef CGAL_USE_GMP
|
||||||
#include <CGAL/Gmpz.h>
|
#include <CGAL/Gmpz.h>
|
||||||
|
|
@ -82,6 +83,12 @@ int main()
|
||||||
TESTIT(CGAL::Lazy_exact_nt<QMPF>, "Lazy_exact_nt<Quotient<MP_Float> >")
|
TESTIT(CGAL::Lazy_exact_nt<QMPF>, "Lazy_exact_nt<Quotient<MP_Float> >")
|
||||||
TESTIT(CGAL::Interval_nt<>, "Interval_nt<>")
|
TESTIT(CGAL::Interval_nt<>, "Interval_nt<>")
|
||||||
|
|
||||||
|
// Boost.Multiprecision
|
||||||
|
#ifdef CGAL_USE_BOOST_MP
|
||||||
|
TESTIT(boost::multiprecision::cpp_int, "cpp_int")
|
||||||
|
TESTIT(boost::multiprecision::cpp_rational, "cpp_rational")
|
||||||
|
#endif
|
||||||
|
|
||||||
// GMP based NTs
|
// GMP based NTs
|
||||||
#ifdef CGAL_USE_GMP
|
#ifdef CGAL_USE_GMP
|
||||||
TESTIT(CGAL::Gmpz, "Gmpz")
|
TESTIT(CGAL::Gmpz, "Gmpz")
|
||||||
|
|
@ -90,6 +97,10 @@ int main()
|
||||||
TESTIT(CGAL::Mpzf, "Mpzf")
|
TESTIT(CGAL::Mpzf, "Mpzf")
|
||||||
# endif
|
# endif
|
||||||
TESTIT(CGAL::Gmpq, "Gmpq")
|
TESTIT(CGAL::Gmpq, "Gmpq")
|
||||||
|
# ifdef CGAL_USE_BOOST_MP
|
||||||
|
TESTIT(boost::multiprecision::mpz_int, "mpz_int")
|
||||||
|
TESTIT(boost::multiprecision::mpq_rational, "mpq_rational")
|
||||||
|
# endif
|
||||||
#endif // CGAL_USE_GMP
|
#endif // CGAL_USE_GMP
|
||||||
#ifdef CGAL_USE_GMPXX
|
#ifdef CGAL_USE_GMPXX
|
||||||
TESTIT(mpz_class, "mpz_class")
|
TESTIT(mpz_class, "mpz_class")
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,21 @@
|
||||||
|
|
||||||
#include <CGAL/Periodic_2_Delaunay_triangulation_2.h>
|
#include <CGAL/Periodic_2_Delaunay_triangulation_2.h>
|
||||||
#include <CGAL/Periodic_2_Delaunay_triangulation_traits_2.h>
|
#include <CGAL/Periodic_2_Delaunay_triangulation_traits_2.h>
|
||||||
|
#include <CGAL/Periodic_2_triangulation_face_base_2.h>
|
||||||
|
#include <CGAL/Periodic_2_triangulation_vertex_base_2.h>
|
||||||
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
||||||
typedef CGAL::Periodic_2_Delaunay_triangulation_traits_2<K> Gt;
|
typedef CGAL::Periodic_2_Delaunay_triangulation_traits_2<K> Gt;
|
||||||
typedef CGAL::Triangulation_vertex_base_with_info_2<unsigned, Gt> Vb;
|
typedef CGAL::Periodic_2_triangulation_vertex_base_2<Gt> Vbb;
|
||||||
typedef CGAL::Periodic_2_triangulation_face_base_2<Gt> Fb;
|
typedef CGAL::Triangulation_vertex_base_with_info_2<unsigned, Gt, Vbb> Vb;
|
||||||
typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
|
typedef CGAL::Periodic_2_triangulation_face_base_2<Gt> Fb;
|
||||||
typedef CGAL::Periodic_2_Delaunay_triangulation_2<Gt, Tds> Delaunay;
|
typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
|
||||||
typedef Delaunay::Point Point;
|
typedef CGAL::Periodic_2_Delaunay_triangulation_2<Gt, Tds> Delaunay;
|
||||||
|
typedef Delaunay::Point Point;
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,21 @@
|
||||||
|
|
||||||
#include <CGAL/Periodic_2_Delaunay_triangulation_2.h>
|
#include <CGAL/Periodic_2_Delaunay_triangulation_2.h>
|
||||||
#include <CGAL/Periodic_2_Delaunay_triangulation_traits_2.h>
|
#include <CGAL/Periodic_2_Delaunay_triangulation_traits_2.h>
|
||||||
|
#include <CGAL/Periodic_2_triangulation_face_base_2.h>
|
||||||
|
#include <CGAL/Periodic_2_triangulation_vertex_base_2.h>
|
||||||
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
||||||
typedef CGAL::Periodic_2_Delaunay_triangulation_traits_2<K> Gt;
|
typedef CGAL::Periodic_2_Delaunay_triangulation_traits_2<K> Gt;
|
||||||
typedef CGAL::Triangulation_vertex_base_with_info_2<unsigned, Gt> Vb;
|
typedef CGAL::Periodic_2_triangulation_vertex_base_2<Gt> Vbb;
|
||||||
typedef CGAL::Periodic_2_triangulation_face_base_2<Gt> Fb;
|
typedef CGAL::Triangulation_vertex_base_with_info_2<unsigned, Gt, Vbb> Vb;
|
||||||
typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
|
typedef CGAL::Periodic_2_triangulation_face_base_2<Gt> Fb;
|
||||||
typedef CGAL::Periodic_2_Delaunay_triangulation_2<Gt, Tds> Delaunay;
|
typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
|
||||||
typedef Delaunay::Point Point;
|
typedef CGAL::Periodic_2_Delaunay_triangulation_2<Gt, Tds> Delaunay;
|
||||||
|
typedef Delaunay::Point Point;
|
||||||
|
|
||||||
//a functor that returns a std::pair<Point,unsigned>.
|
//a functor that returns a std::pair<Point,unsigned>.
|
||||||
//the unsigned integer is incremented at each call to
|
//the unsigned integer is incremented at each call to
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,21 @@
|
||||||
|
|
||||||
#include <CGAL/Periodic_2_Delaunay_triangulation_traits_2.h>
|
#include <CGAL/Periodic_2_Delaunay_triangulation_traits_2.h>
|
||||||
#include <CGAL/Periodic_2_Delaunay_triangulation_2.h>
|
#include <CGAL/Periodic_2_Delaunay_triangulation_2.h>
|
||||||
|
#include <CGAL/Periodic_2_triangulation_face_base_2.h>
|
||||||
|
#include <CGAL/Periodic_2_triangulation_vertex_base_2.h>
|
||||||
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
||||||
typedef CGAL::Periodic_2_Delaunay_triangulation_traits_2<K> Gt;
|
typedef CGAL::Periodic_2_Delaunay_triangulation_traits_2<K> Gt;
|
||||||
typedef CGAL::Triangulation_vertex_base_with_info_2<unsigned, Gt> Vb;
|
typedef CGAL::Periodic_2_triangulation_vertex_base_2<Gt> Vbb;
|
||||||
typedef CGAL::Periodic_2_triangulation_face_base_2<Gt> Fb;
|
typedef CGAL::Triangulation_vertex_base_with_info_2<unsigned, Gt, Vbb> Vb;
|
||||||
typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
|
typedef CGAL::Periodic_2_triangulation_face_base_2<Gt> Fb;
|
||||||
typedef CGAL::Periodic_2_Delaunay_triangulation_2<Gt, Tds> Delaunay;
|
typedef CGAL::Triangulation_data_structure_2<Vb, Fb> Tds;
|
||||||
typedef Delaunay::Point Point;
|
typedef CGAL::Periodic_2_Delaunay_triangulation_2<Gt, Tds> Delaunay;
|
||||||
|
typedef Delaunay::Point Point;
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,16 @@ project( Periodic_3_mesh_3_Examples )
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
|
||||||
# CGAL and its components
|
if(POLICY CMP0074)
|
||||||
find_package( CGAL QUIET )
|
cmake_policy(SET CMP0074 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
if ( NOT CGAL_FOUND )
|
# CGAL and its components
|
||||||
|
find_package( CGAL QUIET COMPONENTS ImageIO )
|
||||||
|
|
||||||
|
if ( CGAL_FOUND )
|
||||||
|
include( ${CGAL_USE_FILE} )
|
||||||
|
else()
|
||||||
message(STATUS "This project requires the CGAL library, and will not be compiled.")
|
message(STATUS "This project requires the CGAL library, and will not be compiled.")
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,14 @@ project( Periodic_3_mesh_3_Tests )
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
|
||||||
find_package( CGAL QUIET )
|
if(POLICY CMP0074)
|
||||||
|
cmake_policy(SET CMP0074 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package( CGAL QUIET COMPONENTS ImageIO )
|
||||||
|
|
||||||
if ( CGAL_FOUND )
|
if ( CGAL_FOUND )
|
||||||
|
include( ${CGAL_USE_FILE} )
|
||||||
|
|
||||||
# Use Eigen
|
# Use Eigen
|
||||||
find_package(Eigen3 3.1.0 REQUIRED) #(3.1.0 or greater)
|
find_package(Eigen3 3.1.0 REQUIRED) #(3.1.0 or greater)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,13 @@ if(Qt5_FOUND)
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
endif(Qt5_FOUND)
|
endif(Qt5_FOUND)
|
||||||
|
|
||||||
if (CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND AND TARGET Qt5::qcollectiongenerator)
|
if( Qt5Help_VERSION VERSION_LESS 5.12 )
|
||||||
|
set(CGAL_QCOLLECTIONGENERATOR_TARGET Qt5::qcollectiongenerator)
|
||||||
|
else()
|
||||||
|
set(CGAL_QCOLLECTIONGENERATOR_TARGET Qt5::qhelpgenerator)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND AND TARGET ${CGAL_QCOLLECTIONGENERATOR_TARGET})
|
||||||
|
|
||||||
# UI files (Qt Designer files)
|
# UI files (Qt Designer files)
|
||||||
qt5_wrap_ui ( UI_FILES MainWindow.ui )
|
qt5_wrap_ui ( UI_FILES MainWindow.ui )
|
||||||
|
|
@ -39,15 +45,10 @@ if (CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND AND TARGET Qt5::qcollectiongener
|
||||||
qt5_generate_moc( "MainWindow.h" "${CMAKE_CURRENT_BINARY_DIR}/moc_MainWindow.cpp" )
|
qt5_generate_moc( "MainWindow.h" "${CMAKE_CURRENT_BINARY_DIR}/moc_MainWindow.cpp" )
|
||||||
qt5_generate_moc( "Viewer.h" "${CMAKE_CURRENT_BINARY_DIR}/moc_Viewer.cpp" )
|
qt5_generate_moc( "Viewer.h" "${CMAKE_CURRENT_BINARY_DIR}/moc_Viewer.cpp" )
|
||||||
|
|
||||||
if(DEFINED QT_QCOLLECTIONGENERATOR_EXECUTABLE)
|
|
||||||
else()
|
|
||||||
set(QT_QCOLLECTIONGENERATOR_EXECUTABLE qcollectiongenerator)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# generate QtAssistant collection file
|
# generate QtAssistant collection file
|
||||||
add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Periodic_3_triangulation_3.qhc
|
add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Periodic_3_triangulation_3.qhc
|
||||||
DEPENDS Periodic_3_triangulation_3.qhp Periodic_3_triangulation_3.qhcp
|
DEPENDS Periodic_3_triangulation_3.qhp Periodic_3_triangulation_3.qhcp
|
||||||
COMMAND Qt5::qcollectiongenerator
|
COMMAND ${CGAL_QCOLLECTIONGENERATOR_TARGET}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Periodic_3_triangulation_3.qhcp
|
${CMAKE_CURRENT_SOURCE_DIR}/Periodic_3_triangulation_3.qhcp
|
||||||
-o ${CMAKE_CURRENT_BINARY_DIR}/Periodic_3_triangulation_3.qhc
|
-o ${CMAKE_CURRENT_BINARY_DIR}/Periodic_3_triangulation_3.qhc
|
||||||
)
|
)
|
||||||
|
|
@ -83,8 +84,8 @@ else ()
|
||||||
set(PERIODIC_TRIANGULATION_MISSING_DEPS "Qt5, ${PERIODIC_TRIANGULATION_MISSING_DEPS}")
|
set(PERIODIC_TRIANGULATION_MISSING_DEPS "Qt5, ${PERIODIC_TRIANGULATION_MISSING_DEPS}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT TARGET Qt5::qcollectiongenerator)
|
if (NOT TARGET ${CGAL_QCOLLECTIONGENERATOR_TARGET})
|
||||||
set(PERIODIC_TRIANGULATION_MISSING_DEPS "qcollectiongenerator, ${PERIODIC_TRIANGULATION_MISSING_DEPS}")
|
set(PERIODIC_TRIANGULATION_MISSING_DEPS "${CGAL_QCOLLECTIONGENERATOR_TARGET}, ${PERIODIC_TRIANGULATION_MISSING_DEPS}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "NOTICE: This demo requires ${PERIODIC_TRIANGULATION_MISSING_DEPS}and will not be compiled.")
|
message(STATUS "NOTICE: This demo requires ${PERIODIC_TRIANGULATION_MISSING_DEPS}and will not be compiled.")
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,13 @@ find_package(CGAL COMPONENTS Qt5)
|
||||||
|
|
||||||
find_package(Qt5 QUIET COMPONENTS Xml Script Help OpenGL Svg)
|
find_package(Qt5 QUIET COMPONENTS Xml Script Help OpenGL Svg)
|
||||||
|
|
||||||
|
if( Qt5Help_VERSION VERSION_LESS 5.12 )
|
||||||
|
set(CGAL_QCOLLECTIONGENERATOR_TARGET Qt5::qcollectiongenerator)
|
||||||
|
else()
|
||||||
|
set(CGAL_QCOLLECTIONGENERATOR_TARGET Qt5::qhelpgenerator)
|
||||||
|
endif()
|
||||||
|
|
||||||
if ( CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND AND TARGET Qt5::qcollectiongenerator )
|
if ( CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND AND TARGET ${CGAL_QCOLLECTIONGENERATOR_TARGET} )
|
||||||
|
|
||||||
include_directories (BEFORE ./ )
|
include_directories (BEFORE ./ )
|
||||||
|
|
||||||
|
|
@ -41,7 +46,7 @@ if ( CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND AND TARGET Qt5::qcollectiongene
|
||||||
# generate QtAssistant collection file
|
# generate QtAssistant collection file
|
||||||
add_custom_command ( OUTPUT Periodic_Lloyd_3.qhc
|
add_custom_command ( OUTPUT Periodic_Lloyd_3.qhc
|
||||||
DEPENDS Periodic_Lloyd_3.qhp Periodic_Lloyd_3.qhcp
|
DEPENDS Periodic_Lloyd_3.qhp Periodic_Lloyd_3.qhcp
|
||||||
COMMAND Qt5::qcollectiongenerator
|
COMMAND ${CGAL_QCOLLECTIONGENERATOR_TARGET}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Periodic_Lloyd_3.qhcp
|
${CMAKE_CURRENT_SOURCE_DIR}/Periodic_Lloyd_3.qhcp
|
||||||
-o ${CMAKE_CURRENT_BINARY_DIR}/Periodic_Lloyd_3.qhc
|
-o ${CMAKE_CURRENT_BINARY_DIR}/Periodic_Lloyd_3.qhc
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
|
@ -69,7 +74,7 @@ else( CGAL_FOUND AND CGAL_Qt5_FOUND AND Qt5_FOUND AND QT_QCOLLECTIONGENERATOR_EX
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT QT_QCOLLECTIONGENERATOR_EXECUTABLE)
|
if(NOT QT_QCOLLECTIONGENERATOR_EXECUTABLE)
|
||||||
set(PERIODIC_LLOYD_MISSING_DEPS "qcollectiongenerator, ${PERIODIC_LLOYD_MISSING_DEPS}")
|
set(PERIODIC_LLOYD_MISSING_DEPS "${CGAL_QCOLLECTIONGENERATOR_TARGET}, ${PERIODIC_LLOYD_MISSING_DEPS}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -488,6 +488,7 @@ create_mst_graph(
|
||||||
CGAL_USE(point_map);
|
CGAL_USE(point_map);
|
||||||
CGAL_USE(k);
|
CGAL_USE(k);
|
||||||
CGAL_USE(kernel);
|
CGAL_USE(kernel);
|
||||||
|
CGAL_USE(index_map);
|
||||||
|
|
||||||
// Bring private stuff to scope
|
// Bring private stuff to scope
|
||||||
using namespace internal;
|
using namespace internal;
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ namespace Polygon_mesh_processing {
|
||||||
return border_counter;
|
return border_counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @ingroup PkgPolygonMeshProcessing
|
/// @ingroup PkgPolygonMeshProcessingRef
|
||||||
/// extracts boundary cycles as a list of halfedges, with one halfedge per border.
|
/// extracts boundary cycles as a list of halfedges, with one halfedge per border.
|
||||||
///
|
///
|
||||||
/// @tparam PolygonMesh a model of `HalfedgeListGraph`
|
/// @tparam PolygonMesh a model of `HalfedgeListGraph`
|
||||||
|
|
|
||||||
|
|
@ -411,6 +411,14 @@ compute_face_face_intersection(const FaceRange& face_range1,
|
||||||
typedef TriangleMesh TM;
|
typedef TriangleMesh TM;
|
||||||
typedef typename boost::graph_traits<TM>::face_descriptor face_descriptor;
|
typedef typename boost::graph_traits<TM>::face_descriptor face_descriptor;
|
||||||
typedef typename CGAL::Box_intersection_d::Box_with_info_d<double, 3, face_descriptor> Box;
|
typedef typename CGAL::Box_intersection_d::Box_with_info_d<double, 3, face_descriptor> Box;
|
||||||
|
|
||||||
|
CGAL::Bbox_3 b1 = CGAL::Polygon_mesh_processing::bbox(tm1, np1),
|
||||||
|
b2 = CGAL::Polygon_mesh_processing::bbox(tm2, np2);
|
||||||
|
|
||||||
|
if(!CGAL::do_overlap(b1, b2))
|
||||||
|
{
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// make one box per facet
|
// make one box per facet
|
||||||
std::vector<Box> boxes1;
|
std::vector<Box> boxes1;
|
||||||
|
|
@ -525,6 +533,11 @@ compute_face_polyline_intersection( const FaceRange& face_range,
|
||||||
|
|
||||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||||
|
|
||||||
|
CGAL::Bbox_3 b1 = CGAL::Polygon_mesh_processing::bbox(tm, np),
|
||||||
|
b2 = CGAL::bbox_3(polyline.begin(), polyline.end());
|
||||||
|
|
||||||
|
if(!CGAL::do_overlap(b1,b2))
|
||||||
|
return out;
|
||||||
typedef TriangleMesh TM;
|
typedef TriangleMesh TM;
|
||||||
typedef typename boost::graph_traits<TM>::face_descriptor face_descriptor;
|
typedef typename boost::graph_traits<TM>::face_descriptor face_descriptor;
|
||||||
typedef typename GetVertexPointMap<TM, NamedParameters>::const_type VertexPointMap;
|
typedef typename GetVertexPointMap<TM, NamedParameters>::const_type VertexPointMap;
|
||||||
|
|
@ -648,7 +661,18 @@ compute_face_polylines_intersection(const FaceRange& face_range,
|
||||||
using boost::get_param;
|
using boost::get_param;
|
||||||
|
|
||||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||||
|
|
||||||
|
CGAL::Bbox_3 b1,b2;
|
||||||
|
b1 = CGAL::Polygon_mesh_processing::bbox(tm, np);
|
||||||
|
for(std::size_t i =0; i< polyline_range.size(); ++i)
|
||||||
|
{
|
||||||
|
b2 += CGAL::bbox_3(polyline_range[i].begin(),
|
||||||
|
polyline_range[i].end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!CGAL::do_overlap(b1,b2))
|
||||||
|
return out;
|
||||||
|
|
||||||
typedef TriangleMesh TM;
|
typedef TriangleMesh TM;
|
||||||
typedef typename boost::graph_traits<TM>::face_descriptor face_descriptor;
|
typedef typename boost::graph_traits<TM>::face_descriptor face_descriptor;
|
||||||
typedef typename GetVertexPointMap<TM, NamedParameters>::const_type VertexPointMap;
|
typedef typename GetVertexPointMap<TM, NamedParameters>::const_type VertexPointMap;
|
||||||
|
|
@ -849,18 +873,23 @@ compute_polylines_polylines_intersection(const PolylineRange& polylines1,
|
||||||
std::vector<Box> boxes1;
|
std::vector<Box> boxes1;
|
||||||
std::vector<Box> boxes2;
|
std::vector<Box> boxes2;
|
||||||
std::size_t polylines_size = 0;
|
std::size_t polylines_size = 0;
|
||||||
|
CGAL::Bbox_3 b1, b2;
|
||||||
BOOST_FOREACH(Polyline poly, polylines1)
|
BOOST_FOREACH(Polyline poly, polylines1)
|
||||||
{
|
{
|
||||||
polylines_size += std::distance( boost::begin(poly), boost::end(poly) ) -1;
|
polylines_size += std::distance( boost::begin(poly), boost::end(poly) ) -1;
|
||||||
|
b1 += CGAL::bbox_3(poly.begin(), poly.end());
|
||||||
}
|
}
|
||||||
boxes1.reserve( polylines_size );
|
boxes1.reserve( polylines_size );
|
||||||
polylines_size = 0;
|
polylines_size = 0;
|
||||||
BOOST_FOREACH(Polyline poly, polylines2)
|
BOOST_FOREACH(Polyline poly, polylines2)
|
||||||
{
|
{
|
||||||
polylines_size += std::distance( boost::begin(poly), boost::end(poly) ) -1;
|
polylines_size += std::distance( boost::begin(poly), boost::end(poly) ) -1;
|
||||||
|
b2 += CGAL::bbox_3(poly.begin(), poly.end());
|
||||||
}
|
}
|
||||||
boxes2.reserve(polylines_size);
|
boxes2.reserve(polylines_size);
|
||||||
|
|
||||||
|
if(!CGAL::do_overlap(b1,b2))
|
||||||
|
return out;
|
||||||
std::size_t range_size = std::distance( boost::begin(polylines1), boost::end(polylines1) );
|
std::size_t range_size = std::distance( boost::begin(polylines1), boost::end(polylines1) );
|
||||||
for(std::size_t j = 0; j < range_size; ++j)
|
for(std::size_t j = 0; j < range_size; ++j)
|
||||||
{
|
{
|
||||||
|
|
@ -1207,7 +1236,6 @@ bool do_intersect(const TriangleMesh& tm1,
|
||||||
{
|
{
|
||||||
using boost::choose_param;
|
using boost::choose_param;
|
||||||
using boost::get_param;
|
using boost::get_param;
|
||||||
|
|
||||||
bool test_overlap = choose_param(get_param(np1, internal_np::overlap_test),false) ||
|
bool test_overlap = choose_param(get_param(np1, internal_np::overlap_test),false) ||
|
||||||
choose_param(get_param(np2, internal_np::overlap_test),false);
|
choose_param(get_param(np2, internal_np::overlap_test),false);
|
||||||
|
|
||||||
|
|
@ -1298,7 +1326,6 @@ bool do_intersect(const TriangleMesh& tm,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
typedef boost::function_output_iterator<internal::Throw_at_first_output> OutputIterator;
|
typedef boost::function_output_iterator<internal::Throw_at_first_output> OutputIterator;
|
||||||
|
|
@ -1354,7 +1381,6 @@ bool do_intersect(const TriangleMesh& tm,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
typedef boost::function_output_iterator<internal::Throw_at_first_output> OutputIterator;
|
typedef boost::function_output_iterator<internal::Throw_at_first_output> OutputIterator;
|
||||||
|
|
@ -1381,7 +1407,6 @@ bool do_intersect(const TriangleMesh& tm,
|
||||||
>::type* = 0)
|
>::type* = 0)
|
||||||
{
|
{
|
||||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||||
|
|
||||||
return CGAL::Polygon_mesh_processing::do_intersect(tm, polylines, parameters::all_default());
|
return CGAL::Polygon_mesh_processing::do_intersect(tm, polylines, parameters::all_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1404,7 +1429,6 @@ bool do_intersect(const TriangleMesh& tm,
|
||||||
>::type* = 0)
|
>::type* = 0)
|
||||||
{
|
{
|
||||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||||
|
|
||||||
return CGAL::Polygon_mesh_processing::do_intersect(tm, polyline, parameters::all_default());
|
return CGAL::Polygon_mesh_processing::do_intersect(tm, polyline, parameters::all_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1649,7 +1649,7 @@ bool remove_degenerate_faces( TriangleMesh& tmesh,
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class TriangleMesh>
|
template<class TriangleMesh>
|
||||||
std::size_t remove_degenerate_faces(TriangleMesh& tmesh)
|
bool remove_degenerate_faces(TriangleMesh& tmesh)
|
||||||
{
|
{
|
||||||
return remove_degenerate_faces(tmesh,
|
return remove_degenerate_faces(tmesh,
|
||||||
CGAL::Polygon_mesh_processing::parameters::all_default());
|
CGAL::Polygon_mesh_processing::parameters::all_default());
|
||||||
|
|
|
||||||
|
|
@ -359,6 +359,11 @@ add_executable ( CGAL_Mesh_3 Mesh_3.cpp )
|
||||||
add_dependencies(CGAL_Mesh_3 Mesh_3)
|
add_dependencies(CGAL_Mesh_3 Mesh_3)
|
||||||
target_link_libraries( CGAL_Mesh_3 PRIVATE polyhedron_demo )
|
target_link_libraries( CGAL_Mesh_3 PRIVATE polyhedron_demo )
|
||||||
add_to_cached_list( CGAL_EXECUTABLE_TARGETS CGAL_Mesh_3 )
|
add_to_cached_list( CGAL_EXECUTABLE_TARGETS CGAL_Mesh_3 )
|
||||||
|
|
||||||
|
add_executable ( CGAL_Classification Classification.cpp )
|
||||||
|
add_dependencies(CGAL_Classification Classification)
|
||||||
|
target_link_libraries( CGAL_Classification PRIVATE polyhedron_demo )
|
||||||
|
add_to_cached_list( CGAL_EXECUTABLE_TARGETS CGAL_Classification )
|
||||||
#
|
#
|
||||||
# Exporting
|
# Exporting
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include "Polyhedron_demo.h"
|
||||||
|
#include <clocale>
|
||||||
|
#include <CGAL/Qt/resources.h>
|
||||||
|
#include <QSurfaceFormat>
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Defines the entry point of the demo.
|
||||||
|
* Creates the application and sets a main window.
|
||||||
|
*/
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QSurfaceFormat fmt;
|
||||||
|
|
||||||
|
fmt.setVersion(4, 3);
|
||||||
|
fmt.setRenderableType(QSurfaceFormat::OpenGL);
|
||||||
|
fmt.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
|
fmt.setOption(QSurfaceFormat::DebugContext);
|
||||||
|
QSurfaceFormat::setDefaultFormat(fmt);
|
||||||
|
QStringList keywords;
|
||||||
|
keywords << "Classification";
|
||||||
|
Polyhedron_demo app(argc, argv,
|
||||||
|
"Classification demo",
|
||||||
|
"CGAL Classification Demo",
|
||||||
|
keywords);
|
||||||
|
//We set the locale to avoid any trouble with VTK
|
||||||
|
std::setlocale(LC_ALL, "C");
|
||||||
|
return app.try_exec();
|
||||||
|
}
|
||||||
|
|
@ -261,8 +261,10 @@ MainWindow::MainWindow(const QStringList &keywords, bool verbose, QWidget* paren
|
||||||
|
|
||||||
connect(viewer, SIGNAL(requestContextMenu(QPoint)),
|
connect(viewer, SIGNAL(requestContextMenu(QPoint)),
|
||||||
this, SLOT(contextMenuRequested(QPoint)));
|
this, SLOT(contextMenuRequested(QPoint)));
|
||||||
connect(viewer, SIGNAL(sendMessage(QString)),
|
connect(viewer, &Viewer::sendMessage,
|
||||||
this, SLOT(information(QString)));
|
this, [](QString s){
|
||||||
|
information(s);
|
||||||
|
});
|
||||||
|
|
||||||
// The contextMenuPolicy of infoLabel is now the default one, so that one
|
// The contextMenuPolicy of infoLabel is now the default one, so that one
|
||||||
// can easily copy-paste its text.
|
// can easily copy-paste its text.
|
||||||
|
|
@ -402,45 +404,70 @@ MainWindow::MainWindow(const QStringList &keywords, bool verbose, QWidget* paren
|
||||||
}
|
}
|
||||||
|
|
||||||
//Recursive function that do a pass over a menu and its sub-menus(etc.) and hide them when they are empty
|
//Recursive function that do a pass over a menu and its sub-menus(etc.) and hide them when they are empty
|
||||||
void filterMenuOperations(QMenu* menu, bool showFullMenu)
|
void filterMenuOperations(QMenu* menu, QString filter, bool keep_from_here)
|
||||||
{
|
{
|
||||||
Q_FOREACH(QAction* action, menu->actions()) {
|
QList<QAction*> buffer;
|
||||||
if(QMenu* menu = action->menu())
|
Q_FOREACH(QAction* action, menu->actions())
|
||||||
{
|
buffer.append(action);
|
||||||
filterMenuOperations(menu, showFullMenu);
|
while(!buffer.isEmpty()){
|
||||||
action->setVisible(showFullMenu && !(menu->isEmpty()));
|
|
||||||
|
Q_FOREACH(QAction* action, buffer) {
|
||||||
|
if(QMenu* submenu = action->menu())
|
||||||
|
{
|
||||||
|
bool keep = true;
|
||||||
|
if(!keep_from_here){
|
||||||
|
keep = submenu->menuAction()->text().contains(filter, Qt::CaseInsensitive);
|
||||||
|
if(!keep)
|
||||||
|
{
|
||||||
|
Q_FOREACH(QAction* subaction, submenu->actions())
|
||||||
|
{
|
||||||
|
submenu->removeAction(subaction);
|
||||||
|
buffer.append(subaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
menu->addAction(submenu->menuAction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filterMenuOperations(submenu, filter, keep);
|
||||||
|
action->setVisible(!(submenu->isEmpty()));
|
||||||
|
}
|
||||||
|
else if(action->text().contains(filter, Qt::CaseInsensitive)){
|
||||||
|
menu->addAction(action);
|
||||||
|
}
|
||||||
|
buffer.removeAll(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::filterOperations()
|
void MainWindow::filterOperations()
|
||||||
{
|
{
|
||||||
static QVector<QAction*> to_remove;
|
//return actions to their true menu
|
||||||
Q_FOREACH(QAction* action, to_remove)
|
Q_FOREACH(QMenu* menu, action_menu_map.values())
|
||||||
ui->menuOperations->removeAction(action);
|
{
|
||||||
QString filter=operationSearchBar.text();
|
Q_FOREACH(QAction* action, menu->actions())
|
||||||
if(!filter.isEmpty())
|
{
|
||||||
Q_FOREACH(const PluginNamePair& p, plugins) {
|
if(action != searchAction)
|
||||||
Q_FOREACH(QAction* action, p.first->actions()) {
|
menu->removeAction(action);
|
||||||
action->setVisible( p.first->applicable(action)
|
|
||||||
&& action->text().contains(filter, Qt::CaseInsensitive));
|
|
||||||
if(action->menu() != ui->menuOperations){
|
|
||||||
ui->menuOperations->addAction(action);
|
|
||||||
to_remove.push_back(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
Q_FOREACH(const PluginNamePair& p, plugins) {
|
|
||||||
Q_FOREACH(QAction* action, p.first->actions()) {
|
|
||||||
action->setVisible( p.first->applicable(action)
|
|
||||||
&& action->text().contains(filter, Qt::CaseInsensitive));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// do a pass over all menus in Operations and their sub-menus(etc.) and hide them when they are empty
|
|
||||||
}
|
}
|
||||||
filterMenuOperations(ui->menuOperations, filter.isEmpty());
|
Q_FOREACH(QAction* action, action_menu_map.keys())
|
||||||
|
{
|
||||||
|
action_menu_map[action]->addAction(action);
|
||||||
|
}
|
||||||
|
QString filter=operationSearchBar.text();
|
||||||
|
Q_FOREACH(const PluginNamePair& p, plugins) {
|
||||||
|
Q_FOREACH(QAction* action, p.first->actions()) {
|
||||||
|
action->setVisible( p.first->applicable(action)
|
||||||
|
&& (action->text().contains(filter, Qt::CaseInsensitive)
|
||||||
|
|| action->property("subMenuName")
|
||||||
|
.toString().contains(filter, Qt::CaseInsensitive)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do a pass over all menus in Operations and their sub-menus(etc.) and hide them when they are empty
|
||||||
|
filterMenuOperations(ui->menuOperations, filter, false);
|
||||||
|
operationSearchBar.setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <CGAL/Three/exceptions.h>
|
#include <CGAL/Three/exceptions.h>
|
||||||
|
|
@ -549,12 +576,14 @@ void MainWindow::setMenus(QString name, QString parentName, QAction* a )
|
||||||
menu_map[parentName] = new QMenu(parentName, this);
|
menu_map[parentName] = new QMenu(parentName, this);
|
||||||
// add the submenu in the menu
|
// add the submenu in the menu
|
||||||
menu_map[parentName]->addMenu(menu_map[menuName]);
|
menu_map[parentName]->addMenu(menu_map[menuName]);
|
||||||
|
action_menu_map[menu_map[menuName]->menuAction()] = menu_map[parentName];
|
||||||
|
|
||||||
// only add the action in the last submenu
|
// only add the action in the last submenu
|
||||||
if(slash_index==-1)
|
if(slash_index==-1)
|
||||||
{
|
{
|
||||||
ui->menuOperations->removeAction(a);
|
ui->menuOperations->removeAction(a);
|
||||||
menu_map[menuName]->addAction(a);
|
menu_map[menuName]->addAction(a);
|
||||||
|
action_menu_map[a] = menu_map[menuName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -765,6 +794,7 @@ bool MainWindow::initPlugin(QObject* obj)
|
||||||
// If action does not belong to the menus, add it to "Operations" menu
|
// If action does not belong to the menus, add it to "Operations" menu
|
||||||
if(!childs.contains(action)) {
|
if(!childs.contains(action)) {
|
||||||
ui->menuOperations->addAction(action);
|
ui->menuOperations->addAction(action);
|
||||||
|
action_menu_map[action] = ui->menuOperations;
|
||||||
}
|
}
|
||||||
// Show and enable menu item
|
// Show and enable menu item
|
||||||
addAction(action);
|
addAction(action);
|
||||||
|
|
@ -893,17 +923,17 @@ void MainWindow::message(QString message, QString colorName, QString font) {
|
||||||
ui->consoleTextEdit->verticalScrollBar()->setValue(ui->consoleTextEdit->verticalScrollBar()->maximum());
|
ui->consoleTextEdit->verticalScrollBar()->setValue(ui->consoleTextEdit->verticalScrollBar()->maximum());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::information(QString text) {
|
void MainWindow::message_information(QString text) {
|
||||||
statusBar()->setStyleSheet("color: blue");
|
statusBar()->setStyleSheet("color: blue");
|
||||||
this->message("INFO: " + text, "blue");
|
this->message("INFO: " + text, "blue");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::warning(QString text) {
|
void MainWindow::message_warning(QString text) {
|
||||||
statusBar()->setStyleSheet("color: orange");
|
statusBar()->setStyleSheet("color: orange");
|
||||||
this->message("WARNING: " + text, "orange");
|
this->message("WARNING: " + text, "orange");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::error(QString text) {
|
void MainWindow::message_error(QString text) {
|
||||||
statusBar()->setStyleSheet("color: red");
|
statusBar()->setStyleSheet("color: red");
|
||||||
this->message("ERROR: " + text, "red");
|
this->message("ERROR: " + text, "red");
|
||||||
}
|
}
|
||||||
|
|
@ -968,6 +998,8 @@ void MainWindow::reloadItem() {
|
||||||
Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
|
Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
|
||||||
{
|
{
|
||||||
item = scene->item(id);
|
item = scene->item(id);
|
||||||
|
if(!item)//secure items like selection items that get deleted when their "parent" item is reloaded.
|
||||||
|
continue;
|
||||||
QString filename = item->property("source filename").toString();
|
QString filename = item->property("source filename").toString();
|
||||||
QString loader_name = item->property("loader_name").toString();
|
QString loader_name = item->property("loader_name").toString();
|
||||||
if(filename.isEmpty() || loader_name.isEmpty()) {
|
if(filename.isEmpty() || loader_name.isEmpty()) {
|
||||||
|
|
@ -986,9 +1018,9 @@ void MainWindow::reloadItem() {
|
||||||
new_item->setRenderingMode(item->renderingMode());
|
new_item->setRenderingMode(item->renderingMode());
|
||||||
new_item->setVisible(item->visible());
|
new_item->setVisible(item->visible());
|
||||||
Scene_item_with_properties *property_item = dynamic_cast<Scene_item_with_properties*>(new_item);
|
Scene_item_with_properties *property_item = dynamic_cast<Scene_item_with_properties*>(new_item);
|
||||||
|
scene->replaceItem(scene->item_id(item), new_item, true);
|
||||||
if(property_item)
|
if(property_item)
|
||||||
property_item->copyProperties(item);
|
property_item->copyProperties(item);
|
||||||
scene->replaceItem(scene->item_id(item), new_item, true);
|
|
||||||
new_item->invalidateOpenGLBuffers();
|
new_item->invalidateOpenGLBuffers();
|
||||||
item->deleteLater();
|
item->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
@ -1169,22 +1201,19 @@ bool MainWindow::open(QString filename, QString loader_name) {
|
||||||
CGAL::Three::Scene_item* MainWindow::loadItem(QFileInfo fileinfo, CGAL::Three::Polyhedron_demo_io_plugin_interface* loader) {
|
CGAL::Three::Scene_item* MainWindow::loadItem(QFileInfo fileinfo, CGAL::Three::Polyhedron_demo_io_plugin_interface* loader) {
|
||||||
CGAL::Three::Scene_item* item = NULL;
|
CGAL::Three::Scene_item* item = NULL;
|
||||||
if(!fileinfo.isFile() || !fileinfo.isReadable()) {
|
if(!fileinfo.isFile() || !fileinfo.isReadable()) {
|
||||||
throw std::invalid_argument(QString("File %1 is not a readable file.")
|
|
||||||
.arg(fileinfo.absoluteFilePath()).toStdString());
|
|
||||||
}
|
|
||||||
//test if the file is empty.
|
|
||||||
if(fileinfo.size() == 0) {
|
|
||||||
QMessageBox::warning(this, tr("Error"),
|
QMessageBox::warning(this, tr("Error"),
|
||||||
tr("The file you are trying to load is empty.\n"));
|
QString("File %1 is not a readable file.")
|
||||||
return 0;
|
.arg(fileinfo.absoluteFilePath()));
|
||||||
}
|
}
|
||||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
|
|
||||||
item = loader->load(fileinfo);
|
item = loader->load(fileinfo);
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
if(!item) {
|
if(!item) {
|
||||||
throw std::logic_error(QString("Could not load item from file %1 using plugin %2")
|
QMessageBox::warning(this, tr("Error"),
|
||||||
.arg(fileinfo.absoluteFilePath()).arg(loader->name()).toStdString());
|
QString("Could not load item from file %1 using plugin %2")
|
||||||
|
.arg(fileinfo.absoluteFilePath()).arg(loader->name()));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
item->setProperty("source filename", fileinfo.absoluteFilePath());
|
item->setProperty("source filename", fileinfo.absoluteFilePath());
|
||||||
|
|
@ -1207,11 +1236,13 @@ void MainWindow::selectSceneItem(int i)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
QItemSelection s =
|
QItemSelection s =
|
||||||
proxyModel->mapSelectionFromSource(scene->createSelection(i));
|
proxyModel->mapSelectionFromSource(scene->createSelection(i));
|
||||||
|
QModelIndex mi = proxyModel->mapFromSource(scene->getModelIndexFromId(i).first());
|
||||||
|
sceneView->setCurrentIndex(mi);
|
||||||
sceneView->selectionModel()->select(s,
|
sceneView->selectionModel()->select(s,
|
||||||
QItemSelectionModel::ClearAndSelect);
|
QItemSelectionModel::ClearAndSelect);
|
||||||
sceneView->scrollTo(s.indexes().first());
|
sceneView->scrollTo(s.indexes().first());
|
||||||
|
sceneView->setCurrentIndex(sceneView->selectionModel()->selectedIndexes().first());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1226,6 +1257,8 @@ void MainWindow::selectSceneItems(QList<int> is)
|
||||||
QItemSelection s =
|
QItemSelection s =
|
||||||
proxyModel->mapSelectionFromSource(scene->createSelection(is));
|
proxyModel->mapSelectionFromSource(scene->createSelection(is));
|
||||||
|
|
||||||
|
QModelIndex i = proxyModel->mapFromSource(scene->getModelIndexFromId(is.first()).first());
|
||||||
|
sceneView->setCurrentIndex(i);
|
||||||
sceneView->selectionModel()->select(s,
|
sceneView->selectionModel()->select(s,
|
||||||
QItemSelectionModel::ClearAndSelect);
|
QItemSelectionModel::ClearAndSelect);
|
||||||
sceneView->scrollTo(s.indexes().first());
|
sceneView->scrollTo(s.indexes().first());
|
||||||
|
|
@ -1409,6 +1442,15 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
||||||
QMap<QString, QAction*> menu_actions;
|
QMap<QString, QAction*> menu_actions;
|
||||||
QVector<QMenu*> slider_menus;
|
QVector<QMenu*> slider_menus;
|
||||||
bool has_stats = false;
|
bool has_stats = false;
|
||||||
|
bool has_reload = false;
|
||||||
|
Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
|
||||||
|
{
|
||||||
|
if(!scene->item(id)->property("source filename").toString().isEmpty())
|
||||||
|
{
|
||||||
|
has_reload = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
Q_FOREACH(QAction* action, scene->item(main_index)->contextMenu()->actions())
|
Q_FOREACH(QAction* action, scene->item(main_index)->contextMenu()->actions())
|
||||||
{
|
{
|
||||||
if(action->property("is_groupable").toBool())
|
if(action->property("is_groupable").toBool())
|
||||||
|
|
@ -1571,10 +1613,13 @@ void MainWindow::showSceneContextMenu(const QPoint& p) {
|
||||||
connect(actionStatistics, SIGNAL(triggered()),
|
connect(actionStatistics, SIGNAL(triggered()),
|
||||||
this, SLOT(statisticsOnItem()));
|
this, SLOT(statisticsOnItem()));
|
||||||
}
|
}
|
||||||
|
if(has_reload)
|
||||||
|
{
|
||||||
QAction* reload = menu.addAction(tr("&Reload Item from File"));
|
QAction* reload = menu.addAction(tr("&Reload Item from File"));
|
||||||
reload->setProperty("is_groupable", true);
|
reload->setProperty("is_groupable", true);
|
||||||
connect(reload, SIGNAL(triggered()),
|
connect(reload, SIGNAL(triggered()),
|
||||||
this, SLOT(reloadItem()));
|
this, SLOT(reloadItem()));
|
||||||
|
}
|
||||||
QAction* saveas = menu.addAction(tr("&Save as..."));
|
QAction* saveas = menu.addAction(tr("&Save as..."));
|
||||||
connect(saveas, SIGNAL(triggered()),
|
connect(saveas, SIGNAL(triggered()),
|
||||||
this, SLOT(on_actionSaveAs_triggered()));
|
this, SLOT(on_actionSaveAs_triggered()));
|
||||||
|
|
@ -1963,9 +2008,11 @@ bool MainWindow::on_actionErase_triggered()
|
||||||
|
|
||||||
void MainWindow::on_actionEraseAll_triggered()
|
void MainWindow::on_actionEraseAll_triggered()
|
||||||
{
|
{
|
||||||
scene->setSelectedItem(0);
|
QList<int> all_ids;
|
||||||
while(on_actionErase_triggered()) {
|
for(int i = 0; i < scene->numberOfEntries(); ++i)
|
||||||
}
|
all_ids.push_back(i);
|
||||||
|
scene->setSelectedItemsList(all_ids);
|
||||||
|
on_actionErase_triggered();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionDuplicate_triggered()
|
void MainWindow::on_actionDuplicate_triggered()
|
||||||
|
|
@ -2202,6 +2249,8 @@ void MainWindow::on_actionLookAt_triggered()
|
||||||
if( i == QDialog::Accepted &&
|
if( i == QDialog::Accepted &&
|
||||||
dialog.has_correct_coordinates() )
|
dialog.has_correct_coordinates() )
|
||||||
{
|
{
|
||||||
|
if(viewer->camera()->frame()->isSpinning())
|
||||||
|
viewer->camera()->frame()->stopSpinning();
|
||||||
viewerShow((float)dialog.get_x()+viewer->offset().x,
|
viewerShow((float)dialog.get_x()+viewer->offset().x,
|
||||||
(float)dialog.get_y()+viewer->offset().y,
|
(float)dialog.get_y()+viewer->offset().y,
|
||||||
(float)dialog.get_z()+viewer->offset().z);
|
(float)dialog.get_z()+viewer->offset().z);
|
||||||
|
|
@ -2722,3 +2771,20 @@ void MainWindow::invalidate_bbox(bool do_recenter)
|
||||||
updateViewerBBox(true);
|
updateViewerBBox(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_action_Save_triggered()
|
||||||
|
{
|
||||||
|
if(QMessageBox::question(this, "Save", "Are you sure you want to override these files ?")
|
||||||
|
== QMessageBox::No)
|
||||||
|
return;
|
||||||
|
Scene_item* item = nullptr;
|
||||||
|
Q_FOREACH(Scene::Item_id id, scene->selectionIndices())
|
||||||
|
{
|
||||||
|
item = scene->item(id);
|
||||||
|
if(!item->property("source filename").toString().isEmpty())
|
||||||
|
{
|
||||||
|
QString filename = item->property("source filename").toString();
|
||||||
|
save(filename, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ public Q_SLOTS:
|
||||||
//! If `b` is true, recenters the scene.
|
//! If `b` is true, recenters the scene.
|
||||||
void updateViewerBBox(bool b);
|
void updateViewerBBox(bool b);
|
||||||
//! Opens a script or a file with the default loader if there is.
|
//! Opens a script or a file with the default loader if there is.
|
||||||
void open(QString);
|
void open(QString) Q_DECL_OVERRIDE;
|
||||||
//! Is called when the up button is pressed.
|
//! Is called when the up button is pressed.
|
||||||
void on_upButton_pressed();
|
void on_upButton_pressed();
|
||||||
//! Is called when the down button is pressed.
|
//! Is called when the down button is pressed.
|
||||||
|
|
@ -220,15 +220,15 @@ public Q_SLOTS:
|
||||||
/*!
|
/*!
|
||||||
* Displays a text preceded by the mention "INFO :".
|
* Displays a text preceded by the mention "INFO :".
|
||||||
*/
|
*/
|
||||||
void information(QString);
|
void message_information(QString) Q_DECL_OVERRIDE;
|
||||||
/*!
|
/*!
|
||||||
* Displays a blue text preceded by the mention "WARNING :".
|
* Displays a blue text preceded by the mention "WARNING :".
|
||||||
*/
|
*/
|
||||||
void warning(QString);
|
void message_warning(QString) Q_DECL_OVERRIDE;
|
||||||
/*!
|
/*!
|
||||||
* Displays a red text preceded by the mention "ERROR :".
|
* Displays a red text preceded by the mention "ERROR :".
|
||||||
*/
|
*/
|
||||||
void error(QString);
|
void message_error(QString) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
//!Displays a text in the chosen html color with the chosen html font.
|
//!Displays a text in the chosen html color with the chosen html font.
|
||||||
|
|
||||||
|
|
@ -327,6 +327,8 @@ protected Q_SLOTS:
|
||||||
//Preferences edition
|
//Preferences edition
|
||||||
//!Opens the Preferences dialog.
|
//!Opens the Preferences dialog.
|
||||||
void on_actionPreferences_triggered();
|
void on_actionPreferences_triggered();
|
||||||
|
//!Save selected items if able.
|
||||||
|
void on_action_Save_triggered();
|
||||||
// save as...
|
// save as...
|
||||||
//!Opens a dialog to save selected item if able.
|
//!Opens a dialog to save selected item if able.
|
||||||
void on_actionSaveAs_triggered();
|
void on_actionSaveAs_triggered();
|
||||||
|
|
@ -384,7 +386,7 @@ protected:
|
||||||
* Calls writeSettings() and set the flag accepted for the event.
|
* Calls writeSettings() and set the flag accepted for the event.
|
||||||
* @see writeSettings()
|
* @see writeSettings()
|
||||||
*/
|
*/
|
||||||
void closeEvent(QCloseEvent *event);
|
void closeEvent(QCloseEvent *event)Q_DECL_OVERRIDE;
|
||||||
/*! Returns the currently selected item in the Geometric Objects view. Returns -1
|
/*! Returns the currently selected item in the Geometric Objects view. Returns -1
|
||||||
* if none is selected.
|
* if none is selected.
|
||||||
*/
|
*/
|
||||||
|
|
@ -418,7 +420,7 @@ private:
|
||||||
QAction* actionAddToGroup;
|
QAction* actionAddToGroup;
|
||||||
QAction* actionResetDefaultLoaders;
|
QAction* actionResetDefaultLoaders;
|
||||||
CGAL::Three::Three* three;
|
CGAL::Three::Three* three;
|
||||||
void print_message(QString message) { messages->information(message); }
|
void print_message(QString message) { messages->message_information(message); }
|
||||||
Messages_interface* messages;
|
Messages_interface* messages;
|
||||||
|
|
||||||
QDialog *statistics_dlg;
|
QDialog *statistics_dlg;
|
||||||
|
|
@ -453,6 +455,9 @@ private:
|
||||||
QMap<QString, QPair<QStringList, QString> >plugin_metadata_map;
|
QMap<QString, QPair<QStringList, QString> >plugin_metadata_map;
|
||||||
QMap<QString, bool> ignored_map;
|
QMap<QString, bool> ignored_map;
|
||||||
const QStringList& accepted_keywords;
|
const QStringList& accepted_keywords;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QAction*, QMenu*> action_menu_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ifndef MAINWINDOW_H
|
#endif // ifndef MAINWINDOW_H
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
<addaction name="actionErase"/>
|
<addaction name="actionErase"/>
|
||||||
<addaction name="actionEraseAll"/>
|
<addaction name="actionEraseAll"/>
|
||||||
<addaction name="actionDuplicate"/>
|
<addaction name="actionDuplicate"/>
|
||||||
|
<addaction name="action_Save"/>
|
||||||
<addaction name="actionSaveAs"/>
|
<addaction name="actionSaveAs"/>
|
||||||
<addaction name="actionSa_ve_Scene_as_Script"/>
|
<addaction name="actionSa_ve_Scene_as_Script"/>
|
||||||
<addaction name="actionSaveSnapshot"/>
|
<addaction name="actionSaveSnapshot"/>
|
||||||
|
|
@ -498,6 +499,11 @@
|
||||||
<string>Sa&ve the Scene as a Script File...</string>
|
<string>Sa&ve the Scene as a Script File...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Save">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Save</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
class Messages_interface {
|
class Messages_interface {
|
||||||
public:
|
public:
|
||||||
virtual ~Messages_interface() {}
|
virtual ~Messages_interface() {}
|
||||||
virtual void warning(QString) = 0;
|
virtual void message_warning(QString) = 0;
|
||||||
virtual void error(QString) = 0;
|
virtual void message_error(QString) = 0;
|
||||||
virtual void information(QString) = 0;
|
virtual void message_information(QString) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_INTERFACE(Messages_interface,
|
Q_DECLARE_INTERFACE(Messages_interface,
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
std::size_t last;
|
std::size_t last;
|
||||||
};
|
};
|
||||||
#endif // CGAL_LINKED_WITH_TBB
|
#endif // CGAL_LINKED_WITH_TBB
|
||||||
|
#include <CGAL/Three/Three.h>
|
||||||
typedef CGAL::Simple_cartesian<double> Simple_kernel;
|
typedef CGAL::Simple_cartesian<double> Simple_kernel;
|
||||||
typedef Simple_kernel::FT FT;
|
typedef Simple_kernel::FT FT;
|
||||||
typedef Simple_kernel::Point_3 Point;
|
typedef Simple_kernel::Point_3 Point;
|
||||||
|
|
@ -253,7 +253,7 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFinite() const { return true; }
|
bool isFinite() const { return false; }
|
||||||
bool isEmpty() const { return is_tree_empty; }
|
bool isEmpty() const { return is_tree_empty; }
|
||||||
//computed in constructor
|
//computed in constructor
|
||||||
void compute_bbox() const {}
|
void compute_bbox() const {}
|
||||||
|
|
@ -1193,12 +1193,12 @@ void Polyhedron_demo_cut_plugin::apply(Item* item, QMap< QObject*, Facets_tree*>
|
||||||
const Mesh& mesh = *item->polyhedron();
|
const Mesh& mesh = *item->polyhedron();
|
||||||
if(!CGAL::is_triangle_mesh(mesh))
|
if(!CGAL::is_triangle_mesh(mesh))
|
||||||
{
|
{
|
||||||
messages->warning(QString("%1 ignored (not a triangulated mesh)").arg(item->name()));
|
CGAL::Three::Three::warning(QString("%1 ignored (not a triangulated mesh)").arg(item->name()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!CGAL::is_closed(mesh))
|
if(!CGAL::is_closed(mesh))
|
||||||
{
|
{
|
||||||
messages->warning(QString("%1 is not closed. Signed function will not be displayed.").arg(item->name()));
|
CGAL::Three::Three::warning(QString("%1 is not closed. Signed function will not be displayed.").arg(item->name()));
|
||||||
}
|
}
|
||||||
if(f_trees.find(item) == f_trees.end()) {
|
if(f_trees.find(item) == f_trees.end()) {
|
||||||
PPMAP<Mesh> pmap(item->polyhedron());
|
PPMAP<Mesh> pmap(item->polyhedron());
|
||||||
|
|
@ -1392,7 +1392,7 @@ void Polyhedron_demo_cut_plugin::computeIntersection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(does_intersect)
|
if(does_intersect)
|
||||||
messages->information(QString("cut (%1 ms). %2 edges.").arg(time.elapsed()).arg(edges_item->edges.size()));
|
CGAL::Three::Three::information(QString("cut (%1 ms). %2 edges.").arg(time.elapsed()).arg(edges_item->edges.size()));
|
||||||
edges_item->invalidateOpenGLBuffers();
|
edges_item->invalidateOpenGLBuffers();
|
||||||
edges_item->itemChanged();
|
edges_item->itemChanged();
|
||||||
ready_to_cut = false;
|
ready_to_cut = false;
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ private Q_SLOTS:
|
||||||
static_cast<CGAL::Three::Viewer_interface*>(
|
static_cast<CGAL::Three::Viewer_interface*>(
|
||||||
CGAL::QGLViewer::QGLViewerPool().first())->installEventFilter(this);
|
CGAL::QGLViewer::QGLViewerPool().first())->installEventFilter(this);
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
messageInterface->information("Press `W` to switch between Wireframe and Transparency mode.");
|
CGAL::Three::Three::information("Press `W` to switch between Wireframe and Transparency mode.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ if(EIGEN3_FOUND)
|
||||||
|
|
||||||
if (Boost_SERIALIZATION_FOUND AND Boost_IOSTREAMS_FOUND AND (NOT WIN32 OR Boost_ZLIB_FOUND))
|
if (Boost_SERIALIZATION_FOUND AND Boost_IOSTREAMS_FOUND AND (NOT WIN32 OR Boost_ZLIB_FOUND))
|
||||||
qt5_wrap_ui( classificationUI_FILES Classification_widget.ui Classification_advanced_widget.ui )
|
qt5_wrap_ui( classificationUI_FILES Classification_widget.ui Classification_advanced_widget.ui )
|
||||||
polyhedron_demo_plugin(classification_plugin Classification_plugin Point_set_item_classification Cluster_classification Surface_mesh_item_classification ${classificationUI_FILES})
|
polyhedron_demo_plugin(classification_plugin Classification_plugin Point_set_item_classification Cluster_classification Surface_mesh_item_classification ${classificationUI_FILES} KEYWORDS Classification)
|
||||||
|
|
||||||
set(classification_linked_libraries ${classification_linked_libraries}
|
set(classification_linked_libraries ${classification_linked_libraries}
|
||||||
${Boost_SERIALIZATION_LIBRARY}
|
${Boost_SERIALIZATION_LIBRARY}
|
||||||
|
|
@ -37,9 +37,22 @@ if(EIGEN3_FOUND)
|
||||||
else()
|
else()
|
||||||
message(STATUS "NOTICE: OpenCV was not found. OpenCV random forest predicate for classification won't be available.")
|
message(STATUS "NOTICE: OpenCV was not found. OpenCV random forest predicate for classification won't be available.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(TensorFlow QUIET)
|
||||||
|
if (TensorFlow_FOUND)
|
||||||
|
message(STATUS "Found TensorFlow")
|
||||||
|
set(classification_linked_libraries ${classification_linked_libraries}
|
||||||
|
${TensorFlow_LIBRARY})
|
||||||
|
set(classification_compile_definitions ${classification_compile_definitions}
|
||||||
|
"-DCGAL_LINKED_WITH_TENSORFLOW")
|
||||||
|
include_directories( ${TensorFlow_INCLUDE_DIR} )
|
||||||
|
else()
|
||||||
|
message(STATUS "NOTICE: TensorFlow not found, Neural Network predicate for classification won't be available.")
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(classification_plugin PUBLIC scene_points_with_normal_item
|
target_link_libraries(classification_plugin PUBLIC scene_points_with_normal_item
|
||||||
scene_polylines_item scene_polygon_soup_item scene_surface_mesh_item scene_selection_item scene_color_ramp ${classification_linked_libraries})
|
scene_polylines_item scene_polygon_soup_item scene_surface_mesh_item scene_selection_item scene_color_ramp ${classification_linked_libraries})
|
||||||
|
add_dependencies(classification_plugin point_set_selection_plugin selection_plugin)
|
||||||
target_compile_definitions(classification_plugin PUBLIC ${classification_compile_definitions})
|
target_compile_definitions(classification_plugin PUBLIC ${classification_compile_definitions})
|
||||||
else()
|
else()
|
||||||
message(STATUS "NOTICE: Boost Serialization or IO Streams or ZLIB not found. Classification plugin won't be available.")
|
message(STATUS "NOTICE: Boost Serialization or IO Streams or ZLIB not found. Classification plugin won't be available.")
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include <CGAL/Three/Scene_interface.h>
|
#include <CGAL/Three/Scene_interface.h>
|
||||||
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
|
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
|
||||||
|
#include <CGAL/Three/Three.h>
|
||||||
|
|
||||||
#include <CGAL/Random.h>
|
#include <CGAL/Random.h>
|
||||||
#include <CGAL/Real_timer.h>
|
#include <CGAL/Real_timer.h>
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
#include <QRadioButton>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
|
@ -43,6 +45,11 @@
|
||||||
#include <boost/graph/adjacency_list.hpp>
|
#include <boost/graph/adjacency_list.hpp>
|
||||||
#include <CGAL/boost/graph/split_graph_into_polylines.h>
|
#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;
|
using namespace CGAL::Three;
|
||||||
|
|
||||||
class Polyhedron_demo_classification_plugin :
|
class Polyhedron_demo_classification_plugin :
|
||||||
|
|
@ -120,6 +127,7 @@ class Polyhedron_demo_classification_plugin :
|
||||||
color_button->setStyleSheet(s);
|
color_button->setStyleSheet(s);
|
||||||
color_button->update();
|
color_button->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -129,7 +137,7 @@ public:
|
||||||
qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()))
|
qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()))
|
||||||
|| qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
|
|| qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
|
||||||
}
|
}
|
||||||
void print_message(QString message) { messages->information(message); }
|
void print_message(QString message) { CGAL::Three::Three::information(message); }
|
||||||
QList<QAction*> actions() const { return QList<QAction*>() << actionClassification; }
|
QList<QAction*> actions() const { return QList<QAction*>() << actionClassification; }
|
||||||
|
|
||||||
using Polyhedron_demo_plugin_helper::init;
|
using Polyhedron_demo_plugin_helper::init;
|
||||||
|
|
@ -185,82 +193,87 @@ public:
|
||||||
addDockWidget(dock_widget);
|
addDockWidget(dock_widget);
|
||||||
addDockWidget(dock_widget_adv);
|
addDockWidget(dock_widget_adv);
|
||||||
|
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
|
||||||
ui_widget.classifier->addItem (tr("Random Forest (OpenCV %1.%2)")
|
|
||||||
.arg(CV_MAJOR_VERSION)
|
|
||||||
.arg(CV_MINOR_VERSION));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
color_att = QColor (75, 75, 77);
|
color_att = QColor (75, 75, 77);
|
||||||
|
|
||||||
ui_widget.menu->setMenu (new QMenu("Classification Menu", ui_widget.menu));
|
QAction* compute_features = ui_widget.features_menu->addAction ("Compute features...");
|
||||||
|
|
||||||
connect(ui_widget.classifier, SIGNAL(currentIndexChanged(int)), this,
|
|
||||||
SLOT(on_classifier_changed(int)));
|
|
||||||
|
|
||||||
QAction* compute_features = ui_widget.menu->menu()->addAction ("Compute features");
|
|
||||||
connect(compute_features, SIGNAL(triggered()), this,
|
connect(compute_features, SIGNAL(triggered()), this,
|
||||||
SLOT(on_compute_features_button_clicked()));
|
SLOT(on_compute_features_button_clicked()));
|
||||||
|
|
||||||
ui_widget.menu->menu()->addSection ("Training");
|
action_statistics = ui_widget.features_menu->addAction ("Show feature statistics");
|
||||||
|
connect(action_statistics, SIGNAL(triggered()), this,
|
||||||
|
SLOT(on_statistics_clicked()));
|
||||||
|
|
||||||
action_train = ui_widget.menu->menu()->addAction ("Train classifier");
|
action_reset_local = ui_widget.training_menu->addAction ("Reset training set of selection");
|
||||||
action_train->setShortcut(Qt::SHIFT | Qt::Key_T);
|
|
||||||
connect(action_train, SIGNAL(triggered()), this,
|
|
||||||
SLOT(on_train_clicked()));
|
|
||||||
|
|
||||||
action_reset_local = ui_widget.menu->menu()->addAction ("Reset training set of selection");
|
|
||||||
connect(action_reset_local, SIGNAL(triggered()), this,
|
connect(action_reset_local, SIGNAL(triggered()), this,
|
||||||
SLOT(on_reset_training_set_of_selection_clicked()));
|
SLOT(on_reset_training_set_of_selection_clicked()));
|
||||||
|
|
||||||
action_reset = ui_widget.menu->menu()->addAction ("Reset all training sets");
|
action_reset = ui_widget.training_menu->addAction ("Reset all training sets");
|
||||||
connect(action_reset, SIGNAL(triggered()), this,
|
connect(action_reset, SIGNAL(triggered()), this,
|
||||||
SLOT(on_reset_training_sets_clicked()));
|
SLOT(on_reset_training_sets_clicked()));
|
||||||
|
|
||||||
action_random_region = ui_widget.menu->menu()->addAction ("Select random region");
|
action_random_region = ui_widget.training_menu->addAction ("Select random region");
|
||||||
action_random_region->setShortcut(Qt::SHIFT | Qt::Key_S);
|
action_random_region->setShortcut(Qt::SHIFT | Qt::Key_S);
|
||||||
connect(action_random_region, SIGNAL(triggered()), this,
|
connect(action_random_region, SIGNAL(triggered()), this,
|
||||||
SLOT(on_select_random_region_clicked()));
|
SLOT(on_select_random_region_clicked()));
|
||||||
|
|
||||||
action_validate = ui_widget.menu->menu()->addAction ("Validate labels of current selection as training sets");
|
action_validate = ui_widget.training_menu->addAction ("Validate labels of current selection as training sets");
|
||||||
connect(action_validate, SIGNAL(triggered()), this,
|
connect(action_validate, SIGNAL(triggered()), this,
|
||||||
SLOT(on_validate_selection_clicked()));
|
SLOT(on_validate_selection_clicked()));
|
||||||
|
|
||||||
action_save_config = ui_widget.menu->menu()->addAction ("Save classifier's current configuration");
|
classifier = ui_widget.classifier_menu->addSection (CGAL_CLASSIFICATION_ETHZ_ID);
|
||||||
action_load_config = ui_widget.menu->menu()->addAction ("Load configuration for classifier");
|
|
||||||
|
action_train = ui_widget.classifier_menu->addAction ("Train...");
|
||||||
|
action_train->setShortcut(Qt::SHIFT | Qt::Key_T);
|
||||||
|
connect(action_train, SIGNAL(triggered()), this,
|
||||||
|
SLOT(on_train_clicked()));
|
||||||
|
|
||||||
|
ui_widget.classifier_menu->addSeparator();
|
||||||
|
|
||||||
|
action_run = ui_widget.classifier_menu->addAction ("Classify");
|
||||||
|
connect(action_run, SIGNAL(triggered()), this,
|
||||||
|
SLOT(on_run_button_clicked()));
|
||||||
|
|
||||||
|
action_run_smoothed = ui_widget.classifier_menu->addAction ("Classify with local smoothing...");
|
||||||
|
connect(action_run_smoothed, SIGNAL(triggered()), this,
|
||||||
|
SLOT(on_run_smoothed_button_clicked()));
|
||||||
|
|
||||||
|
action_run_graphcut = ui_widget.classifier_menu->addAction ("Classify with Graph Cut...");
|
||||||
|
connect(action_run_graphcut, SIGNAL(triggered()), this,
|
||||||
|
SLOT(on_run_graphcut_button_clicked()));
|
||||||
|
|
||||||
|
ui_widget.classifier_menu->addSeparator();
|
||||||
|
|
||||||
|
action_save_config = ui_widget.classifier_menu->addAction ("Save current configuration...");
|
||||||
|
action_load_config = ui_widget.classifier_menu->addAction ("Load configuration...");
|
||||||
connect(action_save_config, SIGNAL(triggered()), this,
|
connect(action_save_config, SIGNAL(triggered()), this,
|
||||||
SLOT(on_save_config_button_clicked()));
|
SLOT(on_save_config_button_clicked()));
|
||||||
connect(action_load_config, SIGNAL(triggered()), this,
|
connect(action_load_config, SIGNAL(triggered()), this,
|
||||||
SLOT(on_load_config_button_clicked()));
|
SLOT(on_load_config_button_clicked()));
|
||||||
|
|
||||||
ui_widget.menu->menu()->addSection ("Algorithms");
|
ui_widget.classifier_menu->addSeparator();
|
||||||
|
|
||||||
action_run = ui_widget.menu->menu()->addAction ("Classification");
|
QAction* switch_classifier = ui_widget.classifier_menu->addAction ("Switch to another classifier...");
|
||||||
connect(action_run, SIGNAL(triggered()), this,
|
connect(switch_classifier, SIGNAL(triggered()), this,
|
||||||
SLOT(on_run_button_clicked()));
|
SLOT(on_switch_classifier_clicked()));
|
||||||
|
|
||||||
action_run_smoothed = ui_widget.menu->menu()->addAction ("Classification with local smoothing");
|
|
||||||
connect(action_run_smoothed, SIGNAL(triggered()), this,
|
|
||||||
SLOT(on_run_smoothed_button_clicked()));
|
|
||||||
|
|
||||||
action_run_graphcut = ui_widget.menu->menu()->addAction ("Classification with Graph Cut");
|
|
||||||
connect(action_run_graphcut, SIGNAL(triggered()), this,
|
|
||||||
SLOT(on_run_graphcut_button_clicked()));
|
|
||||||
|
|
||||||
ui_widget.menu->menu()->addSeparator();
|
|
||||||
|
|
||||||
QAction* close = ui_widget.menu->menu()->addAction ("Close");
|
|
||||||
connect(close, SIGNAL(triggered()), this,
|
|
||||||
SLOT(ask_for_closing()));
|
|
||||||
|
|
||||||
connect(ui_widget.display, SIGNAL(currentIndexChanged(int)), this,
|
connect(ui_widget.display, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(on_display_button_clicked(int)));
|
SLOT(on_display_button_clicked(int)));
|
||||||
|
|
||||||
|
connect(ui_widget.minDisplay, SIGNAL(released()), this,
|
||||||
|
SLOT(on_min_display_button_clicked()));
|
||||||
|
connect(ui_widget.maxDisplay, SIGNAL(released()), this,
|
||||||
|
SLOT(on_max_display_button_clicked()));
|
||||||
|
|
||||||
connect(ui_widget_adv.selected_feature, SIGNAL(currentIndexChanged(int)), this,
|
connect(ui_widget_adv.selected_feature, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(on_selected_feature_changed(int)));
|
SLOT(on_selected_feature_changed(int)));
|
||||||
connect(ui_widget_adv.feature_weight, SIGNAL(valueChanged(int)), this,
|
connect(ui_widget_adv.feature_weight, SIGNAL(valueChanged(int)), this,
|
||||||
SLOT(on_feature_weight_changed(int)));
|
SLOT(on_feature_weight_changed(int)));
|
||||||
|
|
||||||
|
connect(ui_widget.help, SIGNAL(clicked()), this,
|
||||||
|
SLOT(on_help_clicked()));
|
||||||
|
connect(ui_widget.close, SIGNAL(clicked()), this,
|
||||||
|
SLOT(ask_for_closing()));
|
||||||
|
|
||||||
QObject* scene_obj = dynamic_cast<QObject*>(scene_interface);
|
QObject* scene_obj = dynamic_cast<QObject*>(scene_interface);
|
||||||
if(scene_obj)
|
if(scene_obj)
|
||||||
{
|
{
|
||||||
|
|
@ -296,10 +309,26 @@ public Q_SLOTS:
|
||||||
dock_widget->raise();
|
dock_widget->raise();
|
||||||
if (Scene_points_with_normal_item* points_item
|
if (Scene_points_with_normal_item* points_item
|
||||||
= qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex())))
|
= qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex())))
|
||||||
|
{
|
||||||
create_from_item(points_item);
|
create_from_item(points_item);
|
||||||
|
QAction* ps_selection = mw->findChild<QAction*>("actionPointSetSelection");
|
||||||
|
if (ps_selection)
|
||||||
|
ps_selection->trigger();
|
||||||
|
else
|
||||||
|
print_message("Warning: can't find Point Set Selection plugin");
|
||||||
|
}
|
||||||
else if (Scene_surface_mesh_item* mesh_item
|
else if (Scene_surface_mesh_item* mesh_item
|
||||||
= qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex())))
|
= qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex())))
|
||||||
|
{
|
||||||
create_from_item(mesh_item);
|
create_from_item(mesh_item);
|
||||||
|
QAction* sm_selection = mw->findChild<QAction*>("actionSelection");
|
||||||
|
if (sm_selection)
|
||||||
|
sm_selection->trigger();
|
||||||
|
else
|
||||||
|
print_message("Warning: can't find Surface Mesh Selection plugin");
|
||||||
|
}
|
||||||
|
|
||||||
|
on_help_clicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -342,31 +371,39 @@ public Q_SLOTS:
|
||||||
|
|
||||||
void disable_everything ()
|
void disable_everything ()
|
||||||
{
|
{
|
||||||
ui_widget.menu->setEnabled(false);
|
ui_widget.features_menu->setEnabled(false);
|
||||||
ui_widget.display->setEnabled(false);
|
ui_widget.training_menu->setEnabled(false);
|
||||||
ui_widget.classifier->setEnabled(false);
|
ui_widget.classifier_menu->setEnabled(false);
|
||||||
|
ui_widget.view->setEnabled(false);
|
||||||
ui_widget.frame->setEnabled(false);
|
ui_widget.frame->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void enable_computation()
|
void enable_computation()
|
||||||
{
|
{
|
||||||
ui_widget.menu->setEnabled(true);
|
ui_widget.features_menu->setEnabled(true);
|
||||||
|
ui_widget.training_menu->setEnabled(true);
|
||||||
|
ui_widget.classifier_menu->setEnabled(false);
|
||||||
|
action_statistics->setEnabled(false);
|
||||||
action_train->setEnabled(false);
|
action_train->setEnabled(false);
|
||||||
action_reset_local->setEnabled(false);
|
action_reset_local->setEnabled(true);
|
||||||
action_reset->setEnabled(false);
|
action_reset->setEnabled(true);
|
||||||
action_random_region->setEnabled(false);
|
action_random_region->setEnabled(true);
|
||||||
action_validate->setEnabled(false);
|
action_validate->setEnabled(true);
|
||||||
action_save_config->setEnabled(false);
|
action_save_config->setEnabled(false);
|
||||||
action_load_config->setEnabled(false);
|
action_load_config->setEnabled(false);
|
||||||
action_run->setEnabled(false);
|
action_run->setEnabled(false);
|
||||||
action_run_smoothed->setEnabled(false);
|
action_run_smoothed->setEnabled(false);
|
||||||
action_run_graphcut->setEnabled(false);
|
action_run_graphcut->setEnabled(false);
|
||||||
ui_widget.display->setEnabled(true);
|
ui_widget.view->setEnabled(true);
|
||||||
ui_widget.classifier->setEnabled(true);
|
ui_widget.frame->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void enable_classif()
|
void enable_classif()
|
||||||
{
|
{
|
||||||
|
ui_widget.features_menu->setEnabled(true);
|
||||||
|
ui_widget.training_menu->setEnabled(true);
|
||||||
|
ui_widget.classifier_menu->setEnabled(true);
|
||||||
|
action_statistics->setEnabled(true);
|
||||||
action_train->setEnabled(true);
|
action_train->setEnabled(true);
|
||||||
action_reset_local->setEnabled(true);
|
action_reset_local->setEnabled(true);
|
||||||
action_reset->setEnabled(true);
|
action_reset->setEnabled(true);
|
||||||
|
|
@ -420,9 +457,15 @@ public Q_SLOTS:
|
||||||
ui_widget_adv.selected_feature->clear();
|
ui_widget_adv.selected_feature->clear();
|
||||||
classif->fill_display_combo_box(ui_widget.display, ui_widget_adv.selected_feature);
|
classif->fill_display_combo_box(ui_widget.display, ui_widget_adv.selected_feature);
|
||||||
if (index >= ui_widget.display->count())
|
if (index >= ui_widget.display->count())
|
||||||
|
{
|
||||||
ui_widget.display->setCurrentIndex(1);
|
ui_widget.display->setCurrentIndex(1);
|
||||||
|
change_color (classif, 1);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
ui_widget.display->setCurrentIndex(index);
|
ui_widget.display->setCurrentIndex(index);
|
||||||
|
change_color (classif, index);
|
||||||
|
}
|
||||||
ui_widget_adv.selected_feature->setCurrentIndex(0);
|
ui_widget_adv.selected_feature->setCurrentIndex(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -448,12 +491,6 @@ public Q_SLOTS:
|
||||||
dynamic_cast<Surface_mesh_item_classification*>(it->second)->set_selection_item(selection_item);
|
dynamic_cast<Surface_mesh_item_classification*>(it->second)->set_selection_item(selection_item);
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
else if (Scene_points_with_normal_item* points_item
|
|
||||||
= qobject_cast<Scene_points_with_normal_item*>(item))
|
|
||||||
return create_from_item(points_item);
|
|
||||||
else if (Scene_surface_mesh_item* mesh_item
|
|
||||||
= qobject_cast<Scene_surface_mesh_item*>(item))
|
|
||||||
return create_from_item(mesh_item);
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -504,25 +541,68 @@ public Q_SLOTS:
|
||||||
update_plugin_from_item(classif);
|
update_plugin_from_item(classif);
|
||||||
return classif;
|
return classif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get_classifier ()
|
||||||
|
{
|
||||||
|
if (classifier->text() == QString(CGAL_CLASSIFICATION_ETHZ_ID))
|
||||||
|
return 1;
|
||||||
|
if (classifier->text() == QString(CGAL_CLASSIFICATION_TENSORFLOW_ID))
|
||||||
|
return 3;
|
||||||
|
if (classifier->text() == QString(CGAL_CLASSIFICATION_OPENCV_ID))
|
||||||
|
return 2;
|
||||||
|
if (classifier->text() == QString(CGAL_CLASSIFICATION_SOWF_ID))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
std::cerr << "Error: unknown classifier" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void run (Item_classification_base* classif, int method,
|
void run (Item_classification_base* classif, int method,
|
||||||
std::size_t subdivisions = 1,
|
std::size_t subdivisions = 1,
|
||||||
double smoothing = 0.5)
|
double smoothing = 0.5)
|
||||||
{
|
{
|
||||||
classif->run (method, ui_widget.classifier->currentIndex(), subdivisions, smoothing);
|
classif->run (method, get_classifier(), subdivisions, smoothing);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_classifier_changed (int index)
|
void on_help_clicked()
|
||||||
{
|
{
|
||||||
if (index == 0)
|
QMessageBox::information(dock_widget, QString("Classification"),
|
||||||
{
|
QString("Classification\n"
|
||||||
dock_widget_adv->show();
|
"\n"
|
||||||
dock_widget_adv->raise();
|
"Welcome to CGAL Classification! Please read carefully this notice\n"
|
||||||
}
|
"before using the plugin.\n"
|
||||||
else
|
"\n"
|
||||||
dock_widget_adv->hide();
|
"[QUICK INTRODUCTION]\n"
|
||||||
|
"\n"
|
||||||
|
"In order to classify, you need to perform the following steps:\n"
|
||||||
|
"\n"
|
||||||
|
"1. Compute the features\n"
|
||||||
|
"2. Set up the labels (ground, vegetation, etc.) that you need\n"
|
||||||
|
"3. Select a training set for each of these labels\n"
|
||||||
|
"4. Train the classifier\n"
|
||||||
|
"\n"
|
||||||
|
"You can then either select more inliers for training and train again\n"
|
||||||
|
"to improve the results, classify with or without regularization or\n"
|
||||||
|
"save the classifier's configuration.\n"
|
||||||
|
"\n"
|
||||||
|
"When loading a classifier's configuration, the computed features\n"
|
||||||
|
"should be the same (same number of scales, etc.) and the labels should\n"
|
||||||
|
"be the same as when the classifier's configuration was saved.\n"
|
||||||
|
"\n"
|
||||||
|
"For more information, please refer to the CGAL manual.\n"
|
||||||
|
"\n"
|
||||||
|
"[IMPORTANT NOTICE ON SAVING CLASSIFIED ITEMS]\n"
|
||||||
|
"\n"
|
||||||
|
"If you intend to save the file after classifying, PLEASE CLOSE THE\n"
|
||||||
|
"CLASSIFICATION PLUGIN FIRST: for visualization, colors are saved in\n"
|
||||||
|
"the point set. If you do not close the classification plugin, colors\n"
|
||||||
|
"will be saved and might overwrite existing colors of the point cloud.\n"
|
||||||
|
"\n"
|
||||||
|
"Classification results will be saved if you use the PLY or LAS\n"
|
||||||
|
"formats. Training will be saved if you use the PLY format.\n"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_compute_features_button_clicked()
|
void on_compute_features_button_clicked()
|
||||||
{
|
{
|
||||||
Item_classification_base* classif
|
Item_classification_base* classif
|
||||||
|
|
@ -533,17 +613,26 @@ public Q_SLOTS:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = false;
|
QMultipleInputDialog dialog ("Compute Features", mw);
|
||||||
int nb_scales = QInputDialog::getInt((QWidget*)mw,
|
QSpinBox* scales = dialog.add<QSpinBox> ("Number of scales:");
|
||||||
tr("Compute Features"), // dialog title
|
scales->setRange (1, 99);
|
||||||
tr("Number of scales:"), // field label
|
scales->setValue (5);
|
||||||
5, 1, 99, 1, &ok);
|
|
||||||
if (!ok)
|
QDoubleSpinBox* voxel_size = dialog.add<QDoubleSpinBox> ("Voxel size (0 for automatic):");
|
||||||
|
voxel_size->setRange (0.0, 10000.0);
|
||||||
|
voxel_size->setValue (0.0);
|
||||||
|
voxel_size->setSingleStep (0.01);
|
||||||
|
|
||||||
|
if (dialog.exec() != QDialog::Accepted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
|
|
||||||
classif->compute_features (std::size_t(nb_scales));
|
float vsize = float(voxel_size->value());
|
||||||
|
if (vsize == 0.f)
|
||||||
|
vsize = -1.f; // auto value
|
||||||
|
|
||||||
|
classif->compute_features (std::size_t(scales->value()), vsize);
|
||||||
|
|
||||||
update_plugin_from_item(classif);
|
update_plugin_from_item(classif);
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
@ -562,18 +651,19 @@ public Q_SLOTS:
|
||||||
|
|
||||||
QString filename;
|
QString filename;
|
||||||
|
|
||||||
if (ui_widget.classifier->currentIndex() == 0)
|
int classifier = get_classifier();
|
||||||
|
if (classifier == 0) // Sum of Weighted Featuers
|
||||||
filename = QFileDialog::getSaveFileName(mw,
|
filename = QFileDialog::getSaveFileName(mw,
|
||||||
tr("Save classification configuration"),
|
tr("Save classification configuration"),
|
||||||
tr("%1 (CGAL classif config).xml").arg(classif->item()->name()),
|
tr("%1 (CGAL classif config).xml").arg(classif->item()->name()),
|
||||||
"CGAL classification configuration (*.xml);;");
|
"CGAL classification configuration (*.xml);;");
|
||||||
else if (ui_widget.classifier->currentIndex() == 1)
|
else if (classifier == 1) // Random Forest (ETHZ)
|
||||||
filename = QFileDialog::getSaveFileName(mw,
|
filename = QFileDialog::getSaveFileName(mw,
|
||||||
tr("Save classification configuration"),
|
tr("Save classification configuration"),
|
||||||
tr("%1 (ETHZ random forest config).gz").arg(classif->item()->name()),
|
tr("%1 (ETHZ random forest config).gz").arg(classif->item()->name()),
|
||||||
"Compressed ETHZ random forest configuration (*.gz);;");
|
"Compressed ETHZ random forest configuration (*.gz);;");
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
else if (ui_widget.classifier->currentIndex() == 2)
|
else if (classifier == 2) // Random Forest (OpenCV)
|
||||||
filename = QFileDialog::getSaveFileName(mw,
|
filename = QFileDialog::getSaveFileName(mw,
|
||||||
tr("Save classification configuration"),
|
tr("Save classification configuration"),
|
||||||
tr("%1 (OpenCV %2.%3 random forest config).xml")
|
tr("%1 (OpenCV %2.%3 random forest config).xml")
|
||||||
|
|
@ -584,6 +674,13 @@ public Q_SLOTS:
|
||||||
.arg(CV_MAJOR_VERSION)
|
.arg(CV_MAJOR_VERSION)
|
||||||
.arg(CV_MINOR_VERSION));
|
.arg(CV_MINOR_VERSION));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
else if (classifier == 3) // Neural Network (TensorFlow)
|
||||||
|
filename = QFileDialog::getSaveFileName(mw,
|
||||||
|
tr("Save classification configuration"),
|
||||||
|
tr("%1 (CGAL Neural Network config).xml").arg(classif->item()->name()),
|
||||||
|
"CGAL TensorFlow Neural Network classification configuration (*.xml);;");
|
||||||
|
#endif
|
||||||
|
|
||||||
if (filename == QString())
|
if (filename == QString())
|
||||||
return;
|
return;
|
||||||
|
|
@ -591,8 +688,7 @@ public Q_SLOTS:
|
||||||
|
|
||||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
|
|
||||||
classif->save_config (filename.toStdString().c_str(),
|
classif->save_config (filename.toStdString().c_str(), classifier);
|
||||||
ui_widget.classifier->currentIndex());
|
|
||||||
|
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
|
@ -609,18 +705,19 @@ public Q_SLOTS:
|
||||||
}
|
}
|
||||||
QString filename;
|
QString filename;
|
||||||
|
|
||||||
if (ui_widget.classifier->currentIndex() == 0)
|
int classifier = get_classifier();
|
||||||
|
if (classifier == 0) // SOWF
|
||||||
filename = QFileDialog::getOpenFileName(mw,
|
filename = QFileDialog::getOpenFileName(mw,
|
||||||
tr("Open CGAL classification configuration"),
|
tr("Open CGAL classification configuration"),
|
||||||
".",
|
".",
|
||||||
"CGAL classification configuration (*.xml);;All Files (*)");
|
"CGAL classification configuration (*.xml);;All Files (*)");
|
||||||
else if (ui_widget.classifier->currentIndex() == 1)
|
else if (classifier == 1) // ETHZ
|
||||||
filename = QFileDialog::getOpenFileName(mw,
|
filename = QFileDialog::getOpenFileName(mw,
|
||||||
tr("Open ETHZ random forest configuration"),
|
tr("Open ETHZ random forest configuration"),
|
||||||
".",
|
".",
|
||||||
"Compressed ETHZ random forest configuration (*.gz);;All Files (*)");
|
"Compressed ETHZ random forest configuration (*.gz);;All Files (*)");
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
else if (ui_widget.classifier->currentIndex() == 2)
|
else if (classifier == 2) // OpenCV
|
||||||
filename = QFileDialog::getOpenFileName(mw,
|
filename = QFileDialog::getOpenFileName(mw,
|
||||||
tr("Open OpenCV %2.%3 random forest configuration")
|
tr("Open OpenCV %2.%3 random forest configuration")
|
||||||
.arg(CV_MAJOR_VERSION)
|
.arg(CV_MAJOR_VERSION)
|
||||||
|
|
@ -630,14 +727,21 @@ public Q_SLOTS:
|
||||||
.arg(CV_MAJOR_VERSION)
|
.arg(CV_MAJOR_VERSION)
|
||||||
.arg(CV_MINOR_VERSION));
|
.arg(CV_MINOR_VERSION));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
else if (classifier == 3) // TensorFlow
|
||||||
|
filename = QFileDialog::getOpenFileName(mw,
|
||||||
|
tr("Open CGAL Neural Network classification configuration"),
|
||||||
|
".",
|
||||||
|
tr("CGAL Neural Network classification configuration (*.xml);;All Files (*)"));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (filename == QString())
|
if (filename == QString())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
|
|
||||||
classif->load_config (filename.toStdString().c_str(),
|
classif->load_config (filename.toStdString().c_str(), classifier);
|
||||||
ui_widget.classifier->currentIndex());
|
|
||||||
update_plugin_from_item(classif);
|
update_plugin_from_item(classif);
|
||||||
run (classif, 0);
|
run (classif, 0);
|
||||||
|
|
||||||
|
|
@ -645,7 +749,32 @@ public Q_SLOTS:
|
||||||
item_changed(classif->item());
|
item_changed(classif->item());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void change_color (Item_classification_base* classif, int index)
|
||||||
|
{
|
||||||
|
float vmin = std::numeric_limits<float>::infinity();
|
||||||
|
float vmax = std::numeric_limits<float>::infinity();
|
||||||
|
|
||||||
|
classif->change_color (index, &vmin, &vmax);
|
||||||
|
|
||||||
|
if (vmin == std::numeric_limits<float>::infinity() || vmax == std::numeric_limits<float>::infinity())
|
||||||
|
{
|
||||||
|
ui_widget.minDisplay->setEnabled(false);
|
||||||
|
ui_widget.minDisplay->setText("Min");
|
||||||
|
ui_widget.maxDisplay->setEnabled(false);
|
||||||
|
ui_widget.maxDisplay->setText("Max");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui_widget.minDisplay->setEnabled(true);
|
||||||
|
ui_widget.minDisplay->setText(tr("Min (%1)").arg(vmin));
|
||||||
|
ui_widget.maxDisplay->setEnabled(true);
|
||||||
|
ui_widget.maxDisplay->setText(tr("Max (%1)").arg(vmax));
|
||||||
|
}
|
||||||
|
|
||||||
|
item_changed(classif->item());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void on_display_button_clicked(int index)
|
void on_display_button_clicked(int index)
|
||||||
{
|
{
|
||||||
Item_classification_base* classif
|
Item_classification_base* classif
|
||||||
|
|
@ -653,7 +782,87 @@ public Q_SLOTS:
|
||||||
if(!classif)
|
if(!classif)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
classif->change_color (index);
|
change_color (classif, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
float display_button_value (QPushButton* button)
|
||||||
|
{
|
||||||
|
std::string text = button->text().toStdString();
|
||||||
|
|
||||||
|
std::size_t pos1 = text.find('(');
|
||||||
|
if (pos1 == std::string::npos)
|
||||||
|
return std::numeric_limits<float>::infinity();
|
||||||
|
std::size_t pos2 = text.find(')');
|
||||||
|
if (pos2 == std::string::npos)
|
||||||
|
return std::numeric_limits<float>::infinity();
|
||||||
|
|
||||||
|
std::string fstring (text.begin() + pos1 + 1,
|
||||||
|
text.begin() + pos2);
|
||||||
|
|
||||||
|
return float (std::atof(fstring.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_min_display_button_clicked()
|
||||||
|
{
|
||||||
|
Item_classification_base* classif
|
||||||
|
= get_classification();
|
||||||
|
if(!classif)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float vmin = display_button_value (ui_widget.minDisplay);
|
||||||
|
float vmax = display_button_value (ui_widget.maxDisplay);
|
||||||
|
|
||||||
|
if (vmin == std::numeric_limits<float>::infinity()
|
||||||
|
|| vmax == std::numeric_limits<float>::infinity())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
vmin = float(QInputDialog::getDouble((QWidget*)mw,
|
||||||
|
tr("Set display ramp minimum value (saturate under):"),
|
||||||
|
tr("Minimum value (pale blue):"),
|
||||||
|
double(vmin),
|
||||||
|
-10000000.0,
|
||||||
|
double(vmax), 5, &ok));
|
||||||
|
if (!ok)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int index = ui_widget.display->currentIndex();
|
||||||
|
|
||||||
|
classif->change_color (index, &vmin, &vmax);
|
||||||
|
ui_widget.minDisplay->setText(tr("Min* (%1)").arg(vmin));
|
||||||
|
|
||||||
|
item_changed(classif->item());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_max_display_button_clicked()
|
||||||
|
{
|
||||||
|
Item_classification_base* classif
|
||||||
|
= get_classification();
|
||||||
|
if(!classif)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float vmin = display_button_value (ui_widget.minDisplay);
|
||||||
|
float vmax = display_button_value (ui_widget.maxDisplay);
|
||||||
|
|
||||||
|
if (vmin == std::numeric_limits<float>::infinity()
|
||||||
|
|| vmax == std::numeric_limits<float>::infinity())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
vmax = float(QInputDialog::getDouble((QWidget*)mw,
|
||||||
|
tr("Set display ramp maximum value (saturate over):"),
|
||||||
|
tr("Maximum value (dark red):"),
|
||||||
|
double(vmax),
|
||||||
|
double(vmin),
|
||||||
|
10000000.0, 5, &ok));
|
||||||
|
if (!ok)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int index = ui_widget.display->currentIndex();
|
||||||
|
|
||||||
|
classif->change_color (index, &vmin, &vmax);
|
||||||
|
ui_widget.maxDisplay->setText(tr("Max* (%1)").arg(vmax));
|
||||||
|
|
||||||
item_changed(classif->item());
|
item_changed(classif->item());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -845,6 +1054,7 @@ public Q_SLOTS:
|
||||||
add_new_label (classif, n);
|
add_new_label (classif, n);
|
||||||
|
|
||||||
add_label_button();
|
add_label_button();
|
||||||
|
update_plugin_from_item(classif);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_use_config_building_clicked()
|
void on_use_config_building_clicked()
|
||||||
|
|
@ -1065,6 +1275,68 @@ public Q_SLOTS:
|
||||||
(bbox.zmin() + bbox.zmax()) / 2.) + offset);
|
(bbox.zmin() + bbox.zmax()) / 2.) + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_statistics_clicked()
|
||||||
|
{
|
||||||
|
Item_classification_base* classif
|
||||||
|
= get_classification();
|
||||||
|
if(!classif)
|
||||||
|
{
|
||||||
|
print_message("Error: there is no point set classification item!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
|
std::string str = classif->feature_statistics();
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
|
QMultipleInputDialog dialog ("Feature Statistics", mw);
|
||||||
|
QLabel* text = dialog.add<QLabel> ("");
|
||||||
|
text->setText(str.c_str());
|
||||||
|
dialog.exec_no_cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_switch_classifier_clicked()
|
||||||
|
{
|
||||||
|
QMultipleInputDialog dialog ("Which classifier do you want to use?", mw);
|
||||||
|
|
||||||
|
QRadioButton* ethz = dialog.add<QRadioButton> (CGAL_CLASSIFICATION_ETHZ_ID);
|
||||||
|
ethz->setChecked(true);
|
||||||
|
|
||||||
|
QRadioButton* sowf = dialog.add<QRadioButton> (CGAL_CLASSIFICATION_SOWF_ID);
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
QRadioButton* tensorflow = dialog.add<QRadioButton> (CGAL_CLASSIFICATION_TENSORFLOW_ID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
|
QRadioButton* opencv = dialog.add<QRadioButton> (CGAL_CLASSIFICATION_OPENCV_ID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dialog.exec() != QDialog::Accepted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ethz->isChecked())
|
||||||
|
classifier->setText(CGAL_CLASSIFICATION_ETHZ_ID);
|
||||||
|
else if (sowf->isChecked())
|
||||||
|
classifier->setText(CGAL_CLASSIFICATION_SOWF_ID);
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
else if (tensorflow->isChecked())
|
||||||
|
classifier->setText(CGAL_CLASSIFICATION_TENSORFLOW_ID);
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
|
else if (opencv->isChecked())
|
||||||
|
classifier->setText(CGAL_CLASSIFICATION_OPENCV_ID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (sowf->isChecked())
|
||||||
|
{
|
||||||
|
dock_widget_adv->show();
|
||||||
|
dock_widget_adv->raise();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dock_widget_adv->hide();
|
||||||
|
}
|
||||||
|
|
||||||
void on_train_clicked()
|
void on_train_clicked()
|
||||||
{
|
{
|
||||||
Item_classification_base* classif
|
Item_classification_base* classif
|
||||||
|
|
@ -1075,41 +1347,48 @@ public Q_SLOTS:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nb_trials = 0;
|
QMultipleInputDialog dialog ("Train Classifier", mw);
|
||||||
int num_trees = 0;
|
|
||||||
int max_depth = 0;
|
int classifier = get_classifier();
|
||||||
|
if (classifier == 0) // SOWF
|
||||||
if (ui_widget.classifier->currentIndex() == 0)
|
|
||||||
{
|
{
|
||||||
bool ok = false;
|
QSpinBox* trials = dialog.add<QSpinBox> ("Number of trials: ", "trials");
|
||||||
nb_trials = QInputDialog::getInt((QWidget*)mw,
|
trials->setRange (1, 99999);
|
||||||
tr("Train Classifier"), // dialog title
|
trials->setValue (800);
|
||||||
tr("Number of trials:"), // field label
|
|
||||||
800, 1, 99999, 50, &ok);
|
|
||||||
if (!ok)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else
|
else if (classifier == 1 || classifier == 2) // random forest
|
||||||
{
|
{
|
||||||
QMultipleInputDialog dialog ("Train Random Forest Classifier", mw);
|
QSpinBox* trees = dialog.add<QSpinBox> ("Number of trees: ", "num_trees");
|
||||||
QSpinBox* trees = dialog.add<QSpinBox> ("Number of trees: ");
|
|
||||||
trees->setRange (1, 9999);
|
trees->setRange (1, 9999);
|
||||||
trees->setValue (25);
|
trees->setValue (25);
|
||||||
QSpinBox* depth = dialog.add<QSpinBox> ("Maximum depth of tree: ");
|
QSpinBox* depth = dialog.add<QSpinBox> ("Maximum depth of tree: ", "max_depth");
|
||||||
depth->setRange (1, 9999);
|
depth->setRange (1, 9999);
|
||||||
depth->setValue (20);
|
depth->setValue (20);
|
||||||
|
|
||||||
if (dialog.exec() != QDialog::Accepted)
|
|
||||||
return;
|
|
||||||
num_trees = trees->value();
|
|
||||||
max_depth = depth->value();
|
|
||||||
}
|
}
|
||||||
|
else if (classifier == 3) // neural network
|
||||||
|
{
|
||||||
|
QSpinBox* trials = dialog.add<QSpinBox> ("Number of trials: ", "trials");
|
||||||
|
trials->setRange (1, 99999);
|
||||||
|
trials->setValue (500);
|
||||||
|
QDoubleSpinBox* rate = dialog.add<QDoubleSpinBox> ("Learning rate: ", "learning_rate");
|
||||||
|
rate->setRange (0.00001, 10000.0);
|
||||||
|
rate->setValue (0.001);
|
||||||
|
rate->setDecimals (5);
|
||||||
|
QSpinBox* batch = dialog.add<QSpinBox> ("Batch size: ", "batch_size");
|
||||||
|
batch->setRange (1, 2000000000);
|
||||||
|
batch->setValue (1000);
|
||||||
|
dialog.add<QLineEdit> ("Hidden layer size(s): ", "hidden_layers");
|
||||||
|
QCheckBox* restart = dialog.add<QCheckBox> ("Restart from scratch: ", "restart");
|
||||||
|
restart->setChecked (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialog.exec() != QDialog::Accepted)
|
||||||
|
return;
|
||||||
|
|
||||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
CGAL::Real_timer t;
|
CGAL::Real_timer t;
|
||||||
t.start();
|
t.start();
|
||||||
classif->train(ui_widget.classifier->currentIndex(), nb_trials,
|
classif->train(classifier, dialog);
|
||||||
num_trees, max_depth);
|
|
||||||
t.stop();
|
t.stop();
|
||||||
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
|
std::cerr << "Done in " << t.time() << " second(s)" << std::endl;
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
@ -1170,6 +1449,10 @@ public Q_SLOTS:
|
||||||
connect(change_color, SIGNAL(triggered()), this,
|
connect(change_color, SIGNAL(triggered()), this,
|
||||||
SLOT(on_color_changed_clicked()));
|
SLOT(on_color_changed_clicked()));
|
||||||
|
|
||||||
|
QAction* change_name = label_buttons.back().menu->addAction ("Change name");
|
||||||
|
connect(change_name, SIGNAL(triggered()), this,
|
||||||
|
SLOT(on_name_changed_clicked()));
|
||||||
|
|
||||||
QAction* create = label_buttons.back().menu->addAction ("Create point set item from labeled points");
|
QAction* create = label_buttons.back().menu->addAction ("Create point set item from labeled points");
|
||||||
connect(create, SIGNAL(triggered()), this,
|
connect(create, SIGNAL(triggered()), this,
|
||||||
SLOT(on_create_point_set_item()));
|
SLOT(on_create_point_set_item()));
|
||||||
|
|
@ -1244,7 +1527,7 @@ public Q_SLOTS:
|
||||||
label_buttons.erase (label_buttons.begin() + position);
|
label_buttons.erase (label_buttons.begin() + position);
|
||||||
add_label_button();
|
add_label_button();
|
||||||
}
|
}
|
||||||
|
update_plugin_from_item(classif);
|
||||||
item_changed(classif->item());
|
item_changed(classif->item());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1270,7 +1553,11 @@ public Q_SLOTS:
|
||||||
int position = row_index * 3 + column_index;
|
int position = row_index * 3 + column_index;
|
||||||
|
|
||||||
QColor color = label_buttons[position].color;
|
QColor color = label_buttons[position].color;
|
||||||
color = QColorDialog::getColor(color, (QWidget*)mw, "Change of color of label");
|
color = QColorDialog::getColor(color, (QWidget*)mw, "Change color of label");
|
||||||
|
|
||||||
|
if (!color.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
label_buttons[position].change_color (color);
|
label_buttons[position].change_color (color);
|
||||||
classif->change_label_color (position,
|
classif->change_label_color (position,
|
||||||
color);
|
color);
|
||||||
|
|
@ -1279,6 +1566,47 @@ public Q_SLOTS:
|
||||||
item_changed(classif->item());
|
item_changed(classif->item());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_name_changed_clicked()
|
||||||
|
{
|
||||||
|
Item_classification_base* classif
|
||||||
|
= get_classification();
|
||||||
|
if(!classif)
|
||||||
|
{
|
||||||
|
print_message("Error: there is no point set classification item!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton* label_clicked = qobject_cast<QPushButton*>(QObject::sender()->parent()->parent());
|
||||||
|
if (label_clicked == NULL)
|
||||||
|
std::cerr << "Error" << std::endl;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int index = ui_widget.labelGrid->indexOf(label_clicked);
|
||||||
|
int row_index, column_index, row_span, column_span;
|
||||||
|
ui_widget.labelGrid->getItemPosition(index, &row_index, &column_index, &row_span, &column_span);
|
||||||
|
|
||||||
|
int position = row_index * 3 + column_index;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
QString name =
|
||||||
|
QInputDialog::getText((QWidget*)mw,
|
||||||
|
tr("Change name of label"), // dialog title
|
||||||
|
tr("New name:"), // field label
|
||||||
|
QLineEdit::Normal,
|
||||||
|
classif->label(position)->name().c_str(),
|
||||||
|
&ok);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
return;
|
||||||
|
|
||||||
|
classif->change_label_name (position, name.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
update_plugin_from_item(classif);
|
||||||
|
classif->update_color ();
|
||||||
|
item_changed(classif->item());
|
||||||
|
}
|
||||||
|
|
||||||
void on_add_selection_to_training_set_clicked()
|
void on_add_selection_to_training_set_clicked()
|
||||||
{
|
{
|
||||||
Item_classification_base* classif
|
Item_classification_base* classif
|
||||||
|
|
@ -1311,10 +1639,7 @@ public Q_SLOTS:
|
||||||
Item_classification_base* classif
|
Item_classification_base* classif
|
||||||
= get_classification();
|
= get_classification();
|
||||||
if(!classif)
|
if(!classif)
|
||||||
{
|
|
||||||
print_message("Error: there is no point set classification item!");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (classif->number_of_features() <= (std::size_t)v)
|
if (classif->number_of_features() <= (std::size_t)v)
|
||||||
return;
|
return;
|
||||||
|
|
@ -1407,16 +1732,19 @@ private:
|
||||||
|
|
||||||
QDockWidget* dock_widget;
|
QDockWidget* dock_widget;
|
||||||
QDockWidget* dock_widget_adv;
|
QDockWidget* dock_widget_adv;
|
||||||
QAction* action_train;
|
QAction* action_statistics;
|
||||||
QAction* action_reset_local;
|
QAction* action_reset_local;
|
||||||
QAction* action_reset;
|
QAction* action_reset;
|
||||||
QAction* action_random_region;
|
QAction* action_random_region;
|
||||||
QAction* action_validate;
|
QAction* action_validate;
|
||||||
QAction* action_save_config;
|
|
||||||
QAction* action_load_config;
|
QAction* classifier;
|
||||||
|
QAction* action_train;
|
||||||
QAction* action_run;
|
QAction* action_run;
|
||||||
QAction* action_run_smoothed;
|
QAction* action_run_smoothed;
|
||||||
QAction* action_run_graphcut;
|
QAction* action_run_graphcut;
|
||||||
|
QAction* action_save_config;
|
||||||
|
QAction* action_load_config;
|
||||||
|
|
||||||
std::vector<LabelButton> label_buttons;
|
std::vector<LabelButton> label_buttons;
|
||||||
QPushButton* label_button;
|
QPushButton* label_button;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>289</width>
|
<width>411</width>
|
||||||
<height>154</height>
|
<height>194</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
|
@ -21,112 +21,151 @@
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="dockWidgetContents">
|
<widget class="QWidget" name="dockWidgetContents">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="menu">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Classification</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>View:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="display">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Real colors</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Classification</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Training sets</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QMenuBar" name="menubar">
|
||||||
<property name="sizePolicy">
|
<widget class="QMenu" name="features_menu">
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
<property name="title">
|
||||||
<horstretch>0</horstretch>
|
<string>Features</string>
|
||||||
<verstretch>0</verstretch>
|
</property>
|
||||||
</sizepolicy>
|
</widget>
|
||||||
|
<widget class="QMenu" name="training_menu">
|
||||||
|
<property name="title">
|
||||||
|
<string>Training Sets</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="classifier_menu">
|
||||||
|
<property name="title">
|
||||||
|
<string>Classifier</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<addaction name="features_menu"/>
|
||||||
|
<addaction name="training_menu"/>
|
||||||
|
<addaction name="classifier_menu"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="help">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Classifier:</string>
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../Polyhedron_3.qrc">
|
||||||
|
<normaloff>:/cgal/icons/resources/help_button.png</normaloff>:/cgal/icons/resources/help_button.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="classifier">
|
<widget class="QPushButton" name="close">
|
||||||
<property name="currentIndex">
|
<property name="text">
|
||||||
<number>1</number>
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../Polyhedron_3.qrc">
|
||||||
|
<normaloff>:/cgal/icons/check-off.png</normaloff>:/cgal/icons/check-off.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Sum of Weighted Features</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Random Forest (ETHZ)</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frame">
|
<widget class="QGroupBox" name="view">
|
||||||
|
<property name="title">
|
||||||
|
<string>View</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="display">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Real colors</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Classification</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Training sets</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="minDisplay">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Min</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>65</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="maxDisplay">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Max</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="frame">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShape">
|
<property name="title">
|
||||||
<enum>QFrame::StyledPanel</enum>
|
<string>Labels</string>
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Raised</enum>
|
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
|
|
@ -155,6 +194,8 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources>
|
||||||
|
<include location="../../Polyhedron_3.qrc"/>
|
||||||
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
#include <CGAL/Timer.h>
|
#include <CGAL/Timer.h>
|
||||||
#include <CGAL/Memory_sizer.h>
|
#include <CGAL/Memory_sizer.h>
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
#include <CGAL/Three/Viewer_interface.h>
|
#include <CGAL/Three/Viewer_interface.h>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
@ -21,6 +23,7 @@
|
||||||
|
|
||||||
Cluster_classification::Cluster_classification(Scene_points_with_normal_item* points)
|
Cluster_classification::Cluster_classification(Scene_points_with_normal_item* points)
|
||||||
: m_points (points)
|
: m_points (points)
|
||||||
|
, m_input_is_las (false)
|
||||||
{
|
{
|
||||||
m_index_color = 1;
|
m_index_color = 1;
|
||||||
|
|
||||||
|
|
@ -59,6 +62,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po
|
||||||
boost::tie (las_classif, las_found) = m_points->point_set()->property_map<unsigned char>("classification");
|
boost::tie (las_classif, las_found) = m_points->point_set()->property_map<unsigned char>("classification");
|
||||||
if (las_found)
|
if (las_found)
|
||||||
{
|
{
|
||||||
|
m_input_is_las = true;
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
{
|
{
|
||||||
|
|
@ -126,20 +130,26 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po
|
||||||
|
|
||||||
if (training_found)
|
if (training_found)
|
||||||
{
|
{
|
||||||
if (las_found && (m_training[*it] == 0 || m_training[*it] == 1)) // Unclassified class in LAS
|
if (std::size_t(current_idx) != used_indices.size()) // Empty indices -> reorder indices in point set
|
||||||
m_training[*it] = -1;
|
{
|
||||||
else if (m_training[*it] != -1)
|
if (las_found && (m_training[*it] == 0 || m_training[*it] == 1)) // Unclassified class in LAS
|
||||||
m_training[*it] = used_indices[std::size_t(m_training[*it])];
|
m_training[*it] = -1;
|
||||||
if (c != -1)
|
else if (m_training[*it] != -1)
|
||||||
|
m_training[*it] = used_indices[std::size_t(m_training[*it])];
|
||||||
|
}
|
||||||
|
if (c != -1 && m_training[*it] != -1)
|
||||||
m_clusters[c].training() = m_training[*it];
|
m_clusters[c].training() = m_training[*it];
|
||||||
}
|
}
|
||||||
if (classif_found)
|
if (classif_found)
|
||||||
{
|
{
|
||||||
if (las_found && (m_classif[*it] == 0 || m_classif[*it] == 1)) // Unclassified class in LAS
|
if (std::size_t(current_idx) != used_indices.size()) // Empty indices -> reorder indices in point set
|
||||||
m_classif[*it] = -1;
|
{
|
||||||
else if (m_classif[*it] != -1)
|
if (las_found && (m_classif[*it] == 0 || m_classif[*it] == 1)) // Unclassified class in LAS
|
||||||
m_classif[*it] = used_indices[std::size_t(m_classif[*it])];
|
m_classif[*it] = -1;
|
||||||
if (c != -1)
|
else if (m_classif[*it] != -1)
|
||||||
|
m_classif[*it] = used_indices[std::size_t(m_classif[*it])];
|
||||||
|
}
|
||||||
|
if (c != -1 && m_classif[*it] != -1)
|
||||||
m_clusters[c].label() = m_classif[*it];
|
m_clusters[c].label() = m_classif[*it];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,9 +231,12 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po
|
||||||
update_comments_of_point_set_item();
|
update_comments_of_point_set_item();
|
||||||
|
|
||||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||||
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
m_ethz = NULL;
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
m_random_forest = NULL;
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
m_neural_network = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Compute neighborhood
|
// Compute neighborhood
|
||||||
|
|
@ -272,17 +285,6 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po
|
||||||
|
|
||||||
Cluster_classification::~Cluster_classification()
|
Cluster_classification::~Cluster_classification()
|
||||||
{
|
{
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
int c = m_cluster_id[*it];
|
|
||||||
if (c != -1)
|
|
||||||
{
|
|
||||||
m_training[*it] = m_clusters[c].training();
|
|
||||||
m_classif[*it] = m_clusters[c].label();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_sowf != NULL)
|
if (m_sowf != NULL)
|
||||||
delete m_sowf;
|
delete m_sowf;
|
||||||
if (m_ethz != NULL)
|
if (m_ethz != NULL)
|
||||||
|
|
@ -290,9 +292,100 @@ Cluster_classification::~Cluster_classification()
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
if (m_random_forest != NULL)
|
if (m_random_forest != NULL)
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
delete m_neural_network;
|
||||||
#endif
|
#endif
|
||||||
if (m_points != NULL)
|
if (m_points != NULL)
|
||||||
{
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
int c = m_cluster_id[*it];
|
||||||
|
if (c != -1)
|
||||||
|
{
|
||||||
|
m_training[*it] = m_clusters[c].training();
|
||||||
|
m_classif[*it] = m_clusters[c].label();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_training[*it] = -1;
|
||||||
|
m_classif[*it] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For LAS saving, convert classification info in the LAS standard
|
||||||
|
// if (m_input_is_las)
|
||||||
|
{
|
||||||
|
Point_set::Property_map<unsigned char> las_classif
|
||||||
|
= m_points->point_set()->add_property_map<unsigned char>("classification", 0).first;
|
||||||
|
|
||||||
|
std::vector<unsigned char> label_indices;
|
||||||
|
|
||||||
|
unsigned char custom = 19;
|
||||||
|
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||||
|
{
|
||||||
|
if (m_labels[i]->name() == "ground")
|
||||||
|
label_indices.push_back (2);
|
||||||
|
else if (m_labels[i]->name() == "low_veget")
|
||||||
|
label_indices.push_back (3);
|
||||||
|
else if (m_labels[i]->name() == "med_veget" || m_labels[i]->name() == "vegetation")
|
||||||
|
label_indices.push_back (4);
|
||||||
|
else if (m_labels[i]->name() == "high_veget")
|
||||||
|
label_indices.push_back (5);
|
||||||
|
else if (m_labels[i]->name() == "building" || m_labels[i]->name() == "roof")
|
||||||
|
label_indices.push_back (6);
|
||||||
|
else if (m_labels[i]->name() == "noise")
|
||||||
|
label_indices.push_back (7);
|
||||||
|
else if (m_labels[i]->name() == "reserved" || m_labels[i]->name() == "facade")
|
||||||
|
label_indices.push_back (8);
|
||||||
|
else if (m_labels[i]->name() == "water")
|
||||||
|
label_indices.push_back (9);
|
||||||
|
else if (m_labels[i]->name() == "rail")
|
||||||
|
label_indices.push_back (10);
|
||||||
|
else if (m_labels[i]->name() == "road_surface")
|
||||||
|
label_indices.push_back (11);
|
||||||
|
else if (m_labels[i]->name() == "reserved_2")
|
||||||
|
label_indices.push_back (12);
|
||||||
|
else if (m_labels[i]->name() == "wire_guard")
|
||||||
|
label_indices.push_back (13);
|
||||||
|
else if (m_labels[i]->name() == "wire_conduct")
|
||||||
|
label_indices.push_back (14);
|
||||||
|
else if (m_labels[i]->name() == "trans_tower")
|
||||||
|
label_indices.push_back (15);
|
||||||
|
else if (m_labels[i]->name() == "wire_connect")
|
||||||
|
label_indices.push_back (16);
|
||||||
|
else if (m_labels[i]->name() == "bridge_deck")
|
||||||
|
label_indices.push_back (17);
|
||||||
|
else if (m_labels[i]->name() == "high_noise")
|
||||||
|
label_indices.push_back (18);
|
||||||
|
else
|
||||||
|
label_indices.push_back (custom ++);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->end(); ++ it)
|
||||||
|
{
|
||||||
|
int c = m_classif[*it];
|
||||||
|
unsigned char lc = 1; // unclassified in LAS standard
|
||||||
|
if (c != -1)
|
||||||
|
lc = label_indices[std::size_t(c)];
|
||||||
|
|
||||||
|
las_classif[*it] = lc;
|
||||||
|
|
||||||
|
int t = m_training[*it];
|
||||||
|
unsigned char lt = 1; // unclassified in LAS standard
|
||||||
|
if (t != -1)
|
||||||
|
lt = label_indices[std::size_t(t)];
|
||||||
|
|
||||||
|
m_training[*it] = int(lt);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_points->point_set()->remove_property_map (m_classif);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
reset_colors();
|
reset_colors();
|
||||||
erase_item();
|
erase_item();
|
||||||
}
|
}
|
||||||
|
|
@ -313,17 +406,7 @@ void Cluster_classification::backup_existing_colors_and_add_new()
|
||||||
m_points->point_set()->remove_colors();
|
m_points->point_set()->remove_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_red = m_points->point_set()->add_property_map<unsigned char>("red").first;
|
m_points->point_set()->add_colors();
|
||||||
m_green = m_points->point_set()->add_property_map<unsigned char>("green").first;
|
|
||||||
m_blue = m_points->point_set()->add_property_map<unsigned char>("blue").first;
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
m_red[*it] = 0;
|
|
||||||
m_green[*it] = 0;
|
|
||||||
m_blue[*it] = 0;
|
|
||||||
}
|
|
||||||
m_points->point_set()->check_colors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cluster_classification::reset_colors()
|
void Cluster_classification::reset_colors()
|
||||||
|
|
@ -334,40 +417,13 @@ void Cluster_classification::reset_colors()
|
||||||
{
|
{
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
{
|
m_points->point_set()->set_color(*it, m_color[*it]);
|
||||||
m_red[*it] = m_color[*it][0];
|
|
||||||
m_green[*it] = m_color[*it][1];
|
|
||||||
m_blue[*it] = m_color[*it][2];
|
|
||||||
}
|
|
||||||
m_points->point_set()->remove_property_map(m_color);
|
m_points->point_set()->remove_property_map(m_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write point set to .PLY file
|
void Cluster_classification::change_color (int index, float* vmin, float* vmax)
|
||||||
bool Cluster_classification::write_output(std::ostream& stream)
|
|
||||||
{
|
|
||||||
if (m_features.size() == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
reset_indices();
|
|
||||||
|
|
||||||
stream.precision (std::numeric_limits<double>::digits10 + 2);
|
|
||||||
|
|
||||||
// std::vector<Color> colors;
|
|
||||||
// for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
|
||||||
// {
|
|
||||||
// Color c = {{ (unsigned char)(m_labels[i].second.red()),
|
|
||||||
// (unsigned char)(m_labels[i].second.green()),
|
|
||||||
// (unsigned char)(m_labels[i].second.blue()) }};
|
|
||||||
// colors.push_back (c);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m_psc->write_classification_to_ply (stream);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Cluster_classification::change_color (int index)
|
|
||||||
{
|
{
|
||||||
m_index_color = index;
|
m_index_color = index;
|
||||||
|
|
||||||
|
|
@ -377,138 +433,173 @@ void Cluster_classification::change_color (int index)
|
||||||
static Color_ramp ramp;
|
static Color_ramp ramp;
|
||||||
ramp.build_rainbow();
|
ramp.build_rainbow();
|
||||||
reset_indices();
|
reset_indices();
|
||||||
|
|
||||||
if (index_color == -1) // item color
|
if (index_color == -1) // item color
|
||||||
{
|
m_points->point_set()->remove_colors();
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
m_red[*it] = 0;
|
|
||||||
m_green[*it] = 0;
|
|
||||||
m_blue[*it] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index_color == 0) // real colors
|
|
||||||
{
|
|
||||||
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
m_red[*it] = m_color[*it][0];
|
|
||||||
m_green[*it] = m_color[*it][1];
|
|
||||||
m_blue[*it] = m_color[*it][2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index_color == 1) // classif
|
|
||||||
{
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
QColor color (0, 0, 0);
|
|
||||||
int cid = m_cluster_id[*it];
|
|
||||||
if (cid != -1)
|
|
||||||
{
|
|
||||||
std::size_t c = m_clusters[cid].label();
|
|
||||||
|
|
||||||
if (c != std::size_t(-1))
|
|
||||||
color = m_label_colors[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
m_red[*it] = color.red();
|
|
||||||
m_green[*it] = color.green();
|
|
||||||
m_blue[*it] = color.blue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index_color == 2) // training
|
|
||||||
{
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
QColor color (0, 0, 0);
|
|
||||||
int cid = m_cluster_id[*it];
|
|
||||||
float div = 1;
|
|
||||||
|
|
||||||
if (cid != -1)
|
|
||||||
{
|
|
||||||
int c = m_clusters[cid].training();
|
|
||||||
int c2 = m_clusters[cid].label();
|
|
||||||
|
|
||||||
if (c != -1)
|
|
||||||
color = m_label_colors[std::size_t(c)];
|
|
||||||
|
|
||||||
if (c != c2)
|
|
||||||
div = 2;
|
|
||||||
}
|
|
||||||
m_red[*it] = (color.red() / div);
|
|
||||||
m_green[*it] = (color.green() / div);
|
|
||||||
m_blue[*it] = (color.blue() / div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index_color == 3) // clusters
|
|
||||||
{
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
int cid = m_cluster_id[*it];
|
|
||||||
|
|
||||||
if (cid != -1)
|
|
||||||
{
|
|
||||||
srand(cid);
|
|
||||||
m_red[*it] = 64 + rand() % 192;
|
|
||||||
m_green[*it] = 64 + rand() % 192;
|
|
||||||
m_blue[*it] = 64 + rand() % 192;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_red[*it] = 0;
|
|
||||||
m_green[*it] = 0;
|
|
||||||
m_blue[*it] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Feature_handle feature = m_features[index_color - 4];
|
if (!m_points->point_set()->has_colors())
|
||||||
|
m_points->point_set()->add_colors();
|
||||||
float max = 0.;
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
if (index_color == 0) // real colors
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
{
|
||||||
int cid = m_cluster_id[*it];
|
|
||||||
if (cid != -1)
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
m_points->point_set()->set_color(*it, m_color[*it]);
|
||||||
|
}
|
||||||
|
else if (index_color == 1) // classif
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
{
|
{
|
||||||
if (feature->value(cid) > max)
|
QColor color (0, 0, 0);
|
||||||
max = feature->value(cid);
|
int cid = m_cluster_id[*it];
|
||||||
|
if (cid != -1)
|
||||||
|
{
|
||||||
|
std::size_t c = m_clusters[cid].label();
|
||||||
|
|
||||||
|
if (c != std::size_t(-1))
|
||||||
|
color = m_label_colors[c];
|
||||||
|
}
|
||||||
|
|
||||||
|
m_points->point_set()->set_color(*it, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (index_color == 2) // training
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
{
|
||||||
int cid = m_cluster_id[*it];
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
if (cid != -1)
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
{
|
{
|
||||||
float v = std::max (0.f, feature->value(cid) / max);
|
QColor color (0, 0, 0);
|
||||||
m_red[*it] = (unsigned char)(ramp.r(v) * 255);
|
int cid = m_cluster_id[*it];
|
||||||
m_green[*it] = (unsigned char)(ramp.g(v) * 255);
|
float div = 1;
|
||||||
m_blue[*it] = (unsigned char)(ramp.b(v) * 255);
|
|
||||||
|
if (cid != -1)
|
||||||
|
{
|
||||||
|
int c = m_clusters[cid].training();
|
||||||
|
int c2 = m_clusters[cid].label();
|
||||||
|
|
||||||
|
if (c != -1)
|
||||||
|
color = m_label_colors[std::size_t(c)];
|
||||||
|
|
||||||
|
if (c != c2)
|
||||||
|
div = 2;
|
||||||
|
}
|
||||||
|
m_points->point_set()->set_color(*it, color.red() / div, color.green() / div, color.blue() / div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (index_color == 3) // clusters
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
int cid = m_cluster_id[*it];
|
||||||
|
|
||||||
|
if (cid != -1)
|
||||||
|
{
|
||||||
|
srand(cid);
|
||||||
|
m_points->point_set()->set_color(*it, 64 + rand() % 192, 64 + rand() % 192, 64 + rand() % 192);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_points->point_set()->set_color(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::size_t corrected_index = index_color - 4;
|
||||||
|
if (corrected_index < m_labels.size()) // Display label probabilities
|
||||||
|
{
|
||||||
|
if (m_label_probabilities.size() <= corrected_index ||
|
||||||
|
m_label_probabilities[corrected_index].size() != m_clusters.size())
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
m_points->point_set()->set_color(*it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
int cid = m_cluster_id[*it];
|
||||||
|
if (cid != -1)
|
||||||
|
{
|
||||||
|
float v = std::max (0.f, std::min(1.f, m_label_probabilities[corrected_index][cid]));
|
||||||
|
m_points->point_set()->set_color(*it, ramp.r(v) * 255, ramp.g(v) * 255, ramp.b(v) * 255);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_points->point_set()->set_color(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_red[*it] = 0;
|
corrected_index -= m_labels.size();
|
||||||
m_green[*it] = 0;
|
if (corrected_index >= m_features.size())
|
||||||
m_blue[*it] = 0;
|
{
|
||||||
|
std::cerr << "Error: trying to access feature " << corrected_index << " out of " << m_features.size() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Feature_handle feature = m_features[corrected_index];
|
||||||
|
|
||||||
|
float min = std::numeric_limits<float>::max();
|
||||||
|
float max = -std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
if (vmin != NULL && vmax != NULL
|
||||||
|
&& *vmin != std::numeric_limits<float>::infinity()
|
||||||
|
&& *vmax != std::numeric_limits<float>::infinity())
|
||||||
|
{
|
||||||
|
min = *vmin;
|
||||||
|
max = *vmax;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
int cid = m_cluster_id[*it];
|
||||||
|
if (cid != -1)
|
||||||
|
{
|
||||||
|
if (feature->value(cid) > max)
|
||||||
|
max = feature->value(cid);
|
||||||
|
if (feature->value(cid) < min)
|
||||||
|
min = feature->value(cid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
int cid = m_cluster_id[*it];
|
||||||
|
if (cid != -1)
|
||||||
|
{
|
||||||
|
float v = (feature->value(cid) - min) / (max - min);
|
||||||
|
if (v < 0.f) v = 0.f;
|
||||||
|
if (v > 1.f) v = 1.f;
|
||||||
|
|
||||||
|
m_points->point_set()->set_color(*it, ramp.r(v) * 255, ramp.g(v) * 255, ramp.b(v) * 255);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_points->point_set()->set_color(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vmin != NULL && vmax != NULL)
|
||||||
|
{
|
||||||
|
*vmin = min;
|
||||||
|
*vmax = max;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->first_selected();
|
for (Point_set::const_iterator it = m_points->point_set()->first_selected();
|
||||||
it != m_points->point_set()->end(); ++ it)
|
it != m_points->point_set()->end(); ++ it)
|
||||||
{
|
m_points->point_set()->set_color(*it, 255, 0, 0);
|
||||||
m_red[*it] = 255;
|
|
||||||
m_green[*it] = 0;
|
|
||||||
m_blue[*it] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Cluster_classification::real_index_color() const
|
int Cluster_classification::real_index_color() const
|
||||||
|
|
@ -532,13 +623,18 @@ void Cluster_classification::reset_indices ()
|
||||||
*(indices.begin() + i) = idx ++;
|
*(indices.begin() + i) = idx ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cluster_classification::compute_features (std::size_t nb_scales)
|
void Cluster_classification::compute_features (std::size_t nb_scales, float voxel_size)
|
||||||
{
|
{
|
||||||
CGAL_assertion (!(m_points->point_set()->empty()));
|
CGAL_assertion (!(m_points->point_set()->empty()));
|
||||||
|
|
||||||
reset_indices();
|
reset_indices();
|
||||||
|
|
||||||
std::cerr << "Computing pointwise features with " << nb_scales << " scale(s)" << std::endl;
|
std::cerr << "Computing pointwise features with " << nb_scales << " scale(s) and ";
|
||||||
|
if (voxel_size == -1)
|
||||||
|
std::cerr << "automatic voxel size" << std::endl;
|
||||||
|
else
|
||||||
|
std::cerr << "voxel size = " << voxel_size << std::endl;
|
||||||
|
|
||||||
m_features.clear();
|
m_features.clear();
|
||||||
|
|
||||||
Point_set::Vector_map normal_map;
|
Point_set::Vector_map normal_map;
|
||||||
|
|
@ -556,7 +652,7 @@ void Cluster_classification::compute_features (std::size_t nb_scales)
|
||||||
|
|
||||||
Feature_set pointwise_features;
|
Feature_set pointwise_features;
|
||||||
|
|
||||||
Generator generator (*(m_points->point_set()), m_points->point_set()->point_map(), nb_scales);
|
Generator generator (*(m_points->point_set()), m_points->point_set()->point_map(), nb_scales, voxel_size);
|
||||||
|
|
||||||
CGAL::Real_timer t;
|
CGAL::Real_timer t;
|
||||||
t.start();
|
t.start();
|
||||||
|
|
@ -620,11 +716,24 @@ void Cluster_classification::compute_features (std::size_t nb_scales)
|
||||||
|
|
||||||
delete m_sowf;
|
delete m_sowf;
|
||||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||||
delete m_ethz;
|
if (m_ethz != NULL)
|
||||||
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
{
|
||||||
|
delete m_ethz;
|
||||||
|
m_ethz = NULL;
|
||||||
|
}
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
delete m_random_forest;
|
if (m_random_forest != NULL)
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
{
|
||||||
|
delete m_random_forest;
|
||||||
|
m_random_forest = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::cerr << "Features = " << m_features.size() << std::endl;
|
std::cerr << "Features = " << m_features.size() << std::endl;
|
||||||
|
|
@ -696,8 +805,7 @@ void Cluster_classification::add_remaining_point_set_properties_as_features(Feat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cluster_classification::train(int classifier, unsigned int nb_trials,
|
void Cluster_classification::train(int classifier, const QMultipleInputDialog& dialog)
|
||||||
std::size_t num_trees, std::size_t max_depth)
|
|
||||||
{
|
{
|
||||||
if (m_features.size() == 0)
|
if (m_features.size() == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -706,6 +814,11 @@ void Cluster_classification::train(int classifier, unsigned int nb_trials,
|
||||||
}
|
}
|
||||||
reset_indices();
|
reset_indices();
|
||||||
|
|
||||||
|
m_label_probabilities.clear();
|
||||||
|
m_label_probabilities.resize (m_labels.size());
|
||||||
|
for (std::size_t i = 0; i < m_label_probabilities.size(); ++ i)
|
||||||
|
m_label_probabilities[i].resize (m_clusters.size(), -1);
|
||||||
|
|
||||||
std::vector<std::size_t> nb_label (m_labels.size(), 0);
|
std::vector<std::size_t> nb_label (m_labels.size(), 0);
|
||||||
std::size_t nb_total = 0;
|
std::size_t nb_total = 0;
|
||||||
|
|
||||||
|
|
@ -730,30 +843,80 @@ void Cluster_classification::train(int classifier, unsigned int nb_trials,
|
||||||
|
|
||||||
if (classifier == 0)
|
if (classifier == 0)
|
||||||
{
|
{
|
||||||
m_sowf->train<Concurrency_tag>(training, nb_trials);
|
m_sowf->train<Concurrency_tag>(training, dialog.get<QSpinBox>("trials")->value());
|
||||||
CGAL::Classification::classify<Concurrency_tag> (m_clusters,
|
CGAL::Classification::classify<Concurrency_tag> (m_clusters,
|
||||||
m_labels, *m_sowf,
|
m_labels, *m_sowf,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
}
|
}
|
||||||
else if (classifier == 1)
|
else if (classifier == 1)
|
||||||
{
|
{
|
||||||
m_ethz->train(training, true, num_trees, max_depth);
|
if (m_ethz != NULL)
|
||||||
|
delete m_ethz;
|
||||||
|
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
||||||
|
m_ethz->train<Concurrency_tag>(training, true,
|
||||||
|
dialog.get<QSpinBox>("num_trees")->value(),
|
||||||
|
dialog.get<QSpinBox>("max_depth")->value());
|
||||||
CGAL::Classification::classify<Concurrency_tag> (m_clusters,
|
CGAL::Classification::classify<Concurrency_tag> (m_clusters,
|
||||||
m_labels, *m_ethz,
|
m_labels, *m_ethz,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
}
|
}
|
||||||
else
|
else if (classifier == 2)
|
||||||
{
|
{
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
if (m_random_forest != NULL)
|
if (m_random_forest != NULL)
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
m_random_forest = new Random_forest (m_labels, m_features,
|
m_random_forest = new Random_forest (m_labels, m_features,
|
||||||
int(max_depth), 5, 15,
|
dialog.get<QSpinBox>("max_depth")->value(), 5, 15,
|
||||||
int(num_trees));
|
dialog.get<QSpinBox>("num_trees")->value());
|
||||||
m_random_forest->train (training);
|
m_random_forest->train (training);
|
||||||
CGAL::Classification::classify<Concurrency_tag> (m_clusters,
|
CGAL::Classification::classify<Concurrency_tag> (m_clusters,
|
||||||
m_labels, *m_random_forest,
|
m_labels, *m_random_forest,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (classifier == 3)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
{
|
||||||
|
if (m_neural_network->initialized())
|
||||||
|
{
|
||||||
|
if (dialog.get<QCheckBox>("restart")->isChecked())
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
|
||||||
|
std::vector<std::size_t> hidden_layers;
|
||||||
|
|
||||||
|
std::string hl_input = dialog.get<QLineEdit>("hidden_layers")->text().toStdString();
|
||||||
|
if (hl_input != "")
|
||||||
|
{
|
||||||
|
std::istringstream iss(hl_input);
|
||||||
|
int s;
|
||||||
|
while (iss >> s)
|
||||||
|
hidden_layers.push_back (std::size_t(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_neural_network->train (training,
|
||||||
|
dialog.get<QCheckBox>("restart")->isChecked(),
|
||||||
|
dialog.get<QSpinBox>("trials")->value(),
|
||||||
|
dialog.get<QDoubleSpinBox>("learning_rate")->value(),
|
||||||
|
dialog.get<QSpinBox>("batch_size")->value(),
|
||||||
|
hidden_layers);
|
||||||
|
|
||||||
|
CGAL::Classification::classify<Concurrency_tag> (m_clusters,
|
||||||
|
m_labels, *m_neural_network,
|
||||||
|
indices, m_label_probabilities);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -778,11 +941,36 @@ bool Cluster_classification::run (int method, int classifier,
|
||||||
if (classifier == 0)
|
if (classifier == 0)
|
||||||
run (method, *m_sowf, subdivisions, smoothing);
|
run (method, *m_sowf, subdivisions, smoothing);
|
||||||
else if (classifier == 1)
|
else if (classifier == 1)
|
||||||
|
{
|
||||||
|
if (m_ethz == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: ETHZ Random Forest must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
run (method, *m_ethz, subdivisions, smoothing);
|
run (method, *m_ethz, subdivisions, smoothing);
|
||||||
|
}
|
||||||
|
else if (classifier == 2)
|
||||||
|
{
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
else
|
if (m_random_forest == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: OpenCV Random Forest must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
run (method, *m_random_forest, subdivisions, smoothing);
|
run (method, *m_random_forest, subdivisions, smoothing);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
else if (classifier == 3)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: TensorFlow Neural Network must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
run (method, *m_neural_network, subdivisions, smoothing);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ class Cluster_classification : public Item_classification_base
|
||||||
xcenter + dx, ycenter + dy, zcenter + dz);
|
xcenter + dx, ycenter + dy, zcenter + dz);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_features (std::size_t nb_scales);
|
void compute_features (std::size_t nb_scales, float voxel_size);
|
||||||
void add_remaining_point_set_properties_as_features(Feature_set& feature_set);
|
void add_remaining_point_set_properties_as_features(Feature_set& feature_set);
|
||||||
|
|
||||||
void select_random_region();
|
void select_random_region();
|
||||||
|
|
@ -202,12 +202,11 @@ class Cluster_classification : public Item_classification_base
|
||||||
if (m_index_color == 1 || m_index_color == 2)
|
if (m_index_color == 1 || m_index_color == 2)
|
||||||
change_color (m_index_color);
|
change_color (m_index_color);
|
||||||
}
|
}
|
||||||
void train(int classifier, unsigned int nb_trials,
|
void train(int classifier, const QMultipleInputDialog& dialog);
|
||||||
std::size_t num_trees, std::size_t max_depth);
|
|
||||||
bool run (int method, int classifier, std::size_t subdivisions, double smoothing);
|
bool run (int method, int classifier, std::size_t subdivisions, double smoothing);
|
||||||
|
|
||||||
void update_color () { change_color (m_index_color); }
|
void update_color () { change_color (m_index_color); }
|
||||||
void change_color (int index);
|
void change_color (int index, float* vmin = NULL, float* vmax = NULL);
|
||||||
CGAL::Three::Scene_item* generate_one_item (const char* name,
|
CGAL::Three::Scene_item* generate_one_item (const char* name,
|
||||||
int label) const
|
int label) const
|
||||||
{
|
{
|
||||||
|
|
@ -255,8 +254,6 @@ class Cluster_classification : public Item_classification_base
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool write_output(std::ostream& out);
|
|
||||||
|
|
||||||
QColor add_new_label (const char* name)
|
QColor add_new_label (const char* name)
|
||||||
{
|
{
|
||||||
QColor out = Item_classification_base::add_new_label (name);
|
QColor out = Item_classification_base::add_new_label (name);
|
||||||
|
|
@ -287,13 +284,7 @@ class Cluster_classification : public Item_classification_base
|
||||||
void fill_display_combo_box (QComboBox* cb, QComboBox* cb1) const
|
void fill_display_combo_box (QComboBox* cb, QComboBox* cb1) const
|
||||||
{
|
{
|
||||||
cb->addItem ("Clusters");
|
cb->addItem ("Clusters");
|
||||||
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
Item_classification_base::fill_display_combo_box(cb, cb1);
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "Feature " << m_features[i]->name();
|
|
||||||
cb->addItem (oss.str().c_str());
|
|
||||||
cb1->addItem (oss.str().c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int real_index_color() const;
|
int real_index_color() const;
|
||||||
|
|
@ -391,17 +382,18 @@ class Cluster_classification : public Item_classification_base
|
||||||
|
|
||||||
std::vector<Cluster> m_clusters;
|
std::vector<Cluster> m_clusters;
|
||||||
|
|
||||||
Point_set::Property_map<unsigned char> m_red;
|
|
||||||
Point_set::Property_map<unsigned char> m_green;
|
|
||||||
Point_set::Property_map<unsigned char> m_blue;
|
|
||||||
Point_set::Property_map<Color> m_color;
|
Point_set::Property_map<Color> m_color;
|
||||||
Point_set::Property_map<int> m_cluster_id;
|
Point_set::Property_map<int> m_cluster_id;
|
||||||
Point_set::Property_map<int> m_training;
|
Point_set::Property_map<int> m_training;
|
||||||
Point_set::Property_map<int> m_classif;
|
Point_set::Property_map<int> m_classif;
|
||||||
|
|
||||||
|
std::vector<std::vector<float> > m_label_probabilities;
|
||||||
|
|
||||||
int m_index_color;
|
int m_index_color;
|
||||||
|
|
||||||
boost::shared_ptr<Local_eigen_analysis> m_eigen;
|
boost::shared_ptr<Local_eigen_analysis> m_eigen;
|
||||||
|
|
||||||
|
bool m_input_is_las;
|
||||||
|
|
||||||
}; // end class Cluster_classification
|
}; // end class Cluster_classification
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,20 @@
|
||||||
#include <CGAL/Three/Scene_item.h>
|
#include <CGAL/Three/Scene_item.h>
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QMultipleInputDialog.h>
|
||||||
|
|
||||||
#include <CGAL/Classification/Feature_set.h>
|
#include <CGAL/Classification/Feature_set.h>
|
||||||
#include <CGAL/Classification/Label_set.h>
|
#include <CGAL/Classification/Label_set.h>
|
||||||
#include <CGAL/Classification/Sum_of_weighted_features_classifier.h>
|
#include <CGAL/Classification/Sum_of_weighted_features_classifier.h>
|
||||||
#include <CGAL/Classification/ETHZ_random_forest_classifier.h>
|
#include <CGAL/Classification/ETHZ/Random_forest_classifier.h>
|
||||||
|
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
#include <CGAL/Classification/OpenCV_random_forest_classifier.h>
|
#include <CGAL/Classification/OpenCV/Random_forest_classifier.h>
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
#include <CGAL/Classification/TensorFlow/Neural_network_classifier.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Item_classification_base
|
class Item_classification_base
|
||||||
|
|
@ -22,10 +28,13 @@ public:
|
||||||
typedef CGAL::Classification::Label_set Label_set;
|
typedef CGAL::Classification::Label_set Label_set;
|
||||||
typedef CGAL::Classification::Feature_set Feature_set;
|
typedef CGAL::Classification::Feature_set Feature_set;
|
||||||
typedef CGAL::Classification::Sum_of_weighted_features_classifier Sum_of_weighted_features;
|
typedef CGAL::Classification::Sum_of_weighted_features_classifier Sum_of_weighted_features;
|
||||||
typedef CGAL::Classification::ETHZ_random_forest_classifier ETHZ_random_forest;
|
typedef CGAL::Classification::ETHZ::Random_forest_classifier ETHZ_random_forest;
|
||||||
|
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
typedef CGAL::Classification::OpenCV_random_forest_classifier Random_forest;
|
typedef CGAL::Classification::OpenCV::Random_forest_classifier Random_forest;
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
typedef CGAL::Classification::TensorFlow::Neural_network_classifier<> Neural_network;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -38,8 +47,10 @@ public:
|
||||||
|
|
||||||
virtual CGAL::Bbox_3 bbox() { return item()->bbox(); }
|
virtual CGAL::Bbox_3 bbox() { return item()->bbox(); }
|
||||||
|
|
||||||
virtual void compute_features (std::size_t nb_scales) = 0;
|
virtual void compute_features (std::size_t nb_scales, float voxel_size) = 0;
|
||||||
|
|
||||||
|
virtual std::string feature_statistics () const { return std::string(); }
|
||||||
|
|
||||||
virtual void add_selection_to_training_set (std::size_t label) = 0;
|
virtual void add_selection_to_training_set (std::size_t label) = 0;
|
||||||
virtual void reset_training_set (std::size_t label) = 0;
|
virtual void reset_training_set (std::size_t label) = 0;
|
||||||
virtual void reset_training_set_of_selection() = 0;
|
virtual void reset_training_set_of_selection() = 0;
|
||||||
|
|
@ -47,19 +58,16 @@ public:
|
||||||
|
|
||||||
virtual void select_random_region() = 0;
|
virtual void select_random_region() = 0;
|
||||||
virtual void validate_selection () = 0;
|
virtual void validate_selection () = 0;
|
||||||
virtual void train(int classifier, unsigned int nb_trials,
|
virtual void train(int classifier, const QMultipleInputDialog&) = 0;
|
||||||
std::size_t num_trees, std::size_t max_depth) = 0;
|
|
||||||
virtual bool run (int method, int classifier, std::size_t subdivisions, double smoothing) = 0;
|
virtual bool run (int method, int classifier, std::size_t subdivisions, double smoothing) = 0;
|
||||||
|
|
||||||
virtual void update_color () = 0;
|
virtual void update_color () = 0;
|
||||||
virtual void change_color (int index) = 0;
|
virtual void change_color (int index, float* vmin = NULL, float* vmax = NULL) = 0;
|
||||||
virtual CGAL::Three::Scene_item* generate_one_item (const char* name,
|
virtual CGAL::Three::Scene_item* generate_one_item (const char* name,
|
||||||
int label) const = 0;
|
int label) const = 0;
|
||||||
virtual void generate_one_item_per_label(std::vector<CGAL::Three::Scene_item*>& items,
|
virtual void generate_one_item_per_label(std::vector<CGAL::Three::Scene_item*>& items,
|
||||||
const char* name) const = 0;
|
const char* name) const = 0;
|
||||||
|
|
||||||
virtual bool write_output(std::ostream& out) = 0;
|
|
||||||
|
|
||||||
bool features_computed() const { return (m_features.size() != 0); }
|
bool features_computed() const { return (m_features.size() != 0); }
|
||||||
std::size_t number_of_features() const { return m_features.size(); }
|
std::size_t number_of_features() const { return m_features.size(); }
|
||||||
Feature_handle feature(std::size_t i) { return m_features[i]; }
|
Feature_handle feature(std::size_t i) { return m_features[i]; }
|
||||||
|
|
@ -84,6 +92,11 @@ public:
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
m_random_forest = new Random_forest (m_labels, m_features);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
#endif
|
||||||
|
|
||||||
return m_label_colors.back();
|
return m_label_colors.back();
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +115,13 @@ public:
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
m_random_forest = new Random_forest (m_labels, m_features);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void clear_labels ()
|
virtual void clear_labels ()
|
||||||
{
|
{
|
||||||
m_labels.clear();
|
m_labels.clear();
|
||||||
|
|
@ -118,12 +137,24 @@ public:
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
m_random_forest = new Random_forest (m_labels, m_features);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
std::size_t number_of_labels() const { return m_labels.size(); }
|
std::size_t number_of_labels() const { return m_labels.size(); }
|
||||||
Label_handle label(std::size_t i) { return m_labels[i]; }
|
Label_handle label(std::size_t i) { return m_labels[i]; }
|
||||||
|
|
||||||
virtual void fill_display_combo_box (QComboBox* cb, QComboBox* cb1) const
|
virtual void fill_display_combo_box (QComboBox* cb, QComboBox* cb1) const
|
||||||
{
|
{
|
||||||
|
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Label " << m_labels[i]->name();
|
||||||
|
cb->addItem (oss.str().c_str());
|
||||||
|
cb1->addItem (oss.str().c_str());
|
||||||
|
}
|
||||||
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
|
@ -151,10 +182,17 @@ public:
|
||||||
std::ofstream f (filename, std::ios_base::out | std::ios_base::binary);
|
std::ofstream f (filename, std::ios_base::out | std::ios_base::binary);
|
||||||
m_ethz->save_configuration (f);
|
m_ethz->save_configuration (f);
|
||||||
}
|
}
|
||||||
else
|
else if (classifier == 2)
|
||||||
{
|
{
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
m_random_forest->save_configuration (filename);
|
m_random_forest->save_configuration (filename);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (classifier == 3)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
std::ofstream f (filename);
|
||||||
|
m_neural_network->save_configuration (f);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,13 +211,24 @@ public:
|
||||||
}
|
}
|
||||||
else if (classifier == 1)
|
else if (classifier == 1)
|
||||||
{
|
{
|
||||||
|
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);
|
std::ifstream f (filename, std::ios_base::in | std::ios_base::binary);
|
||||||
m_ethz->load_configuration (f);
|
m_ethz->load_configuration (f);
|
||||||
}
|
}
|
||||||
else
|
else if (classifier == 2)
|
||||||
{
|
{
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
m_random_forest->load_configuration (filename);
|
m_random_forest->load_configuration (filename);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (classifier == 3)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network == NULL)
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
std::ifstream f (filename);
|
||||||
|
m_neural_network->load_configuration (f, true);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,6 +238,10 @@ public:
|
||||||
{
|
{
|
||||||
m_label_colors[position] = color;
|
m_label_colors[position] = color;
|
||||||
}
|
}
|
||||||
|
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 get_new_label_color (const std::string& name)
|
||||||
{
|
{
|
||||||
|
|
@ -248,6 +301,9 @@ protected:
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
Random_forest* m_random_forest;
|
Random_forest* m_random_forest;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
Neural_network* m_neural_network;
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,9 @@
|
||||||
#include <boost/array.hpp>
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
Point_set_item_classification::Point_set_item_classification(Scene_points_with_normal_item* points)
|
Point_set_item_classification::Point_set_item_classification(Scene_points_with_normal_item* points)
|
||||||
: m_points (points),
|
: m_points (points)
|
||||||
m_generator (NULL)
|
, m_generator (NULL)
|
||||||
|
, m_input_is_las (false)
|
||||||
{
|
{
|
||||||
m_index_color = 1;
|
m_index_color = 1;
|
||||||
|
|
||||||
|
|
@ -58,6 +59,7 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n
|
||||||
boost::tie (las_classif, las_found) = m_points->point_set()->property_map<unsigned char>("classification");
|
boost::tie (las_classif, las_found) = m_points->point_set()->property_map<unsigned char>("classification");
|
||||||
if (las_found)
|
if (las_found)
|
||||||
{
|
{
|
||||||
|
m_input_is_las = true;
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
{
|
{
|
||||||
|
|
@ -216,9 +218,12 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n
|
||||||
update_comments_of_point_set_item();
|
update_comments_of_point_set_item();
|
||||||
|
|
||||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||||
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
m_ethz = NULL;
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
m_random_forest = NULL;
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
m_neural_network = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,14 +237,89 @@ Point_set_item_classification::~Point_set_item_classification()
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
if (m_random_forest != NULL)
|
if (m_random_forest != NULL)
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
delete m_neural_network;
|
||||||
#endif
|
#endif
|
||||||
if (m_generator != NULL)
|
if (m_generator != NULL)
|
||||||
delete m_generator;
|
delete m_generator;
|
||||||
if (m_points != NULL)
|
if (m_points != NULL)
|
||||||
|
{
|
||||||
|
// For LAS saving, convert classification info in the LAS standard
|
||||||
|
if (m_input_is_las)
|
||||||
{
|
{
|
||||||
reset_colors();
|
Point_set::Property_map<unsigned char> las_classif
|
||||||
erase_item();
|
= m_points->point_set()->add_property_map<unsigned char>("classification", 0).first;
|
||||||
|
|
||||||
|
std::vector<unsigned char> label_indices;
|
||||||
|
|
||||||
|
unsigned char custom = 19;
|
||||||
|
for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
||||||
|
{
|
||||||
|
if (m_labels[i]->name() == "ground")
|
||||||
|
label_indices.push_back (2);
|
||||||
|
else if (m_labels[i]->name() == "low_veget")
|
||||||
|
label_indices.push_back (3);
|
||||||
|
else if (m_labels[i]->name() == "med_veget" || m_labels[i]->name() == "vegetation")
|
||||||
|
label_indices.push_back (4);
|
||||||
|
else if (m_labels[i]->name() == "high_veget")
|
||||||
|
label_indices.push_back (5);
|
||||||
|
else if (m_labels[i]->name() == "building" || m_labels[i]->name() == "roof")
|
||||||
|
label_indices.push_back (6);
|
||||||
|
else if (m_labels[i]->name() == "noise")
|
||||||
|
label_indices.push_back (7);
|
||||||
|
else if (m_labels[i]->name() == "reserved" || m_labels[i]->name() == "facade")
|
||||||
|
label_indices.push_back (8);
|
||||||
|
else if (m_labels[i]->name() == "water")
|
||||||
|
label_indices.push_back (9);
|
||||||
|
else if (m_labels[i]->name() == "rail")
|
||||||
|
label_indices.push_back (10);
|
||||||
|
else if (m_labels[i]->name() == "road_surface")
|
||||||
|
label_indices.push_back (11);
|
||||||
|
else if (m_labels[i]->name() == "reserved_2")
|
||||||
|
label_indices.push_back (12);
|
||||||
|
else if (m_labels[i]->name() == "wire_guard")
|
||||||
|
label_indices.push_back (13);
|
||||||
|
else if (m_labels[i]->name() == "wire_conduct")
|
||||||
|
label_indices.push_back (14);
|
||||||
|
else if (m_labels[i]->name() == "trans_tower")
|
||||||
|
label_indices.push_back (15);
|
||||||
|
else if (m_labels[i]->name() == "wire_connect")
|
||||||
|
label_indices.push_back (16);
|
||||||
|
else if (m_labels[i]->name() == "bridge_deck")
|
||||||
|
label_indices.push_back (17);
|
||||||
|
else if (m_labels[i]->name() == "high_noise")
|
||||||
|
label_indices.push_back (18);
|
||||||
|
else
|
||||||
|
label_indices.push_back (custom ++);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->end(); ++ it)
|
||||||
|
{
|
||||||
|
int c = m_classif[*it];
|
||||||
|
unsigned char lc = 1; // unclassified in LAS standard
|
||||||
|
if (c != -1)
|
||||||
|
lc = label_indices[std::size_t(c)];
|
||||||
|
|
||||||
|
las_classif[*it] = lc;
|
||||||
|
|
||||||
|
int t = m_training[*it];
|
||||||
|
unsigned char lt = 1; // unclassified in LAS standard
|
||||||
|
if (t != -1)
|
||||||
|
lt = label_indices[std::size_t(t)];
|
||||||
|
|
||||||
|
m_training[*it] = int(lt);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_points->point_set()->remove_property_map (m_classif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset_colors();
|
||||||
|
erase_item();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -256,18 +336,8 @@ void Point_set_item_classification::backup_existing_colors_and_add_new()
|
||||||
|
|
||||||
m_points->point_set()->remove_colors();
|
m_points->point_set()->remove_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_red = m_points->point_set()->add_property_map<unsigned char>("red").first;
|
m_points->point_set()->add_colors();
|
||||||
m_green = m_points->point_set()->add_property_map<unsigned char>("green").first;
|
|
||||||
m_blue = m_points->point_set()->add_property_map<unsigned char>("blue").first;
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
m_red[*it] = 0;
|
|
||||||
m_green[*it] = 0;
|
|
||||||
m_blue[*it] = 0;
|
|
||||||
}
|
|
||||||
m_points->point_set()->check_colors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Point_set_item_classification::reset_colors()
|
void Point_set_item_classification::reset_colors()
|
||||||
|
|
@ -278,40 +348,13 @@ void Point_set_item_classification::reset_colors()
|
||||||
{
|
{
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
{
|
m_points->point_set()->set_color(*it, m_color[*it]);
|
||||||
m_red[*it] = m_color[*it][0];
|
|
||||||
m_green[*it] = m_color[*it][1];
|
|
||||||
m_blue[*it] = m_color[*it][2];
|
|
||||||
}
|
|
||||||
m_points->point_set()->remove_property_map(m_color);
|
m_points->point_set()->remove_property_map(m_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write point set to .PLY file
|
void Point_set_item_classification::change_color (int index, float* vmin, float* vmax)
|
||||||
bool Point_set_item_classification::write_output(std::ostream& stream)
|
|
||||||
{
|
|
||||||
if (m_features.size() == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
reset_indices();
|
|
||||||
|
|
||||||
stream.precision (std::numeric_limits<double>::digits10 + 2);
|
|
||||||
|
|
||||||
// std::vector<Color> colors;
|
|
||||||
// for (std::size_t i = 0; i < m_labels.size(); ++ i)
|
|
||||||
// {
|
|
||||||
// Color c = {{ (unsigned char)(m_labels[i].second.red()),
|
|
||||||
// (unsigned char)(m_labels[i].second.green()),
|
|
||||||
// (unsigned char)(m_labels[i].second.blue()) }};
|
|
||||||
// colors.push_back (c);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m_psc->write_classification_to_ply (stream);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Point_set_item_classification::change_color (int index)
|
|
||||||
{
|
{
|
||||||
m_index_color = index;
|
m_index_color = index;
|
||||||
|
|
||||||
|
|
@ -321,92 +364,129 @@ void Point_set_item_classification::change_color (int index)
|
||||||
static Color_ramp ramp;
|
static Color_ramp ramp;
|
||||||
ramp.build_rainbow();
|
ramp.build_rainbow();
|
||||||
reset_indices();
|
reset_indices();
|
||||||
|
|
||||||
if (index_color == -1) // item color
|
if (index_color == -1) // item color
|
||||||
{
|
m_points->point_set()->remove_colors();
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
m_red[*it] = 0;
|
|
||||||
m_green[*it] = 0;
|
|
||||||
m_blue[*it] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index_color == 0) // real colors
|
|
||||||
{
|
|
||||||
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
m_red[*it] = m_color[*it][0];
|
|
||||||
m_green[*it] = m_color[*it][1];
|
|
||||||
m_blue[*it] = m_color[*it][2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index_color == 1) // classif
|
|
||||||
{
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
QColor color (0, 0, 0);
|
|
||||||
std::size_t c = m_classif[*it];
|
|
||||||
|
|
||||||
if (c != std::size_t(-1))
|
|
||||||
color = m_label_colors[c];
|
|
||||||
|
|
||||||
m_red[*it] = color.red();
|
|
||||||
m_green[*it] = color.green();
|
|
||||||
m_blue[*it] = color.blue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (index_color == 2) // training
|
|
||||||
{
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
{
|
|
||||||
QColor color (0, 0, 0);
|
|
||||||
int c = m_training[*it];
|
|
||||||
int c2 = m_classif[*it];
|
|
||||||
|
|
||||||
if (c != -1)
|
|
||||||
color = m_label_colors[std::size_t(c)];
|
|
||||||
|
|
||||||
float div = 1;
|
|
||||||
if (c != c2)
|
|
||||||
div = 2;
|
|
||||||
|
|
||||||
m_red[*it] = (color.red() / div);
|
|
||||||
m_green[*it] = (color.green() / div);
|
|
||||||
m_blue[*it] = (color.blue() / div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (!m_points->point_set()->has_colors())
|
||||||
|
m_points->point_set()->add_colors();
|
||||||
|
|
||||||
|
if (index_color == 0) // real colors
|
||||||
{
|
{
|
||||||
Feature_handle feature = m_features[index_color - 3];
|
|
||||||
|
|
||||||
float max = 0.;
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
|
||||||
if (feature->value(*it) > max)
|
|
||||||
max = feature->value(*it);
|
|
||||||
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
{
|
m_points->point_set()->set_color(*it, m_color[*it]);
|
||||||
float v = std::max (0.f, feature->value(*it) / max);
|
|
||||||
m_red[*it] = (unsigned char)(ramp.r(v) * 255);
|
|
||||||
m_green[*it] = (unsigned char)(ramp.g(v) * 255);
|
|
||||||
m_blue[*it] = (unsigned char)(ramp.b(v) * 255);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (index_color == 1) // classif
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
QColor color (0, 0, 0);
|
||||||
|
std::size_t c = m_classif[*it];
|
||||||
|
|
||||||
|
if (c != std::size_t(-1))
|
||||||
|
color = m_label_colors[c];
|
||||||
|
|
||||||
|
m_points->point_set()->set_color(*it, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (index_color == 2) // training
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
QColor color (0, 0, 0);
|
||||||
|
int c = m_training[*it];
|
||||||
|
int c2 = m_classif[*it];
|
||||||
|
|
||||||
|
if (c != -1)
|
||||||
|
color = m_label_colors[std::size_t(c)];
|
||||||
|
|
||||||
|
float div = 1;
|
||||||
|
if (c != c2)
|
||||||
|
div = 2;
|
||||||
|
|
||||||
|
m_points->point_set()->set_color(*it, color.red() / div, color.green() / div, color.blue() / div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::size_t corrected_index = index_color - 3;
|
||||||
|
if (corrected_index < m_labels.size()) // Display label probabilities
|
||||||
|
{
|
||||||
|
if (m_label_probabilities.size() <= corrected_index ||
|
||||||
|
m_label_probabilities[corrected_index].size() != m_points->point_set()->size())
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
m_points->point_set()->set_color(*it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
float v = std::max (0.f, std::min(1.f, m_label_probabilities[corrected_index][*it]));
|
||||||
|
m_points->point_set()->set_color(*it, ramp.r(v) * 255, ramp.g(v) * 255, ramp.b(v) * 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
corrected_index -= m_labels.size();
|
||||||
|
if (corrected_index >= m_features.size())
|
||||||
|
{
|
||||||
|
std::cerr << "Error: trying to access feature " << corrected_index << " out of " << m_features.size() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Feature_handle feature = m_features[corrected_index];
|
||||||
|
|
||||||
|
float min = std::numeric_limits<float>::max();
|
||||||
|
float max = -std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
if (vmin != NULL && vmax != NULL
|
||||||
|
&& *vmin != std::numeric_limits<float>::infinity()
|
||||||
|
&& *vmax != std::numeric_limits<float>::infinity())
|
||||||
|
{
|
||||||
|
min = *vmin;
|
||||||
|
max = *vmax;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->end(); ++ it)
|
||||||
|
{
|
||||||
|
float v = feature->value(*it);
|
||||||
|
min = (std::min) (min, v);
|
||||||
|
max = (std::max) (max, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
|
{
|
||||||
|
float v = (feature->value(*it) - min) / (max - min);
|
||||||
|
if (v < 0.f) v = 0.f;
|
||||||
|
if (v > 1.f) v = 1.f;
|
||||||
|
|
||||||
|
m_points->point_set()->set_color(*it, ramp.r(v) * 255, ramp.g(v) * 255, ramp.b(v) * 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vmin != NULL && vmax != NULL)
|
||||||
|
{
|
||||||
|
*vmin = min;
|
||||||
|
*vmax = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->first_selected();
|
for (Point_set::const_iterator it = m_points->point_set()->first_selected();
|
||||||
it != m_points->point_set()->end(); ++ it)
|
it != m_points->point_set()->end(); ++ it)
|
||||||
{
|
m_points->point_set()->set_color(*it, 255, 0, 0);
|
||||||
m_red[*it] = 255;
|
|
||||||
m_green[*it] = 0;
|
|
||||||
m_blue[*it] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Point_set_item_classification::real_index_color() const
|
int Point_set_item_classification::real_index_color() const
|
||||||
|
|
@ -430,7 +510,7 @@ void Point_set_item_classification::reset_indices ()
|
||||||
*(indices.begin() + i) = idx ++;
|
*(indices.begin() + i) = idx ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Point_set_item_classification::compute_features (std::size_t nb_scales)
|
void Point_set_item_classification::compute_features (std::size_t nb_scales, float voxel_size)
|
||||||
{
|
{
|
||||||
CGAL_assertion (!(m_points->point_set()->empty()));
|
CGAL_assertion (!(m_points->point_set()->empty()));
|
||||||
|
|
||||||
|
|
@ -439,7 +519,12 @@ void Point_set_item_classification::compute_features (std::size_t nb_scales)
|
||||||
|
|
||||||
reset_indices();
|
reset_indices();
|
||||||
|
|
||||||
std::cerr << "Computing features with " << nb_scales << " scale(s)" << std::endl;
|
std::cerr << "Computing features with " << nb_scales << " scale(s) and ";
|
||||||
|
if (voxel_size == -1)
|
||||||
|
std::cerr << "automatic voxel size" << std::endl;
|
||||||
|
else
|
||||||
|
std::cerr << "voxel size = " << voxel_size << std::endl;
|
||||||
|
|
||||||
m_features.clear();
|
m_features.clear();
|
||||||
|
|
||||||
Point_set::Vector_map normal_map;
|
Point_set::Vector_map normal_map;
|
||||||
|
|
@ -455,7 +540,7 @@ void Point_set_item_classification::compute_features (std::size_t nb_scales)
|
||||||
if (!echo)
|
if (!echo)
|
||||||
boost::tie (echo_map, echo) = m_points->point_set()->template property_map<boost::uint8_t>("number_of_returns");
|
boost::tie (echo_map, echo) = m_points->point_set()->template property_map<boost::uint8_t>("number_of_returns");
|
||||||
|
|
||||||
m_generator = new Generator (*(m_points->point_set()), m_points->point_set()->point_map(), nb_scales);
|
m_generator = new Generator (*(m_points->point_set()), m_points->point_set()->point_map(), nb_scales, voxel_size);
|
||||||
|
|
||||||
CGAL::Real_timer t;
|
CGAL::Real_timer t;
|
||||||
t.start();
|
t.start();
|
||||||
|
|
@ -480,12 +565,24 @@ void Point_set_item_classification::compute_features (std::size_t nb_scales)
|
||||||
|
|
||||||
delete m_sowf;
|
delete m_sowf;
|
||||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||||
delete m_ethz;
|
if (m_ethz != NULL)
|
||||||
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
{
|
||||||
|
delete m_ethz;
|
||||||
|
m_ethz = NULL;
|
||||||
|
}
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
delete m_random_forest;
|
if (m_random_forest != NULL)
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
{
|
||||||
|
delete m_random_forest;
|
||||||
|
m_random_forest = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
t.stop();
|
t.stop();
|
||||||
|
|
@ -598,8 +695,7 @@ void Point_set_item_classification::add_remaining_point_set_properties_as_featur
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Point_set_item_classification::train(int classifier, unsigned int nb_trials,
|
void Point_set_item_classification::train(int classifier, const QMultipleInputDialog& dialog)
|
||||||
std::size_t num_trees, std::size_t max_depth)
|
|
||||||
{
|
{
|
||||||
if (m_features.size() == 0)
|
if (m_features.size() == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -608,6 +704,11 @@ void Point_set_item_classification::train(int classifier, unsigned int nb_trials
|
||||||
}
|
}
|
||||||
reset_indices();
|
reset_indices();
|
||||||
|
|
||||||
|
m_label_probabilities.clear();
|
||||||
|
m_label_probabilities.resize (m_labels.size());
|
||||||
|
for (std::size_t i = 0; i < m_label_probabilities.size(); ++ i)
|
||||||
|
m_label_probabilities[i].resize (m_points->point_set()->size(), -1);
|
||||||
|
|
||||||
std::vector<int> training (m_points->point_set()->size(), -1);
|
std::vector<int> training (m_points->point_set()->size(), -1);
|
||||||
std::vector<int> indices (m_points->point_set()->size(), -1);
|
std::vector<int> indices (m_points->point_set()->size(), -1);
|
||||||
|
|
||||||
|
|
@ -632,32 +733,83 @@ void Point_set_item_classification::train(int classifier, unsigned int nb_trials
|
||||||
|
|
||||||
if (classifier == 0)
|
if (classifier == 0)
|
||||||
{
|
{
|
||||||
m_sowf->train<Concurrency_tag>(training, nb_trials);
|
m_sowf->train<Concurrency_tag>(training, dialog.get<QSpinBox>("trials")->value());
|
||||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||||
m_labels, *m_sowf,
|
m_labels, *m_sowf,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
}
|
}
|
||||||
else if (classifier == 1)
|
else if (classifier == 1)
|
||||||
{
|
{
|
||||||
m_ethz->train(training, true, num_trees, max_depth);
|
if (m_ethz != NULL)
|
||||||
|
delete m_ethz;
|
||||||
|
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
||||||
|
m_ethz->train<Concurrency_tag>(training, true,
|
||||||
|
dialog.get<QSpinBox>("num_trees")->value(),
|
||||||
|
dialog.get<QSpinBox>("max_depth")->value());
|
||||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||||
m_labels, *m_ethz,
|
m_labels, *m_ethz,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
}
|
}
|
||||||
else
|
else if (classifier == 2)
|
||||||
{
|
{
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
if (m_random_forest != NULL)
|
if (m_random_forest != NULL)
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
m_random_forest = new Random_forest (m_labels, m_features,
|
m_random_forest = new Random_forest (m_labels, m_features,
|
||||||
int(max_depth), 5, 15,
|
dialog.get<QSpinBox>("max_depth")->value(), 5, 15,
|
||||||
int(num_trees));
|
dialog.get<QSpinBox>("num_trees")->value());
|
||||||
m_random_forest->train (training);
|
m_random_forest->train (training);
|
||||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||||
m_labels, *m_random_forest,
|
m_labels, *m_random_forest,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
else if (classifier == 3)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
{
|
||||||
|
if (m_neural_network->initialized())
|
||||||
|
{
|
||||||
|
if (dialog.get<QCheckBox>("restart")->isChecked())
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
|
||||||
|
std::vector<std::size_t> hidden_layers;
|
||||||
|
|
||||||
|
std::string hl_input = dialog.get<QLineEdit>("hidden_layers")->text().toStdString();
|
||||||
|
if (hl_input != "")
|
||||||
|
{
|
||||||
|
std::istringstream iss(hl_input);
|
||||||
|
int s;
|
||||||
|
while (iss >> s)
|
||||||
|
hidden_layers.push_back (std::size_t(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_neural_network->train (training,
|
||||||
|
dialog.get<QCheckBox>("restart")->isChecked(),
|
||||||
|
dialog.get<QSpinBox>("trials")->value(),
|
||||||
|
dialog.get<QDoubleSpinBox>("learning_rate")->value(),
|
||||||
|
dialog.get<QSpinBox>("batch_size")->value(),
|
||||||
|
hidden_layers);
|
||||||
|
|
||||||
|
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||||
|
m_labels, *m_neural_network,
|
||||||
|
indices, m_label_probabilities);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
for (Point_set::const_iterator it = m_points->point_set()->begin();
|
||||||
it != m_points->point_set()->first_selected(); ++ it)
|
it != m_points->point_set()->first_selected(); ++ it)
|
||||||
m_classif[*it] = indices[*it];
|
m_classif[*it] = indices[*it];
|
||||||
|
|
@ -680,11 +832,36 @@ bool Point_set_item_classification::run (int method, int classifier,
|
||||||
if (classifier == 0)
|
if (classifier == 0)
|
||||||
run (method, *m_sowf, subdivisions, smoothing);
|
run (method, *m_sowf, subdivisions, smoothing);
|
||||||
else if (classifier == 1)
|
else if (classifier == 1)
|
||||||
|
{
|
||||||
|
if (m_ethz == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: ETHZ Random Forest must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
run (method, *m_ethz, subdivisions, smoothing);
|
run (method, *m_ethz, subdivisions, smoothing);
|
||||||
|
}
|
||||||
|
else if (classifier == 2)
|
||||||
|
{
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
else
|
if (m_random_forest == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: OpenCV Random Forest must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
run (method, *m_random_forest, subdivisions, smoothing);
|
run (method, *m_random_forest, subdivisions, smoothing);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
else if (classifier == 3)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: TensorFlow Neural Network must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
run (method, *m_neural_network, subdivisions, smoothing);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
//#define CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
|
//#define CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
|
||||||
#define CGAL_CLASSIFICATION_VERBOSE
|
#define CGAL_CLASSIFICATION_VERBOSE
|
||||||
|
#define CGAL_CLASSTRAINING_VERBOSE
|
||||||
|
|
||||||
#include <CGAL/Three/Scene_item.h>
|
#include <CGAL/Three/Scene_item.h>
|
||||||
|
|
||||||
|
|
@ -116,7 +117,35 @@ class Point_set_item_classification : public Item_classification_base
|
||||||
xcenter + dx, ycenter + dy, zcenter + dz);
|
xcenter + dx, ycenter + dy, zcenter + dz);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_features (std::size_t nb_scales);
|
void compute_features (std::size_t nb_scales, float voxel_size);
|
||||||
|
|
||||||
|
std::string feature_statistics() const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < m_features.size(); ++ i)
|
||||||
|
{
|
||||||
|
float vmin = std::numeric_limits<float>::max();
|
||||||
|
float vmax = -std::numeric_limits<float>::max();
|
||||||
|
float vmean = 0.f;
|
||||||
|
std::size_t nb = 0;
|
||||||
|
|
||||||
|
for (Point_set::const_iterator it = m_points->point_set()->begin_or_selection_begin();
|
||||||
|
it != m_points->point_set()->end(); ++ it)
|
||||||
|
{
|
||||||
|
float v = m_features[i]->value(std::size_t(it - m_points->point_set()->begin()));
|
||||||
|
vmin = (std::min) (vmin, v);
|
||||||
|
vmax = (std::max) (vmax, v);
|
||||||
|
vmean += v;
|
||||||
|
++ nb;
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << m_features[i]->name() << " in [ " << vmin << " ; " << vmax << " ], mean = " << vmean / nb << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
void add_remaining_point_set_properties_as_features();
|
void add_remaining_point_set_properties_as_features();
|
||||||
|
|
||||||
void select_random_region();
|
void select_random_region();
|
||||||
|
|
@ -129,8 +158,12 @@ class Point_set_item_classification : public Item_classification_base
|
||||||
Pmap pmap;
|
Pmap pmap;
|
||||||
boost::tie (pmap, okay) = m_points->point_set()->template property_map<Type>(name.c_str());
|
boost::tie (pmap, okay) = m_points->point_set()->template property_map<Type>(name.c_str());
|
||||||
if (okay)
|
if (okay)
|
||||||
|
{
|
||||||
|
std::cerr << "Adding property<" << CGAL::demangle(typeid(Type).name()) << ">("
|
||||||
|
<< name << ") as feature" << std::endl;
|
||||||
m_features.template add<CGAL::Classification::Feature::Simple_feature <Point_set, Pmap> >
|
m_features.template add<CGAL::Classification::Feature::Simple_feature <Point_set, Pmap> >
|
||||||
(*(m_points->point_set()), pmap, name.c_str());
|
(*(m_points->point_set()), pmap, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
return okay;
|
return okay;
|
||||||
}
|
}
|
||||||
|
|
@ -186,12 +219,11 @@ class Point_set_item_classification : public Item_classification_base
|
||||||
if (m_index_color == 1 || m_index_color == 2)
|
if (m_index_color == 1 || m_index_color == 2)
|
||||||
change_color (m_index_color);
|
change_color (m_index_color);
|
||||||
}
|
}
|
||||||
void train(int classifier, unsigned int nb_trials,
|
void train(int classifier, const QMultipleInputDialog& dialog);
|
||||||
std::size_t num_trees, std::size_t max_depth);
|
|
||||||
bool run (int method, int classifier, std::size_t subdivisions, double smoothing);
|
bool run (int method, int classifier, std::size_t subdivisions, double smoothing);
|
||||||
|
|
||||||
void update_color () { change_color (m_index_color); }
|
void update_color () { change_color (m_index_color); }
|
||||||
void change_color (int index);
|
void change_color (int index, float* vmin = NULL, float* vmax = NULL);
|
||||||
CGAL::Three::Scene_item* generate_one_item (const char* name,
|
CGAL::Three::Scene_item* generate_one_item (const char* name,
|
||||||
int label) const
|
int label) const
|
||||||
{
|
{
|
||||||
|
|
@ -231,8 +263,6 @@ class Point_set_item_classification : public Item_classification_base
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool write_output(std::ostream& out);
|
|
||||||
|
|
||||||
QColor add_new_label (const char* name)
|
QColor add_new_label (const char* name)
|
||||||
{
|
{
|
||||||
QColor out = Item_classification_base::add_new_label (name);
|
QColor out = Item_classification_base::add_new_label (name);
|
||||||
|
|
@ -301,10 +331,11 @@ class Point_set_item_classification : public Item_classification_base
|
||||||
{
|
{
|
||||||
std::vector<int> indices (m_points->point_set()->size(), -1);
|
std::vector<int> indices (m_points->point_set()->size(), -1);
|
||||||
|
|
||||||
|
m_label_probabilities.clear();
|
||||||
if (method == 0)
|
if (method == 0)
|
||||||
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
CGAL::Classification::classify<Concurrency_tag> (*(m_points->point_set()),
|
||||||
m_labels, classifier,
|
m_labels, classifier,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
else if (method == 1)
|
else if (method == 1)
|
||||||
{
|
{
|
||||||
if (m_clusters.empty()) // Use real local smoothing
|
if (m_clusters.empty()) // Use real local smoothing
|
||||||
|
|
@ -367,9 +398,8 @@ class Point_set_item_classification : public Item_classification_base
|
||||||
|
|
||||||
std::vector<Cluster> m_clusters;
|
std::vector<Cluster> m_clusters;
|
||||||
|
|
||||||
Point_set::Property_map<unsigned char> m_red;
|
std::vector<std::vector<float> > m_label_probabilities;
|
||||||
Point_set::Property_map<unsigned char> m_green;
|
|
||||||
Point_set::Property_map<unsigned char> m_blue;
|
|
||||||
Point_set::Property_map<Color> m_color;
|
Point_set::Property_map<Color> m_color;
|
||||||
Point_set::Property_map<int> m_training;
|
Point_set::Property_map<int> m_training;
|
||||||
Point_set::Property_map<int> m_classif;
|
Point_set::Property_map<int> m_classif;
|
||||||
|
|
@ -377,6 +407,8 @@ class Point_set_item_classification : public Item_classification_base
|
||||||
Generator* m_generator;
|
Generator* m_generator;
|
||||||
|
|
||||||
int m_index_color;
|
int m_index_color;
|
||||||
|
|
||||||
|
bool m_input_is_las;
|
||||||
|
|
||||||
}; // end class Point_set_item_classification
|
}; // end class Point_set_item_classification
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include <CGAL/Three/Viewer_interface.h>
|
#include <CGAL/Three/Viewer_interface.h>
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -31,9 +33,12 @@ Surface_mesh_item_classification::Surface_mesh_item_classification(Scene_surface
|
||||||
m_label_colors.push_back (this->get_new_label_color (m_labels[i]->name()));
|
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_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||||
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
m_ethz = NULL;
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
m_random_forest = NULL;
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
m_neural_network = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,6 +52,10 @@ Surface_mesh_item_classification::~Surface_mesh_item_classification()
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
if (m_random_forest != NULL)
|
if (m_random_forest != NULL)
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
delete m_neural_network;
|
||||||
#endif
|
#endif
|
||||||
if (m_generator != NULL)
|
if (m_generator != NULL)
|
||||||
delete m_generator;
|
delete m_generator;
|
||||||
|
|
@ -71,14 +80,7 @@ void Surface_mesh_item_classification::backup_existing_colors_and_add_new()
|
||||||
m_mesh->polyhedron()->add_property_map<face_descriptor, CGAL::Color>("f:color", CGAL::Color(128,128,128)).first;
|
m_mesh->polyhedron()->add_property_map<face_descriptor, CGAL::Color>("f:color", CGAL::Color(128,128,128)).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Surface_mesh_item_classification::write_output(std::ostream& )
|
void Surface_mesh_item_classification::change_color (int index, float* vmin, float* vmax)
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Surface_mesh_item_classification::change_color (int index)
|
|
||||||
{
|
{
|
||||||
m_index_color = index;
|
m_index_color = index;
|
||||||
int index_color = index;
|
int index_color = index;
|
||||||
|
|
@ -132,28 +134,90 @@ void Surface_mesh_item_classification::change_color (int index)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Feature_handle feature = m_features[index_color - 3];
|
std::size_t corrected_index = index_color - 3;
|
||||||
|
if (corrected_index < m_labels.size()) // Display label probabilities
|
||||||
float max = 0.;
|
|
||||||
BOOST_FOREACH(face_descriptor fd, faces(*(m_mesh->polyhedron())))
|
|
||||||
{
|
{
|
||||||
if (feature->value(fd) > max)
|
if (m_label_probabilities.size() <= corrected_index ||
|
||||||
max = feature->value(fd);
|
m_label_probabilities[corrected_index].size() != num_faces(*(m_mesh->polyhedron())))
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(face_descriptor fd, faces(*(m_mesh->polyhedron())))
|
||||||
|
{
|
||||||
|
m_color[fd] = CGAL::Color((unsigned char)(128),
|
||||||
|
(unsigned char)(128),
|
||||||
|
(unsigned char)(128));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(face_descriptor fd, faces(*(m_mesh->polyhedron())))
|
||||||
|
{
|
||||||
|
float v = std::max (0.f, std::min(1.f, m_label_probabilities[corrected_index][fd]));
|
||||||
|
m_color[fd] = CGAL::Color((unsigned char)(ramp.r(v) * 255),
|
||||||
|
(unsigned char)(ramp.g(v) * 255),
|
||||||
|
(unsigned char)(ramp.b(v) * 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
BOOST_FOREACH(face_descriptor fd, faces(*(m_mesh->polyhedron())))
|
|
||||||
{
|
{
|
||||||
float v = std::max (0.f, feature->value(fd) / max);
|
corrected_index -= m_labels.size();
|
||||||
m_color[fd] = CGAL::Color((unsigned char)(ramp.r(v) * 255),
|
if (corrected_index >= m_features.size())
|
||||||
(unsigned char)(ramp.g(v) * 255),
|
{
|
||||||
(unsigned char)(ramp.b(v) * 255));
|
std::cerr << "Error: trying to access feature " << corrected_index << " out of " << m_features.size() << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Feature_handle feature = m_features[corrected_index];
|
||||||
|
|
||||||
|
float min = std::numeric_limits<float>::max();
|
||||||
|
float max = -std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
if (vmin != NULL && vmax != NULL
|
||||||
|
&& *vmin != std::numeric_limits<float>::infinity()
|
||||||
|
&& *vmax != std::numeric_limits<float>::infinity())
|
||||||
|
{
|
||||||
|
min = *vmin;
|
||||||
|
max = *vmax;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(face_descriptor fd, faces(*(m_mesh->polyhedron())))
|
||||||
|
{
|
||||||
|
if (feature->value(fd) > max)
|
||||||
|
max = feature->value(fd);
|
||||||
|
if (feature->value(fd) < min)
|
||||||
|
min = feature->value(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FOREACH(face_descriptor fd, faces(*(m_mesh->polyhedron())))
|
||||||
|
{
|
||||||
|
float v = (feature->value(fd) - min) / (max - min);
|
||||||
|
if (v < 0.f) v = 0.f;
|
||||||
|
if (v > 1.f) v = 1.f;
|
||||||
|
|
||||||
|
m_color[fd] = CGAL::Color((unsigned char)(ramp.r(v) * 255),
|
||||||
|
(unsigned char)(ramp.g(v) * 255),
|
||||||
|
(unsigned char)(ramp.b(v) * 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vmin != NULL && vmax != NULL)
|
||||||
|
{
|
||||||
|
*vmin = min;
|
||||||
|
*vmax = max;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Surface_mesh_item_classification::compute_features (std::size_t nb_scales)
|
void Surface_mesh_item_classification::compute_features (std::size_t nb_scales, float voxel_size)
|
||||||
{
|
{
|
||||||
std::cerr << "Computing features with " << nb_scales << " scale(s)" << std::endl;
|
std::cerr << "Computing features with " << nb_scales << " scale(s) and ";
|
||||||
|
if (voxel_size == -1)
|
||||||
|
std::cerr << "automatic voxel size" << std::endl;
|
||||||
|
else
|
||||||
|
std::cerr << "voxel size = " << voxel_size << std::endl;
|
||||||
|
|
||||||
m_features.clear();
|
m_features.clear();
|
||||||
|
|
||||||
if (m_generator != NULL)
|
if (m_generator != NULL)
|
||||||
|
|
@ -161,7 +225,7 @@ void Surface_mesh_item_classification::compute_features (std::size_t nb_scales)
|
||||||
|
|
||||||
Face_center_map fc_map (m_mesh->polyhedron());
|
Face_center_map fc_map (m_mesh->polyhedron());
|
||||||
|
|
||||||
m_generator = new Generator (*(m_mesh->polyhedron()), fc_map, nb_scales);
|
m_generator = new Generator (*(m_mesh->polyhedron()), fc_map, nb_scales, voxel_size);
|
||||||
|
|
||||||
#ifdef CGAL_LINKED_WITH_TBB
|
#ifdef CGAL_LINKED_WITH_TBB
|
||||||
m_features.begin_parallel_additions();
|
m_features.begin_parallel_additions();
|
||||||
|
|
@ -176,16 +240,29 @@ void Surface_mesh_item_classification::compute_features (std::size_t nb_scales)
|
||||||
|
|
||||||
delete m_sowf;
|
delete m_sowf;
|
||||||
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
m_sowf = new Sum_of_weighted_features (m_labels, m_features);
|
||||||
|
if (m_ethz != NULL)
|
||||||
|
{
|
||||||
|
delete m_ethz;
|
||||||
|
m_ethz = NULL;
|
||||||
|
}
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
delete m_random_forest;
|
if (m_random_forest != NULL)
|
||||||
m_random_forest = new Random_forest (m_labels, m_features);
|
{
|
||||||
|
delete m_random_forest;
|
||||||
|
m_random_forest = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
std::cerr << "Features = " << m_features.size() << std::endl;
|
std::cerr << "Features = " << m_features.size() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Surface_mesh_item_classification::train
|
void Surface_mesh_item_classification::train (int classifier, const QMultipleInputDialog& dialog)
|
||||||
(int classifier, unsigned int nb_trials,
|
|
||||||
std::size_t num_trees, std::size_t max_depth)
|
|
||||||
{
|
{
|
||||||
if (m_features.size() == 0)
|
if (m_features.size() == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -193,6 +270,11 @@ void Surface_mesh_item_classification::train
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_label_probabilities.clear();
|
||||||
|
m_label_probabilities.resize (m_labels.size());
|
||||||
|
for (std::size_t i = 0; i < m_label_probabilities.size(); ++ i)
|
||||||
|
m_label_probabilities[i].resize (num_faces(*(m_mesh->polyhedron())));
|
||||||
|
|
||||||
std::vector<std::size_t> training (num_faces(*(m_mesh->polyhedron())), std::size_t(-1));
|
std::vector<std::size_t> training (num_faces(*(m_mesh->polyhedron())), std::size_t(-1));
|
||||||
std::vector<std::size_t> indices (num_faces(*(m_mesh->polyhedron())), std::size_t(-1));
|
std::vector<std::size_t> indices (num_faces(*(m_mesh->polyhedron())), std::size_t(-1));
|
||||||
|
|
||||||
|
|
@ -216,31 +298,81 @@ void Surface_mesh_item_classification::train
|
||||||
|
|
||||||
if (classifier == 0)
|
if (classifier == 0)
|
||||||
{
|
{
|
||||||
m_sowf->train<Concurrency_tag>(training, nb_trials);
|
m_sowf->train<Concurrency_tag>(training, dialog.get<QSpinBox>("trials")->value());
|
||||||
CGAL::Classification::classify<Concurrency_tag> (m_mesh->polyhedron()->faces(),
|
CGAL::Classification::classify<Concurrency_tag> (m_mesh->polyhedron()->faces(),
|
||||||
m_labels, *m_sowf,
|
m_labels, *m_sowf,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
}
|
}
|
||||||
else if (classifier == 1)
|
else if (classifier == 1)
|
||||||
{
|
{
|
||||||
m_ethz->train(training, true, num_trees, max_depth);
|
if (m_ethz != NULL)
|
||||||
|
delete m_ethz;
|
||||||
|
m_ethz = new ETHZ_random_forest (m_labels, m_features);
|
||||||
|
m_ethz->train<Concurrency_tag>(training, true,
|
||||||
|
dialog.get<QSpinBox>("num_trees")->value(),
|
||||||
|
dialog.get<QSpinBox>("max_depth")->value());
|
||||||
CGAL::Classification::classify<Concurrency_tag> (m_mesh->polyhedron()->faces(),
|
CGAL::Classification::classify<Concurrency_tag> (m_mesh->polyhedron()->faces(),
|
||||||
m_labels, *m_ethz,
|
m_labels, *m_ethz,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
}
|
}
|
||||||
else
|
else if (classifier == 2)
|
||||||
{
|
{
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
if (m_random_forest != NULL)
|
if (m_random_forest != NULL)
|
||||||
delete m_random_forest;
|
delete m_random_forest;
|
||||||
m_random_forest = new Random_forest (m_labels, m_features,
|
m_random_forest = new Random_forest (m_labels, m_features,
|
||||||
int(max_depth), 5, 15,
|
dialog.get<QSpinBox>("max_depth")->value(), 5, 15,
|
||||||
int(num_trees));
|
dialog.get<QSpinBox>("num_trees")->value());
|
||||||
m_random_forest->train (training);
|
m_random_forest->train (training);
|
||||||
|
|
||||||
CGAL::Classification::classify<Concurrency_tag> (m_mesh->polyhedron()->faces(),
|
CGAL::Classification::classify<Concurrency_tag> (m_mesh->polyhedron()->faces(),
|
||||||
m_labels, *m_random_forest,
|
m_labels, *m_random_forest,
|
||||||
indices);
|
indices, m_label_probabilities);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (classifier == 3)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network != NULL)
|
||||||
|
{
|
||||||
|
if (m_neural_network->initialized())
|
||||||
|
{
|
||||||
|
if (dialog.get<QCheckBox>("restart")->isChecked())
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete m_neural_network;
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_neural_network = new Neural_network (m_labels, m_features);
|
||||||
|
|
||||||
|
std::vector<std::size_t> hidden_layers;
|
||||||
|
|
||||||
|
std::string hl_input = dialog.get<QLineEdit>("hidden_layers")->text().toStdString();
|
||||||
|
if (hl_input != "")
|
||||||
|
{
|
||||||
|
std::istringstream iss(hl_input);
|
||||||
|
int s;
|
||||||
|
while (iss >> s)
|
||||||
|
hidden_layers.push_back (std::size_t(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_neural_network->train (training,
|
||||||
|
dialog.get<QCheckBox>("restart")->isChecked(),
|
||||||
|
dialog.get<QSpinBox>("trials")->value(),
|
||||||
|
dialog.get<QDoubleSpinBox>("learning_rate")->value(),
|
||||||
|
dialog.get<QSpinBox>("batch_size")->value(),
|
||||||
|
hidden_layers);
|
||||||
|
|
||||||
|
CGAL::Classification::classify<Concurrency_tag> (m_mesh->polyhedron()->faces(),
|
||||||
|
m_labels, *m_neural_network,
|
||||||
|
indices, m_label_probabilities);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,11 +395,36 @@ bool Surface_mesh_item_classification::run (int method, int classifier,
|
||||||
if (classifier == 0)
|
if (classifier == 0)
|
||||||
run (method, *m_sowf, subdivisions, smoothing);
|
run (method, *m_sowf, subdivisions, smoothing);
|
||||||
else if (classifier == 1)
|
else if (classifier == 1)
|
||||||
|
{
|
||||||
|
if (m_ethz == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: ETHZ Random Forest must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
run (method, *m_ethz, subdivisions, smoothing);
|
run (method, *m_ethz, subdivisions, smoothing);
|
||||||
|
}
|
||||||
|
else if (classifier == 2)
|
||||||
|
{
|
||||||
#ifdef CGAL_LINKED_WITH_OPENCV
|
#ifdef CGAL_LINKED_WITH_OPENCV
|
||||||
else
|
if (m_random_forest == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: OpenCV Random Forest must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
run (method, *m_random_forest, subdivisions, smoothing);
|
run (method, *m_random_forest, subdivisions, smoothing);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
else if (classifier == 3)
|
||||||
|
{
|
||||||
|
#ifdef CGAL_LINKED_WITH_TENSORFLOW
|
||||||
|
if (m_neural_network == NULL)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: TensorFlow Neural Network must be trained or have a configuration loaded first" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
run (method, *m_neural_network, subdivisions, smoothing);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue