Source code for gunz_cm.preprocs.points.downsample
"""Downsampling operations for 3D point clouds."""
__author__ = "Yeremia Gunawan Adhisantoso"
__license__ = "Clear BSD"
__email__ = "adhisant@tnt.uni-hannover.de"
from pydantic import validate_call, ConfigDict
import numpy as np
import pandas as pd
from ...utils.validate import require_positive_int
[docs]@validate_call(config=ConfigDict(arbitrary_types_allowed=True))
def downsample_points(
points: np.ndarray,
ds_ratio: int,
def_coor: float = np.nan,
) -> np.ndarray:
"""Downsample the given points by a specified ratio.
Notes
-----
- The function ensures that the `ds_ratio` is greater than 1.
- Points with all NaN values are ignored during downsampling.
- The resulting array is filled with `def_coor` for indices without valid points.
Parameters
----------
points : np.ndarray
The array of points to be downsampled.
ds_ratio : int
The downsampling ratio. Must be greater than 1.
def_coor : float, optional
The default coordinate value for indices without valid points, by default np.nan.
Returns
-------
np.ndarray
The downsampled points array.
"""
require_positive_int("ds_ratio", ds_ratio, min_value=2)
num_points = points.shape[0]
valid_points_mask = ~np.isnan(points).all(axis=1)
valid_points_ids = np.arange(num_points)[valid_points_mask]
points_df = pd.DataFrame({
'ids': valid_points_ids,
'x': points[valid_points_mask, 0],
'y': points[valid_points_mask, 1],
'z': points[valid_points_mask, 2],
})
points_df['lr_ids'] = points_df['ids'] // ds_ratio
lr_points_df = (
points_df[['lr_ids', 'x', 'y', 'z']]
.groupby('lr_ids')
.mean()
)
num_lr_points = int(np.ceil(num_points/ds_ratio))
valid_lr_ids = np.array(lr_points_df.index)
lr_points = np.full((num_lr_points, 3), def_coor)
lr_points[valid_lr_ids] = lr_points_df.to_numpy()
return lr_points