gpt4 book ai didi

python - Python 中的交互式 BSpline 拟合

转载 作者:太空狗 更新时间:2023-10-30 00:58:42 29 4
gpt4 key购买 nike

使用以下函数,可以在输入点 P 上拟合三次样条:

def plotCurve(P):
pts = np.vstack([P, P[0]])
x, y = pts.T
i = np.arange(len(pts))

interp_i = np.linspace(0, i.max(), 100000 * i.max())

xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)

fig, ax = plt.subplots()
fig,ax=plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y, 'ko')
#plt.show()
return xi,yi

输入点P可以是以下形式:

P=[(921,1181),(951,1230),(993,1243),(1035,1230),
(1065,1181),(1045,1130),(993,1130),(945,1130)]

现在,我希望 P 的这些点可以拖动,这样当我们改变任何点的位置时,样条曲线就会重新适应新点。

以此为引用:https://matplotlib.org/1.4.3/examples/event_handling/poly_editor.html (matplotlib 中的事件处理),我有以下代码:

"""
This is an example to show how to build cross-GUI applications using
matplotlib event handling to interact with objects on the canvas

"""
import numpy as np
from matplotlib.lines import Line2D
from matplotlib.artist import Artist
from matplotlib.mlab import dist_point_to_segment

class PolygonInteractor:
"""
An polygon editor.
Key-bindings

't' toggle vertex markers on and off. When vertex markers are on,
you can move them, delete them

'd' delete the vertex under point

'i' insert a vertex at point. You must be within epsilon of the
line connecting two existing vertices

"""
showverts = True
epsilon = 5 # max pixel distance to count as a vertex hit

def __init__(self, ax, poly):
if poly.figure is None:
raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor')
self.ax = ax
canvas = poly.figure.canvas
self.poly = poly

x, y = zip(*self.poly.xy)
self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True)
self.ax.add_line(self.line)
#self._update_line(poly)

cid = self.poly.add_callback(self.poly_changed)
self._ind = None # the active vert

canvas.mpl_connect('draw_event', self.draw_callback)
canvas.mpl_connect('button_press_event', self.button_press_callback)
canvas.mpl_connect('key_press_event', self.key_press_callback)
canvas.mpl_connect('button_release_event', self.button_release_callback)
canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
self.canvas = canvas


def draw_callback(self, event):
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
self.ax.draw_artist(self.poly)
self.ax.draw_artist(self.line)
self.canvas.blit(self.ax.bbox)

def poly_changed(self, poly):
'this method is called whenever the polygon object is called'
# only copy the artist props to the line (except visibility)
vis = self.line.get_visible()
Artist.update_from(self.line, poly)
self.line.set_visible(vis) # don't use the poly visibility state


def get_ind_under_point(self, event):
'get the index of the vertex under point if within epsilon tolerance'

# display coords
xy = np.asarray(self.poly.xy)
xyt = self.poly.get_transform().transform(xy)
xt, yt = xyt[:, 0], xyt[:, 1]
d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2)
indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
ind = indseq[0]

if d[ind]>=self.epsilon:
ind = None

return ind

def button_press_callback(self, event):
'whenever a mouse button is pressed'
if not self.showverts: return
if event.inaxes==None: return
if event.button != 1: return
self._ind = self.get_ind_under_point(event)

def button_release_callback(self, event):
'whenever a mouse button is released'
if not self.showverts: return
if event.button != 1: return
self._ind = None

def key_press_callback(self, event):
'whenever a key is pressed'
if not event.inaxes: return
if event.key=='t':
self.showverts = not self.showverts
self.line.set_visible(self.showverts)
if not self.showverts: self._ind = None
elif event.key=='d':
ind = self.get_ind_under_point(event)
if ind is not None:
self.poly.xy = [tup for i,tup in enumerate(self.poly.xy) if i!=ind]
self.line.set_data(zip(*self.poly.xy))
elif event.key=='i':
xys = self.poly.get_transform().transform(self.poly.xy)
p = event.x, event.y # display coords
for i in range(len(xys)-1):
s0 = xys[i]
s1 = xys[i+1]
d = dist_point_to_segment(p, s0, s1)
if d<=self.epsilon:
self.poly.xy = np.array(
list(self.poly.xy[:i]) +
[(event.xdata, event.ydata)] +
list(self.poly.xy[i:]))
self.line.set_data(zip(*self.poly.xy))
break


self.canvas.draw()

def motion_notify_callback(self, event):
'on mouse movement'
if not self.showverts: return
if self._ind is None: return
if event.inaxes is None: return
if event.button != 1: return
x,y = event.xdata, event.ydata

self.poly.xy[self._ind] = x,y
self.line.set_data(zip(*self.poly.xy))

self.canvas.restore_region(self.background)
self.ax.draw_artist(self.poly)
self.ax.draw_artist(self.line)
self.canvas.blit(self.ax.bbox)


if __name__ == '__main__':
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon

#theta = np.arange(0, 2*np.pi, 0.1)
#r = 1.5

#xs = r*np.cos(theta)
#ys = r*np.sin(theta)
xs = (921, 951, 993, 1035, 1065, 1045, 993, 945)
ys = (1181, 1230, 1243, 1230, 1181, 1130, 1130, 1130)

poly = Polygon(list(zip(xs, ys)), animated=True)

fig, ax = plt.subplots()
ax.add_patch(poly)
p = PolygonInteractor(ax, poly)

#ax.add_line(p.line)
ax.set_title('Click and drag a point to move it')
#ax.set_xlim((-2,2))
#ax.set_ylim((-2,2))

ax.set_xlim((800, 1300))
ax.set_ylim((1000, 1300))

plt.show()

现在,我想做的是用我的样条拟合函数替换多边形拟合。由于我是新手,所以我无法弄清楚该怎么做。有什么方法可以保持所有相同的功能,但只是通过给定点拟合样条而不是多边形,使其具有交互性并根据点移动重新拟合样条?

我的样条线看起来有点像这样:

enter image description here

我应该怎么做?

或者,如果有任何其他合适的方法可以达到同样的效果,请推荐。

也欢迎使用 MATLAB 提出建议。

最佳答案

当然,您需要将函数添加到 PolygonInteractor。不要让它自己画任何东西。然后向类中添加一个新行 self.line2 ,这将是要更新的行。

最后,让全类也画出你的新线。并使用插值函数的结果更新它。

为方便起见,您可以将多边形 (self.poly) 设为不可见,同时从 self.line 中删除线,只显示点可能有意义。

enter image description here

import numpy as np
from scipy.interpolate import interp1d
from matplotlib.lines import Line2D
from matplotlib.artist import Artist
from matplotlib.mlab import dist_point_to_segment


class PolygonInteractor(object):

"""
A polygon editor.
https://matplotlib.org/gallery/event_handling/poly_editor.html

Key-bindings

't' toggle vertex markers on and off. When vertex markers are on,
you can move them, delete them

'd' delete the vertex under point

'i' insert a vertex at point. You must be within epsilon of the
line connecting two existing vertices

"""

showverts = True
epsilon = 5 # max pixel distance to count as a vertex hit

def __init__(self, ax, poly, visible=False):
if poly.figure is None:
raise RuntimeError('You must first add the polygon to a figure '
'or canvas before defining the interactor')
self.ax = ax
canvas = poly.figure.canvas
self.poly = poly
self.poly.set_visible(visible)

x, y = zip(*self.poly.xy)
self.line = Line2D(x, y, ls="",
marker='o', markerfacecolor='r',
animated=True)
self.ax.add_line(self.line)

self.cid = self.poly.add_callback(self.poly_changed)
self._ind = None # the active vert

canvas.mpl_connect('draw_event', self.draw_callback)
canvas.mpl_connect('button_press_event', self.button_press_callback)
canvas.mpl_connect('key_press_event', self.key_press_callback)
canvas.mpl_connect('button_release_event', self.button_release_callback)
canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
self.canvas = canvas

x,y = self.interpolate()
self.line2 = Line2D(x, y, animated=True)
self.ax.add_line(self.line2)

def interpolate(self):
x, y = self.poly.xy[:].T
i = np.arange(len(x))

interp_i = np.linspace(0, i.max(), 100 * i.max())

xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)

return xi,yi

def draw_callback(self, event):
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
self.ax.draw_artist(self.poly)
self.ax.draw_artist(self.line)
self.ax.draw_artist(self.line2)
# do not need to blit here, this will fire before the screen is
# updated

def poly_changed(self, poly):
'this method is called whenever the polygon object is called'
# only copy the artist props to the line (except visibility)
vis = self.line.get_visible()
Artist.update_from(self.line, poly)
self.line.set_visible(vis) # don't use the poly visibility state


def get_ind_under_point(self, event):
'get the index of the vertex under point if within epsilon tolerance'

# display coords
xy = np.asarray(self.poly.xy)
xyt = self.poly.get_transform().transform(xy)
xt, yt = xyt[:, 0], xyt[:, 1]
d = np.hypot(xt - event.x, yt - event.y)
indseq, = np.nonzero(d == d.min())
ind = indseq[0]

if d[ind] >= self.epsilon:
ind = None

return ind

def button_press_callback(self, event):
'whenever a mouse button is pressed'
if not self.showverts:
return
if event.inaxes is None:
return
if event.button != 1:
return
self._ind = self.get_ind_under_point(event)

def button_release_callback(self, event):
'whenever a mouse button is released'
if not self.showverts:
return
if event.button != 1:
return
self._ind = None

def key_press_callback(self, event):
'whenever a key is pressed'
if not event.inaxes:
return
if event.key == 't':
self.showverts = not self.showverts
self.line.set_visible(self.showverts)
if not self.showverts:
self._ind = None
elif event.key == 'd':
ind = self.get_ind_under_point(event)
if ind is not None:
self.poly.xy = np.delete(self.poly.xy,
ind, axis=0)
self.line.set_data(zip(*self.poly.xy))
elif event.key == 'i':
xys = self.poly.get_transform().transform(self.poly.xy)
p = event.x, event.y # display coords
for i in range(len(xys) - 1):
s0 = xys[i]
s1 = xys[i + 1]
d = dist_point_to_segment(p, s0, s1)
if d <= self.epsilon:
self.poly.xy = np.insert(
self.poly.xy, i+1,
[event.xdata, event.ydata],
axis=0)
self.line.set_data(zip(*self.poly.xy))
break
if self.line.stale:
self.canvas.draw_idle()

def motion_notify_callback(self, event):
'on mouse movement'
if not self.showverts:
return
if self._ind is None:
return
if event.inaxes is None:
return
if event.button != 1:
return
x, y = event.xdata, event.ydata

self.poly.xy[self._ind] = x, y
if self._ind == 0:
self.poly.xy[-1] = x, y
elif self._ind == len(self.poly.xy) - 1:
self.poly.xy[0] = x, y
self.line.set_data(zip(*self.poly.xy))

x,y = self.interpolate()
self.line2.set_data(x,y)

self.canvas.restore_region(self.background)
self.ax.draw_artist(self.poly)
self.ax.draw_artist(self.line)
self.ax.draw_artist(self.line2)
self.canvas.blit(self.ax.bbox)


if __name__ == '__main__':
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon

#theta = np.arange(0, 2*np.pi, 0.1)
#r = 1.5

#xs = r*np.cos(theta)
#ys = r*np.sin(theta)
xs = (921, 951, 993, 1035, 1065, 1045, 993, 945)
ys = (1181, 1230, 1243, 1230, 1181, 1130, 1130, 1130)

poly = Polygon(list(zip(xs, ys)), animated=True)

fig, ax = plt.subplots()
ax.add_patch(poly)
p = PolygonInteractor(ax, poly, visible=False)

ax.set_title('Click and drag a point to move it')

ax.set_xlim((800, 1300))
ax.set_ylim((1000, 1300))

plt.show()

请注意,此处创建的曲线并不是真正的循环曲线。它有一个曲线不是很平滑的起点。这可以通过使用一组真正循环的贝塞尔曲线来规避,如 Create random shape/contour using matplotlib 所示。 .

关于python - Python 中的交互式 BSpline 拟合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49570341/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com