【问题标题】:Removing duplicates of 3D points in a vector in C++在 C++ 中删除向量中 3D 点的重复项
【发布时间】:2016-04-01 13:47:57
【问题描述】:

我正在处理一个点云,即一个点向量,作为计算的结果,其中包含重复的点(最多为云大小的 10%)。

我的实现是根据 x、y 和 z 值对这些点进行排序,然后使用 std::unique 函数。然而,即使排序本身似乎工作正常,生成的云仍然包含重复项。

这是关键代码

bool comparePoint(pcl::PointXYZINormal p1, pcl::PointXYZINormal p2){
if (p1.x != p2.x)
    return p1.x > p2.x;
else if (p1.y != p2.y)
    return  p1.y > p2.y;
else
    return p1.z > p2.z;
}

bool equalPoint(pcl::PointXYZINormal p1, pcl::PointXYZINormal p2){
    if (p1.x == p2.x && p1.y == p2.y && p1.z == p2.z)
        return true;
    return false;
}
void KDsearch::cullDuplePoints(){
    std::sort(points.begin(), points.end(), comparePoint);
    std::unique(points.begin(), points.end(), equalPoint);
}

这里是输出点云的部分提取(x、y 和 z 坐标):

1.96828 -535.09515 2794.8391
1.96627 -636.95264 2914.0366
1.96627 -636.95264 2914.0366
1.9651 108.77433 2350.9841
1.9651 108.77433 2350.9841
1.9642299 -206.19427 5618.4629
1.9642299 -206.19427 5618.4629
1.96386 -1880.3784 1346.0654

那么是 unique 不能正常工作还是我的同等条件有错误?

点本身也包含法线坐标,但它们对于剔除并不重要,所以我没有在代码中使用它们。

【问题讨论】:

  • 你能告诉我们PointXYZNormal的定义吗?
  • 请注意,std::sort 的比较函数应检查 a 是否小于 b。如果你使用的是 C++11,你也可以使用 std::tuple 以整洁的方式进行字典比较:stackoverflow.com/questions/6218812
  • 比较函数不需要检查小于,只要是严格的弱排序(就是这样)就可以了。
  • More 比您可能想知道的关于比较浮点值的信息,但是您仍然应该先看看,否则比较通过计算获得的浮点值是一种令人沮丧的练习(它总是会出现微妙的错误在某些情况下)。
  • 您如何比较这些点以使相同的点彼此相邻?而是你背后的逻辑是什么?没关系,它很简单,我只是累了。=(

标签: c++ point-cloud-library point-clouds


【解决方案1】:

std::unique 不会删除任何内容,它只会移动元素并返回修改后集合中唯一间隔“结束”的迭代器。
(返回的迭代器后面的集合的实际内容是未指定的。)

您需要明确删除重复项:

std::sort(points.begin(), points.end(), comparePoint);
auto unique_end = std::unique(points.begin(), points.end(), equalPoint);
points.erase(unique_end, points.end());

您还需要注意浮点比较。

【讨论】:

  • 很好的解决方案。此外,std::unique 仅删除与第一个值相比的最后一个值。这就是为什么使用sort 函数的原因,以便unique 可以看到连续的值。
  • 如何比较点以使相等的点彼此相邻?如果你想不通,一套可能是更好的选择。
【解决方案2】:

您的问题是比较浮点数是否相等始终是一项困难的练习。您可能会发现您的观点实际上是(例如):

1.965100000001 108.77433 2350.9841
1.965099999999 108.77433 2350.9841

...而那些不相等。

如果您想将彼此相差 0.00001 以内的点视为“相等”,您会遇到问题,即您的“相等”条件不具有传递性。 (0.0000,0,0) 与 (0.000009999,0,0) 和 (-0.00009999,0,0) “接近”,但后两个点彼此“远离”。这是一个一般很难解决的问题。祝你好运!

如果您对坐标值有所了解(例如,它们以毫米为单位,并且值精确到 100 纳米),您可以四舍五入到最接近的 100 纳米,并存储尽可能长的时间。所以:

struct IntPoint {
   const static double ScaleFactor = 10000;
   long long x,y,z;
   IntPoint(const pcl::PointXYZINormal &p)
      : x(llround(p.x*ScaleFactor ))
      , y(llround(p.y*ScaleFactor ))
      , z(llround(p.z*ScaleFactor ))
    {}
};

将您的点云转换为 IntPoint,然后您的排序 + 唯一(+擦除)应该可以工作。

【讨论】:

  • 感谢 Peter Mortensen 的 "eg" -> "for example"(英语作家的好习惯,对非英语母语的读者来说也更容易)。
【解决方案3】:

要删除重复项:您可以这样做:

sort(point.begin(), point.end());
point.erase(unique(point.begin(), point.end()), point.end());

或者只是创建一个集合,根据定义,它只包含向量元素中的唯一元素:

std::set<type> all_unique(point.begin(), point.end());

比较浮点数:考虑浮点数的数学性质1,以及他们的机器继承的问题2二进制表示在比较浮点数时只有一种解决方案,即在精度值epsilon 范围内比较它们。

因此,如果您想比较和订购float x1float x2,它们是您的坐标,您可以通过以下方式进行:

x1 - x2 < epsilon

epsilon 是您正在寻找的准确度。在您的情况下,仅用于说明,函数equalPoint() 可以修改为:

bool equalPoint(pcl::PointXYZINormal p1, pcl::PointXYZINormal p2){
    // equal up to the third digit after the decimal point
    float eps = 0.001;

    if ((p1.x -p2.x) < eps && (p1.y - p2.y) < eps && (p1.z - p2.z) < eps)
        return true;

    return false;
}

1.它们可以相差很小的数量,与整数不同,整数是四舍五入的,可以很容易地比较。

2。计算机并不能完美地映射真正的浮点数,这一事实的结果以截断、四舍五入的形式表示。

【讨论】:

  • 错误,不可数是指实数集的基数。这只是一个舍入 IEEE754 浮点数的问题。
  • @erip 你是完全正确的,这就是为什么我在底部添加了第一个澄清,以表明它们可以有非常小的差异。
猜你喜欢
  • 2014-11-06
  • 2020-04-29
  • 2013-05-04
  • 1970-01-01
  • 2016-08-12
  • 1970-01-01
  • 2011-11-29
  • 1970-01-01
  • 2015-02-03
相关资源
最近更新 更多