Skip to content

Transformation Classes¤

Each functional transform has its class counterpart, except for src.polar.train.data.transforms.functionals.pairwise_max_norm.
In almost all cases, the idea is to have a determinist functional implementation and a random class counterpart.

Common preprocessing¤

Center() ¤

Bases: Transform

See src.polar.train.data.transforms.functionals.center.

Source code in src/polar/train/data/transforms/transforms.py
16
17
def __init__(self) -> None:
    super().__init__()

Normalize() ¤

Bases: Transform

See src.polar.train.data.transforms.functionals.normalize.

Source code in src/polar/train/data/transforms/transforms.py
30
31
def __init__(self) -> None:
    super().__init__()

RandomSample(num_points: int) ¤

Bases: Transform

Sample a unique set of points in each batch element.

See src.polar.train.data.transforms.functionals.sample.

Parameters:

  • num_points (int) –

    Number of points to select per point clouds.

Source code in src/polar/train/data/transforms/transforms.py
46
47
48
49
50
51
52
53
def __init__(self, num_points: int) -> None:
    """_summary_

    Args:
        num_points (int): Number of points to select per point clouds.
    """
    super().__init__()
    self.num_points = num_points

SIM(3)¤

RandomTranslate(max_t: float = 0.0, p: float = 1) ¤

Bases: Transform

Translate each point cloud in a batch by a random unique value in \([-\text{max_t}, \text{max_t}]\).
See src.polar.train.data.transforms.functionals.translate.

Parameters:

  • max_t (float, default: 0.0 ) –

    Maximal norm of the randomly generated translations. Defaults to 0.

  • p (float, default: 1 ) –

    Probability to apply the random translations. Defaults to 1.

Source code in src/polar/train/data/transforms/transforms.py
76
77
78
79
80
81
82
83
84
85
def __init__(self, max_t: float = 0., p: float = 1) -> None:
    """_summary_

    Args:
        max_t (float, optional):
            Maximal norm of the randomly generated translations. Defaults to `0`.
        p (float, optional): Probability to apply the random translations. Defaults to `1`.
    """
    super().__init__(p)
    self.max_t = max_t

RandomRotate(min_angle: float = 0.0, max_angle: float = 180.0, p: float = 1) ¤

Bases: Transform

Rotate each point cloud in a batch by a random unique rotation.
See src.polar.train.data.transforms.functionals.rotate.

Warning

Angles are in degrees.

Parameters:

  • min_angle (float, default: 0.0 ) –

    Minimal relative angle between the identity and the generated rotations. Defaults to 0.

  • max_angle (float, default: 180.0 ) –

    Maximal relative angle between the identity and the generated rotations. Defaults to 180.

  • p (float, default: 1 ) –

    Probability to apply the rotations. Defaults to 1.

Source code in src/polar/train/data/transforms/transforms.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def __init__(self, min_angle: float = 0., max_angle: float = 180., p: float = 1) -> None:
    """_summary_

    !!! Warning
        Angles are in degrees.

    Args:
        min_angle (float, optional):
            Minimal relative angle between the identity and the generated rotations. Defaults to `0`.
        max_angle (float, optional):
            Maximal relative angle between the identity and the generated rotations. Defaults to `180`.
        p (float, optional): Probability to apply the rotations. Defaults to `1`.
    """
    super().__init__(p)
    self.min_angle = min_angle * torch.pi / 180
    self.max_angle = max_angle * torch.pi / 180
random_rotation(n: int, max_angle: float, min_angle: float = 0) -> Tensor staticmethod ¤

See Rodrigues Rotation Formula, section Matrix notation. The rotation through an angle \(\theta\) counterclockwise about the axis \(k = (k_x, k_y, k_z)\) is given by \(K = \begin{pmatrix} 0 & -k_z & k_y \\ k_z & 0 & -k_x \\ - k_y & k_x & 0\end{pmatrix}\) then \(R = I + \sin(\theta)K + (1 - \cos(\theta))K²\). \(R\) is an element of the Lie groupe \(SO(3)\) and \(K\) is an element of the Lie algebra \(\mathfrak{so}(3)\) generating that group. Note that \(R = \exp(\theta K)\).

Source code in src/polar/train/data/transforms/transforms.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@staticmethod
def random_rotation(n: int, max_angle: float, min_angle: float = 0) -> Tensor:
    r""" See [Rodrigues Rotation Formula](https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula),
        section Matrix notation.
    The rotation through an angle $\theta$ counterclockwise about the axis $k = (k_x, k_y, k_z)$ is given by
    $K = 
    \begin{pmatrix}
    0 &  -k_z &  k_y \\
    k_z & 0 & -k_x \\
    - k_y & k_x & 0\end{pmatrix}$
    then
    $R = I + \sin(\theta)K + (1 - \cos(\theta))K²$.
    $R$ is an element of the Lie groupe $SO(3)$ and $K$ is an element of the Lie algebra $\mathfrak{so}(3)$
    generating that group. Note that $R = \exp(\theta K)$.
    """
    axis = torch.randn(n, 3)
    axis /= torch.linalg.vector_norm(axis, dim=1, keepdim=True)
    angle = min_angle + torch.rand(n).squeeze() * (max_angle - min_angle)
    zero = torch.zeros(n)
    K = [[zero, -axis[:, 2], axis[:, 1]], [axis[:, 2], zero, -axis[:, 0]], [-axis[:, 1], axis[:, 0], zero]]
    K = torch.stack([torch.stack(k) for k in K]).permute(2, 0, 1)
    sin = torch.sin(angle)[:, None, None]
    cos = torch.cos(angle)[:, None, None]
    I = torch.eye(3).expand_as(K)
    R = I + sin * K + (1 - cos) * K.bmm(K)
    return R

RandomRigidMotion(max_t: float = 0.0, min_angle: float = 0.0, max_angle: float = 180.0, p: float = 1) ¤

Bases: Transform

Rotate & Translate each point cloud in a batch by a random unique rotation. Min/Max angles in degrees.

See src.polar.train.data.transforms.functionals.apply_rigid_motion.

Source code in src/polar/train/data/transforms/transforms.py
164
165
166
167
168
169
def __init__(
    self, max_t: float = 0., min_angle: float = 0., max_angle: float = 180., p: float = 1
) -> None:
    super().__init__(p)
    self.random_rotate = RandomRotate(min_angle, max_angle)
    self.random_translate = RandomTranslate(max_t)

RandomScale(min_scale: float = 0.0, max_scale: float = 1, p: float = 1) ¤

Bases: Transform

Randomly scale a batch of point clouds, with a unique scale factor for each point cloud.

See src.polar.train.data.transforms.functionals.scale.

Source code in src/polar/train/data/transforms/transforms.py
187
188
189
190
def __init__(self, min_scale: float = 0., max_scale: float = 1, p: float = 1) -> None:
    super().__init__(p)
    self.min_scale = min_scale
    self.max_scale = max_scale

Augment¤

RandomJit(sigma_max: float, p: float = 1) ¤

Bases: Transform

Add gaussian noise with unique random standard deviation per point cloud in a batch. For each batch element, the standard deviation \(\sigma\) is such that \(\sigma \sim \mathcal{U}(0, \text{sigma_max})\).

See src.polar.train.data.transforms.functionals.jit.

Source code in src/polar/train/data/transforms/transforms.py
215
216
217
def __init__(self, sigma_max: float, p: float = 1) -> None:
    super().__init__(p)
    self.sigma_max = sigma_max
apply(pointclouds: Tensor, sigmas: Tensor) -> Tensor ¤

Call src.polar.train.data.transforms.functionals.jit on a batch of point clouds, with params obtained by self.get_params(...).

Source code in src/polar/train/data/transforms/transforms.py
224
225
226
227
228
def apply(self, pointclouds: Tensor, sigmas: Tensor) -> Tensor:
    """ Call [src.polar.train.data.transforms.functionals.jit][] on a batch of point clouds, with
        params obtained by `self.get_params(...)`.
    """
    return F.jit(pointclouds, sigmas)

RandomPlaneCut(keep_ratio: float = 0.7, p: float = 1) ¤

Bases: Transform

Randomly generate a unique plane for each batch element and cut through it, such that a proportion keep_ratio is kept for each point cloud.

See src.polar.train.data.transforms.functionals.plane_cut.

Parameters:

  • keep_ratio (float, default: 0.7 ) –

    Proportion of each point cloud to keep. Defaults to 0.7.

  • p (float, default: 1 ) –

    Probablity to crop a given batch. Defaults to 1.

Source code in src/polar/train/data/transforms/transforms.py
239
240
241
242
243
244
245
246
247
def __init__(self, keep_ratio: float = 0.7, p: float = 1) -> None:
    """_summary_

    Args:
        keep_ratio (float, optional): Proportion of each point cloud to keep. Defaults to 0.7.
        p (float, optional): Probablity to crop a given batch. Defaults to 1.
    """
    super().__init__(p)
    self.keep_ratio = keep_ratio
uniform_sphere(num: int) -> Tensor staticmethod ¤

Uniform sampling on a 3-sphere (Source).

Parameters:

  • num (int) –

    Number of vectors to sample (or None if single). Defaults to None.

Returns:

  • Tensor ( Tensor ) –

    Random vector of size (num, 3) with unit norm. If num is None, returned value will have size (3,).

Source code in src/polar/train/data/transforms/transforms.py
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
@staticmethod
def uniform_sphere(num: int) -> Tensor:
    """ Uniform sampling on a 3-sphere ([Source](https://gist.github.com/andrewbolster/10274979)).

    Args:
        num (int, optional): Number of vectors to sample (or `None` if single). Defaults to `None`.

    Returns:
        Tensor: Random vector of size `(num, 3)` with unit norm. If `num` is `None`, returned value will have
            size `(3,)`.
    """
    phi = torch.distributions.Uniform(0, 2 * torch.pi).rsample(torch.Size((num,)))
    cos_theta = torch.distributions.Uniform(-1.0, 1.0).rsample(torch.Size((num,)))
    theta = torch.arccos(cos_theta)
    x = torch.sin(theta) * torch.cos(phi)
    y = torch.sin(theta) * torch.sin(phi)
    z = torch.cos(theta)
    return torch.stack((x, y, z), dim=-1)
get_params(**data: Tensor) -> dict ¤

Returns a batch of directions sampled in S3, of shape (batch_size, 3) from a dictionary whose values are batches of point clouds of shape (batch_size, num_points, spatial_dim).

Returns:

  • dict

    Dictionary with generated planes and keep ratio value.

Source code in src/polar/train/data/transforms/transforms.py
268
269
270
271
272
273
274
275
276
277
278
def get_params(self, **data: Tensor) -> dict:
    """ Returns a batch of directions sampled in S3, of shape `(batch_size, 3)` from a
        dictionary whose values are batches of point clouds of shape
        `(batch_size, num_points, spatial_dim)`.

    Returns:
        Dictionary with generated planes and keep ratio value.
    """
    batch_size = Transform.get_batch_size(**data)
    planes = RandomPlaneCut.uniform_sphere(batch_size)
    return dict(planes=planes, keep_ratio=self.keep_ratio)
apply(pointclouds: Tensor, planes: Tensor, keep_ratio: float) -> Tensor ¤

Call src.polar.train.data.transforms.functionals.plane_cut on a batch of point clouds, with params obtained by self.get_params(...).

Source code in src/polar/train/data/transforms/transforms.py
280
281
282
283
284
285
286
def apply(self, pointclouds: Tensor, planes: Tensor, keep_ratio: float) -> Tensor:
    """ Call [src.polar.train.data.transforms.functionals.plane_cut][] on a batch of point clouds, with params
    obtained by `self.get_params(...)`.
    """
    augmented_pointclouds, mask = F.plane_cut(pointclouds, planes, keep_ratio, return_mask=True)
    self.last_params['mask'] = mask
    return augmented_pointclouds