繁体   English   中英

使用 Scipy 在凸包上找到一个点的投影

[英]Find the projection of a point on the convex hull with Scipy

从一组点来看,我得到了带有scipy.spatial的凸包,无论是使用Delaunay还是ConvexHull (来自 qhull 库)。 现在我想得到这个凸包外的一个点到船体上的投影(即船体上与外部点距离最小的点)。

这是我到目前为止的代码:

from scipy.spatial import Delaunay, ConvexHull
import numpy as np

hu = np.random.rand(10, 2) ## the set of points to get the hull from
pt = np.array([1.1, 0.5]) ## a point outside
pt2 = np.array([0.4, 0.4]) ## a point inside
hull = ConvexHull(hu) ## get only the convex hull
#hull2 = Delaunay(hu) ## or get the full Delaunay triangulation

import matplotlib.pyplot as plt
plt.plot(hu[:,0], hu[:,1], "ro") ## plot all points
#plt.triplot(hu[:,0], hu[:,1], hull2.simplices.copy()) ## plot the Delaunay triangulation
## Plot the convexhull
for simplex in hull.simplices:
    plt.plot(hu[simplex,0], hu[simplex,1], "ro-")

## Plot the points inside and outside the convex hull 
plt.plot(pt[0], pt[1], "bs")
plt.plot(pt2[0], pt2[1], "bs")
plt.show()

使用图片可能更容易,我想从凸包外的蓝点获得绿色的 x 和 y 坐标。 这个例子是二维的,但我也需要在更高的维度上应用它。 谢谢您的帮助。

凸包和点获得绿色

编辑:问题在这里得到解决,但我在实现它时遇到了麻烦: https : //mathoverflow.net/questions/118088/projection-of-a-point-to-a-convex-hull-in-d-dimensions

我在回答自己。 正如 0Tech 指出的那样, ConvexHull.equations为您提供了每个平面的平面方程(在 2d 中——因此是一条线),形式为: [A, B, C] 因此平面定义为

A*x + B*y + C = 0

在平面上投影点 P=(x0, y0) 在这里解释。 您希望向量上的一个点平行于平面向量 (A, B) 并通过该点投影 P,这条线由 t 参数化:

P_proj = (x, y) = (x0 + A*t, y0 + B*t)

然后您希望您的点在平面上并使用完整平面方程来做到这一点:

A*(x0 + A*t) + B*(y0 + B*t) + C = 0
=> t=-(C + A*x0 + B*y0)/(A**2+B**2)

在(笨拙的)python 中,它给出了任何维度:

from scipy.spatial import Delaunay, ConvexHull
import numpy as np

hu = np.random.rand(10, 2) ## the set of points to get the hull from
pt = np.array([1.1, 0.5]) ## a point outside
pt2 = np.array([0.4, 0.4]) ## a point inside
hull = ConvexHull(hu) ## get only the convex hull
#hull2 = Delaunay(hu) ## or get the full Delaunay triangulation

import matplotlib.pyplot as plt
plt.plot(hu[:,0], hu[:,1], "ro") ## plot all points
#plt.triplot(hu[:,0], hu[:,1], hull2.simplices.copy()) ## plot the Delaunay triangulation
## Plot the convexhull
for simplex in hull.simplices:
    plt.plot(hu[simplex,0], hu[simplex,1], "ro-")

## Plot the points inside and outside the convex hull 
plt.plot(pt[0], pt[1], "bs")
plt.plot(pt2[0], pt2[1], "bs")

for eq in hull.equations:
    t = -(eq[-1] + np.dot(eq[:-1], pt))/(np.sum(eq[:-1]**2))
    pt_proj = pt + eq[:-1]*t
    plt.plot(pt_proj[0], pt_proj[1], "gD-.")
plt.show()

浏览stackoverflow ,让我找到了另一种解决方案,它具有使用段而不是线的优势,因此其中一个段的投影始终位于该段上:

def min_distance(pt1, pt2, p):
    """ return the projection of point p (and the distance) on the closest edge formed by the two points pt1 and pt2"""
    l = np.sum((pt2-pt1)**2) ## compute the squared distance between the 2 vertices
    t = np.max([0., np.min([1., np.dot(p-pt1, pt2-pt1) /l])]) # I let the answer of question 849211 explains this
    proj = pt1 + t*(pt2-pt1) ## project the point
    return proj, np.sum((proj-p)**2) ## return the projection and the point

然后我们可以浏览每个顶点并投影点:

for i in range(len(hull.vertices)):
    pt_proj, d = min_distance(hu[hull.vertices[i]], hu[hull.vertices[(i+1)%len(hull.vertices)]], pt)
    plt.plot([pt[0], pt_proj[0]], [pt[1], pt_proj[1]], "c<:")

和图片,每个平面(线)右侧的蓝点投影为绿色,第一种方法为青色,第二种方法为青色:

投影点数

由于这里(或任何地方)似乎仍然没有一个好的答案,我关注了这篇文章(以及其他文章)并使用二次规划解决了这个问题:

$$ \begin{align}\text{minimize} & \quad \frac{1}{2} x^{T} \mathbf{I} x - z^{T} x\\ \text{subject to} & \quad C x \leq b\\ \end{align} $$

其中$C$是正规方程, $b$是偏移量。 这里的主要区别是我将点投影到凸包内,而不一定要投影到它上面。 即凸包内的点将保持不变,凸包外的点将投影到凸包中最近的点(始终在其表面上)。 通过删除这个等式约束,这是一个相对简单的二次规划问题,我使用quadprog包解决了quadprog问题。

理论上可能有一种更快的方法来做到这一点,但这种方法足够快、简单且健壮:

import numpy as np
from scipy.spatial import ConvexHull
from quadprog import solve_qp

def proj2hull(z, equations):
    """
    Project `z` to the convex hull defined by the
    hyperplane equations of the facets

    Arguments
        z: array, shape (ndim,)
        equations: array shape (nfacets, ndim + 1)

    Returns
        x: array, shape (ndim,)
    """
    G = np.eye(len(z), dtype=float)
    a = np.array(z, dtype=float)
    C = np.array(-equations[:, :-1], dtype=float)
    b = np.array(equations[:, -1], dtype=float)
    x, f, xu, itr, lag, act = solve_qp(G, a, C.T, b, meq=0, factorized=True)
    return x

一个简单的例子:

X = np.random.normal(size=(1000, 5))
z = np.random.normal(scale=2, size=(5))
hull = ConvexHull(X)
y = proj2hull(z, hull.equations)

(编辑:对不起,它看起来不像乳胶正在格式化)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM