平滑二维图形

iay*_*ork 6 python interpolation smooth matplotlib

我有一些需要平滑的模糊矩形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) ;
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述 在此输入图像描述

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

什么是平滑这个的适当方法?

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

fan*_*ang 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样条曲线.这种方法的优点是最终的曲线永远不会过度拍摄.以下图片将让您更好地了解此算法(从此链接拍摄的照片)

原始多边形
原始多边形

应用角切一次
应用角切一次

再次应用切角
在此输入图像描述

有关Chaikin角切割算法的更多详细信息,请参阅此链接.


Joe*_*ton 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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

但是,正如您所看到的,这会在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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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

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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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