diff --git a/CMakeLists.txt b/CMakeLists.txt index 15dfd5d93..05f7298c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project(Easy3D) set(EASY3D_MAJOR_VERSION 2) set(EASY3D_MINOR_VERSION 4) -set(EASY3D_PATCH_VERSION 8) +set(EASY3D_PATCH_VERSION 9) set(EASY3D_VERSION "${EASY3D_MAJOR_VERSION}.${EASY3D_MINOR_VERSION}.${EASY3D_PATCH_VERSION}") ################################################################################ diff --git a/applications/Mapple/dialogs/dialog_point_cloud_simplification.cpp b/applications/Mapple/dialogs/dialog_point_cloud_simplification.cpp index 5175a075e..ccaa1e9a4 100644 --- a/applications/Mapple/dialogs/dialog_point_cloud_simplification.cpp +++ b/applications/Mapple/dialogs/dialog_point_cloud_simplification.cpp @@ -109,10 +109,7 @@ void DialogPointCloudSimplification::constructKdTree() { if (cloud) { if (kdtree_) delete kdtree_; - kdtree_ = new KdTreeSearch_ETH; - kdtree_->begin(); - kdtree_->add_point_cloud(cloud); - kdtree_->end(); + kdtree_ = new KdTreeSearch_ETH(cloud); } } diff --git a/easy3d/algo/point_cloud_normals.cpp b/easy3d/algo/point_cloud_normals.cpp index 5e5a778fd..c70c56f25 100644 --- a/easy3d/algo/point_cloud_normals.cpp +++ b/easy3d/algo/point_cloud_normals.cpp @@ -60,10 +60,7 @@ namespace easy3d { w.start(); LOG(INFO) << "building kd_tree..."; - KdTreeSearch_NanoFLANN kdtree; - kdtree.begin(); - kdtree.add_point_cloud(cloud); - kdtree.end(); + KdTreeSearch_NanoFLANN kdtree(cloud); LOG(INFO) << "done. " << w.time_string(); int num = cloud->n_vertices(); @@ -388,10 +385,7 @@ namespace easy3d { w.start(); LOG(INFO) << "building kd_tree..."; - KdTreeSearch_NanoFLANN kdtree; - kdtree.begin(); - kdtree.add_point_cloud(cloud); - kdtree.end(); + KdTreeSearch_NanoFLANN kdtree(cloud); LOG(INFO) << "done. " << w.time_string(); w.restart(); diff --git a/easy3d/algo/point_cloud_simplification.cpp b/easy3d/algo/point_cloud_simplification.cpp index 04274c57c..c8036fac3 100644 --- a/easy3d/algo/point_cloud_simplification.cpp +++ b/easy3d/algo/point_cloud_simplification.cpp @@ -41,10 +41,7 @@ namespace easy3d { KdTreeSearch *kdtree = tree; bool need_delete(false); if (!kdtree) { - kdtree = new KdTreeSearch_ETH; - kdtree->begin(); - kdtree->add_point_cloud(cloud); - kdtree->end(); + kdtree = new KdTreeSearch_ETH(cloud); need_delete = true; } @@ -164,10 +161,7 @@ namespace easy3d { KdTreeSearch *kdtree = tree; bool need_delete(false); if (!kdtree) { - kdtree = new KdTreeSearch_ETH; - kdtree->begin(); - kdtree->add_point_cloud(cloud); - kdtree->end(); + kdtree = new KdTreeSearch_ETH(cloud); need_delete = true; } @@ -244,10 +238,7 @@ namespace easy3d { if (expected_num >= num) return points_to_delete; // expected num is greater than / equal to given number. - KdTreeSearch_ETH kdtree; - kdtree.begin(); - kdtree.add_point_cloud(cloud); - kdtree.end(); + KdTreeSearch_ETH kdtree(cloud); // the average squared distance to its nearest neighbor; smaller value means highter density std::vector sqr_distance(cloud->n_vertices()); diff --git a/easy3d/kdtree/kdtree_search.cpp b/easy3d/kdtree/kdtree_search.cpp index fa9164b47..34a73c1b8 100644 --- a/easy3d/kdtree/kdtree_search.cpp +++ b/easy3d/kdtree/kdtree_search.cpp @@ -30,7 +30,7 @@ namespace easy3d { - KdTreeSearch::KdTreeSearch() + KdTreeSearch::KdTreeSearch(const PointCloud *cloud) { } diff --git a/easy3d/kdtree/kdtree_search.h b/easy3d/kdtree/kdtree_search.h index 72b9d7d70..a12f1341d 100644 --- a/easy3d/kdtree/kdtree_search.h +++ b/easy3d/kdtree/kdtree_search.h @@ -79,27 +79,13 @@ namespace easy3d { class KdTreeSearch { public: - KdTreeSearch(); - - virtual ~KdTreeSearch(); - - /// \name Tree construction - /// @{ /** - * \brief Begins the construction of a KdTree. + * \brief Constructor. + * \param cloud The point cloud for which a KdTree will be constructed. */ - virtual void begin() = 0; + KdTreeSearch(const PointCloud *cloud); - /** - * \brief Sets the point cloud for which a KdTree will be constructed. - */ - virtual void add_point_cloud(PointCloud *cloud) = 0; - - /** - * \brief Finalizes the construction of a KdTree. - */ - virtual void end() = 0; - /// @} + virtual ~KdTreeSearch(); /// \name Closest point query /// @{ diff --git a/easy3d/kdtree/kdtree_search_ann.cpp b/easy3d/kdtree/kdtree_search_ann.cpp index 6b390b5e0..33e36386e 100644 --- a/easy3d/kdtree/kdtree_search_ann.cpp +++ b/easy3d/kdtree/kdtree_search_ann.cpp @@ -43,32 +43,33 @@ using namespace ANN; namespace easy3d { - KdTreeSearch_ANN::KdTreeSearch_ANN() { - points_ = nullptr; - points_num_ = 0; - tree_ = nullptr; + KdTreeSearch_ANN::KdTreeSearch_ANN(const PointCloud *cloud) : KdTreeSearch(cloud) { k_for_radius_search_ = 32; LOG(INFO) << "KdTreeSearch_ANN: k = 32 for radius search"; - } - - KdTreeSearch_ANN::~KdTreeSearch_ANN() { + // prepare data + points_num_ = int(cloud->n_vertices()); #if COPY_POINT_CLOUD // make a copy of the point cloud when constructing the kd-tree - if (points_) - annDeallocPts(points_); + points_ = annAllocPts(points_num_, 3); + const std::vector& pts = cloud->points(); + for (int i = 0; i < points_num_; ++i) { + const vec3& p = pts[i]; + points_[i][0] = p[0]; + points_[i][1] = p[1]; + points_[i][2] = p[2]; + } #else - if (points_) - delete[] points_; + points_ = new float*[points_num_]; + const std::vector& pts = cloud->points(); + for (int i = 0; i < points_num_; ++i) + points_[i] = const_cast(pts[i].data()); #endif - - delete get_tree(tree_); - annClose(); + // create tree + tree_ = new ANNkd_tree(const_cast(points_), points_num_, 3); } - void KdTreeSearch_ANN::begin() { - points_num_ = 0; - + KdTreeSearch_ANN::~KdTreeSearch_ANN() { #if COPY_POINT_CLOUD // make a copy of the point cloud when constructing the kd-tree if (points_) annDeallocPts(points_); @@ -78,33 +79,7 @@ namespace easy3d { #endif delete get_tree(tree_); - tree_ = nullptr; - } - - - void KdTreeSearch_ANN::end() { - tree_ = new ANNkd_tree(points_, points_num_, 3); - } - - - void KdTreeSearch_ANN::add_point_cloud(PointCloud* cloud) { - points_num_ = int(cloud->n_vertices()); - -#if COPY_POINT_CLOUD // make a copy of the point cloud when constructing the kd-tree - points_ = annAllocPts(points_num_, 3); - const std::vector& pts = cloud->points(); - for (int i = 0; i < points_num_; ++i) { - const vec3& p = pts[i]; - points_[i][0] = p[0]; - points_[i][1] = p[1]; - points_[i][2] = p[2]; - } -#else - points_ = new float*[points_num_]; - std::vector& pts = cloud->points(); - for (int i = 0; i < points_num_; ++i) - points_[i] = pts[i]; -#endif + annClose(); } diff --git a/easy3d/kdtree/kdtree_search_ann.h b/easy3d/kdtree/kdtree_search_ann.h index 359e92257..8b6a1a43f 100644 --- a/easy3d/kdtree/kdtree_search_ann.h +++ b/easy3d/kdtree/kdtree_search_ann.h @@ -41,28 +41,13 @@ namespace easy3d { */ class KdTreeSearch_ANN : public KdTreeSearch { public: - KdTreeSearch_ANN(); - - ~KdTreeSearch_ANN() override; - - - /// \name Tree construction - /// @{ - /** - * \brief Begins the construction of a KdTree. - */ - void begin() override; - /** - * \brief Sets the point cloud for which a KdTree will be constructed. + * \brief Constructor. + * \param cloud The point cloud for which a KdTree will be constructed. */ - void add_point_cloud(PointCloud *cloud) override; + KdTreeSearch_ANN(const PointCloud *cloud); - /** - * \brief Finalizes the construction of a KdTree. - */ - void end() override; - /// @} + ~KdTreeSearch_ANN() override; /// \name Closest point query /// @{ diff --git a/easy3d/kdtree/kdtree_search_eth.cpp b/easy3d/kdtree/kdtree_search_eth.cpp index d13c1bab0..4819623c8 100644 --- a/easy3d/kdtree/kdtree_search_eth.cpp +++ b/easy3d/kdtree/kdtree_search_eth.cpp @@ -35,41 +35,24 @@ namespace easy3d { - KdTreeSearch_ETH::KdTreeSearch_ETH() { - points_num_ = 0; - points_ = nullptr; - tree_ = nullptr; - } - + KdTreeSearch_ETH::KdTreeSearch_ETH(const PointCloud *cloud) : KdTreeSearch(cloud) { + // prepare data + points_num_ = int(cloud->n_vertices()); + const std::vector& points = cloud->points(); + points_ = const_cast(points[0].data()); - KdTreeSearch_ETH::~KdTreeSearch_ETH() { - delete get_tree(tree_); - points_ = nullptr; + // create tree + const int maxBucketSize = 16 ; // number of points per bucket + tree_ = new kdtree::KdTree(reinterpret_cast(points_), points_num_, maxBucketSize); } - void KdTreeSearch_ETH::begin() { + KdTreeSearch_ETH::~KdTreeSearch_ETH() { delete get_tree(tree_); - tree_ = nullptr; - - points_num_ = 0; points_ = nullptr; } - void KdTreeSearch_ETH::end() { - int maxBucketSize = 16 ; // number of points per bucket - tree_ = new kdtree::KdTree(reinterpret_cast(points_), points_num_, maxBucketSize); - } - - - void KdTreeSearch_ETH::add_point_cloud(PointCloud* cloud) { - points_num_ = int(cloud->n_vertices()); - std::vector& points = cloud->points(); - points_ = points[0]; - } - - int KdTreeSearch_ETH::find_closest_point(const vec3& p) const { kdtree::Vector3D v3d( p.x, p.y, p.z ); get_tree(tree_)->setNOfNeighbours( 1 ); diff --git a/easy3d/kdtree/kdtree_search_eth.h b/easy3d/kdtree/kdtree_search_eth.h index e5c3d7219..79a97342b 100644 --- a/easy3d/kdtree/kdtree_search_eth.h +++ b/easy3d/kdtree/kdtree_search_eth.h @@ -41,27 +41,13 @@ namespace easy3d { */ class KdTreeSearch_ETH : public KdTreeSearch { public: - KdTreeSearch_ETH(); - - virtual ~KdTreeSearch_ETH(); - - /// \name Tree construction - /// @{ /** - * \brief Begins the construction of a KdTree. + * \brief Constructor. + * \param cloud The point cloud for which a KdTree will be constructed. */ - virtual void begin(); + KdTreeSearch_ETH(const PointCloud *cloud); - /** - * \brief Sets the point cloud for which a KdTree will be constructed. - */ - virtual void add_point_cloud(PointCloud *cloud); - - /** - * \brief Finalizes the construction of a KdTree. - */ - virtual void end(); - /// @} + virtual ~KdTreeSearch_ETH(); /// \name Closest point query /// @{ diff --git a/easy3d/kdtree/kdtree_search_flann.cpp b/easy3d/kdtree/kdtree_search_flann.cpp index 59d1bdc11..7103d0667 100644 --- a/easy3d/kdtree/kdtree_search_flann.cpp +++ b/easy3d/kdtree/kdtree_search_flann.cpp @@ -34,46 +34,31 @@ namespace easy3d { - KdTreeSearch_FLANN::KdTreeSearch_FLANN() { - points_ = nullptr; - points_num_ = 0; - tree_ = nullptr; - + KdTreeSearch_FLANN::KdTreeSearch_FLANN(const PointCloud *cloud) : KdTreeSearch(cloud) { //checks_ = 32; checks_ = flann::FLANN_CHECKS_AUTOTUNED; - } - - - KdTreeSearch_FLANN::~KdTreeSearch_FLANN() { - delete get_tree(tree_); - } - - - void KdTreeSearch_FLANN::set_checks(int chk) { - checks_ = chk; - } - - void KdTreeSearch_FLANN::begin() { - delete get_tree(tree_); - tree_ = nullptr; - } + // prepare data + points_num_ = int(cloud->n_vertices()); + const std::vector& points = cloud->points(); + points_ = const_cast(points[0].data()); - void KdTreeSearch_FLANN::end() { + // create tree flann::Matrix dataset(points_, points_num_, 3); - // construct a single kd-tree optimized for searching lower dimensionality data flann::Index< flann::L2 >* tree = new flann::Index< flann::L2 >(dataset, flann::KDTreeSingleIndexParams()); tree->buildIndex(); - tree_ = tree; } - void KdTreeSearch_FLANN::add_point_cloud(PointCloud* cloud) { - points_num_ = int(cloud->n_vertices()); - std::vector& points = cloud->points(); - points_ = points[0]; + KdTreeSearch_FLANN::~KdTreeSearch_FLANN() { + delete get_tree(tree_); + } + + + void KdTreeSearch_FLANN::set_checks(int chk) { + checks_ = chk; } diff --git a/easy3d/kdtree/kdtree_search_flann.h b/easy3d/kdtree/kdtree_search_flann.h index e2080741d..cc26030c3 100644 --- a/easy3d/kdtree/kdtree_search_flann.h +++ b/easy3d/kdtree/kdtree_search_flann.h @@ -41,7 +41,11 @@ namespace easy3d { */ class KdTreeSearch_FLANN : public KdTreeSearch { public: - KdTreeSearch_FLANN(); + /** + * \brief Constructor. + * \param cloud The point cloud for which a KdTree will be constructed. + */ + KdTreeSearch_FLANN(const PointCloud *cloud); virtual ~KdTreeSearch_FLANN(); @@ -56,24 +60,6 @@ namespace easy3d { */ void set_checks(int chk); - /// \name Tree construction - /// @{ - /** - * \brief Begins the construction of a KdTree. - */ - virtual void begin(); - - /** - * \brief Sets the point cloud for which a KdTree will be constructed. - */ - virtual void add_point_cloud(PointCloud *cloud); - - /** - * \brief Finalizes the construction of a KdTree. - */ - virtual void end(); - /// @} - /// \name Closest point query /// @{ diff --git a/easy3d/kdtree/kdtree_search_nanoflann.cpp b/easy3d/kdtree/kdtree_search_nanoflann.cpp index c87bc3ccd..2e3577cee 100644 --- a/easy3d/kdtree/kdtree_search_nanoflann.cpp +++ b/easy3d/kdtree/kdtree_search_nanoflann.cpp @@ -69,24 +69,11 @@ namespace easy3d { - KdTreeSearch_NanoFLANN::KdTreeSearch_NanoFLANN() { - points_ = nullptr; - tree_ = nullptr; - } - - - KdTreeSearch_NanoFLANN::~KdTreeSearch_NanoFLANN() { - delete get_tree(tree_); - } - + KdTreeSearch_NanoFLANN::KdTreeSearch_NanoFLANN(const PointCloud *cloud) : KdTreeSearch(cloud) { + // prepare data + points_ = const_cast< std::vector* >(&cloud->points()); - void KdTreeSearch_NanoFLANN::begin() { - delete get_tree(tree_); - tree_ = nullptr; - } - - - void KdTreeSearch_NanoFLANN::end() { + // create tree PointSet* pset = new PointSet(points_); KdTree* tree = new KdTree(pset); tree->buildIndex(); @@ -94,8 +81,8 @@ namespace easy3d { } - void KdTreeSearch_NanoFLANN::add_point_cloud(PointCloud* cloud) { - points_ = &cloud->points(); + KdTreeSearch_NanoFLANN::~KdTreeSearch_NanoFLANN() { + delete get_tree(tree_); } diff --git a/easy3d/kdtree/kdtree_search_nanoflann.h b/easy3d/kdtree/kdtree_search_nanoflann.h index eff954957..5e240a095 100644 --- a/easy3d/kdtree/kdtree_search_nanoflann.h +++ b/easy3d/kdtree/kdtree_search_nanoflann.h @@ -40,27 +40,13 @@ namespace easy3d { */ class KdTreeSearch_NanoFLANN : public KdTreeSearch { public: - KdTreeSearch_NanoFLANN(); - - virtual ~KdTreeSearch_NanoFLANN(); - - /// \name Tree construction - /// @{ /** - * \brief Begins the construction of a KdTree. + * \brief Constructor. + * \param cloud The point cloud for which a KdTree will be constructed. */ - virtual void begin(); + KdTreeSearch_NanoFLANN(const PointCloud *cloud); - /** - * \brief Sets the point cloud for which a KdTree will be constructed. - */ - virtual void add_point_cloud(PointCloud *cloud); - - /** - * \brief Finalizes the construction of a KdTree. - */ - virtual void end(); - /// @} + virtual ~KdTreeSearch_NanoFLANN(); /// \name Closest point query /// @{ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b3634884f..d2ccb3590 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(${PROJECT_NAME} test_timer.cpp test_signal.cpp test_console_style.cpp + test_kdtree.cpp graph.cpp linear_solvers.cpp main.cpp diff --git a/tests/main.cpp b/tests/main.cpp index fcfc68222..0d39b5178 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -39,6 +39,7 @@ int test_point_cloud(); int test_surface_mesh(); int test_polyhedral_mesh(); int test_graph(); +int test_kdtree(); int test_point_cloud_algorithms(); int test_surface_mesh_algorithms(); @@ -90,6 +91,7 @@ int main(int argc, char* argv[]) { result += test_surface_mesh(); result += test_polyhedral_mesh(); result += test_graph(); + result += test_kdtree(); result += test_point_cloud_algorithms(); result += test_surface_mesh_algorithms(); diff --git a/tests/test_kdtree.cpp b/tests/test_kdtree.cpp new file mode 100644 index 000000000..86aeb99f8 --- /dev/null +++ b/tests/test_kdtree.cpp @@ -0,0 +1,107 @@ +/******************************************************************** + * Copyright (C) 2015 Liangliang Nan + * https://3d.bk.tudelft.nl/liangliang/ + * + * This file is part of Easy3D. If it is useful in your research/work, + * I would be grateful if you show your appreciation by citing it: + * ------------------------------------------------------------------ + * Liangliang Nan. + * Easy3D: a lightweight, easy-to-use, and efficient C++ library + * for processing and rendering 3D data. + * Journal of Open Source Software, 6(64), 3255, 2021. + * ------------------------------------------------------------------ + * + * Easy3D is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 3 + * as published by the Free Software Foundation. + * + * Easy3D is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace easy3d; + + +void evaluate(const PointCloud* cloud, KdTreeSearch* tree) { + std::cout << "\tquerying closest vertex (for each point in the point cloud)..."; + StopWatch w; + for (auto v : cloud->vertices()) + tree->find_closest_point(cloud->position(v)); + std::cout << " done. time = " << w.time_string() << std::endl; + + std::cout << "\tquerying K(=16) closest vertex (for each point in the point cloud)..."; + const unsigned int k = 16; + std::vector neighbors; + w.restart(); + for (auto v : cloud->vertices()) { + tree->find_closest_k_points(cloud->position(v), k, neighbors); + } + std::cout << " done. time = " << w.time_string() << std::endl; + + std::cout << "\tquerying the nearest neighbors within a fixed range (for each point in the point cloud). "; + const float radius = cloud->bounding_box().radius() * 0.0001f; + std::cout << " radius = " << radius << "..."; + w.restart(); + for (auto v : cloud->vertices()) + tree->find_points_in_range(cloud->position(v), radius, neighbors); + std::cout << " done. time = " << w.time_string() << std::endl; +} + + +// This examples shows how to use the kd-tree. +int test_kdtree() { + std::cout << "testing kd-tree..." << std::endl; + const std::string file = resource::directory() + "/data/bunny.bin"; + PointCloud* cloud = PointCloudIO::load(file); + if (!cloud) { + LOG(ERROR) << "point cloud doesn't exist: " << file; + return EXIT_FAILURE; + } + std::cout << "\tnumber of points in point cloud: " << cloud->n_vertices() << std::endl; + + std::cout << "------- kd-tree using ANN --------" << std::endl; + std::cout << "\tconstructing kd-tree..."; + StopWatch w; + KdTreeSearch_ANN ann(cloud); + std::cout << " done. time = " << w.time_string() << std::endl; + evaluate(cloud, &ann); + + std::cout << "------- kd-tree using ETH --------" << std::endl; + std::cout << "\tconstructing kd-tree..."; + w.restart(); + KdTreeSearch_ETH eth(cloud); + std::cout << " done. time = " << w.time_string() << std::endl; + evaluate(cloud, ð); + + std::cout << "------- kd-tree using FLANN --------" << std::endl; + std::cout << "\tconstructing kd-tree..."; + w.restart(); + KdTreeSearch_FLANN flann(cloud); + std::cout << " done. time = " << w.time_string() << std::endl; + evaluate(cloud, &flann); + + std::cout << "------- kd-tree using NANOFLANN --------" << std::endl; + std::cout << "\tconstructing kd-tree..."; + w.restart(); + KdTreeSearch_NanoFLANN nanoflann(cloud); + std::cout << " done. time = " << w.time_string() << std::endl; + evaluate(cloud, &nanoflann); + + return EXIT_SUCCESS; +} diff --git a/tests/test_logging.cpp b/tests/test_logging.cpp index 4edc6e5ff..69275e5b8 100644 --- a/tests/test_logging.cpp +++ b/tests/test_logging.cpp @@ -86,7 +86,7 @@ void run_conditional_ccasional_logging() { } -bool test_logging() { +int test_logging() { // CHECK Operation CHECK_NE(1, 2) << ": The world must be ending!"; // Check if it is euqual @@ -152,5 +152,5 @@ bool test_logging() { LOG(INFO) << "---------- TEST has succeeded!!!!!!!!!!!!!!!!! ----------"; LOG(FATAL) << "You should have seen the program crashed - just a test :-)"; - return true; + return EXIT_SUCCESS; }