diff --git a/Classification/doc/Classification/Classification.txt b/Classification/doc/Classification/Classification.txt index e2487567760..700ec6e93ce 100644 --- a/Classification/doc/Classification/Classification.txt +++ b/Classification/doc/Classification/Classification.txt @@ -200,9 +200,11 @@ Example of classification on a point set with medium noise and outliers (left: i \cgal provides [ETHZ_random_forest_classifier](@ref CGAL::Classification::ETHZ_random_forest_classifier), a classifier based on the Random Forest Template Library developed by -Stefan Walk at ETH Zurich \cgalCite{cgal:w-erftl-14}. This classifier -uses a ground truth training set to construct several decision trees -that are then used to assign a label to each input item. +Stefan Walk at ETH Zurich \cgalCite{cgal:w-erftl-14} (the library is +included with the \cgal release, the user does not have to install +anything more). This classifier uses a ground truth training set to +construct several decision trees that are then used to assign a label +to each input item. This classifier cannot be set up by hand and requires a ground truth training set. The training algorithm is significantly faster but @@ -213,7 +215,7 @@ configuration files are larger than those produced by but the output quality is usually significantly better, especially in the cases where many labels are used (more than five). -An [example](\ref Classification_example_random_forest) shows how to +An [example](\ref Classification_example_ethz_random_forest) shows how to use this classifier. For more details about the algorithm, please refer to README provided in the [ETH Zurich's code archive](https://www.ethz.ch/content/dam/ethz/special-interest/baug/igp/photogrammetry-remote-sensing-dam/documents/sourcecode-and-datasets/Random%20Forest/rforest.zip). @@ -231,7 +233,7 @@ 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 Forest instead. -An [example](\ref Classification_example_random_forest) shows how to +An [example](\ref Classification_example_opencv_random_forest) shows how to 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) of OpenCV. @@ -396,11 +398,17 @@ The following example: \cgalExample{Classification/example_generation_and_training.cpp} -\subsection Classification_example_random_forest Random Forest +\subsection Classification_example_ethz_random_forest ETHZ Random Forest -The following example shows how to use classifiers [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 [ETHZ_random_forest_classifier](@ref CGAL::Classification::ETHZ_random_forest_classifier) using an input training set. -\cgalExample{Classification/example_random_forest.cpp} +\cgalExample{Classification/example_ethz_random_forest.cpp} + +\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. + +\cgalExample{Classification/example_opencv_random_forest.cpp} \section Classification_history History diff --git a/Classification/doc/Classification/examples.txt b/Classification/doc/Classification/examples.txt index 2ef96507c17..b0f9daf619e 100644 --- a/Classification/doc/Classification/examples.txt +++ b/Classification/doc/Classification/examples.txt @@ -2,5 +2,6 @@ \example Classification/example_classification.cpp \example Classification/example_feature.cpp \example Classification/example_generation_and_training.cpp -\example Classification/example_random_forest.cpp +\example Classification/example_ethz_random_forest.cpp +\example Classification/example_opencv_random_forest.cpp */ diff --git a/Classification/examples/Classification/CMakeLists.txt b/Classification/examples/Classification/CMakeLists.txt index ec830b242dd..adedc2e73fd 100644 --- a/Classification/examples/Classification/CMakeLists.txt +++ b/Classification/examples/Classification/CMakeLists.txt @@ -10,10 +10,8 @@ cmake_minimum_required(VERSION 2.8.11) find_package( CGAL QUIET COMPONENTS ) if ( NOT CGAL_FOUND ) - message(STATUS "This project requires the CGAL library, and will not be compiled.") return() - endif() # include helper file @@ -24,11 +22,8 @@ include( ${CGAL_USE_FILE} ) find_package( Boost REQUIRED COMPONENTS serialization iostreams) if ( NOT Boost_FOUND ) - message(STATUS "This project requires the Boost library, and will not be compiled.") - return() - endif() find_package( TBB ) @@ -55,17 +50,21 @@ endif() create_single_source_cgal_program( "example_generation_and_training.cpp" CXX_FEATURES ${needed_cxx_features} ) create_single_source_cgal_program( "example_feature.cpp" CXX_FEATURES ${needed_cxx_features} ) -if (Boost_SERIALIZATION_FOUND AND Boost_IOSTREAMS_FOUND) - if( OpenCV_FOUND ) - message(STATUS "Found OpenCV ${OpenCV_VERSION}") - include_directories( ${OpenCV_INCLUDE_DIRS} ) - create_single_source_cgal_program( "example_random_forest.cpp" CXX_FEATURES ${needed_cxx_features} ) - target_link_libraries( example_random_forest ${OpenCV_LIBS} ${Boost_SERIALIZATION_LIBRARY} ${Boost_IOSTREAMS_LIBRARY}) - target_compile_definitions(example_random_forest PUBLIC "-DCGAL_LINKED_WITH_OPENCV") - else() - create_single_source_cgal_program( "example_random_forest.cpp" CXX_FEATURES ${needed_cxx_features} ) - target_link_libraries( example_random_forest ${Boost_SERIALIZATION_LIBRARY} ${Boost_IOSTREAMS_LIBRARY}) - message(STATUS "OpenCV not found, random forest example won't have OpenCV classifier.") - endif() +if( OpenCV_FOUND ) + message(STATUS "Found OpenCV ${OpenCV_VERSION}") + include_directories( ${OpenCV_INCLUDE_DIRS} ) + create_single_source_cgal_program( "example_opencv_random_forest.cpp" CXX_FEATURES ${needed_cxx_features} ) + target_link_libraries( example_opencv_random_forest ${OpenCV_LIBS} ) + target_compile_definitions(example_opencv_random_forest PUBLIC "-DCGAL_LINKED_WITH_OPENCV") +else() + message(STATUS "OpenCV not found, OpenCV random forest example won't be compiled.") +endif() + +if (Boost_SERIALIZATION_FOUND AND Boost_IOSTREAMS_FOUND) + create_single_source_cgal_program( "example_ethz_random_forest.cpp" CXX_FEATURES ${needed_cxx_features} ) + target_link_libraries( example_ethz_random_forest ${Boost_SERIALIZATION_LIBRARY} ${Boost_IOSTREAMS_LIBRARY}) + target_compile_definitions(example_ethz_random_forest PUBLIC "-DCGAL_LINKED_WITH_BOOST_SERIALIZATION") +else() + message(STATUS "Boost serialization and IO streams not found, ETHZ random forest example won't be compiled.") endif() diff --git a/Classification/examples/Classification/example_ethz_random_forest.cpp b/Classification/examples/Classification/example_ethz_random_forest.cpp new file mode 100644 index 00000000000..423056b12e8 --- /dev/null +++ b/Classification/examples/Classification/example_ethz_random_forest.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +typedef CGAL::Simple_cartesian Kernel; +typedef Kernel::Point_3 Point; +typedef CGAL::Point_set_3 Point_set; +typedef Kernel::Iso_cuboid_3 Iso_cuboid_3; + +typedef Point_set::Point_map Pmap; +typedef Point_set::Property_map Imap; +typedef Point_set::Property_map 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 Feature_generator; + + +int main (int argc, char** argv) +{ + std::string filename = "data/b9_training.ply"; + bool use_opencv = false; + + if (argc > 1) + { + if (std::string(argv[1]) == "-cv") + { + use_opencv = true; + if (argc > 2) + filename = argv[2]; + } + else + 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 ("label"); + if (!lm_found) + { + std::cerr << "Error: \"label\" property not found in input file." << std::endl; + return EXIT_FAILURE; + } + + std::vector ground_truth; + ground_truth.reserve (pts.size()); + std::copy (pts.range(label_map).begin(), pts.range(label_map).end(), + std::back_inserter (ground_truth)); + + Feature_set features; + + std::cerr << "Generating features" << std::endl; + CGAL::Real_timer t; + t.start(); + Feature_generator generator (features, pts, pts.point_map(), + 5); // using 5 scales + t.stop(); + std::cerr << "Done in " << t.time() << " second(s)" << std::endl; + + // Add types + Label_set labels; + Label_handle ground = labels.add ("ground"); + Label_handle vegetation = labels.add ("vegetation"); + Label_handle roof = labels.add ("roof"); + + std::vector label_indices(pts.size(), -1); + + std::cerr << "Using ETHZ Random Forest Classifier" << std::endl; + Classification::ETHZ_random_forest_classifier classifier (labels, features); + + std::cerr << "Training" << std::endl; + t.reset(); + t.start(); + classifier.train (ground_truth); + t.stop(); + std::cerr << "Done in " << t.time() << " second(s)" << std::endl; + + t.reset(); + t.start(); + Classification::classify_with_graphcut + (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("red", 0).first; + UCmap green = pts.add_property_map("green", 0).first; + UCmap blue = pts.add_property_map("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; +} diff --git a/Classification/examples/Classification/example_random_forest.cpp b/Classification/examples/Classification/example_opencv_random_forest.cpp similarity index 72% rename from Classification/examples/Classification/example_random_forest.cpp rename to Classification/examples/Classification/example_opencv_random_forest.cpp index f9ba4302531..f14eb20b303 100644 --- a/Classification/examples/Classification/example_random_forest.cpp +++ b/Classification/examples/Classification/example_opencv_random_forest.cpp @@ -1,8 +1,3 @@ -#if defined (_MSC_VER) && !defined (_WIN64) -#pragma warning(disable:4244) // boost::number_distance::distance() - // converts 64 to 32 bits integers -#endif - #include #include #include @@ -51,14 +46,6 @@ int main (int argc, char** argv) filename = argv[1]; } -#ifndef CGAL_LINKED_WITH_OPENCV - if (use_opencv) - { - std::cerr << "OpenCV not available, exiting." << std::endl; - return EXIT_SUCCESS; - } -#endif - std::ifstream in (filename.c_str(), std::ios::binary); Point_set pts; @@ -97,48 +84,23 @@ int main (int argc, char** argv) std::vector label_indices(pts.size(), -1); -#ifdef CGAL_LINKED_WITH_OPENCV - if (use_opencv) - { - std::cerr << "Using OpenCV Random Forest Classifier" << std::endl; - Classification::OpenCV_random_forest_classifier classifier (labels, features); + std::cerr << "Using OpenCV Random Forest Classifier" << std::endl; + Classification::OpenCV_random_forest_classifier classifier (labels, features); - std::cerr << "Training" << std::endl; - t.reset(); - t.start(); - classifier.train (ground_truth); - t.stop(); - std::cerr << "Done in " << t.time() << " second(s)" << std::endl; + std::cerr << "Training" << std::endl; + t.reset(); + t.start(); + classifier.train (ground_truth); + t.stop(); + std::cerr << "Done in " << t.time() << " second(s)" << std::endl; - t.reset(); - t.start(); - Classification::classify_with_graphcut - (pts, pts.point_map(), labels, classifier, - generator.neighborhood().k_neighbor_query(12), - 0.2f, 1, label_indices); - t.stop(); - } - else -#endif - { - std::cerr << "Using ETHZ Random Forest Classifier" << std::endl; - Classification::ETHZ_random_forest_classifier classifier (labels, features); - - std::cerr << "Training" << std::endl; - t.reset(); - t.start(); - classifier.train (ground_truth); - t.stop(); - std::cerr << "Done in " << t.time() << " second(s)" << std::endl; - - t.reset(); - t.start(); - Classification::classify_with_graphcut - (pts, pts.point_map(), labels, classifier, - generator.neighborhood().k_neighbor_query(12), - 0.2f, 1, label_indices); - t.stop(); - } + t.reset(); + t.start(); + Classification::classify_with_graphcut + (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; diff --git a/Classification/examples/Classification/example_random_forest.cmd b/Classification/examples/Classification/example_random_forest.cmd deleted file mode 100644 index 3c5cdf03236..00000000000 --- a/Classification/examples/Classification/example_random_forest.cmd +++ /dev/null @@ -1,2 +0,0 @@ -data/b9_training.ply --cv data/b9_training.ply diff --git a/Classification/include/CGAL/Classification/ETHZ_random_forest_classifier.h b/Classification/include/CGAL/Classification/ETHZ_random_forest_classifier.h index aec68d172d6..55af4855b66 100644 --- a/Classification/include/CGAL/Classification/ETHZ_random_forest_classifier.h +++ b/Classification/include/CGAL/Classification/ETHZ_random_forest_classifier.h @@ -33,10 +33,12 @@ #include #include +#ifdef CGAL_LINKED_WITH_BOOST_SERIALIZATION #include #include #include #include +#endif namespace CGAL { @@ -193,6 +195,7 @@ public: The output file is written in an GZIP container that is readable by the `load_configuration()` method. */ +#if defined(CGAL_LINKED_WITH_BOOST_SERIALIZATION) || defined(DOXYGEN_RUNNING) void save_configuration (std::ostream& output) { boost::iostreams::filtering_ostream outs; @@ -201,6 +204,13 @@ public: boost::archive::text_oarchive oas(outs); oas << BOOST_SERIALIZATION_NVP(*m_rfc); } +#else + void save_configuration (std::ostream&) + { + std::cerr << "Error: can't use ETHZ random forest classifier IO functions, " + << "missing Boost Serialization library." << std::endl; + } +#endif /*! \brief Loads a configuration from the stream `input`. @@ -211,6 +221,7 @@ public: the ones present when the file was generated using `save_configuration()`. */ +#if defined(CGAL_LINKED_WITH_BOOST_SERIALIZATION) || defined(DOXYGEN_RUNNING) void load_configuration (std::istream& input) { liblearning::RandomForest::ForestParams params; @@ -224,6 +235,13 @@ public: boost::archive::text_iarchive ias(ins); ias >> BOOST_SERIALIZATION_NVP(*m_rfc); } +#else + void load_configuration (std::istream&) + { + std::cerr << "Error: can't use ETHZ random forest classifier IO functions, " + << "missing Boost Serialization library." << std::endl; + } +#endif };