Numpyに座標の点群があります。多数のポイントの場合、ポイントがポイントクラウドの凸包にあるかどうかを確認します。
Pyhullを試しましたが、ポイントがConvexHull
にあるかどうかを確認する方法がわかりません。
hull = ConvexHull(np.array([(1, 2), (3, 4), (3, 6)]))
for s in hull.simplices:
s.in_simplex(np.array([2, 3]))
linAlgErrorを発生させます:配列は正方でなければなりません。
Scipyのみを必要とする簡単なソリューションを次に示します。
def in_hull(p, hull):
"""
Test if points in `p` are in `hull`
`p` should be a `NxK` coordinates of `N` points in `K` dimensions
`hull` is either a scipy.spatial.Delaunay object or the `MxK` array of the
coordinates of `M` points in `K`dimensions for which Delaunay triangulation
will be computed
"""
from scipy.spatial import Delaunay
if not isinstance(hull,Delaunay):
hull = Delaunay(hull)
return hull.find_simplex(p)>=0
ブール配列を返します。True
値は、指定された凸包内にあるポイントを示します。次のように使用できます。
tested = np.random.Rand(20,3)
cloud = np.random.Rand(50,3)
print in_hull(tested,cloud)
Matplotlibがインストールされている場合、最初の関数を呼び出して結果をプロットする次の関数を使用することもできます。 Nx2
配列で指定される2Dデータのみ:
def plot_in_hull(p, hull):
"""
plot relative to `in_hull` for 2d data
"""
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection, LineCollection
from scipy.spatial import Delaunay
if not isinstance(hull,Delaunay):
hull = Delaunay(hull)
# plot triangulation
poly = PolyCollection(hull.points[hull.vertices], facecolors='w', edgecolors='b')
plt.clf()
plt.title('in hull')
plt.gca().add_collection(poly)
plt.plot(hull.points[:,0], hull.points[:,1], 'o', hold=1)
# plot the convex hull
edges = set()
Edge_points = []
def add_Edge(i, j):
"""Add a line between the i-th and j-th points, if not in the list already"""
if (i, j) in edges or (j, i) in edges:
# already added
return
edges.add( (i, j) )
Edge_points.append(hull.points[ [i, j] ])
for ia, ib in hull.convex_hull:
add_Edge(ia, ib)
lines = LineCollection(Edge_points, color='g')
plt.gca().add_collection(lines)
plt.show()
# plot tested points `p` - black are inside hull, red outside
inside = in_hull(p,hull)
plt.plot(p[ inside,0],p[ inside,1],'.k')
plt.plot(p[-inside,0],p[-inside,1],'.r')
こんにちは、これを達成するためにプログラムライブラリを使用する方法についてはわかりません。しかし、これを言葉で説明する簡単なアルゴリズムがあります。
凸包を計算する必要がないため、凸包アルゴリズムを使用しません。サブセットが凸包を定義する点のセットの凸結合として表現できるかどうかを確認するだけです。さらに、凸包を見つけることは、特に高次元では計算的に高価です。
実際、ある点が別の点の集合の凸の組み合わせとして表現できるかどうかを見つけるという単なる問題は、線形計画問題として定式化できます。
import numpy as np
from scipy.optimize import linprog
def in_hull(points, x):
n_points = len(points)
n_dim = len(x)
c = np.zeros(n_points)
A = np.r_[points.T,np.ones((1,n_points))]
b = np.r_[x, np.ones(1)]
lp = linprog(c, A_eq=A, b_eq=b)
return lp.success
n_points = 10000
n_dim = 10
Z = np.random.Rand(n_points,n_dim)
x = np.random.Rand(n_dim)
print(in_hull(Z, x))
この例では、10次元で10000ポイントの問題を解決しました。実行時間はミリ秒単位です。 QHullでこれがどれくらいかかるか知りたくないでしょう。
完全を期すために、ここに貧しい人の解決策があります:
import pylab
import numpy
from scipy.spatial import ConvexHull
def is_p_inside_points_hull(points, p):
global hull, new_points # Remove this line! Just for plotting!
hull = ConvexHull(points)
new_points = numpy.append(points, p, axis=0)
new_hull = ConvexHull(new_points)
if list(hull.vertices) == list(new_hull.vertices):
return True
else:
return False
# Test:
points = numpy.random.Rand(10, 2) # 30 random points in 2-D
# Note: the number of points must be greater than the dimention.
p = numpy.random.Rand(1, 2) # 1 random point in 2-D
print is_p_inside_points_hull(points, p)
# Plot:
pylab.plot(points[:,0], points[:,1], 'o')
for simplex in hull.simplices:
pylab.plot(points[simplex,0], points[simplex,1], 'k-')
pylab.plot(p[:,0], p[:,1], '^r')
pylab.show()
考え方は簡単です。点のセットP
の凸包の頂点は、ハルの「内側」にある点p
を追加しても変化しません。 [P1, P2, ..., Pn]
と[P1, P2, ..., Pn, p]
の凸包の頂点は同じです。ただし、p
が「外側」にある場合は、頂点を変更する必要があります。これはn次元で機能しますが、ConvexHull
を2回計算する必要があります。
2次元の2つのプロット例:
偽:
正しい:
equations
のConvexHull
属性を使用します。
def point_in_hull(point, hull, tolerance=1e-12):
return all(
(np.dot(eq[:-1], point) + eq[-1] <= tolerance)
for eq in hull.equations)
言い換えると、すべての方程式(ファセットを記述する)に対して、ポイントと法線ベクトル(eq[:-1]
)とオフセット(eq[-1]
)の間のドット積が小さい場合にのみ、ポイントはハル内にありますゼロ以下。数値の精度の問題のため、ゼロではなく小さな正の定数tolerance = 1e-12
と比較したい場合があります(そうでない場合、凸包の頂点が凸包にないことがわかります)。
デモンストレーション:
import matplotlib.pyplot as plt
import numpy as np
from scipy.spatial import ConvexHull
points = np.array([(1, 2), (3, 4), (3, 6), (2, 4.5), (2.5, 5)])
hull = ConvexHull(points)
np.random.seed(1)
random_points = np.random.uniform(0, 6, (100, 2))
for simplex in hull.simplices:
plt.plot(points[simplex, 0], points[simplex, 1])
plt.scatter(*points.T, alpha=.5, color='k', s=200, marker='v')
for p in random_points:
point_is_in_hull = point_in_hull(p, hull)
marker = 'x' if point_is_in_hull else 'd'
color = 'g' if point_is_in_hull else 'm'
plt.scatter(p[0], p[1], marker=marker, color=color)
2Dポイントクラウドを使用しているように見えるので、凸ポリゴンのポイントインポリゴンテストの inclusion test に進みたいと思います。
Scipyの凸包アルゴリズムでは、2次元以上の次元の凸包を見つけることができます。これは、2D点群に必要なものよりも複雑です。したがって、 this one などの別のアルゴリズムを使用することをお勧めします。これは、ポイントインポリゴンの凸包テストに本当に必要なのは、時計回りの順序での凸包ポイントのリストと、ポリゴンの内側にあるポイントだからです。
このアプローチの時間パフォーマンスは次のとおりです。
ここで、Nはポイントクラウドのポイント数、hはポイントクラウドの凸包のポイント数です。
Scipyを使い続けたい場合は、凸包にする必要があります(そうしました)
>>> from scipy.spatial import ConvexHull
>>> points = np.random.Rand(30, 2) # 30 random points in 2-D
>>> hull = ConvexHull(points)
次に、船体上のポイントのリストを作成します。船体をプロットするdocのコードを次に示します
>>> import matplotlib.pyplot as plt
>>> plt.plot(points[:,0], points[:,1], 'o')
>>> for simplex in hull.simplices:
>>> plt.plot(points[simplex,0], points[simplex,1], 'k-')
それから始めて、船体上の点のリストを計算することを提案します
pts_hull = [(points[simplex,0], points[simplex,1])
for simplex in hull.simplices]
(私は試しませんでしたが)
また、船体を計算してX、Yポイントを返す独自のコードを用意することもできます。
元のデータセットのポイントが船体上にあるかどうかを知りたい場合、これで完了です。
私が望むのは、船体の内側か外側かを知ることです、もう少し作業が必要です。あなたがしなければならないことは
ハルの2つのシンプレックスを結合するすべてのエッジに対して:ポイントが上か下かを決定します
ポイントがすべての線より下、またはすべての線より上にある場合、それは船体の外側にあります
スピードアップとして、ポイントが1つのラインより上に、そして互いに下にあるとすぐに、それは船体の中にあります。
this postに基づいて、4辺の凸領域に対する迅速で汚い解決策を示します(簡単に拡張できます)。
def same_sign(arr): return np.all(arr > 0) if arr[0] > 0 else np.all(arr < 0)
def inside_quad(pts, pt):
a = pts - pt
d = np.zeros((4,2))
d[0,:] = pts[1,:]-pts[0,:]
d[1,:] = pts[2,:]-pts[1,:]
d[2,:] = pts[3,:]-pts[2,:]
d[3,:] = pts[0,:]-pts[3,:]
res = np.cross(a,d)
return same_sign(res), res
points = np.array([(1, 2), (3, 4), (3, 6), (2.5, 5)])
np.random.seed(1)
random_points = np.random.uniform(0, 6, (1000, 2))
print wlk1.inside_quad(points, random_points[0])
res = np.array([inside_quad(points, p)[0] for p in random_points])
print res[:4]
plt.plot(random_points[:,0], random_points[:,1], 'b.')
plt.plot(random_points[res][:,0], random_points[res][:,1], 'r.')