【问题标题】:3d surface plot with xyz coordinates带有 xyz 坐标的 3d 曲面图
【发布时间】:2014-05-03 00:56:00
【问题描述】:

我希望有经验的人可以帮助如何从 xyz 数据准备形状文件。尽管没有提供前面创建 shape file 的步骤,但对于 Churyumov–Gerasimenko 彗星的 here 可以看到一个很好的数据集示例。

我试图更好地理解如何将表面应用于给定的一组 XYZ 坐标。使用 R 包“rgl”直接使用笛卡尔坐标,但是环绕的形状似乎更困难。我找到了 R 包geometry,它提供了QHULL 函数的接口。我尝试使用它来计算 Delaunay 三角面,然后我可以在 rgl 中绘制它。我无法弄清楚与函数delaunayn 相关的一些选项,以可能控制计算这些方面的最大距离。我希望这里有人可能对从 xyz 数据改进表面构造有一些想法。

使用“Stanford bunnny”数据集的示例:

library(onion)
library(rgl)
library(geometry)
data(bunny)

#XYZ point plot
open3d()
points3d(bunny, col=8, size=0.1)
#rgl.snapshot("3d_bunny_points.png")

#Facets following Delaunay triangulation
tc.bunny <- delaunayn(bunny)
open3d()
tetramesh(tc.bunny, bunny, alpha=0.25, col=8)
#rgl.snapshot("3d_bunny_facets.png")

This answer 让我相信 Qhull 的 R 实现可能存在问题。另外,我现在尝试了各种设置(例如delaunayn(bunny, options="Qt")),但效果不大。概述了 Qhull 选项here

编辑:

这是一个额外的(更简单的)球体示例。即使在这里,面的计算也并不总是找到最近的相邻顶点(如果你旋转球你会看到一些面穿过内部)。

library(rgl)
library(geometry)
set.seed(1)
n <- 10
rho <- 1
theta <- seq(0, 2*pi,, n) # azimuthal coordinate running from 0 to 2*pi 
phi <- seq(0, pi,, n) # polar coordinate running from 0 to pi (colatitude)
grd <- expand.grid(theta=theta, phi=phi)

x <- rho * cos(grd$theta) * sin(grd$phi)
y <- rho * sin(grd$theta) * sin(grd$phi)
z <- rho * cos(grd$phi)

set.seed(1)
xyz <- cbind(x,y,z)
tbr = t(surf.tri(xyz, delaunayn(xyz)))
open3d()
rgl.triangles(xyz[tbr,1], xyz[tbr,2], xyz[tbr,3], col = 5, alpha=0.5)
rgl.snapshot("ball.png")

【问题讨论】:

  • 你试过alphashape3d包吗?我不知道这正是你要找的,但你可以尝试这个得到一个更好的情节:ashp &lt;- ashape3d(bunny, alpha = c(0.005)); plot(ashp, col=c(8,8,8))
  • @Frank - 感谢您的建议,但这个例子似乎有同样的问题 - 例如更改alpha = 0.5
  • geometry 包中 Qhull 的 R 实现没有问题:点云的 Delaunay 三角剖分始终与此点云的凸包的 Delaunay 三角剖分相同。所以你的“delaunayised”兔子是凸的。

标签: r plot 3d rgl qhull


【解决方案1】:

我认为使用alphashape3d 包找到了一种可能的解决方案。我不得不尝试一下以获得alpha 的可接受值,这与给定数据集中的距离有关(例如sdbunny 给了我一些见解)。我还在琢磨如何更好地控制顶点和边线的宽度,以免占主导地位,但这可能与rgl中的设置有关。

示例:

library(onion)
library(rgl)
library(geometry)
library(alphashape3d)

data(bunny)
apply(bunny,2,sd)
alphabunny <- ashape3d(bunny, alpha = 0.003)
bg3d(1)
plot.ashape3d(alphabunny, col=c(5,5,5), lwd=0.001, size=0, transparency=rep(0.5,3), indexAlpha = "all")

编辑:

只有通过调整plot.ashape3d 函数,我才能删除边缘和顶点:

plot.ashape3d.2 <- function (x, clear = TRUE, col = c(2, 2, 2), byComponents = FALSE, 
                             indexAlpha = 1, transparency = 1, walpha = FALSE, ...) 
{
  as3d <- x
  triangles <- as3d$triang
  edges <- as3d$edge
  vertex <- as3d$vertex
  x <- as3d$x
  if (class(indexAlpha) == "character") 
    if (indexAlpha == "ALL" | indexAlpha == "all") 
      indexAlpha = 1:length(as3d$alpha)
  if (any(indexAlpha > length(as3d$alpha)) | any(indexAlpha <= 
                                                   0)) {
    if (max(indexAlpha) > length(as3d$alpha)) 
      error = max(indexAlpha)
    else error = min(indexAlpha)
    stop(paste("indexAlpha out of bound : valid range = 1:", 
               length(as3d$alpha), ", problematic value = ", error, 
               sep = ""), call. = TRUE)
  }
  if (clear) {
    rgl.clear()
  }
  if (byComponents) {
    components = components_ashape3d(as3d, indexAlpha)
    if (length(indexAlpha) == 1) 
      components = list(components)
    indexComponents = 0
    for (iAlpha in indexAlpha) {
      if (iAlpha != indexAlpha[1]) 
        rgl.open()
      if (walpha) 
        title3d(main = paste("alpha =", as3d$alpha[iAlpha]))
      cat("Device ", rgl.cur(), " : alpha = ", as3d$alpha[iAlpha], 
          "\n")
      indexComponents = indexComponents + 1
      components[[indexComponents]][components[[indexComponents]] == 
                                      -1] = 0
      colors = c("#000000", sample(rainbow(max(components[[indexComponents]]))))
      tr <- t(triangles[triangles[, 8 + iAlpha] == 2 | 
                          triangles[, 8 + iAlpha] == 3, c("tr1", "tr2", 
                                                          "tr3")])
      if (length(tr) != 0) 
        rgl.triangles(x[tr, 1], x[tr, 2], x[tr, 3], col = colors[1 + 
                                                                   components[[indexComponents]][tr]], alpha = transparency, 
                      ...)
    }
  }
  else {
    for (iAlpha in indexAlpha) {
      if (iAlpha != indexAlpha[1]) 
        rgl.open()
      if (walpha) 
        title3d(main = paste("alpha =", as3d$alpha[iAlpha]))
      cat("Device ", rgl.cur(), " : alpha = ", as3d$alpha[iAlpha], 
          "\n")
      tr <- t(triangles[triangles[, 8 + iAlpha] == 2 | 
                          triangles[, 8 + iAlpha] == 3, c("tr1", "tr2", 
                                                          "tr3")])
      if (length(tr) != 0) 
        rgl.triangles(x[tr, 1], x[tr, 2], x[tr, 3], col = col[1], 
                      , alpha = transparency, ...)
    }
  }
}

alphabunny <- ashape3d(bunny, alpha = c(0.003))
plot.ashape3d.2(alphabunny, col=5, indexAlpha = "all", transparency=1)
bg3d(1)

【讨论】:

  • 所以,我的回答对你有用!我没有将其发布为答案,因为我不确定它是否是您需要的。无论如何,如果您在对ashape3d 的调用中使用了多个alpha 值,并且您想为您尝试的所有alpha 值绘制结果,则只需要使用indexAlpha = "all"
  • @Frank - 老实说,我没有意识到这与您推荐的软件包相同!我通过另一个谷歌搜索找到了它,包的小插图帮助我弄清楚了设置。我会试试你的建议。干杯!
  • @Frank - 看起来我把alphatransparency 混淆了。你应该在这里写一个正式的答案。
  • 调整后的plot.ashape3d.2 的第二个情节非常酷!我发现alpha = 0.0015 的图像非常漂亮。
  • @Frank - 是的,alpha = 0.0015 在某些地方给出了更好的结果,但是如果你仔细观察的话,到处都是漏洞。
【解决方案2】:

这是一种使用核密度估计和来自misc3dcontour3d 函数的方法。我一直在玩,直到找到levels 的值,它工作得很好。它不是非常精确,但您可以调整一些东西以获得更好、更准确的表面。如果您有超过 8GB 的​​内存,那么您可以将n 增加到我在此处所做的之外。

library(rgl)
library(misc3d)
library(onion); data(bunny)

# the larger the n, the longer it takes, the more RAM you need
bunny.dens <- kde3d(bunny[,1],bunny[,2],bunny[,3], n=150, 
    lims=c(-.1,.2,-.1,.2,-.1,.2)) # I chose lim values manually

contour3d(bunny.dens$d, level = 600, 
    color = "pink", color2 = "green", smooth=500)
rgl.viewpoint(zoom=.75)

右边的图片是从底部看的,只是为了展示另一个视图。

您可以在kde3d 中为n 使用更大的值,但这会花费更长的时间,并且如果数组变得太大,您可能会耗尽内存。您也可以尝试不同的带宽(此处使用默认值)。我从Computing and Displaying Isosurfaces in R - Feng & Tierney 2008 采用了这种方法。


使用Rvcg 包的非常相似的等值面方法:

library(Rvcg)
library(rgl)
library(misc3d)
library(onion); data(bunny)

bunny.dens <- kde3d(bunny[,1],bunny[,2],bunny[,3], n=150, 
    lims=c(-.1,.2,-.1,.2,-.1,.2)) # I chose lim values manually

bunny.mesh <- vcgIsosurface(bunny.dens$d, threshold=600)
shade3d(vcgSmooth(bunny.mesh,"HC",iteration=3),col="pink") # do a little smoothing

由于它是一种基于密度估计的方法,我们可以通过增加兔子的密度来获得更多收益。我也在这里使用n=400。成本是计算时间的显着增加,但生成的表面是 hare 更好:

bunny.dens <- kde3d(rep(bunny[,1], 10), # increase density.
                    rep(bunny[,2], 10),
                    rep(bunny[,3], 10), n=400, 
                    lims=c(-.1,.2,-.1,.2,-.1,.2))

bunny.mesh <- vcgIsosurface(bunny.dens$d, threshold=600)
shade3d(vcgSmooth(bunny.mesh,"HC",iteration=1), col="pink")


存在更好、更有效的表面重建方法(例如功率外壳、泊松表面重建、球枢轴算法),但我不知道 R 中是否已经实现了任何方法。

这是一个相关的 Stack Overflow 帖子,其中包含一些重要信息和查看链接(包括代码链接):robust algorithm for surface reconstruction from 3D point cloud?

【讨论】:

  • Feng 和 Tierney (2008) 的出色参考——正是我一直在寻找的。我认为这将为我寻找有关该主题的相关文献提供一个良好的开端。感谢您的所有帮助。
  • 感谢您在这里所做的努力,Frank - 您在这个话题上让我更进一步。我喜欢你演绎的傻腻子兔子!
【解决方案3】:

Rvcg 包于 2016 年 7 月更新至 0.14 版,并添加了球旋转面重建。函数是vcgBallPivoting

library(Rvcg) # needs to be >= version 0.14
library(rgl)
library(onion); data(bunny)

# default parameters
bunnybp <- vcgBallPivoting(bunny, radius = 0.0022, clustering = 0.2, angle = pi/2)
shade3d(bunnybp, col = rainbow(1000), specular = "black")
shade3d(bunnybp, col = "pink", specular = "black") # easier to see problem areas.

球旋转和默认参数设置对于斯坦福兔子来说并不完美(正如 cmets 中的 cuttlefish44 所指出的那样radius = 0.0022 比默认的radius = 0 做得更好),并且您在表面上留下了一些空隙。实际兔子的底座上有 2 个孔,一些扫描限制导致了其他一些孔(如此处所述:https://graphics.stanford.edu/software/scanview/models/bunny.html)。您也许能够找到更好的参数,并且使用vcgBallPivoting 非常快(在我的机器上约为 0.5 秒),但可能需要额外的努力/方法来缩小差距。

【讨论】:

  • 这真的很快 - 感谢您的回答。不幸的是,我无法通过使用参数参数来摆脱这些漏洞。顺便说一句,vcgBallPivoting 函数在使用 RStudio 时会导致 R 崩溃。我已通知包作者。
  • 你的回答对我来说太有趣了。 radius = 0.0022 返回更好的输出,但并不完美...
猜你喜欢
  • 1970-01-01
  • 2013-11-26
  • 2016-09-07
  • 1970-01-01
  • 2013-07-14
  • 2020-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多