【点云抽稀】一种基于均匀分布随机数的点云抽稀算法

文章目录

  • 1. 背景
  • 2. 原理
  • 3. 实现
    • 3.1 定义Utils类
    • 3.2 加入预定义宏,确定层级
    • 3.3 函数实现

1. 背景

在大数据点云的存储中,常常要进行空间分区,一般的策略是构建四叉树或者八叉树。在构建树的过程中,一个不可避免的点就是点云的快速抽稀。

不同层级之间,下一层的数据永远比上一层的数据更加精细,即:上一层数据是从下一层通过某种抽稀算法筛选出来的。常规意义上的抽稀算法,是在内存中进行筛选,但由于大数据点云的特殊性(无法全部读进内存),这些抽稀算法均不满足要求,存在一下问题:

  1. 无法全部读进内存,因此抽稀之后不同分块(分块策略是前提)的效果不一;
  2. 抽稀效率比较低,需要预先将数据读进内存,再进行分层筛选;

某项目中,需要将*.las转换成自定义格式,其转换速率要求很高。而由于las文件在使用LASTools读写时,只能逐点读取,因此需要一种既能解决上述问题,又能保证效率的抽稀算法。

本文介绍的抽稀算法,原理上是利用伪随机数的均匀分布来实现,但同时能兼顾效率和效果。其局限性如下:

  1. 随机数表是存储在内存中,因此数据量过大的情况下,内存占用可能会很高;
  2. 抽稀效率随C++标准库的随机数生成算法影响;
  3. 数据量很小的时候,存在某层数据为空的可能;

2. 原理

基于以下出发点:

  1. 每读一个点,能立即得到该点所属层级——las的逐点读限制;
  2. las文件只需读一次,就能得到抽稀结果——O(n),n为点数;
  3. 保证抽稀结果均匀——加入随机数;

对于第一点,将las文件中的顶点数据,类比成一个存储了顶点的数组:

v v v v v v v v v v v v v v v v

LASTools的读操作器每一次读取的时候,都会将游标移到下一个点。对每一个点来说,本身并没有层级信息,因此需要人为地赋予其层级。

计算层级可以类比分类游戏:一堆小球从入口进入,中间穿插阻碍棒,最终落到容器内:
【点云抽稀】一种基于均匀分布随机数的点云抽稀算法_第1张图片
而将红色小球理解为点云的顶点,将下面的容器理解为结果,则中间的蓝色小球是我们的抽稀算法。

核心原理就是,每次获取一个随机数,这个随机数的值就是当前点所在的层级。而随机数的范围,就是按照n叉树的层级点数比例,计算出来的一个值。具体实现见后文。

3. 实现

3.1 定义Utils类

class Utils
{
private:
    Utils(int lvl);
    ~Utils();

public:
    static Utils &instance();
    int getLayer();

protected:
    char *m_lvl;
    int m_l;
};

3.2 加入预定义宏,确定层级

#ifndef DEFAULT_LEVEL_COUNT
#define DEFAULT_LEVEL_COUNT 7
#endif

3.3 函数实现

const std::size_t g_num[10] = {
    1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144};
const std::size_t g_sum[10] = {
    1,
    1 + 4,
    1 + 4 + 16,
    1 + 4 + 16 + 64,
    1 + 4 + 16 + 64 + 256,
    1 + 4 + 16 + 64 + 256 + 1024,
    1 + 4 + 16 + 64 + 256 + 1024 + 4096,
    1 + 4 + 16 + 64 + 256 + 1024 + 4096 + 16384,
    1 + 4 + 16 + 64 + 256 + 1024 + 4096 + 16384 + 65536,
    1 + 4 + 16 + 64 + 256 + 1024 + 4096 + 16384 + 65536 + 262144,
};

Utils::Utils(int lvl)
{
    m_lvl = new char[g_sum[lvl - 1]];

    std::size_t idx = 0;
    for (int i = 0; i < lvl; ++i)
    {
        std::size_t tms = g_num[i];
        // for (int j = 0; j < tms; ++j)
        // {
        //     m_lvl[idx + j] = (char)i;
        // }
        std::fill_n(m_lvl + idx, tms, (char)i);
        idx += tms;
    }
    m_l = lvl;
}

Utils::~Utils()
{
    delete[] m_lvl;
}

Utils &Utils::instance()
{
    static Utils s_ret(DEFAULT_LEVEL_COUNT);
    return s_ret;
}

int Utils::getLayer()
{
    static std::default_random_engine s_dre;
    static std::uniform_int_distribution<unsigned long> s_uid(g_sum[0], g_sum[m_l - 1]);

    return (int)(m_lvl[s_uid(s_dre)]);
}

你可能感兴趣的:(C++,算法,c++,开发语言)