我有一个Nx3 numpy数组,列出了表面N个三角形面的3个顶点,以及与这些面中的每一个相对应的Nx1数组值。
我想转换(尽可能最好)这些"面孔"值进入"顶点"值,例如通过查找与顶点关联的所有面的平均值。
我目前的解决方案适用于较小的N值,但可以按比例缩放。面孔x没有。顶点,很快就变得不切实际了:
def face2vertVal(faces, facesVals, verts):
# INPUT:
# faces: Nx3 array of N vertex IDs for N faces
# facesVals: Nx1 array of some parameter for each face
# OUTPUT:
# vertsVals: Px1 array of the mean value in "facesVals" that corresponds
# to each vertex
import numpy as np
vertsVals = np.zeros(faces.max()+1)
for vertex in range(0, faces.max()+1):
tmpVals = np.zeros(1)
for row in range(0, faces.shape[0]):
if faces[row].__contains__(vertex):
tmpVals = np.append(tmpVals, facesVals[row])
vertsVals[vertex] = tmpVals[1:].mean()
del tmpVals
return vertsVals
提前致谢。
编辑:矢量化方法非常快,但需要700k面和350k顶点的太多内存。
答案 0 :(得分:4)
编辑:
如果内存存在问题,您还可以拥有算法的“批量”版本,该版本一次只处理有限数量的面:
将numpy导入为np
def face2vertVal(faces, faces_values, batch_size=None):
batch_size = batch_size or len(faces)
faces = np.asarray(faces)
faces_values = np.asarray(faces_values)
vert_idx = np.arange(faces.max() + 1)
vertex_values = np.zeros(len(vert_idx))
vertex_counts = np.zeros(len(vert_idx))
for i in range(0, len(faces), batch_size):
faces_batch = faces[i:i + batch_size]
faces_values_batch = faces_values[i:i + batch_size]
vertex_faces = np.any(faces_batch == vert_idx[:, np.newaxis, np.newaxis], axis=-1)
vertex_values += np.sum(vertex_faces * faces_values_batch, axis=1)
vertex_counts += np.count_nonzero(vertex_faces, axis=1)
return vertex_values / np.maximum(vertex_counts, 0)
# All at once
vertex_values = face2vertVal([[0, 1, 2], [1, 2, 3], [2, 3, 0]], [1, 2, 3])
print(*vertex_values, sep=', ')
# 2.0, 1.5, 2.0, 2.5
# In batches of two
vertex_values_1b = face2vertVal([[0, 1, 2], [1, 2, 3], [2, 3, 0]], [1, 2, 3], 2)
print(*vertex_values_1b, sep=', ')
# 2.0, 1.5, 2.0, 2.5
您可以手动选择batch_size
(或使用某些公式,具体取决于您要使用的内存或其他内容)来平衡速度和内存之间的权衡。
你应该以矢量化方式进行计算,否则会慢得多。这是一种方式:
import numpy as np
def face2vertVal(faces, faces_values):
faces = np.asarray(faces)
faces_values = np.asarray(faces_values)
vert_idx = np.arange(faces.max() + 1)
vertex_faces = np.any(faces == vert_idx[:, np.newaxis, np.newaxis], axis=-1)
vertex_values = np.sum(vertex_faces * faces_values, axis=1) / np.maximum(np.count_nonzero(vertex_faces, axis=1), 0)
return vertex_values
vertex_values = face2vertVal([[0, 1, 2], [1, 2, 3], [2, 3, 0]], [1, 2, 3])
print(*vertex_values, sep=', ')
# 2.0, 1.5, 2.0, 2.5
请注意,这种方法的缺点是,N
面临P
个顶点,需要O(N*P)
个内存,而非向量化算法适用于O(max(N, P))
存储器中。
答案 1 :(得分:2)
您的代码存在一些对性能有很大影响的问题(例如,不要在循环中使用np.append)。 因此,第一步是改进代码并避免不必要的搜索。
在下一步中,我们可以使用jit编译器来获得额外的性能。
<强>代码强>
import numpy as np
import numba as nb
import time
from scipy import spatial
N_verts=350000
#This gernerates a mesh for performance Testing
#https://stackoverflow.com/a/50579387/4045774
def make(N):
verts= np.random.uniform(-10, 10, (N, 3))
faces = spatial.Delaunay(verts[:, :2]).simplices
return verts,faces
@nb.njit()
def face2vertVal(faces, facesVals):
# INPUT:
# faces: Nx3 array of N vertex IDs for N faces
# facesVals: Nx1 array of some parameter for each face
# OUTPUT:
# vertsVals: Px1 array of the mean value in "facesVals" that corresponds
# to each vertex
vertsVals = np.zeros(faces.max()+1)
verts_counts= np.zeros(faces.max()+1)
for i in range(faces.shape[0]):
for j in range(faces.shape[1]):
vertsVals[faces[i,j]]+=facesVals[i]
verts_counts[faces[i,j]]+= 1.
vertsVals=vertsVals/verts_counts
return vertsVals
[verts,faces]=make(N_verts)
facesVals=np.random.rand(faces.shape[0])
res=face2vertVal(faces, facesVals)
<强>性能强>
N_verts=350 000
Pure-Python:2.12s
With Numba (after the compilation which takes about 0.5s at the first call): 18.7ms