从一组点开始,我使用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坐标。这个例子是2d但我也需要在更高的维度上应用它。 谢谢你的帮助。
编辑:问题在这里得到解决,但我无法实现它:https://mathoverflow.net/questions/118088/projection-of-a-point-to-a-convex-hull-in-d-dimensions
答案 0 :(得分:3)
我正在回答自己。
正如0Tech指出的那样,ConvexHull.equations
为您提供了每个平面的平面方程(在2d中 - 因此是一条线),格式为:[A, B, C]
。因此,平面由
A*x + B*y + C = 0
在这里解释在平面上投射点P =(x0,y0):http://www.nabla.hr/CG-LinesPlanesIn3DB5.htm。您希望矢量上的点平行于平面矢量(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<:")
图片中,第一种方法的每个平面(线)右侧的蓝点投影为绿色,第二种方法为青色:
答案 1 :(得分:2)
由于在这里(或任何地方)似乎仍然没有一个好的答案,所以我跟随this post(除其他外)并使用二次编程解决了问题:
$$ \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
包解决了该问题。
理论上可能会有一种更快的方法,但是这种方法足够快,简单且健壮:
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)
(编辑:很抱歉,它看起来好像不是乳胶正在格式化)