Open3d学习计划—高级篇 6(体素化)
Open3D是一個開源庫,支持快速開發和處理3D數據。Open3D在c++和Python中公開了一組精心選擇的數據結構和算法。后端是高度優化的,并且是為并行化而設置的。
本系列學習計劃有Blue同學作為發起人,主要以Open3D官方網站的教程為主進行翻譯與實踐的學習計劃。點云PCL公眾號作為免費的3D視覺,點云交流社區,期待有使用Open3D或者感興趣的小伙伴能夠加入我們的翻譯計劃,貢獻免費交流社區,為使用Open3D提供中文的使用教程。
點云和三角網格是一種十分靈活的,但是不規則的幾何類型。體素網格是通過規則的3D網格來表示的另一種3D幾何類型,并且它可以看作是2D像素在3D上的對照物。Open3d中的VoxelGrid幾何類型能夠被用來處理體素網格數據。
從三角網格中生成
Open3d提供了create_from_triangle_mesh函數能夠從三角網格中生成體素網格。它返回一個體素網格,其中所有與三角形相交的網格被設置為1,其余的設置為0。其中voxel_zie參數是用來設置網格分辨率。
print('input')
mesh = o3dtut.get_bunny_mesh()
# fit to unit cube
mesh.scale(1 / np.max(mesh.get_max_bound() - mesh.get_min_bound()), center=mesh.get_center())
o3d.visualization.draw_geometries([mesh])
print('voxelization')
voxel_grid = o3d.geometry.VoxelGrid.create_from_triangle_mesh(mesh,voxel_size=0.05)
o3d.visualization.draw_geometries([voxel_grid])
input
voxelization
從點云中生成
也能夠使用create_from_point_cloud函數從點云中生成體素網格。如果點云中至少有一個點在體素網格內,則該網格被占用。顏色表示的是該體素中點的平均值。參數voxel_size用來定義網格分辨率。
print('input')
N = 2000
pcd = o3dtut.get_armadillo_mesh().sample_points_poisson_disk(N)
# fit to unit cube
pcd.scale(1 / np.max(pcd.get_max_bound() - pcd.get_min_bound()), center=pcd.get_center())
pcd.colors = o3d.utility.Vector3dVector(np.random.uniform(0,1,size=(N,3)))
o3d.visualization.draw_geometries([pcd])print('voxelization')
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd,voxel_size=0.05)
o3d.visualization.draw_geometries([voxel_grid])
input
voxelization
包含測試
體素網格也能夠用來測試點是否在被占用的網格內。方法check_if_included接受一個(n,3)數組作為輸入,返回一個bool類型的數組。
queries = np.asarray(pcd.points)
output = voxel_grid.check_if_included(o3d.utility.Vector3dVector(queries))
print(output[:10])
體素雕刻
create_from_point_cloud和create_from_triangle_mesh方法只能夠在幾何體的表面創造體素網格。然而從大量的深度圖或者輪廓中雕刻一個體素網格是有可能的。Open3d提供了carve_depth_map和 carve_silhouette方法用于體素雕刻。
下面的代碼展示了使用方法,首先從一個幾何形狀中得到 depthmaps ,之后使用 depthmaps 去雕刻出稠密的體素網格。最后的結果是一個給定形狀的填充的體素網格。
def xyz_spherical(xyz):x = xyz[0]y = xyz[1]z = xyz[2]r = np.sqrt(x * x + y * y + z * z)r_x = np.arccos(y / r)r_y = np.arctan2(z, x)return [r, r_x, r_y]def get_rotation_matrix(r_x, r_y):rot_x = np.asarray([[1, 0, 0], [0, np.cos(r_x), -np.sin(r_x)],[0, np.sin(r_x), np.cos(r_x)]])rot_y = np.asarray([[np.cos(r_y), 0, np.sin(r_y)], [0, 1, 0],[-np.sin(r_y), 0, np.cos(r_y)]])return rot_y.dot(rot_x)def get_extrinsic(xyz):rvec = xyz_spherical(xyz)r = get_rotation_matrix(rvec[1], rvec[2])t = np.asarray([0, 0, 2]).transpose()trans = np.eye(4)trans[:3, :3] = rtrans[:3, 3] = treturn transdef preprocess(model):min_bound = model.get_min_bound()max_bound = model.get_max_bound()center = min_bound + (max_bound - min_bound) / 2.0scale = np.linalg.norm(max_bound - min_bound) / 2.0vertices = np.asarray(model.vertices)vertices -= centermodel.vertices = o3d.utility.Vector3dVector(vertices / scale)return modeldef voxel_carving(mesh,output_filename,camera_path,cubic_size,voxel_resolution,w=300,h=300,use_depth=True,surface_method='pointcloud'):mesh.compute_vertex_normals()camera_sphere = o3d.io.read_triangle_mesh(camera_path)# setup dense voxel gridvoxel_carving = o3d.geometry.VoxelGrid.create_dense(width=cubic_size,height=cubic_size,depth=cubic_size,voxel_size=cubic_size / voxel_resolution,origin=[-cubic_size / 2.0, -cubic_size / 2.0, -cubic_size / 2.0])# rescale geometrycamera_sphere = preprocess(camera_sphere)mesh = preprocess(mesh)# setup visualizer to render depthmapsvis = o3d.visualization.Visualizer()vis.create_window(width=w, height=h, visible=False)vis.add_geometry(mesh)vis.get_render_option().mesh_show_back_face = Truectr = vis.get_view_control()param = ctr.convert_to_pinhole_camera_parameters()# carve voxel gridpcd_agg = o3d.geometry.PointCloud()centers_pts = np.zeros((len(camera_sphere.vertices), 3))for cid, xyz in enumerate(camera_sphere.vertices):# get new camera posetrans = get_extrinsic(xyz)param.extrinsic = transc = np.linalg.inv(trans).dot(np.asarray([0, 0, 0, 1]).transpose())centers_pts[cid, :] = c[:3]ctr.convert_from_pinhole_camera_parameters(param)# capture depth image and make a point cloudvis.poll_events()vis.update_renderer()depth = vis.capture_depth_float_buffer(False)pcd_agg += o3d.geometry.PointCloud.create_from_depth_image(o3d.geometry.Image(depth),param.intrinsic,param.extrinsic,depth_scale=1)# depth map carving methodif use_depth:voxel_carving.carve_depth_map(o3d.geometry.Image(depth), param)else:voxel_carving.carve_silhouette(o3d.geometry.Image(depth), param)print("Carve view %03d/%03d" % (cid + 1, len(camera_sphere.vertices)))vis.destroy_window()# add voxel grid survaceprint('Surface voxel grid from %s' % surface_method)if surface_method == 'pointcloud':voxel_surface = o3d.geometry.VoxelGrid.create_from_point_cloud_within_bounds(pcd_agg,voxel_size=cubic_size / voxel_resolution,min_bound=(-cubic_size / 2, -cubic_size / 2, -cubic_size / 2),max_bound=(cubic_size / 2, cubic_size / 2, cubic_size / 2))elif surface_method == 'mesh':voxel_surface = o3d.geometry.VoxelGrid.create_from_triangle_mesh_within_bounds(mesh,voxel_size=cubic_size / voxel_resolution,min_bound=(-cubic_size / 2, -cubic_size / 2, -cubic_size / 2),max_bound=(cubic_size / 2, cubic_size / 2, cubic_size / 2))else:raise Exception('invalid surface method')voxel_carving_surface = voxel_surface + voxel_carvingreturn voxel_carving_surface, voxel_carving, voxel_surface
mesh = o3dtut.get_armadillo_mesh()output_filename = os.path.abspath("../../TestData/voxelized.ply")
camera_path = os.path.abspath("../../TestData/sphere.ply")
visualization = True
cubic_size = 2.0
voxel_resolution = 128.0voxel_grid, voxel_carving, voxel_surface = voxel_carving(mesh, output_filename, camera_path,cubic_size, voxel_resolution)
Carve view 001/642
Carve view 002/642
Carve view 003/642
Carve view 004/642
…
Carve view 642/642
Surface voxel grid from pointcloud
print("surface voxels")
print(voxel_surface)
o3d.visualization.draw_geometries([voxel_surface])print("carved voxels")
print(voxel_carving)
o3d.visualization.draw_geometries([voxel_carving])print("combined voxels (carved + surface)")
print(voxel_grid)
o3d.visualization.draw_geometries([voxel_grid])
surface voxels
geometry::VoxelGrid with 17215 voxels.
carved voxels
geometry::VoxelGrid with 48370 voxels.
combined voxels (carved + surface)
geometry::VoxelGrid with 50786 voxels.
資源
三維點云論文及相關應用分享
【點云論文速讀】基于激光雷達的里程計及3D點云地圖中的定位方法
3D目標檢測:MV3D-Net
三維點云分割綜述(上)
3D-MiniNet: 從點云中學習2D表示以實現快速有效的3D LIDAR語義分割(2020)
win下使用QT添加VTK插件實現點云可視化GUI
JSNet:3D點云的聯合實例和語義分割
大場景三維點云的語義分割綜述
PCL中outofcore模塊---基于核外八叉樹的大規模點云的顯示
基于局部凹凸性進行目標分割
基于三維卷積神經網絡的點云標記
點云的超體素(SuperVoxel)
基于超點圖的大規模點云分割
更多文章可查看:點云學習歷史文章大匯總
SLAM及AR相關分享
【開源方案共享】ORB-SLAM3開源啦!
【論文速讀】AVP-SLAM:自動泊車系統中的語義SLAM
【點云論文速讀】StructSLAM:結構化線特征SLAM
SLAM和AR綜述
常用的3D深度相機
AR設備單目視覺慣導SLAM算法綜述與評價
SLAM綜述(4)激光與視覺融合SLAM
Kimera實時重建的語義SLAM系統
SLAM綜述(3)-視覺與慣導,視覺與深度學習SLAM
易擴展的SLAM框架-OpenVSLAM
高翔:非結構化道路激光SLAM中的挑戰
SLAM綜述之Lidar SLAM
基于魚眼相機的SLAM方法介紹
往期線上分享錄播匯總
第一期B站錄播之三維模型檢索技術
第二期B站錄播之深度學習在3D場景中的應用
第三期B站錄播之CMake進階學習
第四期B站錄播之點云物體及六自由度姿態估計
第五期B站錄播之點云深度學習語義分割拓展
第六期B站錄播之Pointnetlk解讀
[線上分享錄播]點云配準概述及其在激光SLAM中的應用
[線上分享錄播]cloudcompare插件開發
[線上分享錄播]基于點云數據的?Mesh重建與處理
[線上分享錄播]機器人力反饋遙操作技術及機器人視覺分享
[線上分享錄播]地面點云配準與機載點云航帶平差
點云PCL更多活動請查看:點云PCL活動之應屆生校招群
掃描下方微信視頻號二維碼可查看最新研究成果及相關開源方案的演示:
如果你對Open3D感興趣,或者正在使用該開源方案,就請加入我們,一起翻譯,一起學習,貢獻自己的力量,目前階段主要以微信群為主,有意者發送“Open3D學習計劃”到公眾號后臺,和更多熱愛分享的小伙伴一起交流吧!如果翻譯的有什么問題或者您有更好的意見,請評論交流!!!!
以上內容如有錯誤請留言評論,歡迎指正交流。如有侵權,請聯系刪除
掃描二維碼
? ? ? ? ? ? ? ? ? ?關注我們
讓我們一起分享一起學習吧!期待有想法,樂于分享的小伙伴加入免費星球注入愛分享的新鮮活力。分享的主題包含但不限于三維視覺,點云,高精地圖,自動駕駛,以及機器人等相關的領域。
分享及合作:微信“920177957”(需要按要求備注) 聯系郵箱:dianyunpcl@163.com,歡迎企業來聯系公眾號展開合作。
點一下“在看”你會更好看耶
總結
以上是生活随笔為你收集整理的Open3d学习计划—高级篇 6(体素化)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CamVox:一种低成本、高精度的激光雷
- 下一篇: 快速精确的体素GICP三维点云配准算法