Python三维网格体素化
本文主要是实现将一个网格模型体素化,实现不同分辨率的体素化效果,并且可视化输出为obj文件!
首先利用trimesh对mesh进行采样,然后根据采样点得到各个体素点的占有值。
效果
通过调整分辨率以及采样率(当分辨率变高时建议适量提高采样率)得到以下的效果!
代码
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ @author: Matthieu Zins """ import trimesh import numpy as np import os import argparse """ ====== Voxelize the surface of a mesh ====== """ def create_if_needed(folder): if not os.path.isdir(folder): os.mkdir(folder) parser = argparse.ArgumentParser(description='Pass object name') parser.add_argument('input_mesh', type=str) parser.add_argument('--output_folder', type=str, default="") parser.add_argument('--resolution', type=int, nargs=3, default=[50, 50, 50], help="resolution_X resolution_Y resolution_Z") parser.add_argument('--sampling', type=int, default="100000", help="number of points sampled on the mesh") args = parser.parse_args() input_mesh_filename = args.input_mesh object_name = os.path.splitext(os.path.basename(input_mesh_filename))[0] output_folder = args.output_folder if len(output_folder) == 0: output_folder = object_name RES_X, RES_Y, RES_Z = args.resolution sample_points_count = args.sampling create_if_needed(output_folder) mesh = trimesh.exchange.load.load(input_mesh_filename) # Uniform Points Sampling pts, _ = trimesh.sample.sample_surface_even(mesh, sample_points_count ) # Save sample points sampled_points_mesh = trimesh.Trimesh(vertices=pts) sampled_points_mesh.export(os.path.join(output_folder, object_name + "_resampled_points.ply")) # Adjust the grid origin and voxels size origin = pts.min(axis=0) dimensions = pts.max(axis=0) - pts.min(axis=0) scales = np.divide(dimensions, np.array([RES_X-1, RES_Y-1, RES_Z-1])) scale = np.max(scales) # Voxelize pts -= origin pts /= scale pts_int = np.round(pts).astype(int) grid = np.zeros((RES_X, RES_Y, RES_Z), dtype=int) gooRES_X = np.where(np.logical_and(pts_int[:, 0] >= 0, pts_int[:, 0] < RES_X))[0] gooRES_Y = np.where(np.logical_and(pts_int[:, 1] >= 0, pts_int[:, 1] < RES_Y))[0] gooRES_Z = np.where(np.logical_and(pts_int[:, 2] >= 0, pts_int[:, 2] < RES_Z))[0] goods = np.intersect1d(np.intersect1d(gooRES_X, gooRES_Y), gooRES_Z) pts_int = pts_int[goods, :] grid[pts_int[:, 0], pts_int[:, 1], pts_int[:, 2]] = 1 # Save voxels voxel_pts = np.array([[-0.5, 0.5, -0.5], [0.5, 0.5, -0.5], [0.5, 0.5, 0.5], [-0.5, 0.5, 0.5], [-0.5, -0.5, -0.5], [0.5, -0.5, -0.5], [0.5, -0.5, 0.5], [-0.5, -0.5, 0.5]]) voxel_faces = np.array([[0, 1, 2, 3], [1, 5, 6, 2], [5, 4, 7, 6], [4, 0, 3, 7], [0, 4, 5, 1], [7, 3, 2, 6]]) def get_voxel(i, j, k): global voxel_pts, voxel_faces v = np.array([i, j, k], dtype=float) * scale v += origin points = voxel_pts * scale + v return points, voxel_faces.copy() points = [] faces = [] fi = 0 for i in range(RES_X): for j in range(RES_Y): for k in range(RES_Z): if grid[i, j, k]: p, f = get_voxel(i, j, k) points.append(p) f += fi faces.append(f) fi += 8 points = np.vstack(points) faces = np.vstack(faces) # Write obj mesh with quad faces with open(os.path.join(output_folder, object_name + "_voxels.obj"), "w") as fout: for p in points:fout.write("v " + " ".join(map(str, p)) + "\n") for f in faces+1:fout.write("f " + " ".join(map(str, f)) + "\n") print(object_name, "done.")
运行:
Dependencies
- numpy
- trimesh
## Usage python voxelize_surface.py example/chair.obj --output_folder output --resolution 30 30 30 --sampling 10000
The optional parameters are:
- output_folder (string): folder where the result is saved
- resolution (list): the resolution of the grid
[res_x, res_y, res_z]
- sampling (int): number of points sampled on the mesh surface before voxelization
- 注:输入的类型可以时obj也可以是off以及ply格式!
注意:
若出现如下情况,可将采样点数(sampling)提高!
出现此种情况的原因是采样间隔太大,而体素尺寸太小(分辨太高),所以导致在有些体素的占有值进行判断的时候出现错误。
所以也可以通过降低分辨率来改善此种情况!
Reference:
声明:本文的代码并非原创,来自GitHub中zinsmatt的Surface_Voxels一作!若有侵权请联系撤文!
https://github.com/zinsmatt/Surface_Voxels
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。