easyidp.pointcloud.PointCloud¶
- class easyidp.pointcloud.PointCloud(pcd_path='', offset=[0.0, 0.0, 0.0])¶
EasyIDP defined PointCloud class, consists by point coordinates, and optionally point colors and point normals.
Attributes
The color (RGB) values of point cloud
The Coordinate Reference System (CRS) of point cloud
the file extension to the current point cloud file
the file path to the current point cloud file
The normal vector values of point cloud
The offset value of point cloud
The xyz values of point cloud
The size of point cloud (xyz)
The 2D KDTree of point cloud for fast spatial query
Methods
change_crs(target_crs)Change the point cloud coordinate system in place.
clear()Delete all points and make an empty point cloud.
crop(geometry)Crop point cloud by a 2D geometry.
Returns True if the point cloud contains point colors.
Returns True if the point cloud contains point normals.
Returns True if the point cloud contains points.
save(pcd_path)Save current point cloud to a file, support ply, las, laz format.
select_by_index(indices[, invert])Select points by index and return a new PointCloud.
to_crs(target_crs)Convert point cloud to a new CRS, returning a new PointCloud.
update_offset_value(off_val)Change the offset value without affecting the xyz point values.
- __init__(pcd_path='', offset=[0.0, 0.0, 0.0])¶
The method to initialize the PointCloud class
- Parameters:
pcd_path (str, optional) – The point cloud file path for loading/reading, by default “”, means create an empty point cloud class
offset (list, optional) –
This parameter is used to specify your own offsets rather than the automatically calculated one.
Note
When the point cloud xyz value is too large, need to deduct duplicate values (minus offsets) to save the memory cost and increase the precision.
Caution
For some Pix4D produced pointcloud, the point cloud itself has been offseted, need manually add the offset value back.
Example
Prepare
Cancel the numpy scientific counting method display:
>>> import numpy as np >>> np.set_printoptions(suppress=True)
Package loading:
>>> import easyidp as idp >>> test_data = idp.data.TestData()
Read large xyz point cloud
Most point cloud use the CRS (GPS) coordianate as xyz values directly.
>>> pcd = idp.PointCloud(test_data.pcd.maize_las) >>> pcd.points array([[ 367993.0206, 3955865.095 , 57.9707], [ 367993.146 , 3955865.3131, 57.9703], [ 367992.6317, 3955867.2979, 57.9822], ..., [ 368014.7912, 3955879.4943, 58.0219], [ 368014.1528, 3955883.5785, 58.0321], [ 368016.7278, 3955874.1188, 57.9668]])
If store these values directly, will cost a lot of memeory with precision loss. But with offsets, the data can be stored more neatly in the EasyIDP:
>>> pcd.offset array([ 367900., 3955800., 0.]) >>> pcd._points array([[ 93.0206, 65.095 , 57.9707], [ 93.146 , 65.3131, 57.9703], [ 92.6317, 67.2979, 57.9822], ..., [114.7912, 79.4943, 58.0219], [114.1528, 83.5785, 58.0321], [116.7278, 74.1188, 57.9668]])
Manually specify offset
The previous offset is calculated automatically by EasyIDP, you can also manually specify the offset values:
>>> pcd = idp.PointCloud(test_data.pcd.maize_las, offset=[367800, 3955700, 50]) >>> pcd.offset array([ 367800., 3955700., 50.]) >>> pcd._points array([[193.0206, 165.095 , 7.9707], [193.146 , 165.3131, 7.9703], [192.6317, 167.2979, 7.9822], ..., [214.7912, 179.4943, 8.0219], [214.1528, 183.5785, 8.0321], [216.7278, 174.1188, 7.9668]])
Though the inner stored values changed, it does not affect the final point valus:
>>> pcd.points array([[ 367993.0206, 3955865.095 , 57.9707], [ 367993.146 , 3955865.3131, 57.9703], [ 367992.6317, 3955867.2979, 57.9822], ..., [ 368014.7912, 3955879.4943, 58.0219], [ 368014.1528, 3955883.5785, 58.0321], [ 368016.7278, 3955874.1188, 57.9668]])
Read Pix4D offseted point cloud and add offset back
If you read the Pix4D produced point cloud directly:
>>> pcd = idp.PointCloud(test_data.pcd.lotus_ply_bin) >>> pcd x y z r g b nx ny nz 0 -18.908 -15.778 -0.779 123 103 79 nodata nodata nodata 1 -18.908 -15.777 -0.78 124 104 81 nodata nodata nodata 2 -18.907 -15.775 -0.802 123 103 80 nodata nodata nodata ... ... ... ... ... ... ... ... ... ... 42451 -15.789 -17.961 -0.847 116 98 80 nodata nodata nodata 42452 -15.789 -17.939 -0.84 113 95 76 nodata nodata nodata 42453 -15.786 -17.937 -0.833 115 97 78 nodata nodata nodata
Here the xyz seems not the correct one, when we check the Pix4D project
{name}_offset.xyzfile in the param folders, we can find the offset values stored by Pix4D.>>> with open(test_data.pix4d.lotus_param / "hasu_tanashi_20170525_Ins1RGB_30m_offset.xyz", 'r') as f: ... f.readlines() ['368043.000 3955495.000 98.000']
This often requires user manually add that offset back to point cloud. But EasyIDP supports dealing with such situation easily:
>>> p4d_offset_np = np.array([368043, 3955495, 98]) >>> pcd = idp.PointCloud(test_data.pcd.lotus_ply_bin, p4d_offset_np) >>> pcd x y z r g b nx ny nz 0 368024.092 3955479.222 97.221 123 103 79 nodata nodata nodata 1 368024.092 3955479.223 97.22 124 104 81 nodata nodata nodata 2 368024.093 3955479.225 97.198 123 103 80 nodata nodata nodata ... ... ... ... ... ... ... ... ... ... 42451 368027.211 3955477.039 97.153 116 98 80 nodata nodata nodata 42452 368027.211 3955477.061 97.16 113 95 76 nodata nodata nodata 42453 368027.214 3955477.063 97.167 115 97 78 nodata nodata nodata
Note
You can also obtain the
p4d_offset_npbyeasyidp.Pix4Dobject:>>> p4d = idp.Pix4D(project_path = test_data.pix4d.lotus_folder, ... raw_img_folder = test_data.pix4d.lotus_photos, ... param_folder = test_data.pix4d.lotus_param)) >>> p4d.offset_np array([ 368043., 3955495., 98.])
And feed it to the previous function:
>>> pcd = idp.PointCloud(test_data.pcd.lotus_ply_bin, p4d.offset_np)
- change_crs(target_crs)¶
Change the point cloud coordinate system in place.
Transforms absolute coordinates and automatically recomputes the offset for the new coordinate space. Colors and normals are preserved. Normals are kept without rotation (orientation is relative to the surface plane and stays valid after rigid-body CRS transform). The spatial tree is cleared.
- Parameters:
target_crs (str or pyproj.CRS) – Target coordinate reference system.
- Returns:
Modifies the PointCloud in place.
- Return type:
None
- Raises:
TypeError – If
self.crsis None.
Notes
If the source and target CRS are the same, a warning is logged and no transformation is performed.
Examples
>>> pcd = idp.PointCloud(test_data.pcd.maize_las) >>> pcd.crs = "EPSG:32654" >>> pcd.change_crs("EPSG:4326") >>> pcd.crs.to_epsg() 4326
- clear()¶
Delete all points and make an empty point cloud.
- Returns:
Clears point, color, normal, CRS, offset, and display state.
- Return type:
None
Examples
>>> pcd = idp.PointCloud(test_data.pcd.lotus_ply_bin) >>> pcd.clear() >>> pcd.has_points() False
- colors¶
The color (RGB) values of point cloud
- crop(geometry)¶
Crop point cloud by a 2D geometry.
- Parameters:
geometry (shapely.geometry.Polygon, shapely.geometry.MultiPolygon, or easyidp.ROI) – The 2D crop geometry. -
Polygon/MultiPolygon: return a single PointCloud. -ROI: return a dict of{label: PointCloud}.- Returns:
PointCloud or dict of {str – Cropped point cloud(s).
- Return type:
PointCloud}
- Raises:
TypeError – If
geometryis an unsupported type, with migration guidance.
Examples
Crop by a Shapely polygon:
>>> from shapely.geometry import Polygon >>> pcd = idp.PointCloud(test_data.pcd.lotus_ply_bin) >>> polygon = Polygon([ ... [-18.5, -18.1], [-15.7, -18.1], ... [-15.7, -15.6], [-18.5, -15.6], ... ]) >>> cropped = pcd.crop(polygon) >>> isinstance(cropped, idp.PointCloud) True
Crop by an ROI and keep ROI labels:
>>> roi = idp.ROI(test_data.shp.lotus_shp, name_field=0) >>> crops = pcd.crop(roi) >>> isinstance(crops, dict) True
- crs¶
The Coordinate Reference System (CRS) of point cloud
- file_ext¶
the file extension to the current point cloud file
- file_path¶
the file path to the current point cloud file
- has_colors()¶
Returns True if the point cloud contains point colors.
- Return type:
bool
Examples
>>> pcd = idp.PointCloud(test_data.pcd.lotus_ply_bin) >>> pcd.has_colors() True
- has_normals()¶
Returns True if the point cloud contains point normals.
- Return type:
bool
Examples
>>> pcd = idp.PointCloud() >>> pcd.has_normals() False
- has_points()¶
Returns True if the point cloud contains points.
- Return type:
bool
Examples
>>> pcd = idp.PointCloud() >>> pcd.has_points() False
- normals¶
The normal vector values of point cloud
- offset¶
The offset value of point cloud
Caution
If change this value directly, the xyz value of point cloud will also be changed, just like moving the whole point cloud.
Example
For example, the point cloud like:
>>> import easyidp as idp >>> test_data = idp.data.TestData() >>> pts = idp.PointCloud(test_data.pcd.maize_las) >>> pts x y z r g b 0 367993.021 3955865.095 57.971 28 21 17 1 367993.146 3955865.313 57.97 28 23 19 2 367992.632 3955867.298 57.982 29 22 18 ... ... ... ... ... ... ... 49655 368014.791 3955879.494 58.022 33 28 25 49656 368014.153 3955883.578 58.032 30 40 26 49657 368016.728 3955874.119 57.967 25 20 18 >>> pts.offset array([ 367900., 3955800., 0.])
Change the offset directly:
>>> pts.offset = [300, 200, 50] >>> pts x y z r g b 0 393.021 265.095 107.971 28 21 17 1 393.146 265.313 107.97 28 23 19 2 392.632 267.298 107.982 29 22 18 ... ... ... ... ... ... ... 49655 414.791 279.494 108.022 33 28 25 49656 414.153 283.578 108.032 30 40 26 49657 416.728 274.119 107.967 25 20 18
Caution
If you want to change the offset without affecting the point xyz values, please use
update_offset_value()
- points¶
The xyz values of point cloud
- save(pcd_path)¶
Save current point cloud to a file, support ply, las, laz format.
- Parameters:
pcd_path (str) – The file path of saved point cloud, if file extention not given, will use parent point cloud file extention.
See also
Examples
>>> pcd = idp.PointCloud(test_data.pcd.lotus_ply_bin) >>> pcd.save("lotus_crop.ply")
- select_by_index(indices, invert=False)¶
Select points by index and return a new PointCloud.
- Parameters:
indices (ndarray of int) – Indices of points to select.
invert (bool, optional) – If True, select all points NOT in
indices. Default False.
- Returns:
A new PointCloud containing only the selected points. Empty selection produces an empty PointCloud with CRS and offset preserved.
- Return type:
Examples
>>> pcd = idp.PointCloud(test_data.pcd.lotus_ply_bin) >>> sub = pcd.select_by_index(np.array([0, 5, 10])) >>> sub.shape (3, 3)
- shape¶
The size of point cloud (xyz)
- to_crs(target_crs)¶
Convert point cloud to a new CRS, returning a new PointCloud.
- Parameters:
target_crs (str or pyproj.CRS) – Target coordinate reference system.
- Returns:
A new PointCloud with coordinates transformed to the target CRS. Colors and normals are preserved. Normals are copied without rotation (orientation is relative to the surface plane and stays valid after rigid-body CRS transform).
- Return type:
- Raises:
TypeError – If
self.crsis None.
Examples
>>> pcd.crs = "EPSG:32654" >>> pcd_wgs84 = pcd.to_crs("EPSG:4326")
- tree¶
The 2D KDTree of point cloud for fast spatial query
- update_offset_value(off_val)¶
Change the offset value without affecting the xyz point values.
- Parameters:
off_val (list | tuple | ndarray) – The offset values want to set
Example
For example, the point cloud like:
>>> import easyidp as idp >>> test_data = idp.data.TestData() >>> pts = idp.PointCloud(test_data.pcd.maize_las) >>> pts x y z r g b 0 367993.021 3955865.095 57.971 28 21 17 1 367993.146 3955865.313 57.97 28 23 19 2 367992.632 3955867.298 57.982 29 22 18 ... ... ... ... ... ... ... 49655 368014.791 3955879.494 58.022 33 28 25 49656 368014.153 3955883.578 58.032 30 40 26 49657 368016.728 3955874.119 57.967 25 20 18 >>> pts.offset array([ 367900., 3955800., 0.])
Change the offset without affecting the xyz values:
>>> pts.update_offset_value([360000, 3955000, 50]) >>> pts.offset array([ 360000., 3955000., 50.]) >>> pts.points x y z r g b nx ny nz 0 367993.021 3955865.095 57.971 28 21 17 -0.031496062992125984 0.36220472440944884 0.9291338582677166 1 367993.146 3955865.313 57.97 28 23 19 0.08661417322834646 0.07086614173228346 0.9921259842519685 2 367992.632 3955867.298 57.982 29 22 18 -0.007874015748031496 0.26771653543307083 0.9606299212598425 ... ... ... ... ... ... ... ... ... ... 49655 368014.791 3955879.494 58.022 33 28 25 0.44881889763779526 -0.14960629921259844 0.8740157480314961 49656 368014.153 3955883.578 58.032 30 40 26 0.44881889763779526 -0.29133858267716534 0.8346456692913385 49657 368016.728 3955874.119 57.967 25 20 18 0.3228346456692913 0.26771653543307083 0.8976377952755905
Caution
If you want to change the offset like moving point cloud (also change the xyz values), please use
offset()