[英]In Python, how do I voxelize a 3D mesh
我需要幫助開始使用 Python(我幾乎一無所知)來對從 Rhino 生成的 3D 網格進行體素化。 數據輸入將是一個 .OBJ 文件,輸出也是如此。 這種用法的最終目的是找到建築物內兩點之間的最短距離。 但那是以后的事。 至於現在,我需要先對 3D 網格進行體素化。 體素化原語可能只是一個簡單的立方體。
到目前為止,我可以從 OBJ 文件解析器中讀取,並從解析后的 obj 中讀取 V、VT、VN、F 前綴,並使用這些坐標找到 3D 對象的邊界框。 體素化網格的正確方法應該是什么?
import objParser
import math
inputFile = 'test.obj'
vList = []; vtList = []; vnList = []; fList = []
def parseOBJ(inputFile):
list = []
vList, vtList, vnList, fList = objParser.getObj(inputFile)
print 'in parseOBJ'
#print vList, vtList, vnList, fList
return vList, vtList, vnList, fList
def findBBox(vList):
i = 0; j=0; x_min = float('inf'); x_max = float('-inf'); y_min = float('inf');
y_max = float('-inf'); z_min = float('inf'); z_max = float('-inf');
xWidth = 0; yWidth = 0; zWidth =0
print 'in findBBox'
while i < len(vList):
#find min and max x value
if vList[i][j] < x_min:
x_min = float(vList[i][j])
elif vList[i][j] > x_max:
x_max = float(vList[i][j])
#find min and max y value
if vList[i][j + 1] < y_min:
y_min = float(vList[i][j + 1])
elif vList[i][j + 1] > y_max:
y_max = float(vList[i][j + 1])
#find min and max x value
if vList[i][j + 2] < z_min:
z_min = vList[i][j + 2]
elif vList[i][j + 2] > z_max:
z_max = vList[i][j + 2]
#incriment the counter int by 3 to go to the next set of (x, y, z)
i += 3; j=0
xWidth = x_max - x_min
yWidth = y_max - y_min
zWidth = z_max - z_min
length = xWidth, yWidth, zWidth
volume = xWidth* yWidth* zWidth
print 'x_min, y_min, z_min : ', x_min, y_min, z_min
print 'x_max, y_max, z_max : ', x_max, y_max, z_max
print 'xWidth, yWidth, zWidth : ', xWidth, yWidth, zWidth
return length, volume
def init():
list = parseOBJ(inputFile)
findBBox(list[0])
print init()
我沒用過,但你可以試試這個: http : //packages.python.org/glitter/api/examples.voxelization-module.html
或者這個工具: http : //www.patrickmin.com/binvox/
如果您想自己執行此操作,您有兩種主要方法:
一個“假”的 - 只需對網格的每個三角形進行體素化。 這要簡單得多,您需要做的就是檢查三角形和軸對齊立方體的交集。 然后你只需要做:
for every triagle: for every cube: if triangle intersects cube: set cube = full else: set cube = empty
您需要做的就是實現 BoundingBox-Triangle 交集。 當然,您可以稍微優化一下 for 循環:)
對於每個仍然存在此問題的人。 我寫了一個文件,它制作了 STL 3d 文件的體素(您可以將 .obj 文件另存為 stl)。 我使用了 kolenda 建議的方法來解決這個問題。 這是為了所謂的“假”體素化。 仍在努力填充這個體素。我使用 numpy.stl 來導入文件,只是文件其余部分的標准包。 我為我的程序使用了以下鏈接。
它可能不是最有效的,但它有效。
import numpy as np
import os
from stl import mesh
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
import math
if not os.getcwd() == 'path_to_model':
os.chdir('path_to model')
your_mesh = mesh.Mesh.from_file('file.stl') #the stl file you want to voxelize
## set the height of your mesh
for i in range(len(your_mesh.vectors)):
for j in range(len(your_mesh.vectors[i])):
for k in range(len(your_mesh.vectors[i][j])):
your_mesh.vectors[i][j][k] *= 5
## define functions
def triangle_voxalize(triangle):
trix = []
triy = []
triz= []
triangle = list(triangle)
#corners of triangle in array formats
p0 = np.array(triangle[0])
p1 = np.array(triangle[1])
p2 = np.array(triangle[2])
#vectors and the plane of the triangle
v0 = p1 - p0
v1 = p2 - p1
v2 = p0 - p2
v3 = p2 - p0
plane = np.cross(v0,v3)
#minimun and maximun coordinates of the triangle
for i in range(3):
trix.append(triangle[i][0])
triy.append(triangle[i][1])
triz.append(triangle[i][2])
minx, maxx = int(np.floor(np.min(trix))), int(np.ceil(np.max(trix)))
miny, maxy = int(np.floor(np.min(triy))), int(np.ceil(np.max(triy)))
minz, maxz = int(np.floor(np.min(triz))), int(np.ceil(np.max(triz)))
#safe the points that are inside triangle
points = []
#go through each point in the box of minimum and maximum x,y,z
for x in range (minx,maxx+1):
for y in range(miny,maxy+1):
for z in range(minz,maxz+1):
#check the diagnals of each voxel cube if they are inside triangle
if LinePlaneCollision(triangle,plane,p0,[1,1,1],[x-0.5,y-0.5,z-0.5],[x,y,z]):
points.append([x,y,z])
elif LinePlaneCollision(triangle,plane,p0,[-1,-1,1],[x+0.5,y+0.5,z-0.5],[x,y,z]):
points.append([x,y,z])
elif LinePlaneCollision(triangle,plane,p0,[-1,1,1],[x+0.5,y-0.5,z-0.5],[x,y,z]):
points.append([x,y,z])
elif LinePlaneCollision(triangle,plane,p0,[1,-1,1],[x-0.5,y+0.5,z-0.5],[x,y,z]):
points.append([x,y,z])
#check edge cases and if the triangle is completly inside the box
elif intersect(triangle,[x,y,z],v0,p0):
points.append([x,y,z])
elif intersect(triangle,[x,y,z],v1,p1):
points.append([x,y,z])
elif intersect(triangle,[x,y,z],v2,p2):
points.append([x,y,z])
#return the points that are inside the triangle
return(points)
#check if the point is on the triangle border
def intersect(triangle,point,vector,origin):
x,y,z = point[0],point[1],point[2]
origin = np.array(origin)
#check the x faces of the voxel point
for xcube in range(x,x+2):
xcube -= 0.5
if LinePlaneCollision(triangle,[1,0,0], [xcube,y,z], vector, origin,[x,y,z]):
return(True)
#same for y and z
for ycube in range(y,y+2):
ycube -= 0.5
if LinePlaneCollision(triangle,[0,1,0], [x,ycube,z], vector, origin,[x,y,z]):
return(True)
for zcube in range(z,z+2):
zcube -= 0.5
if LinePlaneCollision(triangle,[0,0,1], [x,y,zcube], vector, origin,[x,y,z]):
return(True)
#check if the point is inside the triangle (in case the whole tri is in the voxel point)
if origin[0] <= x+0.5 and origin[0] >= x-0.5:
if origin[1] <= y+0.5 and origin[1] >= y-0.5:
if origin[2] <= z+0.5 and origin[2] >= z-0.5:
return(True)
return(False)
# I modified this file to suit my needs:
# https://gist.github.com/TimSC/8c25ca941d614bf48ebba6b473747d72
#check if the cube diagnals cross the triangle in the cube
def LinePlaneCollision(triangle,planeNormal, planePoint, rayDirection, rayPoint,point, epsilon=1e-6):
planeNormal = np.array(planeNormal)
planePoint = np.array(planePoint)
rayDirection = np.array(rayDirection)
rayPoint = np.array(rayPoint)
ndotu = planeNormal.dot(rayDirection)
if abs(ndotu) < epsilon:
return(False)
w = rayPoint - planePoint
si = -planeNormal.dot(w) / ndotu
Psi = w + si * rayDirection + planePoint
#check if they cross inside the voxel cube
if np.abs(Psi[0]-point[0]) <= 0.5 and np.abs(Psi[1]-point[1]) <= 0.5 and np.abs(Psi[2]-point[2]) <= 0.5:
#check if the point is inside the triangle and not only on the plane
if PointInTriangle(Psi, triangle):
return (True)
return (False)
# I used the following file for the next 2 functions, I converted them to python. Read the article. It explains everything way better than I can.
# https://blackpawn.com/texts/pointinpoly#:~:text=A%20common%20way%20to%20check,triangle%2C%20otherwise%20it%20is%20not.
#check if point is inside triangle
def SameSide(p1,p2, a,b):
cp1 = np.cross(b-a, p1-a)
cp2 = np.cross(b-a, p2-a)
if np.dot(cp1, cp2) >= 0:
return (True)
return (False)
def PointInTriangle(p, triangle):
a = triangle[0]
b = triangle[1]
c = triangle[2]
if SameSide(p,a, b,c) and SameSide(p,b, a,c) and SameSide(p,c, a,b):
return (True)
return (False)
##
my_mesh = your_mesh.vectors.copy() #shorten the name
voxel = []
for i in range (len(my_mesh)): # go though each triangle and voxelize it
new_voxel = triangle_voxalize(my_mesh[i])
for j in new_voxel:
if j not in voxel: #if the point is new add it to the voxel
voxel.append(j)
##
print(len(voxel)) #number of points in the voxel
#split in x,y,z points
x_points = []
y_points = []
z_points = []
for a in range (len(voxel)):
x_points.append(voxel[a][0])
y_points.append(voxel[a][1])
z_points.append(voxel[a][2])
## plot the voxel
ax = plt.axes(projection="3d")
ax.scatter3D(x_points, y_points, z_points)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
## plot 1 layer of the voxel
for a in range (len(z_points)):
if z_points[a] == 300:
plt.scatter(x_points[a],y_points[a])
plt.show()
你可以做到這一點pymadcad類PositionMap有一個非常優化的方法光柵化的點,線和三角形成體素。
這只是填充網格表面的體素,而不填充內部。 但是使用這些功能,內部將始終與外部分開;)
這里應該是什么:
from madcad.hashing import PositionMap
from madcad.io import read
# load the obj file in a madcad Mesh
mymesh = read('mymesh.obj')
# choose the cell size of the voxel
size = 1
voxel = set() # this is a sparse voxel
hasher = PositionMap(size) # ugly object creation, just to use one of its methods
for face in mymesh.faces:
voxel.update(hasher.keysfor(mymesh.facepoints(face)))
是的,它不是那么漂亮,因為即使存在這些功能,madcad 還沒有(還)任何慣用的方法來做到這一點。
set
,而不是一個 3d 布爾值數組(存儲大部分零的成本非常高且過度殺傷)hasher.keysfor
生成輸入基元到達的體素單元的所有位置(此處為三角形)set
是一個哈希表,可以非常有效地檢查元素是否在內部(例如,可以非常有效地檢查單元格是否被原語占用)似乎您正在嘗試使用一些尋路方法來找到您在這座建築物內的最短距離。 體素化該區域是一個很好的方法。 如果您需要嘗試,還有另一個:
尋路(如 A* 或 dikstra)通常適用於圖。 它可以是體素中的連接單元,也可以是任何類型的不規則間隔單元。
因此,另一種解決方案是通過生成四面體來對建築物牆壁之間的3d 空間進行三角測量。 不是將算法從體素單元傳播到體素單元,而是從四面體傳播到四面體。 四面體確實具有從隨機網格生成速度更快的優勢,而且不需要與區域體積成比例的內存,並且不會失去任何障礙精度(四面體具有自適應尺寸)。
您可以在此處查看它的外觀。
對於仍然感興趣的任何人,您可以嘗試似乎正是為此目的而制作的voxeltool 。
從文檔:
體素工具為 Rhino 提供輕量級體素幾何。 它允許您從網格、breps、曲線和點快速生成和操作體素化幾何,並提供體素網格之間的布爾運算。 它可以將體素網格轉換為實體網格外殼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.