平滑二维图形

时间:2014-12-24 21:16:07

标签: python matplotlib interpolation smooth

我有一些需要平滑的模糊矩形2D图形。一个简化的例子:

fig, ax1 = plt.subplots(1,1, figsize=(3,3))
xs1 = [-0.25,  -0.625, -0.125, -1.25, -1.125, -1.25, 0.875, 1.0, 1.0, 0.5, 1.0, 0.625, -0.25]
ys1 = [1.25, 1.375, 1.5, 1.625, 1.75, 1.875, 1.875, 1.75, 1.625, 1.5, 1.375, 1.25, 1.25]

ax1.plot(xs1, ys1)
ax1.set_ylim(0.5,2.5)
ax1.set_xlim(-2,2) ;

enter image description here enter image description here

我已经尝试了scipy.interpolate.RectBivariateSpline,但显然需要所有点的数据(例如热图)和scipy.interpolate.interp1d,但是,合理地说,想要生成1d平滑版本。

平滑此问题的适当方法是什么?

修改以更好地修改/解释我的目标。我不需要这些线路来完成所有要点;事实上,我更喜欢他们没有经历所有的观点,因为有明确的异常点,而且应该""与邻居平均,或类似的方法。我已经包含了一个粗略的手册草图,上面是我的想法。

3 个答案:

答案 0 :(得分:12)

Chaikin的切角算法可能是您理想的选择。对于顶点为P0,P1,... P(N-1)的给定多边形,角切割算法将为P(i)和P(i + 1)定义的每个线段生成2个新顶点

Q(i)=(3/4)P(i)+(1/4)P(i + 1)
R(i)=(1/4)P(i)+(3/4)P(i + 1)

因此,您的新多边形将具有2N个顶点。然后,您可以再次重复应用新多边形上的切角,直到达到所需的分辨率。结果将是具有许多顶点的多边形,但在显示时它们看起来会很平滑。可以证明,由此拐角切割方法产生的结果曲线将收敛为二次B样条曲线。这种方法的优点是最终的曲线永远不会过度拍摄。以下图片将让您更好地了解此算法(图片取自this link

原始多边形
Original Polygon

一次涂角切割 Apply corner cutting once

再次应用角切割
enter image description here

有关Chaikin角切割算法的详细信息,请参阅此 link

答案 1 :(得分:7)

实际上,您可以使用scipy.interpolate.inter1d。您需要将多边形的x和y分量视为单独的系列。

作为方形的快速示例:

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d

x = [0, 1, 1, 0, 0]
y = [0, 0, 1, 1, 0]

t = np.arange(len(x))
ti = np.linspace(0, t.max(), 10 * t.size)

xi = interp1d(t, x, kind='cubic')(ti)
yi = interp1d(t, y, kind='cubic')(ti)

fig, ax = plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y)
ax.margins(0.05)
plt.show()

enter image description here

但是,正如您所看到的,这会在0,0。

处产生一些问题

这种情况正在发生,因为样条线段不仅仅取决于两点。我们插值的方式中,第一个和最后一个点没有“连接”。我们可以通过用倒数第二个和第二个点“填充”x和y序列来解决这个问题,以便在端点处存在样条曲线的“环绕”边界条件。

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d

x = [0, 1, 1, 0, 0]
y = [0, 0, 1, 1, 0]

# Pad the x and y series so it "wraps around".
# Note that if x and y are numpy arrays, you'll need to
# use np.r_ or np.concatenate instead of addition!
orig_len = len(x)
x = x[-3:-1] + x + x[1:3]
y = y[-3:-1] + y + y[1:3]

t = np.arange(len(x))
ti = np.linspace(2, orig_len + 1, 10 * orig_len)

xi = interp1d(t, x, kind='cubic')(ti)
yi = interp1d(t, y, kind='cubic')(ti)

fig, ax = plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y)
ax.margins(0.05)
plt.show()

enter image description here

只是为了显示您的数据的样子:

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d

x = [-0.25, -0.625, -0.125, -1.25, -1.125, -1.25,
     0.875, 1.0, 1.0, 0.5, 1.0, 0.625, -0.25]
y = [1.25, 1.375, 1.5, 1.625, 1.75, 1.875, 1.875,
     1.75, 1.625, 1.5, 1.375, 1.25, 1.25]

# Pad the x and y series so it "wraps around".
# Note that if x and y are numpy arrays, you'll need to
# use np.r_ or np.concatenate instead of addition!
orig_len = len(x)
x = x[-3:-1] + x + x[1:3]
y = y[-3:-1] + y + y[1:3]

t = np.arange(len(x))
ti = np.linspace(2, orig_len + 1, 10 * orig_len)

xi = interp1d(t, x, kind='cubic')(ti)
yi = interp1d(t, y, kind='cubic')(ti)

fig, ax = plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y)
ax.margins(0.05)
plt.show()

enter image description here

请注意,使用此方法会产生相当多的“过冲”。这是由于三次样条插值。 @ pythonstarter的建议是另一种处理它的好方法,但贝塞尔曲线会遇到同样的问题(它们在数学上基本相同,只是控制点的定义方式)。有许多其他方法来处理平滑,包括专门用于平滑多边形的方法(例如,具有指数内核的多项式近似(PAEK))。我从来没有试过实现PAEK,所以我不确定它是如何涉及的。如果您需要更强大地执行此操作,您可以尝试查看PAEK或其他类似方法。

答案 2 :(得分:3)

这是答案的更多评论,但也许您可以尝试将此多边形定义为贝塞尔曲线。代码相当简单,我确信您熟悉这条曲线的工作原理。在那种情况下,该曲线将是控制多边形。但它并不完美:首先它不是这个多边形的“平滑版本”,而是一条曲线,另一件事;曲线越高,控制多边形越小。我想说的是,也许你应该尝试使用数学工具来解决这个问题,而不是尝试使用编程技巧来平滑而不是使多边形平滑