スポンサーリンク

nanoflannでKdTreeを使う

nanoflannというヘッダオンリーのライブラリがあり、KdTreeを実装している。

flannとついているがFLANN(Fast Library for Approximate Nearest Neighbors)とは別物らしい。

https://github.com/jlblancoc/nanoflann

ヘッダオンリーで、nanoflann.hppをプロジェクトにインクルードするだけで使える。

さらに、点群型をテンプレートで指定するのでOpen3D等の他のライブラリと併用するときも便利。

#include <iostream>
#include <vector>
#include <array>

#include "nanoflann.hpp"

// 点群データを表現するクラス
struct MyPointCloud {

    // 点群データ
    std::vector< std::array<float,3> > points;

    // nanoflannで必要となるインタフェース関数
    inline size_t kdtree_get_point_count() const {
        return points.size();
    }

    // nanoflannが呼び出す、点群の座標を返す関数
    // dim [0] [1] [2] は、それぞれ x, y, z
    inline float kdtree_get_pt(const size_t idx, const size_t dim) const {
        return points[idx][dim];
    }

    // BBOXは使わなくても定義は必要
    template <class BBOX>
    bool kdtree_get_bbox(BBOX&) const {
        return false;
    }

};

///////////////////////////////////////////////////////////
// KdTreeの定義(L2ノルム L2_Simple_Adaptor を使う3次元の点群)
// L2ノルムは、ユークリッド距離の2乗
// L1ノルムは、マンハッタン距離
using KdTree = nanoflann::KDTreeSingleIndexAdaptor<
    nanoflann::L2_Simple_Adaptor<float, MyPointCloud>,
    MyPointCloud,
    3 /* 次元数 */>;
///////////////////////////////////////////////////////////

void createCloud(MyPointCloud& cloud) {
    // サンプル点群を作成
    for (size_t i = 0; i < 30000; i++) {
        cloud.points.push_back(std::array<float, 3>{
            static_cast<float>(rand()) / RAND_MAX,
            static_cast<float>(rand()) / RAND_MAX,            
            static_cast<float>(rand()) / RAND_MAX
            }
        );
    }
}
int main()
{

    MyPointCloud cloud;

    createCloud(cloud);

    ////////////////////////////////////////////////////////////////////////
    // leaf max size は1にしているので、最後まで分割される
    // leaf max size を2以上にすると、ツリーの末端では配列になり、総当たりでの探索になる
    KdTree tree(
        3 /* 次元数 */, 
        cloud, 
        nanoflann::KDTreeSingleIndexAdaptorParams(1 /* leaf max size */)
    );
    tree.buildIndex();
    ////////////////////////////////////////////////////////////////////////

    float P[3] = { 0.5, 0.5, 0.5 };
    float radius = 0.2;
    float search_radius = radius * radius; // 半径の二乗

    // 結果の格納先
    std::vector<nanoflann::ResultItem<unsigned int, float>> ret_matches;

    nanoflann::SearchParameters params;
    // 探索
    const size_t nMatches = tree.radiusSearch(
        P,
        search_radius,
        ret_matches,
        params
    );
    ////////////////////////////////////////////////////////////////////////
    std::cout << "found " << nMatches << " points\n";

    for (size_t i = 0; i < nMatches; i++) {
        std::cout << i << " point index: " << ret_matches[i].first << " distance: " << sqrt(ret_matches[i].second) << std::endl;
    }

}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: