在轮廓图中平滑多边形?


52

这是等高线图,所有级别的多边形都可用。

让我们问问如何平滑多边形,使所有顶点保留在其精确位置?

实际上,轮廓是在网格数据的顶部制作的,您可能会建议然后对网格数据进行平滑处理,因此最终的轮廓将更加平滑。请注意,这并不是我所希望的,因为诸如高斯滤波器之类的平滑功能将删除少量数据,并将更改第三个变量的范围,例如在我的应用程序中不允许的高度。

实际上,我正在寻找一段代码(最好在Python中),该代码可以使2D多边形(任何类型:凸形,凹形,自相交等)平滑化,并且相当容易(忘记代码页)并且准确。

仅供参考,ArcGIS中有一个功能可以完美地做到这一点,但是对于这个问题,我不是选择使用第三方商业应用程序。

在此处输入图片说明


1)

Scipy.interpolate:

在此处输入图片说明

如您所见,样条曲线(红色)不令人满意!

2)

这是使用此处给出的代码得到的结果。运行不正常!

在此处输入图片说明

3)

对我来说,最好的解决方案应该是如下图所示,其中仅通过更改一个值即可逐渐平滑正方形。我希望有类似的概念可以平滑任何形式的多边形。

在此处输入图片说明

满足样条通过点的条件:

在此处输入图片说明

4)

这是我在数据上逐行在Python中实现“ whuber的想法” 的方法。由于结果不好,可能存在一些错误。

在此处输入图片说明

K = 2是一场灾难,因此对于k> = 4。

5)

我在有问题的位置上删除了一个点,现在所得到的样条曲线与凸纹样条相同。但这仍然是一个问题,为什么该方法不适用于所有情况?

在此处输入图片说明

6)

可以通过以下方式很好地平滑whuber的数据(由矢量图形软件绘制),其中平滑添加了一个额外的点(与更新比较)

4):

在此处输入图片说明

7)

有关某些标志性形状,请参见Python版本的Whuber代码的结果:

在此处输入图片说明
请注意,该方法似乎不适用于折线。对于角折线(轮廓),绿色是我想要的,但变成红色。这需要解决,因为等高线图总是多段线,尽管闭合的多段线可以像我的示例中那样视为多边形。还不是更新4中出现的问题尚未解决。

8)[我的最后一个]

这是最终的解决方案(不完美!):

在此处输入图片说明

请记住,您将不得不对恒星所指向的区域执行某些操作。我的代码中可能存在错误,或者建议的方法需要进一步开发以考虑所有情况并提供所需的输出。


您如何生成“多边形”轮廓?它们是否总是线条,因为与DEM边缘相交的轮廓永远不会闭合?
pistachionut,2012年

我使用了GRASS中的v.generalize函数对轮廓线进行了平滑处理,尽管效果不错,但对于轮廓非常密集的地图可能需要一段时间。
pistachionut,2012年

@pistachionut您可能会认为轮廓线是折线。我在第一阶段正在寻找纯代码。如果不可用,则使用Python的light软件包。
开发人员

也许看scipy.org/Cookbook/Interpolation,因为它听起来像你想花键
PolyGeo

1
链接中的@Pablo Bezier曲线适用于折线。Whuber的多边形效果很好。因此他们可以共同解决这个问题。非常感谢您免费分享您的知识。
开发人员

Answers:


37

样条数字序列的大多数方法将样条多边形。诀窍是使样条线在端点处平滑“闭合”。为此,将“顶点”环绕两端。然后分别样条化x和y坐标。

这是中的工作示例R。它使用spline基本统计信息包中提供的默认立方过程。为了获得更多控制,请替换几乎所有您喜欢的过程:仅确保它通过数字花键(即对它们进行插值),而不是仅将它们用作“控制点”。

#
# Splining a polygon.
#
#   The rows of 'xy' give coordinates of the boundary vertices, in order.
#   'vertices' is the number of spline vertices to create.
#              (Not all are used: some are clipped from the ends.)
#   'k' is the number of points to wrap around the ends to obtain
#       a smooth periodic spline.
#
#   Returns an array of points. 
# 
spline.poly <- function(xy, vertices, k=3, ...) {
    # Assert: xy is an n by 2 matrix with n >= k.

    # Wrap k vertices around each end.
    n <- dim(xy)[1]
    if (k >= 1) {
        data <- rbind(xy[(n-k+1):n,], xy, xy[1:k, ])
    } else {
        data <- xy
    }

    # Spline the x and y coordinates.
    data.spline <- spline(1:(n+2*k), data[,1], n=vertices, ...)
    x <- data.spline$x
    x1 <- data.spline$y
    x2 <- spline(1:(n+2*k), data[,2], n=vertices, ...)$y

    # Retain only the middle part.
    cbind(x1, x2)[k < x & x <= n+k, ]
}

为了说明其用法,让我们创建一个小(但很复杂)的多边形。

#
# Example polygon, randomly generated.
#
set.seed(17)
n.vertices <- 10
theta <- (runif(n.vertices) + 1:n.vertices - 1) * 2 * pi / n.vertices
r <- rgamma(n.vertices, shape=3)
xy <- cbind(cos(theta) * r, sin(theta) * r)

使用前面的代码对它进行样条化。若要使样条曲线更平滑,请从100增加顶点数;否则,增加顶点数。要使其不那么平滑,请减少顶点数量。

s <- spline.poly(xy, 100, k=3)

为了查看结果,我们绘制(a)以红色虚线绘制的原始多边形,显示第一个和最后一个顶点之间的间隙(即,不闭合其边界折线);(b)花键为灰色,再次显示其间隙。(由于间隙太小,其端点用蓝点突出显示。)

plot(s, type="l", lwd=2, col="Gray")
lines(xy, col="Red", lty=2, lwd=2)
points(xy, col="Red", pch=19)
points(s, cex=0.8)
points(s[c(1,dim(s)[1]),], col="Blue", pch=19)

花键多边形


5
好答案。有什么方法可以保证轮廓不会由于平滑而最终交叉?
Kirk Kuykendall

这是个好问题,@ Kirk。我不知道有任何方法可以保证这种平滑形式不交叉。(实际上,我什至没有看到如何保证平滑多段线是非自相交的。但是,对于大多数轮廓来说,这并不是一个大问题。)为此,您需要返回原始轮廓DEM而是使用更好的方法来首先计算轮廓。(这里更好的方法-他们已经知道了很长的时间-但据我所知一些最流行的GISes的不使用它们。)
whuber

首先,我仍在努力以Python来实现您的答案,但仍未成功。其次,如果将方法应用于正方形,将会产生什么结果?您可以参考我在问题中提出的内容。
2012年

1
我接受了这个答案,因为它提供了很好的解决方案。即使它不是一个完美的解决方案,但它给了我一些解决方法,希望我能找到一种满足我在问题和评论中提到的要点的解决方案。您也可以考虑对问题[QC]进行评论,这有很多技巧。最后,我应该说,安装了可爱的Scipy软件包后,对python的翻译几乎是简单的。还要考虑Pablo在质量控制中的评论是折线(即Bezier曲线)的可能解决方案。祝大家好运。
开发人员

1
看到您的答案,我后悔没有很好地照顾我的数学!!!
vinayan

2

我知道这是一篇旧文章,但是它在Google上显示了我想要的内容,因此我认为应该发布解决方案。

我不认为这是2D曲线拟合练习,而是3D曲线。通过将数据视为3D,我们可以确保曲线永不交叉,并且可以使用其他轮廓的信息来改进对当前轮廓的估计。

以下iPython摘录使用SciPy提供的三次插值。请注意,只要所有轮廓的高度等距,我绘制的z值都不重要。

In [1]: %pylab inline
        pylab.rcParams['figure.figsize'] = (10, 10)
        Populating the interactive namespace from numpy and matplotlib

In [2]: import scipy.interpolate as si

        xs = np.array([0.0, 0.0, 4.5, 4.5,
                       0.3, 1.5, 2.3, 3.8, 3.7, 2.3,
                       1.5, 2.2, 2.8, 2.2,
                       2.1, 2.2, 2.3])
        ys = np.array([0.0, 3.0, 3.0, 0.0,
                       1.1, 2.3, 2.5, 2.3, 1.1, 0.5,
                       1.1, 2.1, 1.1, 0.8,
                       1.1, 1.3, 1.1])
        zs = np.array([0,   0,   0,   0,
                       1,   1,   1,   1,   1,   1,
                       2,   2,   2,   2,
                       3,   3,   3])
        pts = np.array([xs, ys]).transpose()

        # set up a grid for us to resample onto
        nx, ny = (100, 100)
        xrange = np.linspace(np.min(xs[zs!=0])-0.1, np.max(xs[zs!=0])+0.1, nx)
        yrange = np.linspace(np.min(ys[zs!=0])-0.1, np.max(ys[zs!=0])+0.1, ny)
        xv, yv = np.meshgrid(xrange, yrange)
        ptv = np.array([xv, yv]).transpose()

        # interpolate over the grid
        out = si.griddata(pts, zs, ptv, method='cubic').transpose()

        def close(vals):
            return np.concatenate((vals, [vals[0]]))

        # plot the results
        levels = [1, 2, 3]
        plt.plot(close(xs[zs==1]), close(ys[zs==1]))
        plt.plot(close(xs[zs==2]), close(ys[zs==2]))
        plt.plot(close(xs[zs==3]), close(ys[zs==3]))
        plt.contour(xrange, yrange, out, levels)
        plt.show()

三次插值结果

这里的结果看起来不是最好的,但是控制点很少,它们仍然是完全有效的。注意如何拉出绿色的拟合线以遵循更宽的蓝色轮廓。


拟合的平滑曲线应尽可能靠近原始多边形/折线。
开发人员

1

我几乎准确地写了您要查找的软件包...但是它在Perl中,并且已经有十多年了:GD :: Polyline。它使用2D三次方Bezier曲线,并将“平滑”任意的Polygon或“ Polyline”(当时叫我的名字,现在通常称为“ LineString”)。

该算法分两个步骤:给定多边形中的点,在每个点之间添加两个Bezier控制点。然后调用简单算法对样条曲线进行逐段逼近。

第二部分很简单;第一部分是一点艺术。这是见解:将“控制段”视为顶点N :vN。控制段为三个共线点:[cNa, vN, cNb]。中心点是顶点。该控制段的斜率等于从顶点N-1到顶点N + 1的斜率。该段的左侧部分的长度是从顶点N-1到顶点N的长度的1/3,而该段的右侧部分的长度是从顶点N到顶点N + 1的长度的1/3。

如果原始曲线是四个顶点: [v1, v2, v3, v4]则每个顶点现在将具有以下形式的控制线段:[c2a, v2, c2b]。像这样将[v1, c1b, c2a, v2, c2b, c3a, v3, c3b, c4a, v4]它们串在一起:然后一次将它们四个作为Bezier的四个点:[v1, c1b, c2a, v2],然后[v2, c2b, c3a, v3],等等。由于[c2a, v2, c2b]是共线的,因此生成的曲线在每个顶点处都将是平滑的。

因此,这也满足了参数化曲线“紧密度”的要求:对于“更紧密”的曲线,请使用小于1/3的值,对于“更柔和”的配合,请使用较大的值。无论哪种情况,生成的曲线总是穿过原始的给定点。

这样就产生了一条平滑的曲线,“包围”了原始多边形。我还可以通过某种方式“刻画”平滑曲线...但是我在CPAN代码中看不到这一点。

无论如何,我目前没有可用的Python版本,也没有任何数字。但是...如果/当我将其移植到Python时,我一定会在这里发布。


无法评估Perl代码,请添加图形以演示其工作方式。
开发人员
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.