因為最近做深度學習數據集需要處理大量三維模型,一開始都是用Magics手動處理,每個模型需要曲面偏置+布爾運算+幾何修復等等,一套連招下來要一兩分鐘,熟練後最快最快也要30秒,幾百組數據下來,花費的時間暫且不提,但長時間機械地處理數據是真的容易讓人產生不適。後來逐漸開始接觸一些Python的三維函數包,仿佛打開新世界,這才是批量處理三維模型的正確打開方式!所以特地寫下這篇文章來記錄這段時間處理三維模型的心得。由於Python的三維函數包很多,功能上有所交叉,面向對象也有所不同,所以在執行模型處理任務時可能需要同時使用多個函數包,這篇文章主要就我之前常用的幾個函數包的基本功能進行簡單整理,包含幾何建模、模型編輯、模型修復等,更詳細的用法可參考函數包官方文檔,鏈接放在最後。
可能有人會問,平時畫三維模型,比如變速器、減速器箱體等,使用Solidworks、CATIA、UG或者國產的雲圖三維這類商業CAD軟件就夠了,為什麼會想到用Python做三維建模?商業CAD軟件確實好用,功能強大,像達索、西門子這些老牌公司,經過這麼多年的技術積累與業務擴張,旗下軟件基本可以應付工業場景的各種三維建模需求(有時也會綜合使用各類專用軟件,比如針對某類零件的設計軟件)。但很多非相關專業的同學可能也會有CAD軟件的使用需求,比如給自己的論文畫個三維示意圖,做一些三維數據可視化,或者完成一些簡單的參數化建模,這些情況下特地去安裝一個龐大的CAD軟件,就好像為了擰一個螺絲而買下一整套昂貴的螺絲刀套裝,並不是很划算。另外,很多通用三維軟件可能並不支持數據的批量處理,如果有那也基本需要進行二次開發,如果像我一樣,需要處理大量三維模型的話,使用商業CAD軟件可能會比較費時費力。在上述場景下,使用Python三維函數包可能更加合適。一方面,開源免費光環的加持可以大大降低安裝成本,且一般對硬件要求不高。另一方面,由於整體處於編程環境,使用者可以按照自己的設想,以更加自由的方式完成三維建模或者模型處理。當然,自由的代價是學習成本的提升,你需要做到充分熟悉函數庫的語法,否則還是不如很多商軟的傻瓜式交互操作簡單。
(CAD軟件 vsPython三維函數包)
如果能合理使用Python提供的三維函數包的話,就能以快捷高效的方式完成一些輕量級的建模,從而大大提升科研效率。但目前很多函數包的官方文檔對使用者並不是特別友好,函數功能介紹僅局限於參數含義說明,不會說明這個函數的適用範圍以及與其他函數的區別等,純靠自己摸索的話還是比較費勁的。所以這篇文章就我之前使用過的幾個Python三維函數包的基本用法做簡單介紹,可以減少自己查官方文檔的時間,函數包主要包括Open3D、Pyvista、Pymeshfix、VTK以及Trimesh。當然Python的三維函數包遠不止這些,以後有機會接觸到的話可以再跟大家分享,如果有推薦的三維庫也可以後台私信我哦。(相關Python環境的配置以及三維函數庫的安裝這邊就不介紹了,網上可以查到,都比較簡單)
立方體、圓柱體、三稜錐等基本幾何體的繪製,比較簡單,不做過多介紹,直接上一些案例。更多幾何體的函數可從官網查詢。# 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軟件直觀,對空間想象能力有一定要求,不適合複雜三維物體建模。當然如果你有足夠耐心的話也可以嘗試一下用這些基本幾何體去組合生成一個複雜的模型。(大佬都已經可以在我的世界裡堆芯片、搭神經網絡、做計算機了,還有什麼是做不到的?)
如果手上有一個體素類模型,比如拓撲優化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。
除了上述傳統三維重建算法,基於深度學習的解決方案也已經越來越多,藉助深度學習強大的擬合能力以及三維信息捕捉能力,這類算法可以處理缺失面積更大的模型,重建出更具真實感的物體,感興趣的可以自行檢索相關文獻,以後有機會可以給大家分享相關文獻。
模型編輯包含的範圍非常廣,使用場景也都不太一樣,這邊主要介紹一些相對常用的功能。主要包括三維物體的平移、旋轉、縮放等,一般三維庫都具備這些功能,這裡僅介紹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])
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])
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])
此處以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)

本來是想找一個函數庫演示一下remesh效果的,但我熟悉的幾個函數庫似乎都沒remesh功能,這很不合理
也可能是我沒找到,如果有的話歡迎告知,所以這邊就拿MeshLab軟件演示一下好了。使用的是Isotropic Explicit Remesh算法。如圖所示,可以讓散亂的、質量較差的三角面片重新劃分為接近正三角形的高質量網格,這步在有限元計算的網格劃分前處理中是常用且重要的步驟,一般會先對面網格進行remesh再劃分體網格。
布爾運算包括交、並和減,常用於兩個模型的組合建模,此處以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軟件的一個地方,不夠穩定。
表面光順的使用場景也很多,有時候用三維掃描儀掃描出來的模型噪聲比較嚴重,或者拓撲優化出來的模型有很強的階梯紋理,都可以使用光順算法對其表面進行平滑處理。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軟件的研發需要堅實的技術積累,沒有捷徑可走。
Pyvista:https://docs.pyvista.org/pymeshfix:https://pymeshfix.pyvista.org/trimsh:https://trimsh.org/open3d:http://www.open3d.org/docs/release
鑽石舞台 發表在 痞客邦 留言(0) 人氣()