簡體   English   中英

使用貝塞爾曲線,路徑'contains_points'會產生不正確的結果

[英]Path 'contains_points' yields incorrect results with Bezier curve

我試圖基於matplotlib Path對象選擇數據區域,但是當路徑包含貝塞爾曲線(不僅僅是直線)時,所選區域不會完全填充曲線。 看起來它正在嘗試,但曲線的遠端被切斷了。

例如,以下代碼定義了一條相當簡單的閉合路徑,其中包含一條直線和一條三次曲線。 當我查看contains_points方法的True / False結果時,它似乎與曲線本身或原始頂點不匹配。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch

# Make the Path
verts = [(1.0, 1.5), (-2.0, 0.25), (-1.0, 0.0), (1.0, 0.5), (1.0, 1.5)]
codes = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4, Path.CLOSEPOLY]
path1 = Path(verts, codes)

# Make a field with points to select
nx, ny = 101, 51
x = np.linspace(-2, 2, nx)
y = np.linspace(0, 2, ny)

yy, xx = np.meshgrid(y, x)
pts = np.column_stack((xx.ravel(), yy.ravel()))

# Construct a True/False array of contained points
tf = path1.contains_points(pts).reshape(nx, ny)

# Make a PathPatch for display
patch1 = PathPatch(path1, facecolor='c', edgecolor='b', lw=2, alpha=0.5)

# Plot the true/false array, the patch, and the vertices
fig, ax = plt.subplots()
ax.imshow(tf.T, origin='lower', extent=(x[0], x[-1], y[0], y[-1]))
ax.add_patch(patch1)
ax.plot(*zip(*verts), 'ro-')

plt.show()

這給了我這個情節:

真實點不能完全填滿曲線

看起來有某種近似 - 這只是matplotlib中計算的一個基本限制,還是我做錯了什么?

可以自己計算曲線內的點數,但如果我不需要,我希望不要重新發明這個輪子。

值得注意的是,使用二次曲線的簡單構造似乎可以正常工作:

模擬圓的二次曲線工作正常

我正在使用matplotlib 2.0.0。

這與評估路徑的空間有關,如GitHub問題#6076中所述 來自mdboom的評論:

通過將曲線轉換為線段然后基於線段轉換交點來完成路徑交叉。 通過以1.0的增量“采樣”曲線來進行此轉換。 當路徑已經在顯示空間中縮放時,這通常是正確的做法,因為以比單個像素更精細的分辨率對曲線進行采樣實際上沒有幫助。 但是,當您在此處計算數據空間中的交點時,我們顯然需要以更精細的分辨率進行采樣。

這是在討論交叉點,但contains_points也會受到影響。 此增強功能仍處於打開狀態,因此我們必須查看它是否在下一個里程碑中得到解決。 與此同時,有幾種選擇:

1)如果您要顯示補丁,可以使用顯示轉換。 在上面的示例中,添加以下內容演示了正確的行為(基於tacaswell對重復問題#8734的評論,現已關閉):

# Work in transformed (pixel) coordinates
hit_patch = path1.transformed(ax.transData)
tf1 = hit_patch.contains_points(ax.transData.transform(pts)).reshape(nx, ny)

ax.imshow(tf2.T, origin='lower', extent=(x[0], x[-1], y[0], y[-1]))

2)如果您不使用顯示器並且只想使用路徑進行計算,最好的辦法是自己簡單地形成Bezier曲線並創建一條線段以外的路徑。 用下面的path2計算代替path1的形成將產生所需的結果。

from scipy.special import binom
def bernstein(n, i, x):
    coeff = binom(n, i)
    return coeff * (1-x)**(n-i) * x**i

def bezier(ctrlpts, nseg):
    x = np.linspace(0, 1, nseg)
    outpts = np.zeros((nseg, 2))
    n = len(ctrlpts)-1
    for i, point in enumerate(ctrlpts):
        outpts[:,0] += bernstein(n, i, x) * point[0]
        outpts[:,1] += bernstein(n, i, x) * point[1]

    return outpts

verts1 = [(1.0, 1.5), (-2.0, 0.25), (-1.0, 0.0), (1.0, 0.5), (1.0, 1.5)]
nsegments = 31
verts2 = np.concatenate([bezier(verts1[:4], nsegments), np.array([verts1[4]])])
codes2 = [Path.MOVETO] + [Path.LINETO]*(nsegments-1) + [Path.CLOSEPOLY]
path2 = Path(verts2, codes2)

這兩種方法都會產生如下所示的內容:

真假匹配貝塞爾曲線

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM