Skip to content

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:

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] ΒΆ

forward(
    data: Dict[str, Any],
    predictor: Callable[[Dict[str, Any]], Tensor],
) -> Tensor

Run the inference strategy.

Parameters:

  • data (Dict[str, Any]) –

    Packed-batch dict. Must contain pos and batch keys (the exact names are configurable on subclasses that expose pos_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] ΒΆ

SimpleInferer(softmax: bool = False)

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_passes is inferred from the sequence length.
  • None: the input is left as-is and the base inferer is simply run num_passes times, voting over its own randomness (random sub-batches, random padding).

Parameters:

  • base (Inferer) –

    Underlying Inferer invoked 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 None for un-augmented passes. Any Dict[str, Any] -> Dict[str, Any] callable works, including Compose. When a sequence is given, num_passes is ignored.

  • num_passes (Optional[int], default: None ) –

    Number of TTA passes when transforms is a single callable or None. 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 is num_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. Leave False when 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 a tqdm progress 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 under inverse_key so 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. >1 amortises 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 under TTAInferer the 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 transform records 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 before transform runs, and the map is popped before the predictor is called. Leave None when the transform preserves row count.

  • seed (Optional[int], default: None ) –

    RNG seed for the per-pass index shuffle. None draws from the global generator, so torch.manual_seed seeds the inferer together with the transforms. An int is offset by the number of calls the instance has made: repeated calls (e.g. TTAInferer views) draw different shuffles, and a fresh instance replays the same sequence.

  • progress (bool, default: False ) –

    If True, show a tqdm progress 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)\)) and batch (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 predictor call. 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 overlap possibility mass. Higher values give more thorough coverage at the cost of more iterations. 0.0 is 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" requires aggregate="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})\)); use sw_batch_size=1 with 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 when aggregate="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 under inverse_key so 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 transform records 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 before transform runs, and the map is popped before the predictor is called. Leave None when the transform preserves row count.

  • progress (bool, default: False ) –

    If True, show a tqdm progress bar per batch element.

  • seed (Optional[int], default: None ) –

    RNG seed for the per-point initial possibility scores. None draws from the global generator, so torch.manual_seed seeds 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 with softmax=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 under category_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 to SimpleInferer (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)\)) and batch (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 to radius / 10; 0 disables it.

  • inner_ratio (float, default: 0.7 ) –

    Fraction of radius inside 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_ratio mask 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 under inverse_key so 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 transform records 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 before transform runs, and the map is popped before the predictor is called. Leave None when the transform preserves row count.

  • progress (bool, default: False ) –

    If True, show a tqdm progress bar per batch element.

  • seed (Optional[int], default: None ) –

    RNG seed for the initial potentials and the center jitter. None draws from the global generator, so torch.manual_seed seeds 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)\)) and batch (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_size shared between adjacent blocks, in \([0, 1)\). 0.0 gives a strict non-overlapping partition; 0.5 means 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. None passes the whole block as one element. Use it when the architecture needs a bounded or fixed \(N\): pair it with a DivisiblePad-style transform that pads each block to a multiple of roi_num_points and writes its source-to-padded index map under inverse_key, and every element is then exactly roi_num_points long.

  • sw_batch_size (int, default: 1 ) –

    Number of elements collated into one predictor call. Purely a throughput knob: it changes the number of calls, never the output and never the size of an element. Set together with roi_num_points, a call holds at most sw_batch_size elements of at most roi_num_points points each. Small blocks are dominated by per-call overhead, where a larger value is several times faster. Output-neutrality assumes predictor respects the packed batch index; one that pools globally without it will differ at sw_batch_size > 1. Peak memory holds sw_batch_size transformed windows at once, so it scales with block_size rather than with roi_num_points.

  • softmax (bool, default: False ) –

    If True, softmax each block's logits before accumulating. Use True when averaging predictions across multiple blocks or TTA passes. Set False to 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, so argmax is 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_key so 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. None tiles 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 pos units) 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 transform callable.

  • inverse_key (Optional[str], default: None ) –

    Dict key under which a row-altering transform records 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 before transform runs, so a registered pipeline's inverse never 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. Leave None when the transform preserves row count, or when no transform is used.

  • progress (bool, default: False ) –

    If True, show a tqdm progress bar per batch element.

  • seed (Optional[int], default: None ) –

    RNG seed for sub-batch permutations when roi_num_points is set. None draws from the global generator, so torch.manual_seed seeds 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=True or of raw logits when softmax=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 (2 is 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) + flip composed transforms for TTAInferer.

Example
from torch_pointcloud.inferers import TTAInferer, VoxelPartitionInferer, simple_tta_transforms

inferer = TTAInferer(
    base=VoxelPartitionInferer(voxel_size=0.02, softmax=True, aggregate="sum"),
    transforms=simple_tta_transforms(),
)