【问题标题】:Fast way to find the triangle inside a mesh that encloses a point快速找到包围点的网格内的三角形
【发布时间】:2015-11-21 04:03:56
【问题描述】:

我遇到了我需要完成的任务的性能问题。目前的瓶颈之一是从非结构化网格中获取插值字段值。

慢速部分是,给定一个 2D 点和一个非结构化的 2D 网格,找到紧邻该点的网格点。最好能找到它所在的三角形。

现在我正在使用 CGAL,但它太慢了。使用当前的实现,整个任务需要几天才能完成,在高端 CPU 上并行运行。

我认为慢的部分是 CGAL::natural_neighbor_coordinates_2。

#ifndef FIELD_INTERPOLATOR_H
#define FIELD_INTERPOLATOR_H

#include "Vec.h"

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Interpolation_traits_2.h>
#include <CGAL/natural_neighbor_coordinates_2.h>
#include <CGAL/interpolation_functions.h>

#include <map>
#include <vector>

typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Delaunay_triangulation_2< Kernel > Delaunay_triangulation;

typedef Kernel::FT FieldType;
typedef Kernel::Point_2 MeshType;

struct FieldInterpolator23 {

    Delaunay_triangulation m_triangulation;

    std::map< MeshType, FieldType, Kernel::Less_xy_2 > m_vX;
    std::map< MeshType, FieldType, Kernel::Less_xy_2 > m_vY;
    std::map< MeshType, FieldType, Kernel::Less_xy_2 > m_vZ;

    typedef CGAL::Data_access< std::map< MeshType, FieldType, Kernel::Less_xy_2 > > ValueAccess;

    FieldInterpolator23() {}

    FieldInterpolator23( const std::vector< TN::Vec2 > & mesh, const std::vector< TN::Vec3 > & field )
    {
        const int N = mesh.size();
        for ( int i = 0; i < N; ++i ) {

            MeshType p( mesh[i].x(), mesh[i].y() );

            m_triangulation.insert( p );
            m_vX.insert( std::make_pair( p, field[i].x() ) );
            m_vY.insert( std::make_pair( p, field[i].y() ) ); 
            m_vZ.insert( std::make_pair( p, field[i].z() ) );                        
        }       
    }

    void set( const std::vector< TN::Vec2 > & mesh, const std::vector< TN::Vec3 > & field ) {

        m_triangulation.clear();
        m_vX.clear();
        m_vY.clear();
        m_vZ.clear();

        const int N = mesh.size();
        for ( int i = 0; i < N; ++i ) {

            MeshType p( mesh[i].x(), mesh[i].y() );

            m_triangulation.insert( p );
            m_vX.insert( std::make_pair( p, field[i].x() ) );
            m_vY.insert( std::make_pair( p, field[i].y() ) );
            m_vZ.insert( std::make_pair( p, field[i].z() ) );
        }
    }

    TN::Vec3 operator() ( TN::Vec2 p ) {

        MeshType pos( p.x(), p.y() );

        std::vector< std::pair< MeshType, FieldType > > coords;

        FieldType norm =
            CGAL::natural_neighbor_coordinates_2( m_triangulation, pos, std::back_inserter( coords ) ).second;

        FieldType resX =
            CGAL::linear_interpolation(
                coords.begin(), 
                coords.end(),
                norm,
                ValueAccess( m_vX )
        );

        FieldType resY =
            CGAL::linear_interpolation(
                coords.begin(), 
                coords.end(),
                norm,
                ValueAccess( m_vY )
        );

        FieldType resZ =
            CGAL::linear_interpolation(
                coords.begin(), 
                coords.end(),
                norm,
                ValueAccess( m_vZ )
        );

        return TN::Vec3( resX, resY, resZ );
    }
};

#endif

谁能指出一个可接受的更高性能解决方案的方向,无论是不同的库还是算法?

【问题讨论】:

  • 三角剖分的顶点数是多少?您需要在三角剖分中定位多少个查询点?
  • 大约有 100 万个网格点,我需要做大约 20 亿次查询。
  • 如果你事先知道查询,它有助于对它们进行排序(比如沿着空间填充曲线,CGAL 有相应的功能),然后按此顺序进行点位置查询,传递每个查询以前的位置作为起点。这样,大多数查询只会检查起点是否已经是直角三角形,而其他查询最多需要步行 1 或 2 步才能到达。
  • 如果您的查询点在规则网格上,一个快速的方法是三角形填充:创建一个像素对应查询点的图像,并使用三角形 ID 作为填充颜色填充每个三角形.这将使查询成为恒定时间。 (对于如此大量的查询,这在您的情况下可能不切实际 - 除非您可以通过子窗口工作。)

标签: c++ performance computational-geometry cgal


【解决方案1】:

CGAL 包含一个实现 Triangulation Hierarchy哪个

实现了一个用数据结构增强的三角测量,以有效地回答点位置查询。 [...] 数据结构仍然很小,并且可以在真实数据上实现快速的点位置查询。

它的性能对于 Delaunay 三角剖分是最佳的。



图 36.8

【讨论】:

  • 我切换到分层三角剖分,但它只导致了轻微的性能提升。作为一项测试,我尝试天真地遍历整个网格节点集以找到最接近的节点并返回它的值。使用这种方法,整个任务的完成速度比使用 CGAL 三角测量快 10 倍左右。
  • @MVTC 这听起来很奇怪,但我不太明白你在比较什么。您是否有一个可以分享的基准,它只进行点定位并显示幼稚迭代比三角测量的方法更快?
  • @MVTC:蛮力线性搜索优于对数搜索,这很奇怪。我假设您将预处理放在一边,只查看查询时间?
  • 预处理被搁置。此外,在我的应用程序中,使用定位函数查找包围点的人脸也比自然邻居函数快得多,这很奇怪,因为 natural_neighbors_coord_2 函数接缝只在内部使用定位。时差如此之大,以至于三角测量一定有问题?我需要更多时间来调查发生了什么。我会将其标记为已接受的答案,然后如果我弄清楚为什么会发生这种情况,请提供更新。
【解决方案2】:

根据我自己的经验,Axis Aligned Bouding Box 树对于这个问题相当有效(而且我观察到比“在三角测量中行走”,即使用 locate() 具有更高的性能)。

我正在使用我自己的实现(用于 3D 网格,但可以轻松适应 2D): http://alice.loria.fr/software/geogram/doc/html/classGEO_1_1MeshFacetsAABB.html

AABB 树也在 CGAL 中实现: http://doc.cgal.org/latest/AABB_tree/

注意:如果您的查询点是结构化的(例如按规则网格组织),那么有几种方法可以获得大量性能,例如:

  • 在 CGAL 中使用 Delaunay 三角剖分的 locate() 函数,该函数将提示作为参数,对于提示,使用与前一点相关的三角形之一。这确保了点定位算法不必走得太远。使用这种方法,收益通常非常显着。如果您不想更改您的类 API,您可以有一个可变成员来存储提示(如果点不是结构化的,但需要对它们进行空间排序,另请参阅 Marc Glisse 的评论)。

  • 在点上“绘制”三角形(每个三角形将其值“发送”到它所覆盖的点)。这可以通过用于在屏幕上绘制三角形的“计算机图形”算法来完成。这种方法的收益更为重要(但需要更多的工作,它会彻底改变你的算法)。

【讨论】:

    【解决方案3】:

    如果网格是不变的,则可以使用哈希表在 O(1) 时间内访问任何条目。假设通过水平 x 和垂直 y 值访问数据,在网格周围放置一个边界框,并将其垂直和水平切成大致等于平均网格元素面积的正方形。将 Nx 设置为垂直切片的数量,将 Ny 设置为水平切片的数量。如果网格数据从 X_min 扩展到 X_max 和 Y_min 到 Y_max,则设置 Sx = Nx/(X_max-Xmin) 和 Sy = Ny/(Y_max-Y_min)。然后从左到右递增地对垂直列进行编号,从round(Sx*X_min) 到round(Sx*X_max)。同样,从下到上递增地对水平行进行编号,从 round(Sy*Y_min) 开始到 round(Sy*Y_max)。

    如果网格大致为正方形,则大约有 1000 列和 1000 行,并且几乎每个正方形都与一个三角形相关联。如果网格有不规则的形状或孔洞,许多方块可能不会落在网格上。这对哈希表没有影响。要设置哈希表,每个正方形不能关联多个三角形。如果两个或多个三角形要与同一个正方形相关联,则正方形太大。要获得更多的正方形,请设置更小的区域以获得更大的 Nx 和 Ny。现在每个三角形都与#X 列和#Y 行中的一个正方形相关联,因此可以生成一百万个关键字。对于列 #X 和行 #Y 中的单元格,设置关键字字符串“±#X±#Y”,其中 ±#X 和 ±#Y 表示带有前导“+”号的整数,如果数字大于 —1 并且如果数字小于零,则前导“—”号。要使所有关键字具有相同的长度,请用前导零填充整数值。典型的唯一关键字可能类似于“-0378+0087”或“+0029-1007”。在这些示例中,每个关键字正好是 10 个字符。按照三角形的存储顺序设置哈希表中的关键字。

    要使用哈希表,对于给定的 x 和 y 点,设置整数 ix = round(Sx*x) 和整数 iy = round(Sy*y) 并根据以下公式形成关键字“±ix±iy” ix 和 iy 的符号。如果 x 和 y 在网格中,则关键字“±ix±iy”应该在哈希表中,它应该返回包围 x 和 y 的三角形或非常接近该点的三角形的索引。在某些情况下,生成的“±ix±iy”关键字可能不在哈希表中。在这种情况下,哈希表函数将返回一些随机三角形索引。发生这种情况时,最好的解决方法是干扰 ix 和/或 iy 一个单位并生成另一个关键字。当然,如果该点在网格之外,哈希表可能会发回大量随机三角形索引。因此,请确保该点从一开始就有效。

    此讨论假设三角形的形状合理且面积大致均匀。如果不是这种情况并且多个三角形必须适合同一个正方形或多个正方形必须适合同一个三角形,则解决方法是在侧面生成一个重定向表。当几个三角形适合同一个正方形时,将最中心的三角形分配给正方形,并理解然后需要快速搜索所需的三角形。当几个正方形必须适合同一个三角形时,将其中一个正方形指向三角形索引,将所有其他正方形指向一个索引在三角形索引之后开始的间接表。然后,间接表将用于指向包含这些正方形的三角形索引。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-09
      • 2020-02-19
      • 1970-01-01
      • 2019-01-29
      • 2012-02-29
      • 1970-01-01
      • 2015-02-12
      • 1970-01-01
      相关资源
      最近更新 更多