我需要在一组点中找到“附近”的邻居。
上图中有 10 个点。红线是 Delaunay 三角剖分 的边缘,黑色星星标记中间边缘线,蓝线是 Voronoi 细分。点 1 有三个“近”邻居,即 4、6 和 7,但没有 2 和 3,它们几乎与边 1-7 对齐,但距离更远。
识别近邻(或“好”边缘)的好方法是什么?看图,在我看来,要么选择中点落在与 Voronoi 线相交的边,要么将那些接触 Voronoi 单元的边视为“近”邻居,可能是一个很好的解决方案(3-5 的分类)可以选择任何一种方式)。有没有一种有效的方法可以在 Matlab 中实现任一解决方案(顺便说一句,我很高兴获得一个好的通用算法,然后可以将其转换为 Matlab)?
I need to find "near" neighbors among a set of points.
There are 10 points in the above image. Red lines are edges from the Delaunay Triangulation, black stars mark the mid-lines of the edges, blue lines are the Voronoi tesselation. Point 1 has three "near" neighbors, i.e. 4, 6, and 7, but not 2 and 3, who are almost in line with the edge 1-7, but much further away.
What is a good way to identify the near neighbors (or "good" edges)? Looking at the figure, it seems to me that either selecting edges whose mid-point falls onto the intersection with the Voronoi lines, or considering as "near" neighbors those with touching Voronoi cells could be a good solution (the classification of 3-5 can go either way). Is there an efficient way of implementing either of the solutions in Matlab (I'd be happy to get a good general algorithm that I can then translate to Matlab, btw)?
发布评论
评论(3)
您可以通过使用
DelaunayTri
类 及其边
和nearestNeighbor
方法。下面是一个包含 10 个随机对x
和y
值的示例:现在
edgeIndex
是一个 N×2 矩阵,其中每行包含对定义“近”连接的一条边的x
和y
进行索引。下图说明了 Delaunay 三角剖分(红线)、Voronoi 图(蓝线)、三角剖分边缘的中点(黑色星号)以及保留在edgeIndex
中的“好”边缘(粗红线) ):它是如何工作的...
Voronoi 图由一系列 Voronoi 多边形或单元组成。在上图中,每个单元代表给定三角剖分顶点周围的区域,该区域包含空间中比任何其他顶点更接近该顶点的所有点。因此,当您有 2 个顶点不靠近任何其他顶点(例如图像中的顶点 6 和 8)时,连接这些顶点的线的中点落在 Voronoi 单元之间的分隔线上,以表示顶点。
然而,当存在靠近连接 2 个给定顶点的线的第三个顶点时,第三个顶点的 Voronoi 单元可以在 2 个给定顶点之间延伸,穿过连接它们的线并包围该线的中点。因此,与这两个给定顶点之间的距离相比,第三个顶点可以被认为是与这两个给定顶点“更近”的邻居。在图像中,顶点 7 的 Voronoi 单元延伸到顶点 1 和 2(以及 1 和 3)之间的区域,因此顶点 7 被认为比顶点 2(或 3)更靠近顶点 1。
在某些情况下,该算法可能不会将两个顶点视为“近”邻居,即使它们的 Voronoi 单元接触。图像中的顶点 3 和 5 就是一个示例,其中顶点 2 被视为与顶点 3 或 5 的距离比顶点 3 或 5 之间的距离更近。
You can implement your first idea of selecting edges whose mid-points fall on the intersection with the Voronoi lines by making use of the
DelaunayTri
class and itsedges
andnearestNeighbor
methods. Here's an example with 10 random pairs ofx
andy
values:And now
edgeIndex
is an N-by-2 matrix where each row contains the indices intox
andy
for one edge that defines a "near" connection. The following plot illustrates the Delaunay triangulation (red lines), Voronoi diagram (blue lines), midpoints of the triangulation edges (black asterisks), and the "good" edges that remain inedgeIndex
(thick red lines):How it works...
The Voronoi diagram is comprised of a series of Voronoi polygons, or cells. In the above image, each cell represents the region around a given triangulation vertex which encloses all the points in space that are closer to that vertex than any other vertex. As a result of this, when you have 2 vertices that aren't close to any other vertices (like vertices 6 and 8 in your image) then the midpoint of the line joining those vertices falls on the separating line between the Voronoi cells for the vertices.
However, when there is a third vertex that is close to the line joining 2 given vertices then the Voronoi cell for the third vertex may extend between the 2 given vertices, crossing the line joining them and enclosing that lines midpoint. This third vertex can therefore be considered a "nearer" neighbor to the 2 given vertices than the 2 vertices are to each other. In your image, the Voronoi cell for vertex 7 extends into the region between vertices 1 and 2 (and 1 and 3), so vertex 7 is considered a nearer neighbor to vertex 1 than vertex 2 (or 3) is.
In some cases, this algorithm may not consider two vertices as "near" neighbors even though their Voronoi cells touch. Vertices 3 and 5 in your image are an example of this, where vertex 2 is considered a nearer neighbor to vertices 3 or 5 than vertices 3 or 5 are to each other.
我同意共享单元边缘是一个好邻居标准(基于此示例)。如果您使用面向网格的数据结构(例如 Gts 中的数据结构),那么识别共享边将是微不足道的。
另一方面,Matlab 使这变得更加“有趣”。假设 voronoi 单元存储为补丁,您可以尝试获取“Faces”补丁属性(请参阅
I would agree that shared cell edges is a good neighbor criterion (based on this example). If you were using a mesh-oriented data structure (like something from Gts), then identifying shared edges would be trivial.
Matlab, on the other hand, makes this more "interesting". Assuming the voronoi cells are stored as patches, you might try obtaining the 'Faces' patch property (see this reference). That should return something like an adjacency matrix that identifies connected vertices. From that (and a little magic), you should be able to determine shared vertices, and then infer shared edges. In my experience, this sort of "search" problem is not well suited to Matlab - if possible, I recommend moving to a system more suited to queries of mesh connectivity.
另一种比创建三角剖分或泰森图更简单的可能性是使用邻域矩阵。
首先将所有点放入二维方阵中。然后您可以运行完整或部分空间排序,因此点将在矩阵内排序。
Y 较小的点可以移动到矩阵的顶行,同样,Y 较大的点会移动到矩阵的底行。对于 X 坐标较小的点也会发生同样的情况,这些点应该移动到左侧的列。对称地,X 值较大的点将进入右侧的列。
完成空间排序后(有很多方法可以实现这一点,通过串行或并行算法),您可以通过仅访问点 P 实际存储在邻域矩阵中的相邻单元来查找给定点 P 的最近点。
您可以在以下论文中阅读有关此想法的更多详细信息(您可以在线找到其 PDF 副本):基于紧急行为的 GPU 上的超大群体模拟。
排序步骤为您提供了有趣的选择。您可以仅使用论文中描述的偶奇转置排序,这非常容易实现(即使在 CUDA 中)。如果您只运行一次,它将为您提供部分排序,如果您的矩阵接近排序,这可能已经很有用。也就是说,如果你的点移动缓慢,它将节省你大量的计算。
如果需要完整排序,可以多次运行此类偶奇转置传递(如以下维基百科页面所述):
http://en.wikipedia.org/wiki/Odd%E2%80%93even_sort
Another possibility, simpler than creating triangulations or voronoi diagrams, is using a neighborhood matrix.
First place all your points into a 2D square matrix. Then you can run a full or partial spatial sort, so points will became ordered inside the matrix.
Points with small Y could move to the top rows of the matrix, and likewise, points with large Y would go to the bottom rows. The same will happen with points with small X coordinates, that should move to the columns on the left. And symmetrically, points with large X value will go to the right columns.
After you did the spatial sort (there are many ways to achieve this, both by serial or parallel algorithms) you can lookup the nearest points of a given point P by just visiting the adjacent cells where point P is actually stored in the neighborhood matrix.
You can read more details for this idea in the following paper (you will find PDF copies of it online): Supermassive Crowd Simulation on GPU based on Emergent Behavior.
The sorting step gives you interesting choices. You can use just the even-odd transposition sort described in the paper, which is very simple to implement (even in CUDA). If you run just one pass of this, it will give you a partial sort, which can be already useful if your matrix is near-sorted. That is, if your points move slowly, it will save you a lot of computation.
If you need a full sort, you can run such even-odd transposition pass several times (as described in the following Wikipedia page):
http://en.wikipedia.org/wiki/Odd%E2%80%93even_sort