Равномерно прореживайте 3D-сетку поверхности, сохраняя двустороннюю симметрию

#r #3d #visualization #mesh #rgl

Вопрос:

Вот образец 3D сетки лицевой поверхности. Как видно из рис. 1 ниже, ориентиры являются двусторонне симметричными. Я хочу сократить количество ориентиров.

Вот face координаты вершин, triang файл триангуляции landPairs , файл с двумя столбцами, содержащий информацию о сопряжении вершин. landPairs не используется для последующего построения графика, но предоставляется в случае необходимости. Все данные доступны здесь:

Вот код для построения исходных вершин перед прореживанием:

 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 ниже (вершины расположены неравномерно и больше не симметричны):
введите описание изображения здесь

Можно видеть, что на прореживаемой грани вершины РАСПОЛОЖЕНЫ НЕ так равномерно, как до прореживания, и первоначально симметричные вершины перестали быть симметричными. Мой вопрос в том, есть ли способ уменьшить количество вершин, гарантируя, что уменьшенные вершины расположены как можно более равномерно, И сохраняя двустороннюю симметрию вершин?

Комментарии:

1. Я не знаю существующего способа сделать то, что вы хотите, но если бы я пытался это сделать, я rgl::clipMesh3d() бы разрезал сетку по линии симметрии, уничтожил результат, а затем продублировал результат по линии симметрии. Все просто, за исключением соединения двух половинок в конце, для чего вам нужно будет написать некоторый код.

Ответ №1:

Вот способ. Начните с вашего кода, затем добавьте это:

 # 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)
 

скриншот

Это имеет слишком много точек вдоль средней линии, но в остальном делает то, что вы хотите.

Отредактировано, чтобы добавить:

Сделать очки более однородными немного сложнее. Если вы не используете bound = TRUE в вызове to vcgQEdecim() , он оставит пробел посередине лица. Чтобы заполнить это, вам нужно добавить квадратики, соединяющие две стороны ребра, но для определения того, какие вершины образуют ребро, требуется новая функция:

 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)
 

скриншот

Комментарии:

1. Очень полезно! Просто следующий вопрос. Настройка bound=T -хороший выбор. Но можно ли несколько уменьшить количество опорных пунктов вдоль границы? Если возможно, я также хотел бы получить новый файл триангуляции после упрощения граничных вершин. Некоторые пользовательские коды были бы весьма признательны.

2. Я думаю, что для этого вам нужно будет привести пограничные вершины в порядок, а затем изменить сетку, чтобы сохранить только percent их. Может Rvcg быть, или какой-то другой пакет имеет функцию, которая поможет в этом.

3. Это не триангуляция, но я добавил некоторый код, чтобы соединить две половины квадроциклами.