Overview
Inference strategies for scenes larger than one forward pass: tiling, windows, and refinement.
Modules:
-
infererβAbstract base class for test-time inference strategies.
-
knn_windowβKNN-window inference for large-scale point cloud segmentation.
-
part_refinementβNearest-neighbor refinement of part labels on top of another inferer's output.
-
potential_sphereβPotential-driven sphere voting for large-scale point cloud segmentation.
-
simpleβSingle forward-pass inference over the whole scene.
-
sliding_windowβSliding-window inference for large-scale point cloud segmentation.
-
ttaβTest-time augmentation (TTA) inferer.
-
voxel_partitionβK-pass voxel-partition inferer with per-point scatter-back aggregation.
Classes:
-
InfererβBase class for test-time inference strategies.
-
KNNWindowInfererβIterative KNN-window inferer for large-scale point cloud segmentation.
-
PartRefinementInfererβNearest-neighbor refinement of part labels on top of another inferer.
-
PotentialSphereInfererβPotential-driven sphere voting inferer for large-scale point cloud segmentation.
-
SimpleInfererβDirect call to the predictor on the whole scene.
-
SlidingWindowInfererβSliding-window inferer for large-scale point cloud segmentation.
-
TTAInfererβTest-time augmentation inferer.
-
VoxelPartitionInfererβ\(K\)-pass voxel-partition inferer with scatter-back aggregation.
Functions:
-
knn_window_inferenceβIterative KNN-window inference for large-scale point cloud segmentation.
-
part_refinement_inferenceβNearest-neighbor refinement of part labels on top of another inferer.
-
potential_sphere_inferenceβPotential-driven sphere voting for large-scale point cloud segmentation.
-
sliding_window_inferenceβSliding-window inference for large-scale point cloud segmentation.
-
simple_tta_transformsβDefault test-time augmentation transforms for indoor semantic segmentation.
Inferer [source] ΒΆ
Base class for test-time inference strategies.
An Inferer decouples how a model is run at test time from the model itself. The model only knows how to map a batch of points to per-point logits; the Inferer decides whether that happens in a single forward pass, over cropped windows, tiled blocks, or repeated under augmentation, and how partial predictions are stitched back into one per-point output. This keeps evaluation code identical regardless of scene size or protocol.
Subclasses implement forward; __call__ delegates to it, mirroring torch.nn.Module. To use an inferer, call the instance directly:
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import SimpleInferer
model = tp.create_model("pointnet.s3dis-area5").eval()
data = {"pos": torch.rand(4096, 3) * 10, "x": torch.rand(4096, 9), "batch": torch.zeros(4096, dtype=torch.long)}
inferer = SimpleInferer()
logits = inferer(data, predictor=lambda d: model(d["x"], d["pos"], d["batch"]))
data is a packed-batch dict (at minimum containing position and batch indices). predictor is any callable taking such a dict and returning per-point logits of shape \((N, C_\text{out})\). Inferers keep nothing of the scene, so one instance can be reused across scenes and wrapped by another inferer.
Inferers that draw random numbers take a seed. None follows the global generator, like the transforms do, so one torch.manual_seed (or seed_everything) seeds the whole evaluation. An int gives the inferer its own stream, offset by the number of calls the instance has made: successive calls (e.g. TTAInferer votes) draw different numbers, and a fresh instance replays the same sequence.
The softmax parameter controls whether partial predictions are converted to softmax probabilities before aggregation, and aggregate how they are combined:
| Inferer | softmax | Aggregated quantity |
|---|---|---|
SimpleInferer | False | predictor output as-is |
SlidingWindowInferer | False | raw logits per block; always probabilities ("max" / "vote") |
KNNWindowInferer | False | raw logits ("mean"); always probabilities ("ema") |
VoxelPartitionInferer | False | raw logits per pass |
PotentialSphereInferer | none | always an EMA of softmax probabilities |
TTAInferer | False | base output as-is |
PartRefinementInferer | none | one-hot refined labels of the base output's argmax |
When the input scene is empty (\(N = 0\)), inferers that never call the predictor return a \((0, 0)\) tensor (the channel count cannot be inferred without a predictor call); SimpleInferer and TTAInferer return whatever the predictor / base inferer produces for the empty input.
To add a custom strategy, subclass Inferer and implement forward:
from torch_pointcloud.inferers import Inferer
class MyInferer(Inferer):
def forward(self, data, predictor):
return predictor(data)
Methods:
-
forwardβRun the inference strategy.
forward abstractmethod [source] ΒΆ
Run the inference strategy.
Parameters:
-
data(Dict[str, Any]) βPacked-batch dict. Must contain
posandbatchkeys (the exact names are configurable on subclasses that exposepos_key/batch_key). -
predictor(Callable[[Dict[str, Any]], Tensor]) βCallable taking a packed dict and returning per-point logits of shape \((N, C_\text{out})\).
Returns:
-
TensorβPer-point output tensor of shape \((N, C_\text{out})\).
KNNWindowInferer [source] ΒΆ
KNNWindowInferer(
roi_num_points: int = 65536,
sw_batch_size: int = 1,
overlap: float = 0.5,
mode: WindowMode = "constant",
sigma_scale: float = 0.125,
aggregate: AggregateMode = "mean",
ema_smoothing: float = 0.95,
softmax: bool = False,
transform: Optional[
Callable[[Dict[str, Any]], Dict[str, Any]]
] = None,
pos_key: str = POS,
batch_key: str = BATCH,
inverse_key: Optional[str] = None,
progress: bool = False,
seed: Optional[int] = None,
)
Bases: Inferer
Iterative KNN-window inferer for large-scale point cloud segmentation.
Maintains a per-point coverage score and iteratively crops KNN windows around the least-covered points until all points are covered. Reuse the same instance across scenes; compose with TTAInferer for multi-augmentation averaging.
All parameters are forwarded verbatim to knn_window_inference, except seed, which is offset by the number of calls the instance has made: repeated calls (e.g. TTAInferer votes) draw different random numbers, and a fresh instance replays the same sequence.
Example
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import KNNWindowInferer
model = tp.create_model("pointnet.s3dis-area5").eval()
data = {
"pos": torch.rand(4096, 3) * 10,
"x": torch.rand(4096, 9),
"batch": torch.zeros(4096, dtype=torch.long),
}
# EMA aggregation: outputs calibrated probabilities directly.
inferer = KNNWindowInferer(roi_num_points=65_536, overlap=0.5, aggregate="ema")
probs = inferer(data, predictor=lambda d: model(d["x"], d["pos"], d["batch"]))
PartRefinementInferer [source] ΒΆ
PartRefinementInferer(
base: Inferer,
part_ids: Optional[Sequence[Sequence[int]]] = None,
min_count: int = 10,
num_neighbors: int = 11,
category_key: str = CATEGORY,
pos_key: str = POS,
batch_key: str = BATCH,
)
Bases: Inferer
Nearest-neighbor refinement of part labels on top of another inferer.
Takes the base inferer's per-point argmax and re-assigns the implausible part labels (rare parts, parts the shape's category does not own) by a nearest-neighbor majority vote, returning one-hot scores.
All parameters are forwarded verbatim to part_refinement_inference.
Example
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import PartRefinementInferer, SimpleInferer
model = tp.create_model("dgcnn.shapenetpart.an-tao").eval()
shapes = {
"pos": torch.rand(2048, 3),
"batch": torch.zeros(2048, dtype=torch.long),
"category": torch.nn.functional.one_hot(torch.tensor([0]), num_classes=16).float(),
}
inferer = PartRefinementInferer(SimpleInferer())
scores = inferer(shapes, predictor=lambda d: model(None, d["pos"], d["batch"], d["category"]))
labels = scores.argmax(dim=1)
PotentialSphereInferer [source] ΒΆ
PotentialSphereInferer(
radius: float,
num_votes: float = 10.0,
potential_size: Optional[float] = None,
jitter: Optional[float] = None,
inner_ratio: float = 0.7,
ema_smoothing: float = 0.95,
sw_batch_size: int = 1,
transform: Optional[
Callable[[Dict[str, Any]], Dict[str, Any]]
] = None,
pos_key: str = POS,
batch_key: str = BATCH,
inverse_key: Optional[str] = None,
progress: bool = False,
seed: Optional[int] = None,
)
Bases: Inferer
Potential-driven sphere voting inferer for large-scale point cloud segmentation.
Covers the scene with radius-defined spheres centered where a coarse potential grid is lowest and blends each sphere's softmax predictions into the running per-point scores by an exponential moving average, until every region has been covered about num_votes times.
All parameters are forwarded verbatim to potential_sphere_inference, except seed, which is offset by the number of calls the instance has made: repeated calls (e.g. TTAInferer votes) draw different random numbers, and a fresh instance replays the same sequence.
Example
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import PotentialSphereInferer
model = tp.create_model("pointnet.s3dis-area5").eval()
room = {"pos": torch.rand(4096, 3) * 3, "x": torch.rand(4096, 9), "batch": torch.zeros(4096, dtype=torch.long)}
inferer = PotentialSphereInferer(radius=1.5, num_votes=10.0)
probs = inferer(room, predictor=lambda d: model(d["x"], d["pos"], d["batch"]))
SimpleInferer [source] ΒΆ
Bases: Inferer
Direct call to the predictor on the whole scene.
The lightest possible Inferer. Use when the model can consume the entire point cloud in one forward pass (object classification, small scenes).
Parameters:
-
softmax(bool, default:False) βIf
True, softmax the predictor output over the last dim before returning it. The default returns the predictor output unchanged.
Example
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import SimpleInferer
model = tp.create_model("pointnet.s3dis-area5").eval()
data = {
"pos": torch.rand(4096, 3) * 10,
"x": torch.rand(4096, 9),
"batch": torch.zeros(4096, dtype=torch.long),
}
inferer = SimpleInferer()
logits = inferer(data, predictor=lambda d: model(d["x"], d["pos"], d["batch"]))
SlidingWindowInferer [source] ΒΆ
SlidingWindowInferer(
block_size: float,
overlap: float = 0.0,
mode: WindowMode = "constant",
sigma_scale: float = 0.125,
roi_num_points: Optional[int] = None,
sw_batch_size: int = 1,
softmax: bool = False,
aggregate: AggregateMode = "mean",
transform: Optional[
Callable[[Dict[str, Any]], Dict[str, Any]]
] = None,
dims: Optional[Sequence[int]] = None,
padding: float = 0.0,
pos_key: str = POS,
batch_key: str = BATCH,
block_bbox_key: str = BLOCK_BBOX,
inverse_key: Optional[str] = None,
progress: bool = False,
seed: Optional[int] = None,
)
Bases: Inferer
Sliding-window inferer for large-scale point cloud segmentation.
Places block centers on a regular grid and accumulates per-point predictions across all blocks that contain each point, blended by distance-based weights. At overlap=0 each point lands in exactly one block and the weight division is a no-op.
All parameters are forwarded verbatim to sliding_window_inference, except seed, which is offset by the number of calls the instance has made: repeated calls (e.g. TTAInferer votes) draw different random numbers, and a fresh instance replays the same sequence.
Example
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import SlidingWindowInferer
model = tp.create_model("pointnet.s3dis-area5").eval()
data = {
"pos": torch.rand(4096, 3) * 10,
"x": torch.rand(4096, 9),
"batch": torch.zeros(4096, dtype=torch.long),
}
# Non-overlapping partition: one prediction per point.
inferer = SlidingWindowInferer(block_size=6.0, overlap=0.0)
# 25 % overlap with Gaussian blending at boundaries:
inferer = SlidingWindowInferer(block_size=6.0, overlap=0.25, mode="gaussian")
probs = inferer(data, predictor=lambda d: model(d["x"], d["pos"], d["batch"]))
TTAInferer [source] ΒΆ
TTAInferer(
base: Inferer,
transforms: Union[
TransformFn, Sequence[TransformFn], None
] = None,
num_passes: Optional[int] = None,
include_identity: bool = False,
aggregate: AggregateMode = "mean",
ema_smoothing: float = 0.95,
softmax: bool = False,
pos_key: str = POS,
progress: bool = False,
)
Bases: Inferer
Test-time augmentation inferer.
Runs the wrapped base inferer once per augmentation pass and aggregates the per-point predictions across passes.
Three modes are supported:
- Single callable: re-sampled independently each pass. Use for random augmentations such as uniformly random rotation. Requires
num_passes. - Sequence of callables: each element is applied to exactly one pass in order. Use for a fixed view set (e.g. 8 evenly-spaced rotations).
num_passesis inferred from the sequence length. None: the input is left as-is and the base inferer is simply runnum_passestimes, voting over its own randomness (random sub-batches, random padding).
Parameters:
-
base(Inferer) βUnderlying
Infererinvoked once per pass. Any concrete inferer works:SimpleInferer(),SlidingWindowInferer(...),KNNWindowInferer(...). -
transforms(Union[TransformFn, Sequence[TransformFn], None], default:None) βSingle callable (re-sampled each pass), a sequence of callables for fixed views, or
Nonefor un-augmented passes. AnyDict[str, Any] -> Dict[str, Any]callable works, includingCompose. When a sequence is given,num_passesis ignored. -
num_passes(Optional[int], default:None) βNumber of TTA passes when
transformsis a single callable orNone. Must be \(\geq 1\). -
include_identity(bool, default:False) βIf
True, run one extra pass on the un-augmented input before the augmented passes (the "clean + N random views" voting protocol), so the total pass count isnum_passes + 1. -
aggregate(AggregateMode, default:'mean') βHow per-point predictions are combined across passes.
"mean"averages per-pass outputs."ema"maintains an exponential moving average of them. -
ema_smoothing(float, default:0.95) βEMA factor \(\alpha \in [0, 1)\) used when
aggregate="ema". -
softmax(bool, default:False) βIf
True, softmax each pass's output before aggregating. LeaveFalsewhen the base inferer already returns probabilities (e.g.SlidingWindowInferer(softmax=True)). -
pos_key(str, default:POS) βDict key for the position tensor (used for the empty-output fallback).
-
progress(bool, default:False) βIf
True, show atqdmprogress bar over the passes.
Example
A 4-pass TTA over random Z rotations and X/Y flips:
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import SlidingWindowInferer, TTAInferer
from torch_pointcloud.transforms import Compose, RandomFlip, RandomRotate
model = tp.create_model("pointnet.s3dis-area5").eval()
data = {
"pos": torch.rand(4096, 3) * 10,
"x": torch.rand(4096, 9),
"batch": torch.zeros(4096, dtype=torch.long),
}
base = SlidingWindowInferer(block_size=6.0)
aug = Compose([
RandomRotate(keys="pos", angle_range=(-180.0, 180.0), axis=2, p=1.0),
RandomFlip(keys="pos", axes=[0, 1], p=0.5),
])
inferer = TTAInferer(base=base, transforms=aug, num_passes=4, aggregate="mean")
probs = inferer(data, predictor=lambda d: model(d["x"], d["pos"], d["batch"]))
Enumerated 8-view TTA (ScanNet ablation):
from torch_pointcloud.inferers import SlidingWindowInferer, TTAInferer
from torch_pointcloud.transforms import Compose, RandomRotate
base = SlidingWindowInferer(block_size=6.0)
views = [Compose([RandomRotate(keys="pos", angle_range=(a, a), axis=2, p=1.0)])
for a in (0.0, 45.0, 90.0, 135.0, 180.0, 225.0, 270.0, 315.0)]
inferer = TTAInferer(base=base, transforms=views, aggregate="mean")
VoxelPartitionInferer [source] ΒΆ
VoxelPartitionInferer(
voxel_size: float,
transform: Optional[
Callable[[Dict[str, Any]], Dict[str, Any]]
] = None,
sw_batch_size: int = 1,
softmax: bool = False,
aggregate: Literal["mean", "sum"] = "mean",
pos_key: str = POS,
batch_key: str = BATCH,
inverse_key: Optional[str] = None,
seed: Optional[int] = None,
progress: bool = False,
)
Bases: Inferer
\(K\)-pass voxel-partition inferer with scatter-back aggregation.
Partitions each batch element's points into FNV voxel buckets at voxel_size and runs the predictor on \(K = \max_v c_v\) sub-clouds per element, where sub-cloud \(i\) picks the \((i \bmod c_v)\)-th point of every bucket. Per-sub-cloud logits are scatter-summed to original-point indices and divided by per-point participation counts; each point is picked \(\lfloor K / c_v \rfloor\) or \(\lfloor K / c_v \rfloor + 1\) times across the \(K\) passes, so every original point gets at least one prediction.
For test-time augmentation, wrap in TTAInferer: each TTA pass triggers a fresh \(K\)-pass voxel partition under that augmentation.
Predictions are scatter-summed in float64 for stable averaging across passes; the returned tensor is cast back to the predictor's output dtype. An empty scene (\(N = 0\)) returns a \((0, 0)\) tensor: the predictor is never called, so the channel count cannot be inferred.
Parameters:
-
voxel_size(float) βSide length of the FNV voxel partition (in the units of
pos). -
transform(Optional[Callable[[Dict[str, Any]], Dict[str, Any]]], default:None) βOptional per-sub-cloud callable applied after slicing each sub-cloud out of
data. Typical use: the per-sub-cloud part of the model's preprocessing (centering, grid coordinates, feature stacking). If it changes the row count (pad, voxelize, ...) it must record a source-to-predictor index map underinverse_keyso the inferer can gather predictions back to the sub-cloud's points. -
sw_batch_size(int, default:1) βNumber of sub-clouds packed into one predictor call via
collate.>1amortises FPS / radius costs on the GPU. -
softmax(bool, default:False) βIf
True, softmax each predictor output before scatter-summing. -
aggregate(Literal['mean', 'sum'], default:'mean') β"mean"divides each point's accumulated predictions by the number of sub-clouds it appeared in;"sum"returns the plain sum. The argmax is the same within one call, but underTTAInfererthe counts differ between views (each view is partitioned on its own augmented positions), so"sum"reproduces the reference protocols that add un-normalized probabilities over views and fragments. -
pos_key(str, default:POS) βDict key for the position tensor.
-
batch_key(str, default:BATCH) βDict key for the per-point batch index.
-
inverse_key(Optional[str], default:None) βDict key under which a row-altering
transformrecords a source-to-predictor long index map of shape \((N_\text{sub},)\) with values in \([0, N_\text{predictor})\). Any scene-level value at this key is dropped beforetransformruns, and the map is popped before the predictor is called. LeaveNonewhen the transform preserves row count. -
seed(Optional[int], default:None) βRNG seed for the per-pass index shuffle.
Nonedraws from the global generator, sotorch.manual_seedseeds the inferer together with the transforms. An int is offset by the number of calls the instance has made: repeated calls (e.g.TTAInfererviews) draw different shuffles, and a fresh instance replays the same sequence. -
progress(bool, default:False) βIf
True, show atqdmprogress bar per batch element.
Example
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import VoxelPartitionInferer
model, info = tp.create_model("pointnet.s3dis-area5", return_info=True)
model.eval()
room = {
"pos": torch.rand(4096, 3) * 4,
"color": torch.rand(4096, 3),
"norm_pos": torch.rand(4096, 3),
"batch": torch.zeros(4096, dtype=torch.long),
}
inferer = VoxelPartitionInferer(voxel_size=0.04, sw_batch_size=4, transform=info["transform"])
logits = inferer(room, predictor=lambda d: model(d["x"], d["pos"], d["batch"]))
knn_window_inference [source] ΒΆ
knn_window_inference(
data: Dict[str, Any],
*,
predictor: Callable[[Dict[str, Any]], Tensor],
roi_num_points: int = 65536,
sw_batch_size: int = 1,
overlap: float = 0.5,
mode: WindowMode = "constant",
sigma_scale: float = 0.125,
aggregate: AggregateMode = "mean",
ema_smoothing: float = 0.95,
softmax: bool = False,
transform: Optional[
Callable[[Dict[str, Any]], Dict[str, Any]]
] = None,
pos_key: str = POS,
batch_key: str = BATCH,
inverse_key: Optional[str] = None,
progress: bool = False,
seed: Optional[int] = None,
) -> Tensor
Iterative KNN-window inference for large-scale point cloud segmentation.
Maintains a per-point coverage score initialized to small random noise. Each iteration selects the sw_batch_size least-covered points as window centers, crops their \(k\) nearest neighbors, runs predictor on the packed crop, and accumulates per-point predictions weighted by distance to the center. The loop ends once every point's coverage score exceeds overlap.
aggregate controls how overlapping window predictions are combined:
"mean": accumulates distance-weighted logits and divides by total weight at the end, producing a weighted-average logit tensor."ema": per-update softmax EMA (\(\text{new} = \alpha \cdot \text{old} + (1 - \alpha) \cdot \text{softmax}(\text{logits})\)), outputting calibrated probabilities without a final softmax step.
Parameters:
-
data(Dict[str, Any]) βDict of per-point tensors. Must contain
pos(shape \((N, D)\)) andbatch(shape \((N,)\)). Any additional tensor whose first dim equals \(N\) (e.g.color,intensity,segment) is automatically sliced to the active window. Non-tensor entries and tensors with a different leading dim (scalar metadata) flow through unchanged. -
predictor(Callable[[Dict[str, Any]], Tensor]) βCallable taking a per-window data dict and returning per-point logits of shape \((M, C_\text{out})\), where \(M\) is the (packed) total point count of the windowed batch and the per-window batch index lives at
window[batch_key]. The output channel count \(C_\text{out}\) is inferred from the first call. -
roi_num_points(int, default:65536) βNumber of points per window (the window size \(k\)).
-
sw_batch_size(int, default:1) βNumber of windows packed into one
predictorcall. Higher values reduce launch overhead at the cost of more memory. -
overlap(float, default:0.5) βCoverage threshold in \((0, 1)\). The loop stops once every point has accumulated at least
overlappossibility mass. Higher values give more thorough coverage at the cost of more iterations.0.0is rejected: the initial coverage scores already satisfy a zero threshold, so no window would ever be predicted. -
mode(WindowMode, default:'constant') βDistance weighting for each window.
"constant"gives equal weight to every point;"gaussian"weights by \(\exp(-d^2 / 2\sigma^2)\) with \(\sigma = \text{sigma\_scale} \cdot \max_i d_i\)."gaussian"requiresaggregate="mean"; EMA updates ignore distance weights. -
sigma_scale(float, default:0.125) βGaussian sigma scale factor (only used when
mode="gaussian"). -
aggregate(AggregateMode, default:'mean') βHow predictions from overlapping windows are combined.
"mean": weighted-average logits (divide by total weight at end)."ema": softmax EMA (\(\text{new} = \alpha \cdot \text{old} + (1 - \alpha) \cdot \text{softmax}(\text{logits})\)); usesw_batch_size=1with EMA to match the reference evaluation protocol. -
ema_smoothing(float, default:0.95) βEMA factor \(\alpha \in [0, 1)\) used when
aggregate="ema". -
softmax(bool, default:False) βIf
True, softmax each window's logits before the weighted-mean accumulation, so overlapping windows average probabilities instead of raw logits. Ignored whenaggregate="ema", which always accumulates softmax probabilities. -
transform(Optional[Callable[[Dict[str, Any]], Dict[str, Any]]], default:None) βOptional callable applied to each window's data dict before the predictor (typical example:
T.Shift(keys=DataKeys.POS, method="centroid")). If it changes the row count (pad, voxelize, ...) it must record a source-to-predictor index map underinverse_keyso the inferer can gather predictions back to the window's points. -
pos_key(str, default:POS) βDict key for the position tensor.
-
batch_key(str, default:BATCH) βDict key for the per-point batch index.
-
inverse_key(Optional[str], default:None) βDict key under which a row-altering
transformrecords a source-to-predictor long index map of shape \((k,)\) with values in \([0, N_\text{predictor})\). Any scene-level value at this key is dropped beforetransformruns, and the map is popped before the predictor is called. LeaveNonewhen the transform preserves row count. -
progress(bool, default:False) βIf
True, show atqdmprogress bar per batch element. -
seed(Optional[int], default:None) βRNG seed for the per-point initial possibility scores.
Nonedraws from the global generator, sotorch.manual_seedseeds the inferer together with the transforms.
Returns:
-
TensorβPer-point output tensor of shape \((N, C_\text{out})\). Aggregation produces
-
Tensorβlogits when
aggregate="mean"(probabilities withsoftmax=True) -
Tensorβand probabilities when
aggregate="ema". An empty scene (\(N = 0\)) returns -
Tensorβa \((0, 0)\) tensor: the predictor is never called, so the channel count
-
Tensorβcannot be inferred.
part_refinement_inference [source] ΒΆ
part_refinement_inference(
data: Dict[str, Any],
*,
predictor: Callable[[Dict[str, Any]], Tensor],
base: Optional[Inferer] = None,
part_ids: Optional[Sequence[Sequence[int]]] = None,
min_count: int = 10,
num_neighbors: int = 11,
category_key: str = CATEGORY,
pos_key: str = POS,
batch_key: str = BATCH,
) -> Tensor
Nearest-neighbor refinement of part labels on top of another inferer.
Runs base, takes the per-point argmax, and for every shape re-assigns the labels that are implausible: a predicted part with fewer than min_count points, or a part the shape's category does not own. Each such label is refined in turn (ascending label order): its points take the majority label of their num_neighbors nearest points of the same shape, the label under refinement excluded from the vote, with the already-refined labels feeding the next votes. This is the post-processing of the PointNeXt ShapeNetPart protocol.
Parameters:
-
data(Dict[str, Any]) βDict of per-point tensors. Must contain
pos(shape \((N, D)\)),batch(shape \((N,)\)) and the per-shape category undercategory_key. -
predictor(Callable[[Dict[str, Any]], Tensor]) βCallable mapping a data dict to per-point part scores of shape \((N, C)\).
-
base(Optional[Inferer], default:None) βInferer running
predictor; defaults toSimpleInferer(one forward on the whole batch). -
part_ids(Optional[Sequence[Sequence[int]]], default:None) βPart labels owned by each category; defaults to the 16-category / 50-part ShapeNetPart table (
ShapeNetPart.seg_ids). -
min_count(int, default:10) βPredicted parts with fewer points than this are refined.
-
num_neighbors(int, default:11) βNumber of nearest neighbors (the point itself included) voting on the new label.
-
category_key(str, default:CATEGORY) βDict key of the per-shape category, one-hot \((B, K)\) or index \((B,)\).
-
pos_key(str, default:POS) βDict key for the position tensor.
-
batch_key(str, default:BATCH) βDict key for the per-point batch index.
Returns:
-
TensorβOne-hot refined labels of shape \((N, C)\), so a metric's argmax recovers the refined labels. An empty
-
Tensorβscene (\(N = 0\)) returns the base output unchanged.
potential_sphere_inference [source] ΒΆ
potential_sphere_inference(
data: Dict[str, Any],
*,
predictor: Callable[[Dict[str, Any]], Tensor],
radius: float,
num_votes: float = 10.0,
potential_size: Optional[float] = None,
jitter: Optional[float] = None,
inner_ratio: float = 0.7,
ema_smoothing: float = 0.95,
sw_batch_size: int = 1,
transform: Optional[
Callable[[Dict[str, Any]], Dict[str, Any]]
] = None,
pos_key: str = POS,
batch_key: str = BATCH,
inverse_key: Optional[str] = None,
progress: bool = False,
seed: Optional[int] = None,
) -> Tensor
Potential-driven sphere voting for large-scale point cloud segmentation.
The scene is covered by spheres of radius \(r\) whose centers are chosen where the cloud has been seen the least: a coarse grid of potentials (one scalar per potential_size cell, initialized with a small random value) tracks coverage, each sphere is centered on the cell with the lowest potential (plus a Gaussian jitter) and raises the potentials of the cells it covers by the Tukey window \((1 - d^2 / r^2)^2\). The predictor runs on every sphere and its softmax probabilities are blended into the running per-point scores by an exponential moving average, restricted to the points within inner_ratio \(\cdot r\) of the center where the sphere's context is complete. The loop stops once every cell's potential reaches num_votes, so each region has been predicted about that many times.
This is the test protocol of KPConv: Flexible and Deformable Convolution for Point Clouds (Thomas et al., 2019) (radius-defined input spheres, test_smooth EMA, potential sampling), and it composes with any model that consumes a packed sphere: the per-sphere transform sees the centered sphere dict and can add the reference's stochastic test-time augmentation and the model's feature stack. Points that no sphere reaches keep all-zero scores.
Parameters:
-
data(Dict[str, Any]) βDict of per-point tensors. Must contain
pos(shape \((N, D)\)) andbatch(shape \((N,)\)); extra per-point tensors are sliced to the active sphere automatically. -
predictor(Callable[[Dict[str, Any]], Tensor]) βCallable mapping a packed sphere dict to per-point logits of shape \((M, C)\).
-
radius(float) βSphere radius, in the units of
pos. -
num_votes(float, default:10.0) βPotential threshold ending the loop, i.e. the number of times every region is covered (KPConv reports its numbers at the first multiple of \(10\)).
-
potential_size(Optional[float], default:None) βCell size of the coarse potential grid. Defaults to
radius / 10. -
jitter(Optional[float], default:None) βStandard deviation of the Gaussian jitter added to each sphere center, clipped at
radius / 2. Defaults toradius / 10;0disables it. -
inner_ratio(float, default:0.7) βFraction of
radiusinside which the sphere's predictions are kept. -
ema_smoothing(float, default:0.95) βEMA factor \(\alpha \in [0, 1)\) of the score update \(\text{new} = \alpha \cdot \text{old} + (1 - \alpha) \cdot \text{softmax}(\text{logits})\).
-
sw_batch_size(int, default:1) βNumber of spheres packed into one predictor call. Centers are still drawn one at a time with the potentials updated in between, as the reference sampler does.
-
transform(Optional[Callable[[Dict[str, Any]], Dict[str, Any]]], default:None) βOptional per-sphere callable applied to the centered sphere dict before the predictor. The transform must keep positions centered on the sphere (the
inner_ratiomask is evaluated on the transformed positions, as the reference does). If it changes the row count (pad, voxelize, ...) it must record a source-to-predictor index map underinverse_keyso the inferer can gather predictions and the mask back to the sphere's points. -
pos_key(str, default:POS) βDict key for the position tensor.
-
batch_key(str, default:BATCH) βDict key for the per-point batch index.
-
inverse_key(Optional[str], default:None) βDict key under which a row-altering
transformrecords a source-to-predictor long index map of shape \((N_\text{sphere},)\) with values in \([0, N_\text{predictor})\). Any scene-level value at this key is dropped beforetransformruns, and the map is popped before the predictor is called. LeaveNonewhen the transform preserves row count. -
progress(bool, default:False) βIf
True, show atqdmprogress bar per batch element. -
seed(Optional[int], default:None) βRNG seed for the initial potentials and the center jitter.
Nonedraws from the global generator, sotorch.manual_seedseeds the inferer together with the transforms.
Returns:
-
TensorβPer-point score tensor of shape \((N, C)\): the EMA of softmax probabilities over the spheres covering
-
Tensorβeach point; points no sphere reaches keep all-zero scores. An empty scene (\(N = 0\)) returns a
-
Tensorβ\((0, 0)\) tensor: the predictor is never called, so the channel count cannot be inferred.
sliding_window_inference [source] ΒΆ
sliding_window_inference(
data: Dict[str, Any],
*,
predictor: Callable[[Dict[str, Any]], Tensor],
block_size: float,
overlap: float = 0.0,
mode: WindowMode = "constant",
sigma_scale: float = 0.125,
roi_num_points: Optional[int] = None,
sw_batch_size: int = 1,
softmax: bool = False,
aggregate: AggregateMode = "mean",
transform: Optional[
Callable[[Dict[str, Any]], Dict[str, Any]]
] = None,
dims: Optional[Sequence[int]] = None,
padding: float = 0.0,
pos_key: str = POS,
batch_key: str = BATCH,
block_bbox_key: str = BLOCK_BBOX,
inverse_key: Optional[str] = None,
progress: bool = False,
seed: Optional[int] = None,
) -> Tensor
Sliding-window inference for large-scale point cloud segmentation.
Places block centers on a regular grid with step \(\text{block\_size} \cdot (1 - \text{overlap})\) and calls predictor once per non-empty block. Each point's predictions from all covering blocks are combined by aggregate: a distance-weighted average ("mean"), the single most confident prediction ("max"), or a count of hard votes ("vote").
With overlap=0 and mode="constant", each point lands in exactly one block and the weight division is a no-op.
Parameters:
-
data(Dict[str, Any]) βDict of per-point tensors. Must contain
pos(shape \((N, D)\)) andbatch(shape \((N,)\)). Extra per-point tensors are sliced to the active block automatically. Non-tensor entries and tensors with a different leading dim flow through unchanged. -
predictor(Callable[[Dict[str, Any]], Tensor]) βCallable taking a per-block data dict and returning logits of shape \((M, C_\text{out})\), where \(M\) is the number of points in the block. The output channel count \(C_\text{out}\) is inferred from the first call.
-
block_size(float) βSide length of each cubic block, in the same units as
data[pos_key]. -
overlap(float, default:0.0) βFraction of
block_sizeshared between adjacent blocks, in \([0, 1)\).0.0gives a strict non-overlapping partition;0.5means adjacent block centers are spaced half a block apart. -
mode(WindowMode, default:'constant') βPer-point weight within each block.
"constant"gives equal weight to all points in the block."gaussian"weights by \(\exp(-d^2 / 2\sigma^2)\) where \(d\) is the distance to the block center across the tiled axes and \(\sigma = \text{sigma\_scale} \cdot \text{block\_size} \cdot \sqrt{D_\text{tiled}} / 2\) with \(D_\text{tiled}\) the number of tiled axes. -
sigma_scale(float, default:0.125) βGaussian sigma scale factor. Only used when
mode="gaussian". -
roi_num_points(Optional[int], default:None) βMaximum points in one batch element. A block with more is split into random sub-batches, each predicted exactly once per block pass.
Nonepasses the whole block as one element. Use it when the architecture needs a bounded or fixed \(N\): pair it with aDivisiblePad-styletransformthat pads each block to a multiple ofroi_num_pointsand writes its source-to-padded index map underinverse_key, and every element is then exactlyroi_num_pointslong. -
sw_batch_size(int, default:1) βNumber of elements collated into one
predictorcall. Purely a throughput knob: it changes the number of calls, never the output and never the size of an element. Set together withroi_num_points, a call holds at mostsw_batch_sizeelements of at mostroi_num_pointspoints each. Small blocks are dominated by per-call overhead, where a larger value is several times faster. Output-neutrality assumespredictorrespects the packed batch index; one that pools globally without it will differ atsw_batch_size > 1. Peak memory holdssw_batch_sizetransformed windows at once, so it scales withblock_sizerather than withroi_num_points. -
softmax(bool, default:False) βIf
True, softmax each block's logits before accumulating. UseTruewhen averaging predictions across multiple blocks or TTA passes. SetFalseto accumulate raw logits."max"and"vote"aggregation always read confidences off the softmax. -
aggregate(AggregateMode, default:'mean') βHow the predictions of the blocks covering a point are combined.
"mean": distance-weighted average of the (softmax) predictions."max": winner-takes-all, each point keeps the prediction of the block that is most confident about it (the PVCNN / PointCNN scene merge)."vote": each block casts one hard vote (its argmax) per point and the output holds the weighted vote fractions, soargmaxis the majority label (the PointNet++ protocol). -
transform(Optional[Callable[[Dict[str, Any]], Dict[str, Any]]], default:None) βOptional callable applied to each block's data dict before the predictor. The transform sees the whole block; if it changes the row count (pad, voxelize, ...) it must record a source-to-predictor index map under
inverse_keyso the inferer can gather predictions back to the original block points. -
dims(Optional[Sequence[int]], default:None) βAxes (indices into
pos's last dim) to tile.Nonetiles every axis (cubic blocks). Pass(0, 1)for 2D tiling that leaves the third axis spanning the full scene height. -
padding(float, default:0.0) βExtra margin (in
posunits) extending each block's membership on every tiled axis. Useful for including a thin context guard band. -
pos_key(str, default:POS) βDict key for the position tensor.
-
batch_key(str, default:BATCH) βDict key for the per-point batch index.
-
block_bbox_key(str, default:BLOCK_BBOX) βDict key under which the block bounding box is exposed to the
transformcallable. -
inverse_key(Optional[str], default:None) βDict key under which a row-altering
transformrecords a source-to-predictor long index map of shape \((N_\text{block},)\) with values in \([0, N_\text{window})\), where \(N_\text{block}\) is the pre-transform block size and \(N_\text{window}\) is the post-transform size. When set, any scene-level value at this key is dropped from the window beforetransformruns, so a registered pipeline'sinversenever becomes the prior the block map composes through; the inferer then pops the block map before calling the predictor and gathers predictions back to block-local rows. LeaveNonewhen the transform preserves row count, or when no transform is used. -
progress(bool, default:False) βIf
True, show atqdmprogress bar per batch element. -
seed(Optional[int], default:None) βRNG seed for sub-batch permutations when
roi_num_pointsis set.Nonedraws from the global generator, sotorch.manual_seedseeds the inferer together with the transforms.
Returns:
-
TensorβPer-point output tensor of shape \((N, C_\text{out})\): with
-
Tensorβaggregate="mean"a distance-weighted average of softmax probabilities -
Tensorβwhen
softmax=Trueor of raw logits whensoftmax=False; with -
Tensorβ"max"the most confident block's probabilities; with"vote"the -
Tensorβper-class vote fractions. An empty scene (\(N = 0\)) returns
-
Tensorβa \((0, 0)\) tensor: the predictor is never called, so the channel count
-
Tensorβcannot be inferred.
simple_tta_transforms [source] ΒΆ
simple_tta_transforms(
rotations: Sequence[float] = (0.0, 90.0, 180.0, 270.0),
scales: Sequence[float] = (1.0, 0.95, 1.05),
flip: bool = True,
axis: int = 2,
keys: KeyCollection = (POS, NORMAL),
scale_keys: KeyCollection = POS,
) -> List[Compose]
Default test-time augmentation transforms for indoor semantic segmentation.
One view per (scale, rotation) pair, scales outermost, plus one x/y flip view when flip is set: the defaults give the 13-view precise-evaluation protocol of the ScanNet / ScanNet200 benchmarks. Rotations and the flip act on every key in keys (positions and normals); scales act on scale_keys only. Missing keys are skipped, so the same views serve scenes with and without normals. A rotation of \(0\) or a scale of \(1\) adds no transform.
Parameters:
-
rotations(Sequence[float], default:(0.0, 90.0, 180.0, 270.0)) βRotation angles in degrees about
axis, applied at every scale. -
scales(Sequence[float], default:(1.0, 0.95, 1.05)) βIsotropic scale factors.
-
flip(bool, default:True) βAppend one view mirrored along the x and y axes.
-
axis(int, default:2) βRotation axis (
2is the up axis). -
keys(KeyCollection, default:(POS, NORMAL)) βKeys rotated and flipped.
-
scale_keys(KeyCollection, default:POS) βKeys scaled.
Returns:
-
List[Compose]βA list of
len(rotations) * len(scales) + flipcomposed transforms forTTAInferer.