均匀抽取 3D 表面网格并保持双边对称性

Decimate 3D surface mesh evenly and preserving bilaterally symmetry

这是 3D 面部表面网格的示例。从下面的图 1 可以看出,界标是双边对称的。我希望减少地标的数量。

这里face是顶点的坐标,triang是三角剖分文件,landPairs是包含顶点配对信息的两列文件。 landPairs 以后不用于绘图,但会在需要时提供。所有数据均来自 here:

这是在抽取之前绘制原始顶点的代码:

library(rgl)
library(Rvcg)

# Customized function to convert vb and it information to 3D mesh
lm2mesh <- function(vb, it) {
    vb <- t(vb)
    vb <- rbind(vb, 1)
    rownames(vb) <- c("xpts", "ypts", "zpts", "")

    it_mat <- t(as.matrix(it))
    rownames(it_mat) <- NULL

    vertices <- c(vb)
    indices <- c(it_mat)

    tmesh3d(vertices = vertices, indices = indices, homogeneous = TRUE, 
            material = NULL, normals = NULL, texcoords = NULL)
}
# Load `face` and `triang`    
face <- as.matrix(read.csv("<PATH>\SampleFace.csv", header=F))
triang <- as.matrix(read.csv("<PATH>\triangulation.csv", header=F))

facemesh <- lm2mesh(face,triang)

# Plot the undecimated mesh
shade3d(facemesh, col="steelblue", specular = "#202020", alpha = 0.7)
plot3d(face, type = "s", col = "red", xlab = "x", ylab = "y", zlab = "z", 
       size = 0.2, aspect = FALSE, alpha = 0.8, add=T)

下图1(顶点均匀分布,绝对左右对称):

# Plot the decimated mesh
open3d()
facemeshdecim <- vcgQEdecim(facemesh,percent=0.1)
shade3d(facemeshdecim, col="steelblue", specular = "#202020", alpha = 0.7)
plot3d(t(facemeshdecim$vb[-4, ]), type = "s", col = "red", xlab = "x", ylab = "y", zlab = "z", 
       size = 0.4, aspect = FALSE, alpha = 0.8, add=T)

下面是图2(顶点分布不均,不再对称):

可以看出,在抽取后的面中,顶点的间距不像抽取前那样均匀,原本对称的顶点不再对称。 我的问题是,是否有一种方法可以减少顶点的数量,同时确保减少的顶点尽可能均匀分布并保持顶点的双边对称性?

这是一个方法。从您的代码开始,然后添加:

# Get the positive part of the face
posface <- clipMesh3d(facemesh, fn="y")

# Decimate it, keeping the boundary
posdeci <- vcgQEdecim(posface, percent=0.1, bound = TRUE)

# Duplicate it in a reflection
negdeci <- posdeci
negdeci$vb[2,] <- -negdeci$vb[2,]

# Join them together
fulldeci <- merge(posdeci, negdeci)

# Plot it
open3d()
shade3d(fulldeci, col="steelblue", specular = "#202020", alpha = 0.7)
plot3d(t(fulldeci$vb[-4, ]), type = "s", col = "red", xlab = "x", ylab = "y", zlab = "z", 
   size = 0.4, aspect = FALSE, alpha = 0.8, add=T)

中间线上的点太多,但除此之外就是你想要的。

编辑添加:

让点更统一有点棘手。如果在调用 vcgQEdecim() 时不使用 bound = TRUE,它会在面部中间留下一个空隙。要填充它,您需要添加连接边两侧的四边形,但找出哪些顶点构成边需要一个新函数:

getBorder <- function(mesh) {
  border <- which(vcgBorder(mesh)$bordervb)
  inorder <- NULL
  repeat{
    i <- 1
    inorder <- c(inorder, border[i])
    repeat{
      found <- FALSE
      tris <- which(apply(mesh$it, 2, function(col) border[i] %in% col))
      for (j in tris) {
        tri <- mesh$it[,j]
        i0 <- which(tri == border[i])
        i1 <- i0 %% 3 + 1
        # keep tri[i1] if the edge from tri[i0] to tri[i1] is external
        tris1 <- which(apply(mesh$it[,tris,drop=FALSE], 2, function(col) all(tri[c(i0, i1)] %in% col)))
        if (length(tris1) == 1) {
          if (tri[i1] %in% inorder)
            break
          inorder <- c(inorder, tri[i1])
          i <- which(border == tri[i1])
          found <- TRUE
          break
        }
      }
      if (!found) break
    }
    border <- setdiff(border, inorder)
    if (!length(border)) break
    inorder <- c(inorder, NA)
  }
  inorder
}

使用该函数,以下代码可以合理地完成工作:

# Try joining halves using quads

posdeci2 <- vcgQEdecim(posface,percent=0.1, bound = FALSE)
negdeci2 <- posdeci2
negdeci2$vb[2,] <- -negdeci2$vb[2,]

# This one has the gap
fulldeci2 <- merge(posdeci2, negdeci2)

# Fill in the gap with quads
# Keep the ones in the middle, but not the outside edge
border <- getBorder(posdeci2)
border <- border[posdeci2$vb[2, border] < 0.005]

borderverts <- posdeci2$vb[, border]
negverts <- negdeci2$vb[, border]

# The quads have both sets of vertices
quadverts <- cbind(borderverts, negverts)
n <- ncol(borderverts)

# We'll assume n > 1
indices <- rbind(1:(n-1), 2:n, n + 2:n, n + 1:(n-1))
quads <- mesh3d(vertices = quadverts, quads = indices)
fulldeci3 <- merge(fulldeci2, quads)

# plot it
open3d()
shade3d(fulldeci3, col="steelblue", specular = "#202020", alpha = 0.7)
plot3d(t(fulldeci3$vb[-4, ]), type = "s", col = "red", xlab = "x", ylab = "y", zlab = "z", 
       size = 0.4, aspect = FALSE, alpha = 0.8, add=T)