Checking if a point is in ConvexHull?

為{幸葍}努か 提交于 2021-02-10 08:44:45

问题


I am having trouble understanding how to compute whether an n-dimensional point is within an n-dimensional ConvexHull.

A very similar question (the same) was asked here: What's an efficient way to find if a point lies in the convex hull of a point cloud?

However, the answers are confusing me or don't seem to work for me, and I have no idea why.

def in_hull(p, hull):
    """ Copied and from the Top Original answer """
    from scipy.spatial import Delaunay
    if not isinstance(hull,Delaunay):
        hull = Delaunay(hull)

    return hull.find_simplex(p)>=0

This function was giving me a lot of false or unwanted results with real data, that I am using it on. However, when debugging I wrote a simple script to test some obvious expectation I would have:

If I construct a ConvexHull out of a group of points, when I check that group of points for "membership", they should all be "members".

results_all = []
for _ in range(5000):
    cloud = np.random.rand(5000, 2)
    result = in_hull(cloud, cloud)
    results_all.append(np.all(result))

arr = np.array(results_all)
print(np.sum(np.logical_not(arr)))

While rare, this seems to fail on randomly generated data (3 out of 5000), the problem is bigger on real data. What I mean by fail, is that I actually get some situations, where not all points are considered members.

Is there something I am doing wrong? Or perhaps completely misunderstanding? I am fairly confused at this point, so would love an explanation of what is going on.

In the end, I want; given a ConvexHull, computed at some previous stage; be able to determine if points lie within the hull.


回答1:


It seems to be an edge case problem with the find_simplex method of the Delaunay object for almost flat simplex (triangle).

Here is a code to find and plot a faulty case with only 3 points:

import matplotlib.pylab as plt
from scipy.spatial import Delaunay
from scipy.spatial import delaunay_plot_2d

for _ in range(5000):
    cloud = np.random.rand(3, 2)

    tri = Delaunay(cloud)

    if np.any( tri.find_simplex(cloud)<0 ):
        print('break at', _)

        delaunay_plot_2d(tri);
        id_break = np.where(tri.find_simplex(cloud)<0)
        plt.plot( *cloud[id_break].ravel(), 'or' );
        break

The other method proposed here seems to work well:

hull = ConvexHull(cloud)

def point_in_hull(point, hull, tolerance=1e-12):
    return all(
        (np.dot(eq[:-1], point) + eq[-1] <= tolerance)
        for eq in hull.equations)

[ point_in_hull(point, hull) for point in cloud ]
# [True, True, True]


来源:https://stackoverflow.com/questions/51771248/checking-if-a-point-is-in-convexhull

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!