Skip to content

mixing

Transforms that mix two samples into one.

Classes:

  • Mix3D –

    Concatenate two scenes into one, offsetting the second scene's instance ids.

  • LaserMix –

    Mix two LiDAR scans by swapping alternating inclination (pitch) bands.

  • PolarMix –

    Mix two LiDAR scans by swapping an azimuth sector and rotate-pasting instance-class points.

Mix3D [source] ΒΆ

Mix3D(
    keys: KeyCollection,
    instance_key: Optional[str] = "instance",
    ignore_index: int = -1,
    p: float = 1.0,
    seed: Optional[int] = None,
)

Bases: Transform, Randomizable

Concatenate two scenes into one, offsetting the second scene's instance ids.

Mix3D: Out-of-Context Data Augmentation for 3D Scenes

Every point-aligned key in keys is concatenated along the point dimension, so the mixed scene holds all points of both inputs. When instance_key is present in both scenes, the second scene's instance ids are shifted past the first scene's maximum id so the merged instances stay disjoint; points labeled ignore_index keep that label and are excluded from the offset.

Unlike the other pairwise mixes, Mix3D keeps all points of both scenes, so the mixed scene has roughly twice as many points as either input.

Mix3D before / after

Parameters:

  • keys (KeyCollection) –

    Point-aligned keys concatenated jointly (e.g. pos, color, normal, segment).

  • instance_key (Optional[str], default: 'instance' ) –

    Key of per-point instance ids to offset, or None to skip instance handling.

  • ignore_index (int, default: -1 ) –

    Instance id treated as "no instance" (kept as-is, ignored by the offset).

  • p (float, default: 1.0 ) –

    Probability of applying the mix; below it the first scene is returned unchanged.

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

    Seed for the probability draw; None draws from the global generator (see Randomizable).

Shape
  • each key in keys: \((N, \ldots)\) and \((M, \ldots)\) inputs, \((N + M, \ldots)\) output.
Example
import torch
import torch_pointcloud.transforms as T

a = {"pos": torch.randn(100, 3), "segment": torch.randint(0, 10, (100,))}
b = {"pos": torch.randn(120, 3), "segment": torch.randint(0, 10, (120,))}
mix = T.Mix3D(keys=("pos", "segment"), instance_key=None)
out = mix(a, b)
print(out["pos"].shape)

LaserMix [source] ΒΆ

LaserMix(
    keys: KeyCollection,
    num_areas: Sequence[int],
    pitch_range: Tuple[float, float],
    pos_key: str = "pos",
    p: float = 1.0,
    seed: Optional[int] = None,
)

Bases: Transform, Randomizable

Mix two LiDAR scans by swapping alternating inclination (pitch) bands.

LaserMix for Semi-Supervised LiDAR Semantic Segmentation

Both scans are partitioned into num_areas inclination bands (one count is drawn per call), and alternating bands are taken from each scan so the mixed scene tiles the full field of view. Every key in keys is masked with the same per-scan selection, keeping per-point correspondence.

See Also

torch_pointcloud.transforms.functional.laser_mix_masks

LaserMix before / after

Parameters:

  • keys (KeyCollection) –

    Point-aligned keys masked jointly (must include pos_key).

  • num_areas (Sequence[int]) –

    Candidate band counts; one is sampled uniformly per call.

  • pitch_range (Tuple[float, float]) –

    Inclination range (min, max) in degrees.

  • pos_key (str, default: 'pos' ) –

    Key of the coordinates used to compute inclination bands.

  • p (float, default: 1.0 ) –

    Probability of applying the mix; below it the first scene is returned unchanged.

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

    Seed for the band count, parity and probability draws; None draws from the global generator (see Randomizable).

Shape
  • each key in keys: \((N, \ldots)\) and \((M, \ldots)\) inputs, \((N' + M', \ldots)\) output.
Example
import torch
import torch_pointcloud.transforms as T

a = {"pos": torch.randn(100, 3), "segment": torch.randint(0, 10, (100,))}
b = {"pos": torch.randn(120, 3), "segment": torch.randint(0, 10, (120,))}
mix = T.LaserMix(keys=("pos", "segment"), num_areas=(3, 4, 5, 6), pitch_range=(-25.0, 3.0))
out = mix(a, b)

PolarMix [source] ΒΆ

PolarMix(
    keys: KeyCollection,
    instance_classes: Sequence[int],
    swap_ratio: float = 0.5,
    rotate_paste_ratio: float = 1.0,
    pos_key: str = "pos",
    segment_key: str = "segment",
    p: float = 1.0,
    seed: Optional[int] = None,
)

Bases: Transform, Randomizable

Mix two LiDAR scans by swapping an azimuth sector and rotate-pasting instance-class points.

PolarMix: A General Data Augmentation Technique for LiDAR Point Clouds

Two independent sub-augmentations run per call. With probability swap_ratio, a random azimuth half-sector of the first scan is replaced by the same sector of the second scan. With probability rotate_paste_ratio, points of the second scan whose segment_key label is in instance_classes are rotated by a random angle about the up axis and appended. Only pos_key is rotated for the pasted points; the other keys are copied unchanged.

See Also

torch_pointcloud.transforms.functional.polar_mix_masks

PolarMix before / after

Parameters:

  • keys (KeyCollection) –

    Point-aligned keys masked and concatenated jointly (must include pos_key).

  • instance_classes (Sequence[int]) –

    Semantic labels whose points are rotate-pasted from the second scan.

  • swap_ratio (float, default: 0.5 ) –

    Probability of swapping the azimuth sector.

  • rotate_paste_ratio (float, default: 1.0 ) –

    Probability of rotate-pasting the instance-class points.

  • pos_key (str, default: 'pos' ) –

    Key of the coordinates used to compute azimuth sectors and to rotate pasted points.

  • segment_key (str, default: 'segment' ) –

    Key of per-point semantic labels used to select the instance classes.

  • p (float, default: 1.0 ) –

    Probability of applying the mix; below it the first scene is returned unchanged.

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

    Seed for the sector, rotation and probability draws; None draws from the global generator (see Randomizable).

Shape
  • each key in keys: \((N, \ldots)\) and \((M, \ldots)\) inputs, \((K, \ldots)\) output.
Example
import torch
import torch_pointcloud.transforms as T

a = {"pos": torch.randn(100, 3), "segment": torch.randint(0, 10, (100,))}
b = {"pos": torch.randn(120, 3), "segment": torch.randint(0, 10, (120,))}
mix = T.PolarMix(keys=("pos", "segment"), instance_classes=(1, 2, 3))
out = mix(a, b)

laser_mix_masks [source] ΒΆ

laser_mix_masks(
    pos: Tensor,
    other_pos: Tensor,
    num_areas: int,
    pitch_range: Tuple[float, float],
    generator: Optional[Generator] = None,
) -> Tuple[Tensor, Tensor]

Return keep-masks that swap alternating inclination (pitch) bands between two LiDAR scans.

Each point's inclination is \(\phi = \arctan2(z, \sqrt{x^2 + y^2})\) in degrees. The range pitch_range is split into num_areas equal bands; a random parity picks whether the even or odd bands are kept from the first scan, with the complementary bands kept from the second. The mixed scene is torch.cat([pos[mask], other_pos[other_mask]]), so the two masks tile the sky.

Parameters:

  • pos (Tensor) –

    Coordinates of the first scan of shape \((N, 3)\).

  • other_pos (Tensor) –

    Coordinates of the second scan of shape \((M, 3)\).

  • num_areas (int) –

    Number of inclination bands to split pitch_range into.

  • pitch_range (Tuple[float, float]) –

    Inclination range (min, max) in degrees.

  • generator (Optional[Generator], default: None ) –

    Random generator for reproducibility.

Returns:

  • Tensor –

    A tuple (mask, other_mask) of boolean tensors of shape \((N,)\) and \((M,)\) that select the

  • Tensor –

    points kept from pos and from other_pos respectively.

Shape
  • pos: \((N, 3)\)
  • other_pos: \((M, 3)\)
  • output: \((N,)\) and \((M,)\)

Raises:

  • ValueError –

    If num_areas is not positive.

Example
import torch
from torch_pointcloud.transforms.functional import laser_mix_masks

pos = torch.randn(100, 3)
other = torch.randn(120, 3)
g = torch.Generator().manual_seed(0)
mask, other_mask = laser_mix_masks(pos, other, num_areas=4, pitch_range=(-25.0, 3.0), generator=g)
mixed = torch.cat([pos[mask], other[other_mask]], dim=0)

polar_mix_masks [source] ΒΆ

polar_mix_masks(
    pos: Tensor,
    other_pos: Tensor,
    generator: Optional[Generator] = None,
) -> Tuple[Tensor, Tensor]

Return keep-masks that swap a random azimuth half-sector between two LiDAR scans.

Each point's azimuth is \(\theta = \arctan2(y, x)\). A random start angle in \([-\pi, \pi)\) defines a half-circle sector \([\theta_0, \theta_0 + \pi)\) that wraps around the \(\pm\pi\) seam, so half of the azimuth range is swapped regardless of the start angle. Points of the first scan outside the sector are kept, and points of the second scan inside the sector are added, so the mixed scene is torch.cat([pos[mask], other_pos[other_mask]]).

Parameters:

  • pos (Tensor) –

    Coordinates of the first scan of shape \((N, 3)\).

  • other_pos (Tensor) –

    Coordinates of the second scan of shape \((M, 3)\).

  • generator (Optional[Generator], default: None ) –

    Random generator for reproducibility.

Returns:

  • Tensor –

    A tuple (mask, other_mask) of boolean tensors of shape \((N,)\) and \((M,)\) that select the

  • Tensor –

    points kept from pos and pasted from other_pos respectively.

Shape
  • pos: \((N, 3)\)
  • other_pos: \((M, 3)\)
  • output: \((N,)\) and \((M,)\)
Example
import torch
from torch_pointcloud.transforms.functional import polar_mix_masks

pos = torch.randn(100, 3)
other = torch.randn(120, 3)
g = torch.Generator().manual_seed(0)
mask, other_mask = polar_mix_masks(pos, other, generator=g)
mixed = torch.cat([pos[mask], other[other_mask]], dim=0)