b-spline python,Python中的交互式BSpline拟合

Using the following function, one can fit a cubic spline on input points 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

The input points P can be of the following form:

P=[(921,1181),(951,1230),(993,1243),(1035,1230),

(1065,1181),(1045,1130),(993,1130),(945,1130)]

Now, I wish to make these points of P draggable, such that as and when we change the position of any point, the spline gets refitted on the new points.

Using this as a reference: https://matplotlib.org/1.4.3/examples/event_handling/poly_editor.html (event handling in matplotlib), I have the following code:

"""

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()

Now, what I wish to do is to replace the polygon fitting with my spline fitting function. As I am new to this I am not able to figure out how to do it.

Is there any way to keep all the same functionalities, but only to fit the spline instead of polygon through the given points, making it interactive and refitting the spline as per point movement?

My spline would look somewhat like this :

a7f8001e66656d8f0ad0cecd754f9280.png

How should I go about doing it?

Or, if there is any other suitable method to achieve the same, kindly recommend.

Suggestions are also welcome using MATLAB.

解决方案

Of course you need to add your function to the PolygonInteractor. Don't let it draw anything itself though.

Then add a new line to the class, self.line2 which will be the line to update.

Finally, let the class draw your new line as well. And update it with the result from your interpolation function.

For convenience you may turn the Polygon (self.poly) invisible, also removing the line from the self.line and only showing the points may make sense.

5fdbceaa7309f1972d1531077f83a87e.gif

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()

Note that the curve created here is not truely cyclic. It has a start point where the curve is not really smooth. This can be circumvented by using a truely cyclic set of bezier curves as shown in Create random shape/contour using matplotlib.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值