使用R

时间:2018-04-26 01:25:08

标签: r spline

我知道有很多方法可以计算出曲线的弧长,但我正在寻找一种有效的方法来计算通过不规则间隔点的分段样条曲线的弧长。

我试图找到长度的实际曲线非常复杂(等高线)所以这里是一个使用圆圈的快速示例,其中实际的arclength已知为2*pi

# Generate "random" data
set.seed(50)
theta = seq(0, 2*pi, length.out = 50) + runif(50, -0.05, 0.05)
theta =  c(0, theta[theta >=0 & theta <= 2*pi], 2*pi)
data = data.frame(x = cos(theta), y = sin(theta))

# Bezier Curve fit
library("bezier")
bezierArcLength(data, t1=0, t2=1)$arc.length

# Calculate arc length using euclidean distance 
library("dplyr")
data$eucdist = sqrt((data$x - lag(data$x))^2 + (data$y - lag(data$y))^2)
print(paste("Euclidean distance:", sum(data$eucdist[-1])))
print(paste("Actual distance:", 2*pi))

# Output
Bezier distance: 5.864282
Euclidean distance: 6.2779
Actual distance: 6.2831

我发现最接近的是https://www.rdocumentation.org/packages/pracma/versions/1.9.9/topics/arclength,但我必须将我的数据参数化为function(t) ...spline(data, t)...以使用arclength。我试过这个,但是拟合的花键沿着圆圈的中间而不是沿着圆周运行。

我尝试过的另一种替代方法(不成功)是拟合分段样条曲线并确定每个样条曲线的长度。

非常感谢任何帮助!

编辑:使用Bezier包添加了替代方法,但找到的弧长甚至比使用欧几里德方法更差。

1 个答案:

答案 0 :(得分:0)

代替社区的答案,我拼凑了一个似乎适用于我所追求的解决方案!我会将我的代码保留在这里以防万一有人有同样的问题并且遇到这个问题。

# Libraries
library("bezier")
library("pracma")
library("dplyr")
# Very slow for loops, sorry! Didn't write it as an apply function
output = data.frame()
for (i in 1:100) {
  # Generate "random" data
  # set.seed(50)
  theta = seq(0, 2*pi, length.out = 50) + runif(50, -0.1, 0.1)
  theta = sort(theta)
  theta =  c(0, theta[theta >=0 & theta <= 2*pi], 2*pi)
  data = data.frame(x = cos(theta), y = sin(theta))
  # Bezier Curve fit
  b = bezierArcLength(data, t1=0, t2=1)$arc.length
  # Pracma Piecewise cubic
  t = atan2(data$y, data$x)
  t = t + ifelse(t < 0, 2*pi, 0)
  csx <- cubicspline(t, data$x)
  csy <- cubicspline(t, data$y)
  dcsx = csx; dcsx$coefs = t(apply(csx$coefs, 1, polyder))
  dcsy = csy; dcsy$coefs = t(apply(csy$coefs, 1, polyder))
  ds <- function(t) sqrt(ppval(dcsx, t)^2 + ppval(dcsy, t)^2)
  s = integral(ds, t[1], t[length(t)])
  # Calculate arc length using euclidean distance 
  data$eucdist = sqrt((data$x - lag(data$x))^2 + (data$y - lag(data$y))^2)
  e = sum(data$eucdist[-1])
  # Use path distance as parametric variable
  data$d = c(0, cumsum(data$eucdist[-1]))
  csx <- cubicspline(data$d, data$x)
  csy <- cubicspline(data$d, data$y)
  dcsx = csx; dcsx$coefs = t(apply(csx$coefs, 1, polyder))
  dcsy = csy; dcsy$coefs = t(apply(csy$coefs, 1, polyder))
  ds <- function(t) sqrt(ppval(dcsx, t)^2 + ppval(dcsy, t)^2)
  d = integral(ds, data$d[1], data$d[nrow(data)])
  # Actual value
  a = 2*pi
  # Append to result
  output = rbind(
    output, 
    data.frame(bezier=b, cubic.spline=s, cubic.spline.error=(s-a)/a*100,
               euclidean.dist=e, euclidean.dist.error=(e-a)/a*100,
               dist.spline=d, dist.spline.error=(d-a)/a*100))
}
# Summary
apply(output, 2, mean)
# Summary output
          bezier         cubic.spline   cubic.spline.error       euclidean.dist euclidean.dist.error           dist.spline    dist.spline.error 
    5.857931e+00         6.283180e+00        -7.742975e-05         6.274913e+00        -1.316564e-01           6.283085683         -0.001585570 

我仍然不太了解bezierArcLength的作用,但我对使用cubicspline包中pracma的解决方案非常满意,因为它很多更准确的。

其他解决方案仍然非常受欢迎!