modality_forcing / flux_rgbd /pointcloud.py
bartduis's picture
Initial public release
e298226
Raw
History Blame Contribute Delete
2.35 kB
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 World Labs.
"""Point-cloud post-processing utilities."""
from __future__ import annotations
import numpy as np
def statistical_outlier_mask(points: np.ndarray, *, num_neighbors: int = 5,
std_ratio: float = 1.0) -> np.ndarray:
"""Boolean inlier mask via statistical outlier rejection.
For each point, the mean distance to its ``num_neighbors`` nearest
neighbours is computed; a point is an outlier when that mean exceeds
``global_mean + std_ratio * global_std``. This removes the flying-pixel
floaters that back-projected depth produces at depth discontinuities.
Mirrors Open3D's ``remove_statistical_outlier``.
Args:
points: ``(N, 3)`` float array.
num_neighbors: neighbours used to estimate each point's local spacing.
std_ratio: smaller is more aggressive (keeps fewer points).
Returns:
``(N,)`` boolean mask, ``True`` for inliers.
"""
n = len(points)
if n <= num_neighbors:
return np.ones(n, dtype=bool)
# Lazy import: only point-cloud export needs scipy.
from scipy.spatial import cKDTree
tree = cKDTree(points)
# k + 1: the nearest neighbour is the point itself (distance 0); drop it.
dists, _ = tree.query(points, k=num_neighbors + 1, workers=-1)
mean_dist = dists[:, 1:].mean(axis=1)
threshold = mean_dist.mean() + std_ratio * float(mean_dist.std())
return mean_dist <= threshold
def depth_edge_mask(depth: np.ndarray, *, rtol: float = 0.04,
kernel_size: int = 3) -> np.ndarray:
"""Boolean mask, ``True`` on depth-discontinuity pixels.
A pixel is an edge when the local depth range (max − min over a
``kernel_size`` window) exceeds ``rtol`` of its depth. Removing these
pixels *before* back-projection deletes the occlusion-boundary "veils"
that bridge foreground→background — the connected floaters that
statistical outlier rejection cannot catch (they have close neighbours).
Mirrors MoGe / utils3d ``depth_edge`` (``rtol`` path).
"""
from scipy.ndimage import maximum_filter, minimum_filter
local_range = (maximum_filter(depth, size=kernel_size)
- minimum_filter(depth, size=kernel_size))
return local_range > rtol * np.maximum(depth, 1e-6)