So I have the following class KdTree:
#include <nanoflann.hpp>
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <cstdlib>
#include <iostream>
struct PointCloud
{
struct Point
{
double x,y,z;
};
std::vector<Point> pts;
inline size_t kdtree_get_point_count() const { return pts.size(); }
inline double kdtree_get_pt(const size_t idx, const size_t dim) const
{
if (dim == 0) return pts[idx].x;
else if (dim == 1) return pts[idx].y;
else if (dim == 2) return pts[idx].z;
}
template <class BBOX>
bool kdtree_get_bbox(BBOX& /* bb */) const { return false; }
};
void generatePointCloud(PointCloud &point, const std::vector<std::vector<double>>& given)
{
point.pts.resize(given.size());
for (size_t i = 0; i < given.size(); ++i) {
point.pts[i].x = given[i][0];
point.pts[i].y = given[i][1];
point.pts[i].z = given[i][2];
}
}
using namespace nanoflann;
using Points = std::vector<std::vector<double>>;
class KdTree {
public:
KdTree(const Points& points) {
PointCloud cloud;
generatePointCloud(cloud, points); // just puts points into cloud format
index = std::make_shared<my_kd_tree>(3 /*dim*/, cloud, KDTreeSingleIndexAdaptorParams(10 /* max leaf */) );
index->buildIndex();
}
size_t GetNearest(const std::vector<double>& pt) const {
double query_pt[3] = { pt[0], pt[1], pt[2] };
const size_t num_results = 1;
size_t ret_index;
double out_dist_sqr;
nanoflann::KNNResultSet<double > resultSet(num_results);
resultSet.init(&ret_index, &out_dist_sqr );
index->findNeighbors(resultSet, &query_pt[0], nanoflann::SearchParams(10));
std::cout << "knnSearch(nn="<<num_results<<"): \n";
std::cout << "ret_index=" << ret_index << " out_dist_sqr=" << out_dist_sqr << endl;
return ret_index;
}
typedef KDTreeSingleIndexAdaptor<
L2_Simple_Adaptor<double , PointCloud > ,
PointCloud,
3 /* dim */
> my_kd_tree;
std::shared_ptr<my_kd_tree> index = nullptr;
};
The problem is that the following code raises segfault:
int main()
{
srand(static_cast<unsigned int>(time(nullptr)));
Points points{{1, 5, 8}, {3, 3, 3}, {1, 1, 0}};
KdTree tree(points);
tree.GetNearest({1, 1, 1});
return 0;
}
But if I put GetNearest
code in constructor (so that I construct index and find pt
's neighbor in constructor itself), or just write constructor and GetNearest
code in main, then everything works just fine.
I am new to nanoflann, can't figure what's wrong. Thanks for the help in advance!
I had to dig into the source nanoflann.hpp to find that the second parameter to the constructor for KDTreeSingleIndexAdaptor
(cloud
in your KdTree
constructor) is passed by reference and stored as a reference. This means that the cloud data you pass to nanoflann needs to stay around until you delete the KDTreeSingleIndexAdaptor
object.
Because you declare PointCloud cloud
as a local variable within your KdTree
constructor, it will be destroyed when the constructor ends leaving the kdtree pointed to by index
with an dangling internal reference.
One solution is to make cloud
a member of KdTree
.