Py学习  »  Python

Python三维模型处理基础

CDA数据分析师 • 1 年前 • 1004 次点击  

“Python-批处理的效率神器。”


因为最近做深度学习数据集需要处理大量三维模型,一开始都是用Magics手动处理,每个模型需要曲面偏置+布尔运算+几何修复等等,一套连招下来要一两分钟,熟练后最快最快也要30秒,几百组数据下来,花费的时间暂且不提,但长时间机械地处理数据是真的容易让人产生不适。
后来逐渐开始接触一些Python的三维函数包,仿佛打开新世界,这才是批量处理三维模型的正确打开方式!所以特地写下这篇文章来记录这段时间处理三维模型的心得。
由于Python的三维函数包很多,功能上有所交叉,面向对象也有所不同,所以在执行模型处理任务时可能需要同时使用多个函数包,这篇文章主要就我之前常用的几个函数包的基本功能进行简单整理,包含几何建模模型编辑模型修复等,更详细的用法可参考函数包官方文档,链接放在最后。
文章内容大体可以分为以下几部分:
(1)什么要使用Python三维函数包?
(2)常用三维函数包的几何建模功能
(3)常用三维函数包的模型编辑功能
(4)常用三维函数包的几何修复功能
(5)

为什么要使用Python三维函数包?
可能有人会问,平时画三维模型,比如变速器、减速器箱体等,使用Solidworks、CATIA、UG或者国产的云图三维这类商业CAD软件就够了,为什么会想到用Python做三维建模?
(是因为买不起正版)
商业CAD软件确实好用,功能强大,像达索、西门子这些老牌公司,经过这么多年的技术积累与业务扩张,旗下软件基本可以应付工业场景的各种三维建模需求(有时也会综合使用各类专用软件,比如针对某类零件的设计软件)。
但很多非相关专业的同学可能也会有CAD软件的使用需求,比如给自己的论文画个三维示意图,做一些三维数据可视化,或者完成一些简单的参数化建模,这些情况下特地去安装一个庞大的CAD软件,就好像为了拧一个螺丝而买下一整套昂贵的螺丝刀套装,并不是很划算。另外,很多通用三维软件可能并不支持数据的批量处理,如果有那也基本需要进行二次开发,如果像我一样,需要处理大量三维模型的话,使用商业CAD软件可能会比较费时费力。
在上述场景下,使用Python三维函数包可能更加合适。一方面,开源免费光环的加持可以大大降低安装成本,且一般对硬件要求不高。另一方面,由于整体处于编程环境,使用者可以按照自己的设想,以更加自由的方式完成三维建模或者模型处理。当然,自由的代价是学习成本的提升,你需要做到充分熟悉函数库的语法,否则还是不如很多商软的傻瓜式交互操作简单。

(CAD软件  vs  Python三维函数包)

如果能合理使用Python提供的三维函数包的话,就能以快捷高效的方式完成一些轻量级的建模,从而大大提升科研效率。但目前很多函数包的官方文档对使用者并不是特别友好,函数功能介绍仅局限于参数含义说明不会说明这个函数的适用范围以及与其他函数的区别等纯靠自己摸索的话还是比较费劲的。
所以这篇文章就我之前使用过的几个Python三维函数包的基本用法做简单介绍,可以减少自己查官方文档的时间,函数包主要包括Open3DPyvistaPymeshfixVTK以及Trimesh。当然Python的三维函数包远不止这些,以后有机会接触到的话可以再跟大家分享,如果有推荐的三维库也可以后台私信我哦。(相关Python环境的配置以及三维函数库的安装这边就不介绍了,网上可以查到,都比较简单)

 常用三维函数包的几何建模功能
(1)基本几何体绘制
立方体、圆柱体、三棱锥等基本几何体的绘制,比较简单,不做过多介绍,直接上一些案例。更多几何体的函数可从官网查询。
# Open3D绘制圆球并可视化# 半径 radius=1.0 分辨率 resolution=40import open3d as o3dmesh = o3d.geometry.TriangleMesh.create_sphere(radius=1.0, resolution=40)mesh.compute_vertex_normals()o3d.visualization.draw_geometries([mesh])

# pyvista绘制圆柱体并可视化# 中心坐标 center=(0.0, 0.0, 0.0) 取向 direction=(1.0, 0.0, 0.0)# 半径 radius=0.2 高度 height=2.0 分辨率 resolution=50import pyvista as pvmesh = pv.Cylinder(center=(0.0, 0.0, 0.0),                    direction=(1.0, 0.0, 0.0),                    radius=0.2,                    height=2.0,                   resolution=50)mesh.plot()

# trimesh绘制长方体并可视化# 尺寸 (4,4,1)import trimeshmesh = trimesh.creation.box(extents=(4, 4, 1))mesh.show()

相对于CAD软件来说,Python基本三维建模功能并不强,难以实现CAD软件常见的自定义草图拉伸、旋转、扫掠等功能,而且也不如CAD软件直观,对空间想象能力有一定要求,不适合复杂三维物体建模。当然如果你有足够耐心的话也可以尝试一下用这些基本几何体去组合生成一个复杂的模型。(大佬都已经可以在我的世界里堆芯片、搭神经网络、做计算机了,还有什么是做不到的?)

(2)三维重建
如果手上有一个体素类模型,比如拓扑优化SIMP算法导出的伪密度优化结果,或者医学常见的CT/MRI影像分割数据,想重建为mesh模型(stl/obj/ply等格式),可以使用Marching Cubes算法(MC算法,通过提取等值面进行三维模型的绘制,非常经典的面绘制算法,应用广泛。参考文献-Marching cubes: A high resolution 3D surface construction algorithm
因为这段时间刚好在参与医疗影像分析的项目,所以就以CT影像三维重建为例说明MC算法的使用。使用的数据来源于开源颅面缺损修复数据集(参考文献-SkullBreak / SkullFix –Dataset for automatic cranial implant design and a benchmark for volumetric shape learning tasks),格式为nii,文件存储于压缩包中,三维重建代码如下:
import nibabel as nibimport numpy as npimport skimage.measureimport torch# 路径nii_data = "skull_complete.nii.gz"img = nib.load(nii_data)voxel = torch.tensor(np.array(img.dataobj,dtype=np.float32)).unsqueeze(0).unsqueeze(0)voxel = torch.nn.functional.interpolate(voxel, scale_factor=0.5, mode='trilinear', align_corners=True)voxel = np.array(voxel.squeeze(0).squeeze(0))voxel = np.pad(voxel, ((5, 5), (5, 5), (5, 5)), 'constant')vertices_recon, faces_recon, _, _ = skimage.measure.marching_cubes(voxel, level=0)mesh = trimesh.Trimesh(vertices_recon, faces_recon, validate=True)mesh.show()
其中nibabel为用于处理CT影像数据的Python函数包,另外使用了Pytorch的interpolate对原始数据进行压缩(用Pytorch较为繁琐,也可以直接numpy切片),否则MC算法耗时会过长。Line11使用了pad是因为这组数据刚好位于图像边缘,重建出来会不封闭,所以给边缘也填充一些数值。Line12使用了skimage提供的MC函数,网上查到的MC算法python实现基本都是调用skimage,所以我当时也是用的是skimage,实际上Pyvista也可以,之后的文章中会提到。
重建结果如下,左侧为CT数据(可使用ITK-SNAP软件查看),右侧为重建的mesh模型。可以看到模型并不是十分光滑,后期可以使用光顺算法对其进行后处理。


除MC算法以外,泊松重建也是使用非常广泛也非常经典的三维重建算法(参考文献-Poisson Surface Reconstruction),但常用于由点云重建mesh或由破损/不完整模型重建封闭(watertight)模型。
把刚刚重建的颅面模型拿来继续用,读取其中的点云,然后使用泊松重建得到mesh模型。代码如下:
import open3d as o3dmesh = o3d.io.read_triangle_mesh("skull.stl")pcd = o3d.geometry.PointCloud()pcd.points = o3d.utility.Vector3dVector(mesh.vertices)pcd.normals = o3d.utility.Vector3dVector(mesh.vertex_normals)pcd.paint_uniform_color((0.74,0.73,0.68))o3d.visualization.draw_geometries([pcd])with o3d.utility.VerbosityContextManager(        o3d.utility.VerbosityLevel.Debug) as cm:    mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(        pcd, depth=6)mesh.compute_vertex_normals()mesh.paint_uniform_color((0.74,0.73,0.68))o3d.visualization.draw_geometries([mesh])
这里使用的是open3d的泊松重建函数,其中depth参数为重建过程中建立的八叉树深度,可以控制重建精度,越大越精细,但相应的计算成本也越高。重建结果如下,左侧为颅面点云模型,右侧为重建的mesh。


除了上述传统三维重建算法,基于深度学习的解决方案也已经越来越多,借助深度学习强大的拟合能力以及三维信息捕捉能力,这类算法可以处理缺失面积更大的模型,重建出更具真实感的物体,感兴趣的可以自行检索相关文献,以后有机会可以给大家分享相关文献。

 常用三维函数包的模型编辑功能
模型编辑包含的范围非常广,使用场景也都不太一样,这边主要介绍一些相对常用的功能。
(1)几何变换
主要包括三维物体的平移、旋转、缩放等,一般三维库都具备这些功能,这里仅介绍Open3D的用法。
仍然以刚刚的颅骨模型为例,Open3D的平移使用方法如下:
import open3d as o3dimport copymesh = o3d.io.read_triangle_mesh("skull.stl")mesh.compute_vertex_normals()mesh_tx = copy.deepcopy(mesh).translate((0, 200, 0))mesh_tx.compute_vertex_normals()o3d.visualization.draw_geometries([mesh, mesh_tx])

Open3D的旋转使用方法如下:
import open3d as o3dimport numpy as npimport copymesh = o3d.io.read_triangle_mesh("skull.stl")mesh.compute_vertex_normals()mesh_r = copy.deepcopy(mesh)# 绕x轴旋转 pi / 4R = mesh.get_rotation_matrix_from_xyz((np.pi / 4, 0, 0))# 旋转中心为 (0,0,0)mesh_r.rotate(R, center=(0,0,0))mesh_r.compute_vertex_normals()o3d.visualization.draw_geometries([mesh, mesh_r])

Open3D的缩放使用方法如下:
import open3d as o3dimport numpy as npimport copymesh = o3d.io.read_triangle_mesh("skull.stl")mesh.compute_vertex_normals()mesh_s = copy.deepcopy(mesh).translate((0, -200, 0))# 缩放系数为 0.5mesh_s.scale(0.5, center=mesh_s.get_center())mesh_s.compute_vertex_normals()o3d.visualization.draw_geometries([mesh, mesh_s])



(2)三角面片简化、细分
此处以Pyvista为例展示三角面片细分以及简化的功能,Open3D、Trimesh等其他函数库基本也支持这些功能。函数代码如下:
import pyvista as pvmesh = pv.read("skull_remesh.stl")mesh.plot(show_edges=True)# 细分 1 次mesh_s = mesh.subdivide(1, subfilter="loop")mesh_s.plot(show_edges=True)# 简化 目标大小为原始大小的 75%mesh_d = mesh.decimate(0.75)mesh_d.plot(show_edges=True)


(3)三角面片重新划分
本来是想找一个函数库演示一下remesh效果的,但我熟悉的几个函数库似乎都没remesh功能,这很不合理也可能是我没找到,如果有的话欢迎告知,所以这边就拿MeshLab软件演示一下好了。使用的是Isotropic Explicit Remesh算法。如图所示,可以让散乱的、质量较差的三角面片重新划分为接近正三角形的高质量网格,这步在有限元计算的网格划分前处理中是常用且重要的步骤,一般会先对面网格进行remesh再划分体网格。


(4)布尔运算
布尔运算包括,常用于两个模型的组合建模,此处以Pyvista为例说明布尔运算使用方法。trimesh也可以做布尔运算,但它需要额外安装blender或scad作为布尔运算的计算后端,比较麻烦,不是很建议使用这个。
假设有一个红色箭头和一个绿色球体,布尔并、减、交的Pyvista实现如下
import pyvista as pvmesh_1 = pv.Arrow().triangulate()mesh_2 = pv.Sphere().triangulate()pl = pv.Plotter()pl.set_background('w')_ = pl.add_mesh(mesh_1,color = 'r',opacity = 0.5)_ = pl.add_mesh(mesh_2,color = 'g',opacity = 0.5)pl.show()# 布尔并union = mesh_1.boolean_union(mesh_2)union.plot(color = 'w',background='w',opacity = 0.5)# 布尔减difference = mesh_1.boolean_difference(mesh_2)difference.plot(color = 'w',background='w')# 布尔交intersection = mesh_1.boolean_intersection(mesh_2)intersection.plot(color = 'w',background='w')
效果如下:

需要注意的是,此处创建球体和箭头模型使用的是Pyvista的内置函数,这个生成的并不是三角面片模型,而是通用的Polydata格式,可能是三角形、四边形等等。而Pyvista的布尔运算函数只支持三角化的模型,所以创建模型后需要用.triangulate()函数对其进行三角化。如果是外部读取的stl/obj/ply等模型或其他函数库转化过来的模型,一般都是三角化的模型,无需再转换。
另外,Pyvista的布尔运算有时候会出现莫名其妙的问题,比如出不了结果啥的,暂时还没有很好的解决办法。感觉这也是Python三维建模不如CAD软件的一个地方,不够稳定。

(5)表面光顺
表面光顺的使用场景也很多,有时候用三维扫描仪扫描出来的模型噪声比较严重,或者拓扑优化出来的模型有很强的阶梯纹理,都可以使用光顺算法对其表面进行平滑处理。
Pyvista、Open3D和Trimesh都内置了光顺功能,且光顺算法较多,比较经典的是拉普拉斯平滑(参考文献:Laplacian Mesh Processing),其他还有Taubin方法、Humphrey方法等等。此处以Pyvista的 smooth函数为例,使用的是拉普拉斯平滑算法。
import pyvista as pvmesh = pv.read("skull.stl")mesh.plot(background='w')mesh_smooth = mesh.smooth(relaxation_factor=0.05)mesh_smooth.plot(background='w')mesh_smooth = mesh.smooth(relaxation_factor=0.1)mesh_smooth.plot(background='w')mesh_smooth = mesh.smooth(relaxation_factor=0.2)mesh_smooth.plot(background='w')
这里主要展示relaxation_factor参数的作用,可以发现随着factor的增大,平滑效果越来越明显。但factor过大的话可能会导致丢失一些细节特征。

 常用三维函数包的几何修复功能
很多时候我们获取的模型并不是完好的,比如三维扫描仪扫出来的模型常常有很多破损,或者两个模型经过编辑之后可能会出现坏边、相交等错误,这些问题会影响模型的后续使用,因此几何修复是三维模型处理必须掌握的技能。
参考Magics软件修复功能中的定义,三维模型常见的几何错误类型包括坏边、缝隙、孔洞、干扰壳体、重叠三角面片以及相交三角面片等。
这边重点介绍Pymeshfix库,细心的小朋友可能会注意到,之前的几个章节一直没提到这个库。因为,看名字就知道了,这个库是专门为mesh修复而生的,并且这个库是依赖Pyvista的,所以在安装Pymeshfix之前需要先安装Pyvista
虽然Pymeshfix对上述几种几何错误都有针对性的修复算法,但大多数时候我们只需要用他的综合修复函数repair或者clean_from_arrays/clean_from_file就够了,可以自动修复上述错误类型。
仍然以之前的颅面模型为例,手动构造一些简单的孔洞缺陷,使用repair函数修复,代码如下:
import pyvista as pvimport pymeshfix as mfmesh = pv.read("skull_before_fix.stl")mesh.plot(background='w')# 封装为 MeshFix 对象meshfix = mf.MeshFix(mesh)meshfix.repair(verbose=True)repaired = meshfix.meshrepaired.plot(background='w')
以下是修复过程中的部分输出信息,可以看到,算法在自动进行补洞,修复退化和交叉三角面片并迭代循环,中间可以实时显示当前模型的顶点数和面片数。
INFO- Loaded 23604 vertices and 47034 faces.Patching holes...
100% done Patched 22 holesFixing degeneracies and intersectionsINFO- ********* ITERATION 0 *********INFO- Removing degeneracies...INFO- Removing self-intersections...
最终修复结果如下,可以看到修复效果还是不错的。不过这边只展示了孔洞的修复,其他错误我这边暂时没有合适的模型,大家可以自行尝试。

说明:Pymeshfix有时候没办法做到完美修复,毕竟几何错误的大小、形式、位置有较大的随机性,依靠确定性的算法未必可以做到100%修复。并且,当模型过大时,修复时间也比较长。
除Pymeshfix以外,有时候也可以尝试一下使用之前提的泊松重建,相对来说计算速度更快。基于深度学习的三维重建有时候也可以用来进行模型修复。

 总结
相信看完上面5000多字长文的你,应该对Python三维模型处理有了一些新的认识。当然,因为三维模型处理所包含的内容非常庞杂,相关算法非常多,这篇文章只能介绍一些皮毛,如果有需要的话建议系统学习一下Mesh处理算法原理,可以参考教材:Polygon Mesh Processing。
另外,如果要开发自主可控的CAD软件的话,不建议直接使用以上开源库,以上库仅可用来应付日常需求。自主CAD软件的研发需要坚实的技术积累,没有捷径可走。

参考链接:
Pyvistahttps://docs.pyvista.org/
pymeshfixhttps://pymeshfix.pyvista.org/
trimshhttps://trimsh.org/
open3dhttp://www.open3d.org/docs/release




 

点这里👇关注我,记得标星哦~



推荐阅读


 

CDA课程咨询

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/137391
 
1004 次点击