import open3d as o3d
import numpy as np
#点云可视化
pcd = o3d.io.read_point_cloud("fragment.ply")
print(pcd,type(pcd))
print(np.asarray(pcd.points))
o3d.visualization.draw_geometries([pcd],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024])
输出:
近照:
io.read_point_cloud 支持多种格式:
PCD
# .PCD v.7 - Point Cloud Data file format
VERSION .7
FIELDS x y z rgb
SIZE 4 4 4 4
TYPE F F F F
COUNT 1 1 1 1
WIDTH 213
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS 213
DATA ascii
0.93773 0.33763 0 4.2108e+06
0.90805 0.35641 0 4.2108e+06
0.81915 0.32 0 4.2108e+06
0.97192 0.278 0 4.2108e+06
0.944 0.29474 0 4.2108e+06
0.98111 0.24247 0 4.2108e+06
0.93655 0.26143 0 4.2108e+06
0.91631 0.27442 0 4.2108e+06
0.81921 0.29315 0 4.2108e+06
0.90701 0.24109 0 4.2108e+06
0.83239 0.23398 0 4.2108e+06
0.99185 0.2116 0 4.2108e+06
0.89264 0.21174 0 4.2108e+06
0.85082 0.21212 0 4.2108e+06
0.81044 0.32222 0 4.2108e+06
0.74459 0.32192 0 4.2108e+06
0.69927 0.32278 0 4.2108e+06
0.8102 0.29315 0 4.2108e+06
#略
详情:http://pointclouds.org/documentation/tutorials/pcd_file_format.html
PLY:
ply
format ascii 1.0
comment author: Greg Turk
comment object: another cube
element vertex 8
property float x
property float y
property float z
property uchar red { start of vertex color }
property uchar green
property uchar blue
element face 7
property list uchar int vertex_index { number of vertices for each face }
element edge 5 { five edges in object }
property int vertex1 { index to first vertex of edge }
property int vertex2 { index to second vertex }
property uchar red { start of edge color }
property uchar green
property uchar blue
end_header
0 0 0 255 0 0 { start of vertex list }
0 0 1 255 0 0
0 1 1 255 0 0
0 1 0 255 0 0
1 0 0 0 0 255
1 0 1 0 0 255
1 1 1 0 0 255
1 1 0 0 0 255
3 0 1 2 { start of face list, begin with a triangle }
3 0 2 3 { another triangle }
4 7 6 5 4 { now some quadrilaterals }
4 0 4 5 1
4 1 5 6 2
4 2 6 7 3
4 3 7 4 0
0 1 255 255 255 { start of edge list, begin with white edge }
1 2 255 255 255
2 3 255 255 255
3 0 255 255 255
2 0 0 0 0 { end with a single black line }
详情:
http://paulbourke.net/dataformats/ply/
TXT:
0.0000000000 0.0000000000 0.0000000000
1.0000000000 0.0000000000 0.0000000000
0.0000000000 1.0000000000 0.0000000000
0.0000000000 0.0000000000 1.0000000000
import open3d as o3d
import numpy as np
#降采样
pcd = o3d.io.read_point_cloud("fragment.ply")
print("Downsample the point cloud with a voxel of 0.05")
downpcd = pcd.voxel_down_sample(voxel_size=0.05). #使用0.05的体素对点云进行下采样
o3d.visualization.draw_geometries([downpcd],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024])
import open3d as o3d
import numpy as np
#降采样
pcd = o3d.io.read_point_cloud("fragment.ply")
print("Downsample the point cloud with a voxel of 0.05")
downpcd = pcd.voxel_down_sample(voxel_size=0.05)
print("Recompute the normal of the downsampled point cloud")
# estimate_normals 计算每个点的法线。该函数查找相邻点,并使用协方差分析计算相邻点的主轴。
'''
KDTreeSearchParamHybridclass的实例作为参数。这两个关键参数,并指定搜索近邻半径和最大。它的搜索半径为10厘米,最多可考虑30个邻居,以节省计算时间。
radius = 0.1. max_nn = 30
'''
downpcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))
print("Print a normal vector of the 0th point")
print(downpcd.normals[0]) #打印第0个点的法线向量
o3d.visualization.draw_geometries([downpcd],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024],
point_show_normal=True)
协方差分析算法产生两个相反的方向作为正常候选。在不知道几何的整体结构的情况下,两者都是正确的。这被称为法线取向问题。Open3D尝试将法线定向为与原始法线(如果存在)对齐。否则,Open3D会进行随机猜测。如果需要关注定向,则需要调用诸如orient_normals_to_align_with_direction和的其他定向函数orient_normals_towards_camera_location。
效果:
Print a normal vector of the 0th point
[-0.27566603 -0.89197839 -0.35830543]
import open3d as o3d
import numpy as np
print("Load a polygon volume and use it to crop the original point cloud")
#
vol = o3d.visualization.read_selection_polygon_volume(
"cropped.json")
pcd = o3d.io.read_point_cloud("fragment.ply")
chair = vol.crop_point_cloud(pcd)
o3d.visualization.draw_geometries([chair],
zoom=0.7,
front=[0.5439, -0.2333, -0.8060],
lookat=[2.4615, 2.1331, 1.338],
up=[-0.1781, -0.9708, 0.1608])
关于cropped.json
{
“axis_max” : 4.022921085357666, // 轴最大值
“axis_min” : -0.76341366767883301, //# 轴最小值
“bounding_polygon” : // 裁剪的多边形
[
[ 2.6509309513852526, 0.0, 1.6834473132326844 ],
[ 2.5786428246917148, 0.0, 1.6892074266735244 ],
[ 2.4625790337552154, 0.0, 1.6665777078297999 ],
[ 2.2228544982251655, 0.0, 1.6168160446813649 ],
[ 2.166993206001413, 0.0, 1.6115495157201662 ],
[ 2.1167895865303286, 0.0, 1.6257706054969348 ],
[ 2.0634657721747383, 0.0, 1.623021658624539 ],
[ 2.0568612343437236, 0.0, 1.5853892911207643 ],
[ 2.1605399001237027, 0.0, 0.96228993255083017 ],
[ 2.1956669387205228, 0.0, 0.95572746049785073 ],
[ 2.2191318790575583, 0.0, 0.88734449982108754 ],
[ 2.2484881847925919, 0.0, 0.87042807267013633 ],
[ 2.6891234157295827, 0.0, 0.94140677988967603 ],
[ 2.7328692490470647, 0.0, 0.98775740674840251 ],
[ 2.7129337547575547, 0.0, 1.0398850034649203 ],
[ 2.7592174072415405, 0.0, 1.0692940558509485 ],
[ 2.7689216419453428, 0.0, 1.0953914441371593 ],
[ 2.6851455625455669, 0.0, 1.6307334122162018 ],
[ 2.6714776099981239, 0.0, 1.675524657088997 ],
[ 2.6579576128816544, 0.0, 1.6819127849749496 ]
],
“class_name” : “SelectionPolygonVolume”, // 使用的类函数
“orthogonal_axis” : “Y”, // 正交轴
“version_major” : 1, //版本号
“version_minor” : 0
}
/*
CloudCompare 等软件可编辑
*/
import open3d as o3d
import numpy as np
# Load data
pcd = o3d.io.read_point_cloud("fragment.ply")
vol = o3d.visualization.read_selection_polygon_volume(
"cropped.json")
chair = vol.crop_point_cloud(pcd)
dists = pcd.compute_point_cloud_distance(chair) #它计算源点云中的每个点到目标点云中最近点的距离。
dists = np.asarray(dists)
#提取距离大于0.01米的点云
ind = np.where(dists > 0.01)[0] #[0]表示行索引 [1]表示列索引
'''
select_by_index(self, indices, invert=False)
Function to select points from input pointcloud into output pointcloud.
Parameters
indices (List[int]) – Indices of points to be selected.
invert (bool, optional, default=False) – Set to True to invert the selection of indices.
Returns
open3d.cpu.pybind.geometry.PointCloud
'''
pcd_without_chair = pcd.select_by_index(ind)
o3d.visualization.draw_geometries([pcd_without_chair],
zoom=0.3412,
front=[0.4257, -0.2125, -0.8795],
lookat=[2.6172, 2.0475, 1.532],
up=[-0.0694, -0.9768, 0.2024])
PointCloud与Open3D中的所有其他几何类型一样,几何类型具有边界体积。目前,Open3D实现了AxisAlignedBoundingBox和OrientedBoundingBox,也可以用于裁剪几何。
import open3d as o3d
import numpy as np
# Load data
pcd = o3d.io.read_point_cloud("fragment.ply")
vol = o3d.visualization.read_selection_polygon_volume(
"cropped.json")
chair = vol.crop_point_cloud(pcd)
'''
get_axis_aligned_bounding_box(self)
Returns an axis-aligned bounding box of the geometry.
Returns
open3d.geometry.AxisAlignedBoundingBox
'''
aabb = chair.get_axis_aligned_bounding_box() #返回几何的轴对齐边界框
aabb.color = (1, 0, 0)
obb = chair.get_oriented_bounding_box() #返回几何体的定向边界框
obb.color = (0, 1, 0)
o3d.visualization.draw_geometries([chair, aabb, obb],
zoom=0.7,
front=[0.5439, -0.2333, -0.8060],
lookat=[2.4615, 2.1331, 1.338],
up=[-0.1781, -0.9708, 0.1608])
点云的凸包是包含所有点的最小凸集。 Open3D包含方法compute_convex_hull,该方法计算点云的凸包。 该实现基于Qhull。
compute_convex_hull: 计算点云的凸包并返回三角网格
create_from_triangle_mesh: 从三角网格的边缘创建LineSet
import open3d as o3d
import numpy as np
# Load data
pcd = o3d.io.read_point_cloud("fragment.ply")
vol = o3d.visualization.read_selection_polygon_volume(
"cropped.json")
chair = vol.crop_point_cloud(pcd)
hull, _ = chair.compute_convex_hull()
hull_ls = o3d.geometry.LineSet.create_from_triangle_mesh(hull)
hull_ls.paint_uniform_color((1, 0, 0))
o3d.visualization.draw_geometries([chair, hull_ls])
import open3d as o3d
import numpy as np
import matplotlib.pyplot as plt
pcd = o3d.io.read_point_cloud("fragment.ply")
'''
给定来自例如深度传感器的点云,我们希望将局部点云群集分组在一起。为此,我们可以使用聚类算法。
Open3D实现了DBSCAN [Ester1996],它是一种基于密度的聚类算法。
该算法在中实现,cluster_dbscan并且需要两个参数:eps定义到聚类中的邻居的距离,并min_points定义形成聚类所需的最小点数。
该函数返回labels,其中标签-1指示噪音。
'''
with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug)\
as cm:labels = np.array(pcd.cluster_dbscan(eps=0.02, min_points=10, print_progress=True))
max_label = labels.max()
print(f"point cloud has {max_label + 1} clusters")
colors = plt.get_cmap("tab20")(labels / (max_label if max_label > 0 else 1))
colors[labels < 0] = 0
pcd.colors = o3d.utility.Vector3dVector(colors[:, :3])
o3d.visualization.draw_geometries([pcd],
zoom=0.455,
front=[-0.4999, -0.1659, -0.8499],
lookat=[2.1813, 2.0619, 2.0999],
up=[0.1204, -0.9852, 0.1215])
该算法定义以选中的点开始蔓延,邻居点距离<=0.02米,最小有10个点就可以构成一个簇;适用于原始点云分隔的比较开的,有明显界限的点云。
原始点云被分成了10个聚簇,每个聚簇不同的颜色,只有3个聚簇的点数比较巨大,明显一些。另外右边角落有一块比较小的片段。
Open3D还支持使用RANSAC从点云中分割几何图元。要找到在点云中具有最大支持的飞机,我们可以使用segment_plane。该方法具有三个参数:distance_threshold定义一个点到一个估计平面所能视为的直线距离的最大值,ransac_n定义一个点随机采样以估计一个平面的数量,以及num_iterations定义一个随机平面被采样和验证的频率。然后,该函数将平面返回为(,,,) 这样对于每个点 (,,) 在飞机上 +++=0。该函数还返回内部点的索引列表。
import open3d as o3d
import numpy as np
pcd = o3d.io.read_point_cloud("fragment.pcd")
plane_model, inliers = pcd.segment_plane(distance_threshold=0.01,
ransac_n=3,
num_iterations=1000)
[a, b, c, d] = plane_model
print(f"Plane equation: {a:.2f}x + {b:.2f}y + {c:.2f}z + {d:.2f} = 0")
inlier_cloud = pcd.select_by_index(inliers)
inlier_cloud.paint_uniform_color([1.0, 1.0, 0])
outlier_cloud = pcd.select_by_index(inliers, invert=True)
o3d.visualization.draw_geometries([inlier_cloud, outlier_cloud],
zoom=0.8,
front=[-0.4999, -0.1659, -0.8499],
lookat=[2.1813, 2.0619, 2.0999],
up=[0.1204, -0.9852, 0.1215])